harden compiler parsing, recovery, and deep-expression handling

This commit is contained in:
2026-06-12 00:28:45 +02:00
parent a5ceb727c1
commit 99ba907f59
13 changed files with 1159 additions and 503 deletions
+69 -10
View File
@@ -19,6 +19,8 @@ Parser :: struct {
delimiter_depth: int,
}
MAX_EXPRESSION_NESTING :: 256
token_text :: proc(parser: ^Parser, tok: token.Token) -> string {
if tok.span.start < 0 || tok.span.end < tok.span.start || tok.span.end > len(parser.source_file.text) {
return ""
@@ -110,7 +112,30 @@ parse_type :: proc(parser: ^Parser) -> ast.Type_Syntax {
return .Invalid
}
parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Token) -> int {
skip_parenthesized :: proc(parser: ^Parser) -> source.Span {
start := current(parser)
depth := 0
end := start
for current(parser).kind != .Eof {
tok := advance(parser)
end = tok
if tok.kind == .Left_Paren {
depth += 1
} else if tok.kind == .Right_Paren {
depth -= 1
if depth == 0 {
break
}
}
}
return span_from(start.span, end.span)
}
parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Token, nesting: int) -> int {
if nesting >= MAX_EXPRESSION_NESTING {
span := skip_parenthesized(parser)
return invalid_expr(parser, span, "expression nesting exceeds 256 levels")
}
left_paren := advance(parser)
parser.delimiter_depth += 1
defer parser.delimiter_depth -= 1
@@ -118,7 +143,7 @@ parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Tok
args.allocator = parser.module.allocator
skip_newlines(parser)
for current(parser).kind != .Right_Paren && current(parser).kind != .Eof {
append(&args, parse_expression(parser))
append(&args, parse_expression_bp(parser, 0, nesting+1))
skip_newlines(parser)
if _, ok := allow(parser, .Comma); ok {
skip_newlines(parser)
@@ -143,7 +168,7 @@ parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Tok
})
}
parse_primary :: proc(parser: ^Parser) -> int {
parse_primary :: proc(parser: ^Parser, nesting: int) -> int {
tok := current(parser)
#partial switch tok.kind {
case .Integer:
@@ -172,7 +197,7 @@ parse_primary :: proc(parser: ^Parser) -> int {
name = advance(parser)
}
if current(parser).kind == .Left_Paren {
return parse_call(parser, qualifier, first, name)
return parse_call(parser, qualifier, first, name, nesting)
}
return add_expr(parser, ast.Expr{
kind=.Name,
@@ -187,11 +212,15 @@ parse_primary :: proc(parser: ^Parser) -> int {
advance(parser)
return invalid_expr(parser, tok.span, "'_' is a write-only sink and cannot be read")
case .Left_Paren:
if nesting >= MAX_EXPRESSION_NESTING {
span := skip_parenthesized(parser)
return invalid_expr(parser, span, "expression nesting exceeds 256 levels")
}
advance(parser)
parser.delimiter_depth += 1
defer parser.delimiter_depth -= 1
skip_newlines(parser)
expr := parse_expression(parser)
expr := parse_expression_bp(parser, 0, nesting+1)
skip_newlines(parser)
if _, ok := allow(parser, .Right_Paren); !ok {
source.add(parser.diagnostics, current(parser).span, "expected ')'")
@@ -213,15 +242,34 @@ parse_primary :: proc(parser: ^Parser) -> int {
return invalid_expr(parser, tok.span, "expected an expression")
}
parse_expression :: proc(parser: ^Parser) -> int {
left := parse_primary(parser)
infix_binding_power :: proc(kind: token.Kind) -> (left, right: int, ok: bool) {
#partial switch kind {
case .Plus:
return 10, 11, true
}
return 0, 0, false
}
parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int) -> int {
if nesting > MAX_EXPRESSION_NESTING {
tok := current(parser)
if tok.kind != .Newline && tok.kind != .Right_Brace && tok.kind != .Eof {
advance(parser)
}
return invalid_expr(parser, tok.span, "expression nesting exceeds 256 levels")
}
left := parse_primary(parser, nesting)
if parser.delimiter_depth > 0 {
skip_newlines(parser)
}
for current(parser).kind == .Plus {
for {
left_power, right_power, ok := infix_binding_power(current(parser).kind)
if !ok || left_power < minimum_binding_power {
break
}
advance(parser)
skip_newlines(parser)
right := parse_primary(parser)
right := parse_expression_bp(parser, right_power, nesting+1)
left_expr := parser.module.exprs[left]
right_expr := parser.module.exprs[right]
left = add_expr(parser, ast.Expr{
@@ -238,6 +286,10 @@ parse_expression :: proc(parser: ^Parser) -> int {
return left
}
parse_expression :: proc(parser: ^Parser) -> int {
return parse_expression_bp(parser, 0, 0)
}
finish_statement :: proc(parser: ^Parser, allow_closing_brace := false) -> int {
if current(parser).kind == .Newline {
skip_newlines(parser)
@@ -536,10 +588,17 @@ parse_top_level :: proc(parser: ^Parser) {
skip_newlines(parser)
c_abi := false
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_C {
if operator.kind == .Colon_Colon &&
current(parser).kind == .Identifier &&
token_text(parser, current(parser)) == "c" {
saved := parser.cursor
c_abi = true
advance(parser)
skip_newlines(parser)
if current(parser).kind != .Keyword_Func {
c_abi = false
parser.cursor = saved
}
}
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Func {
parse_function(parser, name, c_abi)