harden compiler parsing, recovery, and deep-expression handling
This commit is contained in:
+69
-10
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user