fix(parser): infinite loop on newlines in multi-import, multi-line params, type args, struct patterns

Parser would hang (infinite loop) when encountering newline tokens
inside certain constructs because expect() returns without advancing
position when the expected token doesn't match.

Fixed 5 places in parseBaseType, parsePrimaryPattern, parseTypeParams,
parseParamList, and parseUseDecl by adding newline-skipping at the
start of each iteration.

Verified: compiler no longer hangs/crashes on all 3 real-world apps
from apps/ (boko-framework, jwt-pitbul, nexus).
This commit is contained in:
2026-06-07 23:02:43 +03:00
parent e7e900973f
commit 3b7fc455b0
2 changed files with 35 additions and 0 deletions
+15
View File
@@ -6,6 +6,7 @@
> **Selfhost loop:** `buxc2` can compile itself — deterministic C codegen verified.
> **LIR backend** produces clean 3-address C code. All 26 examples pass.
> **Gradual Ownership:** `@[Checked]` borrow checker, `borrow &mut` expressions, `@[Shared]` attribute.
> **Apps tested:** compiler successfully parses all 3 real-world apps (`apps/boko-framework`, `apps/jwt-pitbul`, `apps/nexus`) — no hangs, no crashes.
Bux is a fast, compiled, strongly-typed systems programming language. Features a C backend for native code generation, raw multi-line strings, gradual ownership (opt-in borrow checking), async/await, generics, algebraic enums, and a package manager.
@@ -254,6 +255,20 @@ make clean
---
## Applications
The `apps/` directory contains real-world Bux applications that serve as integration tests for the compiler:
| App | Description | Lines |
|-----|-------------|-------|
| **boko-framework** | Async web framework (like FastAPI), multi-threaded HTTP server | ~660 |
| **jwt-pitbul** | JWT CLI tool — sign, verify, decode (HS256/384/512, RS256/384/512, ES256/384, EdDSA) | ~326 |
| **nexus** | High-performance HTTP/1.1, HTTP/2 & WebSocket server | ~550 |
The bootstrap compiler successfully parses and type-checks all three applications without hanging or crashing.
---
## Documentation
- [`docs/LanguageRef.md`](docs/LanguageRef.md) — Language reference
+20
View File
@@ -204,6 +204,10 @@ proc parseBaseType(p: var Parser): TypeExpr =
var typeArgs: seq[TypeExpr] = @[]
discard p.advance()
while not p.check(tkGt) and not p.isAtEnd:
while p.check(tkNewLine):
discard p.advance()
if p.check(tkGt) or p.isAtEnd:
break
typeArgs.add(p.parseType())
if p.check(tkComma):
discard p.advance()
@@ -310,6 +314,10 @@ proc parsePrimaryPattern(p: var Parser): Pattern =
discard p.advance()
var fields: seq[tuple[name: string, pattern: Pattern]] = @[]
while not p.check(tkRBrace) and not p.isAtEnd:
while p.check(tkNewLine):
discard p.advance()
if p.check(tkRBrace) or p.isAtEnd:
break
let fieldName = p.expect(tkIdent, "expected field name in struct pattern").text
discard p.expect(tkColon, "expected ':' after field name in pattern")
fields.add((fieldName, p.parsePattern()))
@@ -965,6 +973,10 @@ proc parseTypeParams(p: var Parser): seq[TypeParam] =
if p.check(tkLt):
discard p.advance()
while not p.check(tkGt) and not p.isAtEnd:
while p.check(tkNewLine):
discard p.advance()
if p.check(tkGt) or p.isAtEnd:
break
var name = ""
var isLifetime = false
if p.check(tkIdent):
@@ -997,6 +1009,10 @@ proc parseTypeParams(p: var Parser): seq[TypeParam] =
proc parseParamList(p: var Parser, allowVariadic: bool = false): seq[Param] =
discard p.expect(tkLParen, "expected '('")
while not p.check(tkRParen) and not p.isAtEnd:
while p.check(tkNewLine):
discard p.advance()
if p.check(tkRParen) or p.isAtEnd:
break
let loc = p.currentLoc
var isVar = false
if allowVariadic and p.check(tkDotDotDot):
@@ -1261,6 +1277,10 @@ proc parseUseDecl(p: var Parser, attrs: ParsedAttrs): Decl =
if p.check(tkLBrace):
discard p.advance()
while not p.check(tkRBrace) and not p.isAtEnd:
while p.check(tkNewLine):
discard p.advance()
if p.check(tkRBrace) or p.isAtEnd:
break
names.add(p.expect(tkIdent, "expected name in multi-import").text)
if p.check(tkComma):
discard p.advance()