fix nil exceptions with incomplete code parse

This makes the parser more fault tolerant because the different
parse_foo procs return nil when in an invalid state, which is fine most
of the time but when creating a node it would crash accessing its
position.
This commit is contained in:
Laytan Laats
2023-11-12 01:53:14 +01:00
parent 70c1f9d0e1
commit bd19081543
2 changed files with 61 additions and 48 deletions

View File

@@ -7,7 +7,7 @@ import "core:reflect"
import "core:odin/tokenizer"
_ :: intrinsics
new :: proc($T: typeid, pos, end: tokenizer.Pos) -> ^T {
new_from_positions :: proc($T: typeid, pos, end: tokenizer.Pos) -> ^T {
n, _ := mem.new(T)
n.pos = pos
n.end = end
@@ -23,6 +23,15 @@ new :: proc($T: typeid, pos, end: tokenizer.Pos) -> ^T {
return n
}
new_from_pos_and_end_node :: proc($T: typeid, pos: tokenizer.Pos, end: ^Node) -> ^T {
return new(T, pos, end != nil ? end.end : {})
}
new :: proc {
new_from_positions,
new_from_pos_and_end_node,
}
clone :: proc{
clone_node,
clone_expr,

View File

@@ -786,8 +786,11 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
else_stmt = ast.new(ast.Bad_Stmt, p.curr_tok.pos, end_pos(p.curr_tok))
}
}
end := body.end
end: tokenizer.Pos
if body != nil {
end = body.end
}
if else_stmt != nil {
end = else_stmt.end
}
@@ -850,7 +853,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
body = parse_body(p)
}
range_stmt := ast.new(ast.Range_Stmt, tok.pos, body.end)
range_stmt := ast.new(ast.Range_Stmt, tok.pos, body)
range_stmt.for_pos = tok.pos
range_stmt.in_pos = in_tok.pos
range_stmt.expr = rhs
@@ -910,7 +913,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
rhs = assign_stmt.rhs[0]
}
range_stmt := ast.new(ast.Range_Stmt, tok.pos, body.end)
range_stmt := ast.new(ast.Range_Stmt, tok.pos, body)
range_stmt.for_pos = tok.pos
range_stmt.vals = vals
range_stmt.in_pos = assign_stmt.op.pos
@@ -920,7 +923,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
}
cond_expr := convert_stmt_to_expr(p, cond, "boolean expression")
for_stmt := ast.new(ast.For_Stmt, tok.pos, body.end)
for_stmt := ast.new(ast.For_Stmt, tok.pos, body)
for_stmt.for_pos = tok.pos
for_stmt.init = init
for_stmt.cond = cond_expr
@@ -976,7 +979,7 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
lhs[0] = new_blank_ident(p, tok.pos)
rhs[0] = parse_expr(p, true)
as := ast.new(ast.Assign_Stmt, tok.pos, rhs[0].end)
as := ast.new(ast.Assign_Stmt, tok.pos, rhs[0])
as.lhs = lhs
as.op = in_tok
as.rhs = rhs
@@ -1010,14 +1013,14 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
body.stmts = clauses[:]
if is_type_switch {
ts := ast.new(ast.Type_Switch_Stmt, tok.pos, body.end)
ts := ast.new(ast.Type_Switch_Stmt, tok.pos, body)
ts.tag = tag
ts.body = body
ts.switch_pos = tok.pos
return ts
} else {
cond := convert_stmt_to_expr(p, tag, "switch expression")
ts := ast.new(ast.Switch_Stmt, tok.pos, body.end)
ts := ast.new(ast.Switch_Stmt, tok.pos, body)
ts.init = init
ts.cond = cond
ts.body = body
@@ -1044,7 +1047,7 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind:
if p.curr_tok.kind == .Eq {
eq := expect_token(p, .Eq)
value := parse_value(p)
fv := ast.new(ast.Field_Value, elem.pos, value.end)
fv := ast.new(ast.Field_Value, elem.pos, value)
fv.field = elem
fv.sep = eq.pos
fv.value = value
@@ -1137,7 +1140,7 @@ parse_foreign_block :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Foreign_Bl
body.stmts = decls[:]
body.close = close.pos
decl := ast.new(ast.Foreign_Block_Decl, tok.pos, body.end)
decl := ast.new(ast.Foreign_Block_Decl, tok.pos, body)
decl.docs = docs
decl.tok = tok
decl.foreign_library = foreign_library
@@ -1248,7 +1251,7 @@ parse_unrolled_for_loop :: proc(p: ^Parser, inline_tok: tokenizer.Token) -> ^ast
return ast.new(ast.Bad_Stmt, inline_tok.pos, end_pos(p.prev_tok))
}
range_stmt := ast.new(ast.Inline_Range_Stmt, inline_tok.pos, body.end)
range_stmt := ast.new(ast.Inline_Range_Stmt, inline_tok.pos, body)
range_stmt.inline_pos = inline_tok.pos
range_stmt.for_pos = for_tok.pos
range_stmt.val0 = val0
@@ -1304,7 +1307,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
case ^ast.Return_Stmt:
error(p, s.pos, "you cannot defer a return statement")
}
ds := ast.new(ast.Defer_Stmt, tok.pos, stmt.end)
ds := ast.new(ast.Defer_Stmt, tok.pos, stmt)
ds.stmt = stmt
return ds
@@ -1341,8 +1344,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
if tok.kind != .Fallthrough && p.curr_tok.kind == .Ident {
label = parse_ident(p)
}
end := label.end if label != nil else end_pos(tok)
s := ast.new(ast.Branch_Stmt, tok.pos, end)
s := ast.new(ast.Branch_Stmt, tok.pos, label)
s.tok = tok
s.label = label
expect_semicolon(p, s)
@@ -1366,7 +1368,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
if p.curr_tok.kind != .Colon {
end := list[len(list)-1]
expect_semicolon(p, end)
us := ast.new(ast.Using_Stmt, tok.pos, end.end)
us := ast.new(ast.Using_Stmt, tok.pos, end)
us.list = list
return us
}
@@ -1416,13 +1418,13 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
bd.tok = tok
bd.name = name
ce := parse_call_expr(p, bd)
es := ast.new(ast.Expr_Stmt, ce.pos, ce.end)
es := ast.new(ast.Expr_Stmt, ce.pos, ce)
es.expr = ce
return es
case "force_inline", "force_no_inline":
expr := parse_inlining_operand(p, true, tag)
es := ast.new(ast.Expr_Stmt, expr.pos, expr.end)
es := ast.new(ast.Expr_Stmt, expr.pos, expr)
es.expr = expr
return es
case "unroll":
@@ -1444,7 +1446,8 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
return ast.new(ast.Bad_Stmt, tok.pos, end_pos(tag))
case:
stmt := parse_stmt(p)
te := ast.new(ast.Tag_Stmt, tok.pos, stmt.pos)
end := stmt.pos if stmt != nil else end_pos(tok)
te := ast.new(ast.Tag_Stmt, tok.pos, end)
te.op = tok
te.name = name
te.stmt = stmt
@@ -1572,7 +1575,7 @@ convert_stmt_to_body :: proc(p: ^Parser, stmt: ^ast.Stmt) -> ^ast.Stmt {
error(p, stmt.pos, "expected a non-empty statement")
}
bs := ast.new(ast.Block_Stmt, stmt.pos, stmt.end)
bs := ast.new(ast.Block_Stmt, stmt.pos, stmt)
bs.open = stmt.pos
bs.stmts = make([]^ast.Stmt, 1)
bs.stmts[0] = stmt
@@ -1741,7 +1744,7 @@ parse_var_type :: proc(p: ^Parser, flags: ast.Field_Flags) -> ^ast.Expr {
error(p, tok.pos, "variadic field missing type after '..'")
type = ast.new(ast.Bad_Expr, tok.pos, end_pos(tok))
}
e := ast.new(ast.Ellipsis, type.pos, type.end)
e := ast.new(ast.Ellipsis, type.pos, type)
e.expr = type
return e
}
@@ -1808,7 +1811,7 @@ parse_ident_list :: proc(p: ^Parser, allow_poly_names: bool) -> []^ast.Expr {
if is_blank_ident(ident) {
error(p, ident.pos, "invalid polymorphic type definition with a blank identifier")
}
poly_name := ast.new(ast.Poly_Type, tok.pos, ident.end)
poly_name := ast.new(ast.Poly_Type, tok.pos, ident)
poly_name.type = ident
append(&list, poly_name)
} else {
@@ -2154,7 +2157,7 @@ parse_inlining_operand :: proc(p: ^Parser, lhs: bool, tok: tokenizer.Token) -> ^
e.inlining = pi
case:
error(p, tok.pos, "'%s' must be followed by a procedure literal or call", tok.text)
return ast.new(ast.Bad_Expr, tok.pos, expr.end)
return ast.new(ast.Bad_Expr, tok.pos, expr)
}
return expr
}
@@ -2204,7 +2207,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
case .Distinct:
tok := advance_token(p)
type := parse_type(p)
dt := ast.new(ast.Distinct_Type, tok.pos, type.end)
dt := ast.new(ast.Distinct_Type, tok.pos, type)
dt.tok = tok.kind
dt.type = type
return dt
@@ -2215,7 +2218,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
switch name.text {
case "type":
type := parse_type(p)
hp := ast.new(ast.Helper_Type, tok.pos, type.end)
hp := ast.new(ast.Helper_Type, tok.pos, type)
hp.tok = tok.kind
hp.type = type
return hp
@@ -2319,7 +2322,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
tag_call := parse_call_expr(p, tag)
type := parse_type(p)
rt := ast.new(ast.Relative_Type, tok.pos, type.end)
rt := ast.new(ast.Relative_Type, tok.pos, type)
rt.tag = tag_call
rt.type = type
return rt
@@ -2328,7 +2331,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
return parse_inlining_operand(p, lhs, name)
case:
expr := parse_expr(p, lhs)
te := ast.new(ast.Tag_Expr, tok.pos, expr.pos)
end := expr.pos if expr != nil else end_pos(tok)
te := ast.new(ast.Tag_Expr, tok.pos, end)
te.op = tok
te.name = name.text
te.expr = expr
@@ -2456,7 +2460,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
case .Pointer:
tok := expect_token(p, .Pointer)
elem := parse_type(p)
ptr := ast.new(ast.Pointer_Type, tok.pos, elem.end)
ptr := ast.new(ast.Pointer_Type, tok.pos, elem)
ptr.pointer = tok.pos
ptr.elem = elem
return ptr
@@ -2470,7 +2474,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
tok := expect_token(p, .Pointer)
close := expect_token(p, .Close_Bracket)
elem := parse_type(p)
t := ast.new(ast.Multi_Pointer_Type, open.pos, elem.end)
t := ast.new(ast.Multi_Pointer_Type, open.pos, elem)
t.open = open.pos
t.pointer = tok.pos
t.close = close.pos
@@ -2480,7 +2484,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
tok := expect_token(p, .Dynamic)
close := expect_token(p, .Close_Bracket)
elem := parse_type(p)
da := ast.new(ast.Dynamic_Array_Type, open.pos, elem.end)
da := ast.new(ast.Dynamic_Array_Type, open.pos, elem)
da.open = open.pos
da.dynamic_pos = tok.pos
da.close = close.pos
@@ -2500,7 +2504,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
}
close := expect_token(p, .Close_Bracket)
elem := parse_type(p)
at := ast.new(ast.Array_Type, open.pos, elem.end)
at := ast.new(ast.Array_Type, open.pos, elem)
at.open = open.pos
at.len = count
at.close = close.pos
@@ -2514,7 +2518,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
expect_token(p, .Close_Bracket)
value := parse_type(p)
mt := ast.new(ast.Map_Type, tok.pos, value.end)
mt := ast.new(ast.Map_Type, tok.pos, value)
mt.tok_pos = tok.pos
mt.key = key
mt.value = value
@@ -2755,7 +2759,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
expect_token(p, .Close_Bracket)
elem := parse_type(p)
mt := ast.new(ast.Matrix_Type, tok.pos, elem.end)
mt := ast.new(ast.Matrix_Type, tok.pos, elem)
mt.tok_pos = tok.pos
mt.row_count = row_count
mt.column_count = column_count
@@ -2893,7 +2897,7 @@ parse_elem_list :: proc(p: ^Parser) -> []^ast.Expr {
eq := expect_token(p, .Eq)
value := parse_value(p)
fv := ast.new(ast.Field_Value, elem.pos, value.end)
fv := ast.new(ast.Field_Value, elem.pos, value)
fv.field = elem
fv.sep = eq.pos
fv.value = value
@@ -2962,7 +2966,7 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr {
}
value := parse_value(p)
fv := ast.new(ast.Field_Value, arg.pos, value.end)
fv := ast.new(ast.Field_Value, arg.pos, value)
fv.field = arg
fv.sep = eq.pos
fv.value = value
@@ -2993,7 +2997,7 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr {
o := ast.unparen_expr(operand)
if se, ok := o.derived.(^ast.Selector_Expr); ok && se.op.kind == .Arrow_Right {
sce := ast.new(ast.Selector_Call_Expr, ce.pos, ce.end)
sce := ast.new(ast.Selector_Call_Expr, ce.pos, ce)
sce.expr = o
sce.call = ce
return sce
@@ -3101,7 +3105,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
case .Ident:
field := parse_ident(p)
sel := ast.new(ast.Selector_Expr, operand.pos, field.end)
sel := ast.new(ast.Selector_Expr, operand.pos, field)
sel.expr = operand
sel.op = tok
sel.field = field
@@ -3127,7 +3131,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
type.op = question
type.expr = nil
ta := ast.new(ast.Type_Assertion, operand.pos, type.end)
ta := ast.new(ast.Type_Assertion, operand.pos, type)
ta.expr = operand
ta.type = type
@@ -3145,7 +3149,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
case .Ident:
field := parse_ident(p)
sel := ast.new(ast.Selector_Expr, operand.pos, field.end)
sel := ast.new(ast.Selector_Expr, operand.pos, field)
sel.expr = operand
sel.op = tok
sel.field = field
@@ -3225,7 +3229,7 @@ parse_unary_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
close := expect_token(p, .Close_Paren)
expr := parse_unary_expr(p, lhs)
tc := ast.new(ast.Type_Cast, tok.pos, expr.end)
tc := ast.new(ast.Type_Cast, tok.pos, expr)
tc.tok = tok
tc.open = open.pos
tc.type = type
@@ -3237,7 +3241,7 @@ parse_unary_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
op := advance_token(p)
expr := parse_unary_expr(p, lhs)
ac := ast.new(ast.Auto_Cast, op.pos, expr.end)
ac := ast.new(ast.Auto_Cast, op.pos, expr)
ac.op = op
ac.expr = expr
return ac
@@ -3247,8 +3251,8 @@ parse_unary_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
.And:
op := advance_token(p)
expr := parse_unary_expr(p, lhs)
ue := ast.new(ast.Unary_Expr, op.pos, expr.end)
ue := ast.new(ast.Unary_Expr, op.pos, expr)
ue.op = op
ue.expr = expr
return ue
@@ -3258,7 +3262,7 @@ parse_unary_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
error(p, op.pos, "unary '%s' operator is not supported", op.text)
expr := parse_unary_expr(p, lhs)
ue := ast.new(ast.Unary_Expr, op.pos, expr.end)
ue := ast.new(ast.Unary_Expr, op.pos, expr)
ue.op = op
ue.expr = expr
return ue
@@ -3266,7 +3270,7 @@ parse_unary_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
case .Period:
op := advance_token(p)
field := parse_ident(p)
ise := ast.new(ast.Implicit_Selector_Expr, op.pos, field.end)
ise := ast.new(ast.Implicit_Selector_Expr, op.pos, field)
ise.field = field
return ise
@@ -3407,7 +3411,7 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt {
error(p, p.curr_tok.pos, "no right-hand side in assignment statement")
return ast.new(ast.Bad_Stmt, start_tok.pos, end_pos(p.curr_tok))
}
stmt := ast.new(ast.Assign_Stmt, lhs[0].pos, rhs[len(rhs)-1].end)
stmt := ast.new(ast.Assign_Stmt, lhs[0].pos, rhs[len(rhs)-1])
stmt.lhs = lhs
stmt.op = op
stmt.rhs = rhs
@@ -3424,7 +3428,7 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt {
rhs := make([]^ast.Expr, 1)
rhs[0] = expr
stmt := ast.new(ast.Assign_Stmt, lhs[0].pos, rhs[len(rhs)-1].end)
stmt := ast.new(ast.Assign_Stmt, lhs[0].pos, rhs[len(rhs)-1])
stmt.lhs = lhs
stmt.op = op
stmt.rhs = rhs
@@ -3466,7 +3470,7 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt {
error(p, op.pos, "postfix '%s' statement is not supported", op.text)
}
es := ast.new(ast.Expr_Stmt, lhs[0].pos, lhs[0].end)
es := ast.new(ast.Expr_Stmt, lhs[0].pos, lhs[0])
es.expr = lhs[0]
return es
}