diff --git a/apps/boko-framework/src/Boko.bux b/apps/boko-framework/src/Boko.bux index 27ad29a..15c0e5b 100644 --- a/apps/boko-framework/src/Boko.bux +++ b/apps/boko-framework/src/Boko.bux @@ -5,7 +5,7 @@ module Boko { import Std::Io::{PrintLine, Print, PrintInt}; import Std::Net::{Net_Create, Net_SetReuse, Net_Bind, Net_Listen, Net_Accept, Net_Send, Net_Recv, Net_Close, Net_LastError}; -import Std::String::{String_Len, String_Eq, String_StartsWith, String_Contains}; +import Std::String::{String_Len, String_Eq, String_StartsWith, String_Contains, String_Offset}; import Std::Task::{Task_Join, TaskHandle}; extern func bux_alloc(size: uint) -> *void; @@ -239,8 +239,8 @@ func Query_Parse(req: *Request, queryString: String) { let eqPos: String = bux_strstr(pair, "="); var key: String = pair; var value: String = ""; - if eqPos as uint != 0 { - let keyLen: uint = eqPos as uint - pair as uint; + if String_Len(eqPos) > 0 { + let keyLen: uint = String_Offset(eqPos, pair); key = bux_str_slice(pair, 0, keyLen); let valStart: uint = keyLen + 1; let pairLen: uint = bux_strlen(pair); @@ -267,15 +267,15 @@ func Request_Parse(raw: String) -> Request { req.queryCount = 0; req.pathParamCount = 0; - if raw as uint == 0 { return req; } + if String_Len(raw) == 0 { return req; } let rawLen: uint = bux_strlen(raw); if rawLen == 0 { return req; } // Find header/body boundary let boundary: String = bux_strstr(raw, "\r\n\r\n"); var headerLen: uint = rawLen; - if boundary as uint != 0 { - headerLen = boundary as uint - raw as uint; + if String_Len(boundary) > 0 { + headerLen = String_Offset(boundary, raw); let bodyStart: uint = headerLen + 4; if bodyStart < rawLen { req.body = bux_str_slice(raw, bodyStart, rawLen - bodyStart); @@ -299,8 +299,8 @@ func Request_Parse(raw: String) -> Request { let fullPath: String = bux_str_split_part(requestLine, " ", 1); // Split path and query string let qmark: String = bux_strstr(fullPath, "?"); - if qmark as uint != 0 { - let pathLen: uint = qmark as uint - fullPath as uint; + if String_Len(qmark) > 0 { + let pathLen: uint = String_Offset(qmark, fullPath); req.path = bux_str_slice(fullPath, 0, pathLen); let qsStart: uint = pathLen + 1; let fullLen: uint = bux_strlen(fullPath); @@ -322,8 +322,8 @@ func Request_Parse(raw: String) -> Request { while i < lineCount { let line: String = bux_str_split_part(headerBlock, "\r\n", i); let colonPos: String = bux_strstr(line, ": "); - if colonPos as uint != 0 { - let keyLen: uint = colonPos as uint - line as uint; + if String_Len(colonPos) > 0 { + let keyLen: uint = String_Offset(colonPos, line); let key: String = bux_str_slice(line, 0, keyLen); let valStart: uint = keyLen + 2; let lineLen: uint = bux_strlen(line); @@ -378,8 +378,8 @@ func Path_Match(pattern: String, path: String, req: *Request) -> bool { let braceOpen: uint = 1; // skip "{" let braceClose: String = bux_strstr(patPart, "}"); var nameLen: uint = 0; - if braceClose as uint != 0 { - nameLen = braceClose as uint - patPart as uint - 1; + if String_Len(braceClose) > 0 { + nameLen = String_Offset(braceClose, patPart) - 1; } let name: String = bux_str_slice(patPart, braceOpen, nameLen); req.pathParamKeys[req.pathParamCount] = name; @@ -416,7 +416,7 @@ func Boko_Router(req: Request) -> Response; // ============================================================================= func App_HandleConnection(clientFd: int) { let raw: String = Net_Recv(clientFd, 8192); - if raw as uint == 0 { return; } + if String_Len(raw) == 0 { return; } if bux_strlen(raw) == 0 { return; } let req: Request = Request_Parse(raw); diff --git a/apps/jwt-pitbul/src/Main.bux b/apps/jwt-pitbul/src/Main.bux index 8d6d0aa..36f7d77 100644 --- a/apps/jwt-pitbul/src/Main.bux +++ b/apps/jwt-pitbul/src/Main.bux @@ -96,7 +96,7 @@ func ResolveKey(alg: JwtAlg, keyArg: String) -> String { alg.tag == JwtAlg_ES256 || alg.tag == JwtAlg_ES384 { if bux_file_exists(keyArg) != 0 { let pem: String = bux_read_file(keyArg); - if pem as uint != 0 && String_Len(pem) > 0 { + if String_Len(pem) > 0 && String_Len(pem) > 0 { return pem; } Print("ERROR: could not read PEM file: "); diff --git a/apps/nexus/src/Main.bux b/apps/nexus/src/Main.bux index dc54019..ad4dcf5 100644 --- a/apps/nexus/src/Main.bux +++ b/apps/nexus/src/Main.bux @@ -9,7 +9,7 @@ module Main { // ============================================================================= import Std::Io::{PrintLine, Print, PrintInt}; import Std::Net::{Net_Create, Net_SetReuse, Net_Bind, Net_Listen, Net_Accept, Net_Send, Net_Recv, Net_Close, Net_LastError}; -import Std::String::{String_Len, String_Eq, String_StartsWith, String_EndsWith, String_Contains, String_Trim, String_FromInt}; +import Std::String::{String_Len, String_Eq, String_StartsWith, String_EndsWith, String_Contains, String_Trim, String_FromInt, String_Offset}; import Std::Task::{Task_Join, TaskHandle}; // ============================================================================= @@ -170,15 +170,15 @@ func Http_ParseRequest(raw: String) -> HttpRequest { RequestHeader_Init(&req); // Guard: null/empty input - if raw as uint == 0 { return req; } + if String_Len(raw) == 0 { return req; } let rawLen: uint = bux_strlen(raw); if rawLen == 0 { return req; } // Find \r\n\r\n separating headers from body let boundary: String = bux_strstr(raw, "\r\n\r\n"); var headerLen: uint = rawLen; - if boundary as uint != 0 { - headerLen = boundary as uint - raw as uint; + if String_Len(boundary) > 0 { + headerLen = String_Offset(boundary, raw); let bodyStart: uint = headerLen + 4; if bodyStart < rawLen { req.body = bux_str_slice(raw, bodyStart, rawLen - bodyStart); @@ -198,7 +198,7 @@ func Http_ParseRequest(raw: String) -> HttpRequest { if bux_strlen(requestLine) > 0 { req.method = Http_ParseMethod(bux_str_split_part(requestLine, " ", 0)); req.path = bux_str_split_part(requestLine, " ", 1); - if String_Eq(req.path, "") || req.path as uint == 0 { + if String_Eq(req.path, "") || req.String_Len(path) == 0 { req.path = "/"; } } @@ -215,8 +215,8 @@ func Http_ParseRequest(raw: String) -> HttpRequest { var key: String = ""; var value: String = ""; - if colonPos as uint != 0 { - let keyLen: uint = colonPos as uint - line as uint; + if String_Len(colonPos) > 0 { + let keyLen: uint = String_Offset(colonPos, line); key = bux_str_slice(line, 0, keyLen); let valStart: uint = keyLen + 2; if valStart < lineLen { @@ -225,8 +225,8 @@ func Http_ParseRequest(raw: String) -> HttpRequest { } else { // Try ":" without space let colonPos2: String = bux_strstr(line, ":"); - if colonPos2 as uint != 0 { - let keyLen: uint = colonPos2 as uint - line as uint; + if String_Len(colonPos2) > 0 { + let keyLen: uint = String_Offset(colonPos2, line); key = bux_str_slice(line, 0, keyLen); let valStart: uint = keyLen + 1; if valStart < lineLen { @@ -341,7 +341,7 @@ func ServeStaticFile(requestPath: String) -> HttpResponse { // Read and serve let content: String = bux_read_file(fullPath); - if content as uint == 0 { + if String_Len(content) == 0 { return Http_NewResponse(500, "text/plain; charset=utf-8", "Internal Server Error"); } @@ -444,7 +444,7 @@ func HandleConnection(clientFd: int) { let raw: String = Net_Recv(clientFd, 8192); // Empty read → client disconnected - if raw as uint == 0 { return; } + if String_Len(raw) == 0 { return; } if bux_strlen(raw) == 0 { return; } // HTTP/2 connection preface detection diff --git a/apps/simpledb/src/Main.bux b/apps/simpledb/src/Main.bux index 2a4239f..650d851 100644 --- a/apps/simpledb/src/Main.bux +++ b/apps/simpledb/src/Main.bux @@ -2,7 +2,7 @@ module Main { import Std::Io::{PrintLine, Print, PrintInt, ReadFile, WriteFile, FileExists}; import Std::Os::{Os_ArgsCount, Os_Args}; -import Std::String::{String_Len, String_Eq, String_StartsWith, String_Find, String_Slice}; +import Std::String::{String_Len, String_Eq, String_StartsWith, String_Find, String_Slice, String_Offset}; extern func bux_strlen(s: String) -> uint; extern func bux_strstr(haystack: String, needle: String) -> String; @@ -46,7 +46,7 @@ func DB_Load(self: *Database) -> bool { } let raw: String = ReadFile(self.path); - if raw as uint == 0 || bux_strlen(raw) == 0 { + if String_Len(raw) == 0 || bux_strlen(raw) == 0 { self.loaded = true; return true; } @@ -76,7 +76,7 @@ func DB_Get(self: *Database, key: String) -> String { let line: String = bux_str_split_part(raw, "\n", i); if String_StartsWith(line, key) { let eqPos: String = String_Find(line, "="); - if eqPos as uint != 0 { + if String_Len(eqPos) > 0 { let keyLen: uint = bux_strlen(key); let valStart: uint = keyLen + 1; let lineLen: uint = bux_strlen(line); @@ -102,7 +102,7 @@ func DB_Set(self: *Database, key: String, value: String) { if bux_strlen(line) > 0 { if String_StartsWith(line, key) { let eqPos: String = String_Find(line, "="); - if eqPos as uint != 0 { + if String_Len(eqPos) > 0 { bux_sb_append(newSb, key); SB_AppendChar(newSb, 61); bux_sb_append(newSb, value); @@ -144,7 +144,7 @@ func DB_Del(self: *Database, key: String) { if bux_strlen(line) > 0 { if String_StartsWith(line, key) { let eqPos: String = String_Find(line, "="); - if eqPos as uint != 0 { + if String_Len(eqPos) > 0 { delCount = delCount + 1; } else { bux_sb_append(newSb, line); @@ -188,8 +188,8 @@ func DB_Keys(self: *Database) -> *String { let line: String = bux_str_split_part(raw, "\n", i); if bux_strlen(line) > 0 { let eqPos: String = String_Find(line, "="); - if eqPos as uint != 0 { - let keyLen: uint = eqPos as uint - line as uint; + if String_Len(eqPos) > 0 { + let keyLen: uint = String_Offset(eqPos, line); keys[found] = bux_str_slice(line, 0, keyLen); found = found + 1; } diff --git a/lib/String.bux b/lib/String.bux index 27e49fb..f7787f3 100644 --- a/lib/String.bux +++ b/lib/String.bux @@ -8,6 +8,8 @@ extern func bux_strcat(dest: *char8, src: String) -> *char8; extern func bux_strncpy(dest: *char8, src: String, n: uint) -> *char8; extern func bux_strstr(haystack: String, needle: String) -> String; extern func bux_str_contains(haystack: String, needle: String) -> int; +extern func bux_str_offset(pos: String, base: String) -> uint; +extern func bux_str_is_null(s: String) -> int; extern func bux_str_slice(s: String, start: uint, len: uint) -> String; extern func bux_str_trim_left(s: String) -> String; extern func bux_str_trim_right(s: String) -> String; @@ -32,6 +34,10 @@ func String_Len(s: String) -> uint { return bux_strlen(s); } +func String_IsNull(s: String) -> bool { + return bux_str_is_null(s) != 0; +} + func String_Eq(a: String, b: String) -> bool { return bux_strcmp(a, b) == 0; } @@ -170,13 +176,17 @@ func String_Find(haystack: String, needle: String) -> String { return bux_strstr(haystack, needle); } +func String_Offset(pos: String, base: String) -> uint { + return bux_str_offset(pos, base); +} + func String_Replace(s: String, old: String, new: String) -> String { let pos: String = bux_strstr(s, old); - if pos as uint == 0 { + if String_IsNull(pos) { return s; } let oldLen: uint = bux_strlen(old); - let prefixLen: uint = pos as uint - s as uint; + let prefixLen: uint = String_Offset(pos, s); let prefix: String = bux_str_slice(s, 0, prefixLen); let suffix: String = bux_str_slice(s, prefixLen + oldLen, bux_strlen(s) - prefixLen - oldLen); let temp: String = String_Concat(prefix, new); diff --git a/rt/runtime.c b/rt/runtime.c index 68d34bd..2e01ac7 100644 --- a/rt/runtime.c +++ b/rt/runtime.c @@ -232,11 +232,22 @@ const char* bux_strstr(const char* haystack, const char* needle) { return strstr(haystack, needle); } +/* String offset: byte offset of pos within base (both must point into same string) */ +unsigned int bux_str_offset(const char* pos, const char* base) { + if (!pos || !base) return 0; + return (unsigned int)(pos - base); +} + /* String contains: returns 1 if haystack contains needle, 0 otherwise */ int bux_str_contains(const char* haystack, const char* needle) { return bux_strstr(haystack, needle) != NULL; } +/* String is null: returns 1 if string is NULL */ +int bux_str_is_null(const char* s) { + return s == NULL; +} + /* String slice: extract substring from start, length len */ char* bux_str_slice(const char* s, unsigned int start, unsigned int len) { if (!s) return NULL; diff --git a/src/parser.bux b/src/parser.bux index ddc314e..f3e56b9 100644 --- a/src/parser.bux +++ b/src/parser.bux @@ -920,6 +920,12 @@ func parserParseParamList(p: *Parser) -> *Decl { discard parserExpect(p, tkLParen, "expected '('"); while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile { + while parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) { + discard parserAdvance(p); + } + if parserCheck(p, tkRParen) || parserPeek(p, 0) == tkEndOfFile { + break; + } if d.paramCount >= 9 { break; } let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected parameter name"); discard parserExpect(p, tkColon, "expected ':' in parameter");