error handling kickoff
This commit is contained in:
+87
-33
@@ -165,7 +165,7 @@ parse_type_constant :: proc(parser: ^Parser) -> (u64, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
parse_type :: proc(parser: ^Parser) -> ast.Type_Syntax {
|
||||
parse_type_atom :: proc(parser: ^Parser) -> ast.Type_Syntax {
|
||||
tok := current(parser)
|
||||
if tok.kind == .Question {
|
||||
advance(parser)
|
||||
@@ -366,6 +366,25 @@ parse_type :: proc(parser: ^Parser) -> ast.Type_Syntax {
|
||||
return types.INVALID
|
||||
}
|
||||
|
||||
parse_type :: proc(parser: ^Parser) -> ast.Type_Syntax {
|
||||
left := parse_type_atom(parser)
|
||||
for current(parser).kind == .Pipe {
|
||||
operator := advance(parser)
|
||||
right := parse_type_atom(parser)
|
||||
composed, compose_error := types.compose_sum(&parser.module.type_store, left, right)
|
||||
if compose_error == .Unsupported {
|
||||
source.add(parser.diagnostics, operator.span, "only native unbacked enums and tagged unions can be composed with '|'")
|
||||
left = types.INVALID
|
||||
} else if compose_error == .Conflict {
|
||||
source.add(parser.diagnostics, operator.span, "sum composition contains the same variant name with different payload types")
|
||||
left = types.INVALID
|
||||
} else {
|
||||
left = composed
|
||||
}
|
||||
}
|
||||
return left
|
||||
}
|
||||
|
||||
skip_parenthesized :: proc(parser: ^Parser) -> source.Span {
|
||||
start := current(parser)
|
||||
depth := 0
|
||||
@@ -706,7 +725,7 @@ infix_binding_power :: proc(kind: token.Kind) -> (left, right: int, ok: bool) {
|
||||
#partial switch kind {
|
||||
case .Range, .Range_Inclusive:
|
||||
return 0, 1, true
|
||||
case .Keyword_Orelse:
|
||||
case .Keyword_Orelse, .Keyword_Catch:
|
||||
return 2, 3, true
|
||||
case .Keyword_Or:
|
||||
return 4, 5, true
|
||||
@@ -726,6 +745,7 @@ infix_expr_kind :: proc(kind: token.Kind) -> ast.Expr_Kind {
|
||||
#partial switch kind {
|
||||
case .Range, .Range_Inclusive: return .Range
|
||||
case .Keyword_Orelse: return .Orelse
|
||||
case .Keyword_Catch: return .Catch
|
||||
case .Keyword_Or: return .Or
|
||||
case .Keyword_And: return .And
|
||||
case .Equal_Equal: return .Eq
|
||||
@@ -766,7 +786,7 @@ is_simple_range_bound :: proc(expr: ast.Expr) -> bool {
|
||||
|
||||
prefix_binding_power :: proc(kind: token.Kind) -> (right: int, ok: bool) {
|
||||
#partial switch kind {
|
||||
case .Minus, .Ampersand, .Bang:
|
||||
case .Minus, .Ampersand, .Bang, .Keyword_Try:
|
||||
return 20, true
|
||||
}
|
||||
return 0, false
|
||||
@@ -792,6 +812,7 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
|
||||
#partial switch operator.kind {
|
||||
case .Ampersand: prefix_kind = .Address
|
||||
case .Bang: prefix_kind = .Not
|
||||
case .Keyword_Try: prefix_kind = .Try
|
||||
}
|
||||
left = add_expr(parser, ast.Expr{
|
||||
kind=prefix_kind,
|
||||
@@ -921,6 +942,42 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
|
||||
}
|
||||
operator := advance(parser)
|
||||
skip_newlines(parser)
|
||||
if operator.kind == .Keyword_Catch {
|
||||
left_expr := parser.module.exprs[left]
|
||||
if _, pipe_ok := allow(parser, .Pipe); pipe_ok {
|
||||
capture := current(parser)
|
||||
if capture.kind != .Identifier && capture.kind != .Underscore {
|
||||
source.add(parser.diagnostics, capture.span, "expected a catch capture name")
|
||||
} else {
|
||||
advance(parser)
|
||||
}
|
||||
if _, close_ok := allow(parser, .Pipe); !close_ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '|' after catch capture")
|
||||
}
|
||||
body := parse_block(parser)
|
||||
end := previous(parser)
|
||||
left = add_expr(parser, ast.Expr{
|
||||
kind=.Catch,
|
||||
span=span_from(left_expr.span, end.span),
|
||||
name=capture.symbol,
|
||||
left=left,
|
||||
right=ast.INVALID_EXPR,
|
||||
body=body,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
continue
|
||||
}
|
||||
right := parse_expression_bp(parser, right_power, nesting+1)
|
||||
right_expr := parser.module.exprs[right]
|
||||
left = add_expr(parser, ast.Expr{
|
||||
kind=.Catch,
|
||||
span=span_from(left_expr.span, right_expr.span),
|
||||
left=left,
|
||||
right=right,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
continue
|
||||
}
|
||||
right := parse_expression_bp(parser, right_power, nesting+1)
|
||||
left_expr := parser.module.exprs[left]
|
||||
right_expr := parser.module.exprs[right]
|
||||
@@ -1812,6 +1869,10 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
|
||||
}
|
||||
skip_newlines(parser)
|
||||
result := parse_type(parser)
|
||||
error_type := types.INVALID
|
||||
if _, ok := allow(parser, .Bang); ok {
|
||||
error_type = parse_type(parser)
|
||||
}
|
||||
end := previous(parser)
|
||||
ended_by_newline := current(parser).kind == .Newline
|
||||
if current(parser).kind == .Newline {
|
||||
@@ -1832,6 +1893,7 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
|
||||
variadic=variadic,
|
||||
params=params,
|
||||
result=result,
|
||||
error=error_type,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
return
|
||||
@@ -1849,6 +1911,7 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
|
||||
variadic=variadic,
|
||||
params=params,
|
||||
result=result,
|
||||
error=error_type,
|
||||
body=body,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
@@ -1860,13 +1923,14 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout: bool, is_unio
|
||||
// A tagged union spells its discriminant in parens: `union(Enum)` reuses an existing
|
||||
// enum; `union(enum)` synthesizes one from the variant names after the body is parsed.
|
||||
tag := types.INVALID
|
||||
declared_tag := types.INVALID
|
||||
inferred_tag := false
|
||||
if is_union {
|
||||
if _, ok := allow(parser, .Left_Paren); ok {
|
||||
if _, enum_ok := allow(parser, .Keyword_Enum); enum_ok {
|
||||
inferred_tag = true
|
||||
} else {
|
||||
tag = parse_type(parser)
|
||||
declared_tag = parse_type(parser)
|
||||
}
|
||||
if _, close_ok := allow(parser, .Right_Paren); !close_ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected ')' after union tag")
|
||||
@@ -1882,7 +1946,7 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout: bool, is_unio
|
||||
"native union declarations require a body" if is_union else "native struct declarations require a body",
|
||||
)
|
||||
}
|
||||
if !types.define_record(&parser.module.type_store, id, nil, c_layout, true, is_union, tag=tag) {
|
||||
if !types.define_record(&parser.module.type_store, id, nil, c_layout, true, is_union, tag=tag, declared_tag=declared_tag) {
|
||||
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||
}
|
||||
if !ended_by_newline {
|
||||
@@ -1918,36 +1982,29 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout: bool, is_unio
|
||||
if _, ok := allow(parser, .Right_Brace); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '}' after struct fields")
|
||||
}
|
||||
if inferred_tag {
|
||||
if is_union && (inferred_tag || types.is_valid(declared_tag)) {
|
||||
tag = synthesize_union_tag(parser, fields[:])
|
||||
}
|
||||
if !types.define_record(&parser.module.type_store, id, fields[:], c_layout, false, is_union, tag=tag) {
|
||||
if !types.define_record(&parser.module.type_store, id, fields[:], c_layout, false, is_union, tag=tag, declared_tag=declared_tag) {
|
||||
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||
}
|
||||
_ = finish_statement(parser)
|
||||
}
|
||||
|
||||
// synthesize_union_tag builds the anonymous discriminant enum for a `union(enum)`:
|
||||
// one member per variant, dense 0-based, in the smallest fitting unsigned backing
|
||||
// (mirroring `parse_enum`'s unbacked pick).
|
||||
// synthesize_union_tag builds the anonymous runtime discriminant enum for a tagged
|
||||
// union: one member per variant, valued by the program-global (name, payload-type)
|
||||
// ID. The declared tag enum, if any, remains only the validation surface.
|
||||
synthesize_union_tag :: proc(parser: ^Parser, fields: []types.Field) -> types.Type {
|
||||
members := make([]types.Enum_Member, len(fields), parser.module.allocator)
|
||||
defer delete(members, parser.module.allocator)
|
||||
for field, index in fields {
|
||||
members[index] = types.Enum_Member{name=field.name, value=i128(index)}
|
||||
id, ok := types.variant_id(&parser.module.type_store, field.name, field.type)
|
||||
if !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "too many global sum variants for u16 tags")
|
||||
}
|
||||
members[index] = types.Enum_Member{name=field.name, value=i128(id)}
|
||||
}
|
||||
max_value := u64(max(len(fields)-1, 0))
|
||||
backing := types.U8
|
||||
if max_value > 0xff {
|
||||
backing = types.U16
|
||||
}
|
||||
if max_value > 0xffff {
|
||||
backing = types.U32
|
||||
}
|
||||
if max_value > 0xffff_ffff {
|
||||
backing = types.U64
|
||||
}
|
||||
return types.enum_anonymous(&parser.module.type_store, members, backing)
|
||||
return types.enum_anonymous(&parser.module.type_store, members, types.U16)
|
||||
}
|
||||
|
||||
parse_distinct :: proc(parser: ^Parser, name: token.Token) {
|
||||
@@ -2069,16 +2126,13 @@ parse_enum :: proc(parser: ^Parser, name: token.Token) {
|
||||
source.add(parser.diagnostics, start.span, "enum declarations require at least one member")
|
||||
}
|
||||
if !explicit_backing {
|
||||
max_value := u64(max(len(members)-1, 0))
|
||||
backing = types.U8
|
||||
if max_value > 0xff {
|
||||
backing = types.U16
|
||||
}
|
||||
if max_value > 0xffff {
|
||||
backing = types.U32
|
||||
}
|
||||
if max_value > 0xffff_ffff {
|
||||
backing = types.U64
|
||||
backing = types.U16
|
||||
for &member in members {
|
||||
id, ok := types.variant_id(&parser.module.type_store, member.name, types.VOID)
|
||||
if !ok {
|
||||
source.add(parser.diagnostics, start.span, "too many global sum variants for u16 tags")
|
||||
}
|
||||
member.value = i128(id)
|
||||
}
|
||||
}
|
||||
id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol))
|
||||
|
||||
Reference in New Issue
Block a user