selfhost: fix parser infinite loop + lexer token limit

- Parser_Parse: add beforePos safeguard to skip unrecognized tokens
  instead of looping forever when parserParseDecl returns null
- Lexer: increase maxTokens 4096 -> 32768; add explicit break on limit
- CLI: null-check ReadFile result before String_Eq
- Nim CLI: add -O2 to cc invocation for faster self-hosted builds
This commit is contained in:
2026-06-01 14:03:54 +03:00
parent 55beb23220
commit e8d8b62c9d
7 changed files with 25 additions and 9 deletions
+2 -2
View File
@@ -106,7 +106,7 @@ func Cli_Compile(source: String, sourceName: String) -> String {
func Cli_Build(srcPath: String, outPath: String) -> int { func Cli_Build(srcPath: String, outPath: String) -> int {
let source: String = ReadFile(srcPath); let source: String = ReadFile(srcPath);
if String_Eq(source, "") || !FileExists(srcPath) { if source == null as String || String_Eq(source, "") || !FileExists(srcPath) {
Print("Error: cannot read source file: "); Print("Error: cannot read source file: ");
PrintLine(srcPath); PrintLine(srcPath);
return 1; return 1;
@@ -142,7 +142,7 @@ func Cli_Check(srcPath: String) -> int {
Print("Check: "); Print("Check: ");
PrintLine(srcPath); PrintLine(srcPath);
let source: String = ReadFile(srcPath); let source: String = ReadFile(srcPath);
if String_Eq(source, "") { if source == null as String || String_Eq(source, "") {
PrintLine("Error: cannot read source file"); PrintLine("Error: cannot read source file");
return 1; return 1;
} }
+5 -2
View File
@@ -42,7 +42,7 @@ func Lex_IsIdentChar(c: uint32) -> bool {
// Lexer state // Lexer state
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const maxTokens: int = 4096; const maxTokens: int = 32768;
const maxDiags: int = 128; const maxDiags: int = 128;
struct LexerDiag { struct LexerDiag {
@@ -587,7 +587,6 @@ func lexNextToken(lex: *Lexer) {
lexEmitToken(lex, tkEndOfFile); lexEmitToken(lex, tkEndOfFile);
return; return;
} }
let c: uint32 = lexPeek(lex, 0); let c: uint32 = lexPeek(lex, 0);
if c == 10 { // \n if c == 10 { // \n
@@ -673,6 +672,10 @@ func Lexer_Tokenize(source: String) -> *Lexer {
while true { while true {
lexNextToken(lex); lexNextToken(lex);
if lex.tokenCount >= maxTokens {
lexEmitDiag(lex, "too many tokens");
break;
}
if lex.tokens[lex.tokenCount - 1].kind == tkEndOfFile { if lex.tokens[lex.tokenCount - 1].kind == tkEndOfFile {
break; break;
} }
+5
View File
@@ -1195,12 +1195,17 @@ func Parser_Parse(tokens: *LexToken, tokenCount: int) -> *Module {
discard parserAdvance(p); discard parserAdvance(p);
continue; continue;
} }
let beforePos: int = p.pos;
let decl: *Decl = parserParseDecl(p); let decl: *Decl = parserParseDecl(p);
if decl != null as *Decl { if decl != null as *Decl {
decl.childDecl2 = mod.firstItem; // push front decl.childDecl2 = mod.firstItem; // push front
mod.firstItem = decl; mod.firstItem = decl;
mod.itemCount = mod.itemCount + 1; mod.itemCount = mod.itemCount + 1;
} }
// Infinite-loop safeguard: if no progress, skip token
if p.pos == beforePos {
discard parserAdvance(p);
}
} }
return mod; return mod;
+1 -1
View File
@@ -536,7 +536,7 @@ proc cmdBuild*(args: seq[string], opts: GlobalOptions): int =
# Compile with cc # Compile with cc
let outputName = if man.name != "": man.name else: "bux_out" let outputName = if man.name != "": man.name else: "bux_out"
let outputFile = buildDir / outputName let outputFile = buildDir / outputName
let ccCmd = &"cc -pthread -o {outputFile} {cFile} {runtimeDst} {ioDst} -lm 2>&1" let ccCmd = &"cc -O2 -pthread -o {outputFile} {cFile} {runtimeDst} {ioDst} -lm 2>&1"
if opts.verbose: if opts.verbose:
printInfo(&"running: {ccCmd}", useColor) printInfo(&"running: {ccCmd}", useColor)
let (output, exitCode) = execCmdEx(ccCmd) let (output, exitCode) = execCmdEx(ccCmd)
+2 -2
View File
@@ -106,7 +106,7 @@ func Cli_Compile(source: String, sourceName: String) -> String {
func Cli_Build(srcPath: String, outPath: String) -> int { func Cli_Build(srcPath: String, outPath: String) -> int {
let source: String = ReadFile(srcPath); let source: String = ReadFile(srcPath);
if String_Eq(source, "") || !FileExists(srcPath) { if source == null as String || String_Eq(source, "") || !FileExists(srcPath) {
Print("Error: cannot read source file: "); Print("Error: cannot read source file: ");
PrintLine(srcPath); PrintLine(srcPath);
return 1; return 1;
@@ -142,7 +142,7 @@ func Cli_Check(srcPath: String) -> int {
Print("Check: "); Print("Check: ");
PrintLine(srcPath); PrintLine(srcPath);
let source: String = ReadFile(srcPath); let source: String = ReadFile(srcPath);
if String_Eq(source, "") { if source == null as String || String_Eq(source, "") {
PrintLine("Error: cannot read source file"); PrintLine("Error: cannot read source file");
return 1; return 1;
} }
+5 -2
View File
@@ -42,7 +42,7 @@ func Lex_IsIdentChar(c: uint32) -> bool {
// Lexer state // Lexer state
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const maxTokens: int = 4096; const maxTokens: int = 32768;
const maxDiags: int = 128; const maxDiags: int = 128;
struct LexerDiag { struct LexerDiag {
@@ -587,7 +587,6 @@ func lexNextToken(lex: *Lexer) {
lexEmitToken(lex, tkEndOfFile); lexEmitToken(lex, tkEndOfFile);
return; return;
} }
let c: uint32 = lexPeek(lex, 0); let c: uint32 = lexPeek(lex, 0);
if c == 10 { // \n if c == 10 { // \n
@@ -673,6 +672,10 @@ func Lexer_Tokenize(source: String) -> *Lexer {
while true { while true {
lexNextToken(lex); lexNextToken(lex);
if lex.tokenCount >= maxTokens {
lexEmitDiag(lex, "too many tokens");
break;
}
if lex.tokens[lex.tokenCount - 1].kind == tkEndOfFile { if lex.tokens[lex.tokenCount - 1].kind == tkEndOfFile {
break; break;
} }
+5
View File
@@ -1195,12 +1195,17 @@ func Parser_Parse(tokens: *LexToken, tokenCount: int) -> *Module {
discard parserAdvance(p); discard parserAdvance(p);
continue; continue;
} }
let beforePos: int = p.pos;
let decl: *Decl = parserParseDecl(p); let decl: *Decl = parserParseDecl(p);
if decl != null as *Decl { if decl != null as *Decl {
decl.childDecl2 = mod.firstItem; // push front decl.childDecl2 = mod.firstItem; // push front
mod.firstItem = decl; mod.firstItem = decl;
mod.itemCount = mod.itemCount + 1; mod.itemCount = mod.itemCount + 1;
} }
// Infinite-loop safeguard: if no progress, skip token
if p.pos == beforePos {
discard parserAdvance(p);
}
} }
return mod; return mod;