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:
@@ -6,6 +6,7 @@
|
|||||||
> **Selfhost loop:** `buxc2` can compile itself — deterministic C codegen verified.
|
> **Selfhost loop:** `buxc2` can compile itself — deterministic C codegen verified.
|
||||||
> **LIR backend** produces clean 3-address C code. All 26 examples pass.
|
> **LIR backend** produces clean 3-address C code. All 26 examples pass.
|
||||||
> **Gradual Ownership:** `@[Checked]` borrow checker, `borrow &mut` expressions, `@[Shared]` attribute.
|
> **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.
|
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
|
## Documentation
|
||||||
|
|
||||||
- [`docs/LanguageRef.md`](docs/LanguageRef.md) — Language reference
|
- [`docs/LanguageRef.md`](docs/LanguageRef.md) — Language reference
|
||||||
|
|||||||
@@ -204,6 +204,10 @@ proc parseBaseType(p: var Parser): TypeExpr =
|
|||||||
var typeArgs: seq[TypeExpr] = @[]
|
var typeArgs: seq[TypeExpr] = @[]
|
||||||
discard p.advance()
|
discard p.advance()
|
||||||
while not p.check(tkGt) and not p.isAtEnd:
|
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())
|
typeArgs.add(p.parseType())
|
||||||
if p.check(tkComma):
|
if p.check(tkComma):
|
||||||
discard p.advance()
|
discard p.advance()
|
||||||
@@ -310,6 +314,10 @@ proc parsePrimaryPattern(p: var Parser): Pattern =
|
|||||||
discard p.advance()
|
discard p.advance()
|
||||||
var fields: seq[tuple[name: string, pattern: Pattern]] = @[]
|
var fields: seq[tuple[name: string, pattern: Pattern]] = @[]
|
||||||
while not p.check(tkRBrace) and not p.isAtEnd:
|
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
|
let fieldName = p.expect(tkIdent, "expected field name in struct pattern").text
|
||||||
discard p.expect(tkColon, "expected ':' after field name in pattern")
|
discard p.expect(tkColon, "expected ':' after field name in pattern")
|
||||||
fields.add((fieldName, p.parsePattern()))
|
fields.add((fieldName, p.parsePattern()))
|
||||||
@@ -965,6 +973,10 @@ proc parseTypeParams(p: var Parser): seq[TypeParam] =
|
|||||||
if p.check(tkLt):
|
if p.check(tkLt):
|
||||||
discard p.advance()
|
discard p.advance()
|
||||||
while not p.check(tkGt) and not p.isAtEnd:
|
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 name = ""
|
||||||
var isLifetime = false
|
var isLifetime = false
|
||||||
if p.check(tkIdent):
|
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] =
|
proc parseParamList(p: var Parser, allowVariadic: bool = false): seq[Param] =
|
||||||
discard p.expect(tkLParen, "expected '('")
|
discard p.expect(tkLParen, "expected '('")
|
||||||
while not p.check(tkRParen) and not p.isAtEnd:
|
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
|
let loc = p.currentLoc
|
||||||
var isVar = false
|
var isVar = false
|
||||||
if allowVariadic and p.check(tkDotDotDot):
|
if allowVariadic and p.check(tkDotDotDot):
|
||||||
@@ -1261,6 +1277,10 @@ proc parseUseDecl(p: var Parser, attrs: ParsedAttrs): Decl =
|
|||||||
if p.check(tkLBrace):
|
if p.check(tkLBrace):
|
||||||
discard p.advance()
|
discard p.advance()
|
||||||
while not p.check(tkRBrace) and not p.isAtEnd:
|
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)
|
names.add(p.expect(tkIdent, "expected name in multi-import").text)
|
||||||
if p.check(tkComma):
|
if p.check(tkComma):
|
||||||
discard p.advance()
|
discard p.advance()
|
||||||
|
|||||||
Reference in New Issue
Block a user