contextual payload construction
This commit is contained in:
+173
-59
@@ -366,23 +366,40 @@ parse_type_atom :: proc(parser: ^Parser) -> ast.Type_Syntax {
|
||||
return types.INVALID
|
||||
}
|
||||
|
||||
parse_type :: proc(parser: ^Parser) -> ast.Type_Syntax {
|
||||
left := parse_type_atom(parser)
|
||||
parse_type_pipe_tail :: proc(parser: ^Parser, left: ast.Type_Syntax) -> ast.Type_Syntax {
|
||||
result := left
|
||||
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)
|
||||
composed, compose_error := types.compose_sum(&parser.module.type_store, result, 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
|
||||
result = 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
|
||||
result = types.INVALID
|
||||
} else {
|
||||
left = composed
|
||||
result = composed
|
||||
}
|
||||
}
|
||||
return left
|
||||
return result
|
||||
}
|
||||
|
||||
parse_type :: proc(parser: ^Parser) -> ast.Type_Syntax {
|
||||
return parse_type_pipe_tail(parser, parse_type_atom(parser))
|
||||
}
|
||||
|
||||
parse_error_type :: proc(parser: ^Parser) -> ast.Type_Syntax {
|
||||
left := types.INVALID
|
||||
#partial switch current(parser).kind {
|
||||
case .Keyword_Enum:
|
||||
left = parse_inline_enum_type(parser)
|
||||
case .Keyword_Union:
|
||||
left = parse_inline_union_type(parser)
|
||||
case:
|
||||
left = parse_type_atom(parser)
|
||||
}
|
||||
return parse_type_pipe_tail(parser, left)
|
||||
}
|
||||
|
||||
skip_parenthesized :: proc(parser: ^Parser) -> source.Span {
|
||||
@@ -650,11 +667,36 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
||||
return invalid_expr(parser, member.span, "expected an enum member after '.'")
|
||||
}
|
||||
advance(parser)
|
||||
payload := ast.INVALID_EXPR
|
||||
end := member.span
|
||||
if _, ok := allow(parser, .Left_Brace); ok {
|
||||
parser.delimiter_depth += 1
|
||||
skip_newlines(parser)
|
||||
if current(parser).kind == .Right_Brace {
|
||||
source.add(parser.diagnostics, current(parser).span, "contextual variant payload requires exactly one expression")
|
||||
} else {
|
||||
payload = parse_expression_bp(parser, 0, nesting+1)
|
||||
skip_newlines(parser)
|
||||
if _, comma_ok := allow(parser, .Comma); comma_ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "contextual variant payload requires exactly one expression")
|
||||
for current(parser).kind != .Right_Brace && current(parser).kind != .Eof {
|
||||
advance(parser)
|
||||
}
|
||||
}
|
||||
}
|
||||
right_brace, close_ok := allow(parser, .Right_Brace)
|
||||
if !close_ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '}' after contextual variant payload")
|
||||
right_brace = member
|
||||
}
|
||||
parser.delimiter_depth -= 1
|
||||
end = right_brace.span
|
||||
}
|
||||
return add_expr(parser, ast.Expr{
|
||||
kind=.Enum_Literal,
|
||||
span=span_from(start.span, member.span),
|
||||
span=span_from(start.span, end),
|
||||
name=member.symbol,
|
||||
left=ast.INVALID_EXPR,
|
||||
left=payload,
|
||||
right=ast.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
@@ -1871,7 +1913,7 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
|
||||
result := parse_type(parser)
|
||||
error_type := types.INVALID
|
||||
if _, ok := allow(parser, .Bang); ok {
|
||||
error_type = parse_type(parser)
|
||||
error_type = parse_error_type(parser)
|
||||
}
|
||||
end := previous(parser)
|
||||
ended_by_newline := current(parser).kind == .Newline
|
||||
@@ -1917,6 +1959,67 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
|
||||
})
|
||||
}
|
||||
|
||||
parse_record_body :: proc(parser: ^Parser, fields: ^[dynamic]types.Field, expected_open: string) -> bool {
|
||||
if _, ok := allow(parser, .Left_Brace); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, expected_open)
|
||||
return false
|
||||
}
|
||||
skip_newlines(parser)
|
||||
for current(parser).kind != .Right_Brace && current(parser).kind != .Eof {
|
||||
if current(parser).kind != .Identifier {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected a struct field name")
|
||||
for current(parser).kind != .Newline &&
|
||||
current(parser).kind != .Right_Brace &&
|
||||
current(parser).kind != .Eof {
|
||||
advance(parser)
|
||||
}
|
||||
skip_newlines(parser)
|
||||
continue
|
||||
}
|
||||
field_name := advance(parser)
|
||||
field_type := parse_type(parser)
|
||||
append(fields, types.Field{name=u32(field_name.symbol), type=field_type})
|
||||
if _, ok := allow(parser, .Comma); ok {
|
||||
skip_newlines(parser)
|
||||
continue
|
||||
}
|
||||
_ = finish_statement(parser, true)
|
||||
}
|
||||
if _, ok := allow(parser, .Right_Brace); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '}' after struct fields")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
parse_inline_union_type :: proc(parser: ^Parser) -> types.Type {
|
||||
advance(parser)
|
||||
valid := true
|
||||
if _, ok := allow(parser, .Left_Paren); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '(enum)' after inline union error type")
|
||||
valid = false
|
||||
} else {
|
||||
if _, enum_ok := allow(parser, .Keyword_Enum); !enum_ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "inline union error types must use 'union(enum)'")
|
||||
if current(parser).kind != .Right_Paren {
|
||||
_ = parse_type(parser)
|
||||
}
|
||||
valid = false
|
||||
}
|
||||
if _, close_ok := allow(parser, .Right_Paren); !close_ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected ')' after union tag")
|
||||
valid = false
|
||||
}
|
||||
}
|
||||
fields: [dynamic]types.Field
|
||||
fields.allocator = parser.module.allocator
|
||||
defer delete(fields)
|
||||
if !parse_record_body(parser, &fields, "expected '{' after inline union error type") || !valid {
|
||||
return types.INVALID
|
||||
}
|
||||
tag := synthesize_union_tag(parser, fields[:])
|
||||
return types.union_anonymous(&parser.module.type_store, fields[:], tag)
|
||||
}
|
||||
|
||||
parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout: bool, is_union := false) {
|
||||
start := advance(parser)
|
||||
id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol))
|
||||
@@ -1954,34 +2057,10 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout: bool, is_unio
|
||||
}
|
||||
return
|
||||
}
|
||||
advance(parser)
|
||||
fields: [dynamic]types.Field
|
||||
fields.allocator = parser.module.allocator
|
||||
defer delete(fields)
|
||||
skip_newlines(parser)
|
||||
for current(parser).kind != .Right_Brace && current(parser).kind != .Eof {
|
||||
if current(parser).kind != .Identifier {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected a struct field name")
|
||||
for current(parser).kind != .Newline &&
|
||||
current(parser).kind != .Right_Brace &&
|
||||
current(parser).kind != .Eof {
|
||||
advance(parser)
|
||||
}
|
||||
skip_newlines(parser)
|
||||
continue
|
||||
}
|
||||
field_name := advance(parser)
|
||||
field_type := parse_type(parser)
|
||||
append(&fields, types.Field{name=u32(field_name.symbol), type=field_type})
|
||||
if _, ok := allow(parser, .Comma); ok {
|
||||
skip_newlines(parser)
|
||||
continue
|
||||
}
|
||||
_ = finish_statement(parser, true)
|
||||
}
|
||||
if _, ok := allow(parser, .Right_Brace); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '}' after struct fields")
|
||||
}
|
||||
_ = parse_record_body(parser, &fields, "expected '{' after struct fields")
|
||||
if is_union && (inferred_tag || types.is_valid(declared_tag)) {
|
||||
tag = synthesize_union_tag(parser, fields[:])
|
||||
}
|
||||
@@ -2033,26 +2112,19 @@ parse_alias :: proc(parser: ^Parser, name: token.Token) {
|
||||
_ = finish_statement(parser)
|
||||
}
|
||||
|
||||
parse_enum :: proc(parser: ^Parser, name: token.Token) {
|
||||
start := advance(parser)
|
||||
explicit_backing := false
|
||||
backing := types.INVALID
|
||||
if _, ok := allow(parser, .Left_Paren); ok {
|
||||
explicit_backing = true
|
||||
backing = parse_type(parser)
|
||||
if _, close_ok := allow(parser, .Right_Paren); !close_ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected ')' after enum backing type")
|
||||
}
|
||||
}
|
||||
parse_enum_body :: proc(
|
||||
parser: ^Parser,
|
||||
start: source.Span,
|
||||
explicit_backing: bool,
|
||||
backing: ^types.Type,
|
||||
members: ^[dynamic]types.Enum_Member,
|
||||
expected_open: string,
|
||||
) -> bool {
|
||||
skip_newlines(parser)
|
||||
if _, ok := allow(parser, .Left_Brace); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '{' after enum declaration")
|
||||
_ = finish_statement(parser)
|
||||
return
|
||||
source.add(parser.diagnostics, current(parser).span, expected_open)
|
||||
return false
|
||||
}
|
||||
members: [dynamic]types.Enum_Member
|
||||
members.allocator = parser.module.allocator
|
||||
defer delete(members)
|
||||
next_value: i128
|
||||
previous_value: i128
|
||||
has_previous := false
|
||||
@@ -2070,7 +2142,7 @@ parse_enum :: proc(parser: ^Parser, name: token.Token) {
|
||||
}
|
||||
member := advance(parser)
|
||||
duplicate := false
|
||||
for existing in members {
|
||||
for existing in members^ {
|
||||
if existing.name == u32(member.symbol) {
|
||||
duplicate = true
|
||||
break
|
||||
@@ -2108,7 +2180,7 @@ parse_enum :: proc(parser: ^Parser, name: token.Token) {
|
||||
source.add(parser.diagnostics, member.span, "enum values must be strictly increasing")
|
||||
}
|
||||
if !duplicate {
|
||||
append(&members, types.Enum_Member{name=u32(member.symbol), value=value})
|
||||
append(members, types.Enum_Member{name=u32(member.symbol), value=value})
|
||||
}
|
||||
previous_value = value
|
||||
has_previous = true
|
||||
@@ -2122,19 +2194,61 @@ parse_enum :: proc(parser: ^Parser, name: token.Token) {
|
||||
if _, ok := allow(parser, .Right_Brace); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '}' after enum members")
|
||||
}
|
||||
if len(members) == 0 {
|
||||
source.add(parser.diagnostics, start.span, "enum declarations require at least one member")
|
||||
if len(members^) == 0 {
|
||||
source.add(parser.diagnostics, start, "enum declarations require at least one member")
|
||||
}
|
||||
if !explicit_backing {
|
||||
backing = types.U16
|
||||
for &member in members {
|
||||
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")
|
||||
source.add(parser.diagnostics, start, "too many global sum variants for u16 tags")
|
||||
}
|
||||
member.value = i128(id)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
parse_inline_enum_type :: proc(parser: ^Parser) -> types.Type {
|
||||
start := advance(parser)
|
||||
valid := true
|
||||
if _, ok := allow(parser, .Left_Paren); ok {
|
||||
source.add(parser.diagnostics, start.span, "inline enum error types cannot declare a backing type")
|
||||
_ = parse_type(parser)
|
||||
if _, close_ok := allow(parser, .Right_Paren); !close_ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected ')' after enum backing type")
|
||||
}
|
||||
valid = false
|
||||
}
|
||||
backing := types.INVALID
|
||||
members: [dynamic]types.Enum_Member
|
||||
members.allocator = parser.module.allocator
|
||||
defer delete(members)
|
||||
if !parse_enum_body(parser, start.span, false, &backing, &members, "expected '{' after inline enum error type") || !valid {
|
||||
return types.INVALID
|
||||
}
|
||||
return types.enum_anonymous(&parser.module.type_store, members[:], backing)
|
||||
}
|
||||
|
||||
parse_enum :: proc(parser: ^Parser, name: token.Token) {
|
||||
start := advance(parser)
|
||||
explicit_backing := false
|
||||
backing := types.INVALID
|
||||
if _, ok := allow(parser, .Left_Paren); ok {
|
||||
explicit_backing = true
|
||||
backing = parse_type(parser)
|
||||
if _, close_ok := allow(parser, .Right_Paren); !close_ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected ')' after enum backing type")
|
||||
}
|
||||
}
|
||||
members: [dynamic]types.Enum_Member
|
||||
members.allocator = parser.module.allocator
|
||||
defer delete(members)
|
||||
if !parse_enum_body(parser, start.span, explicit_backing, &backing, &members, "expected '{' after enum declaration") {
|
||||
_ = finish_statement(parser)
|
||||
return
|
||||
}
|
||||
id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol))
|
||||
if !types.define_enum(&parser.module.type_store, id, backing, members[:], explicit_backing) {
|
||||
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||
|
||||
Reference in New Issue
Block a user