reflection foundation, tuples, debug.print
This commit is contained in:
+186
-19
@@ -497,7 +497,17 @@ parse_call_args :: proc(parser: ^Parser, nesting: int) -> ([]ast.Expr_Id, token.
|
||||
args.allocator = parser.module.allocator
|
||||
skip_newlines(parser)
|
||||
for current(parser).kind != .Right_Paren && current(parser).kind != .Eof {
|
||||
append(&args, parse_expression_bp(parser, 0, nesting+1))
|
||||
if hole, ok := allow(parser, .Underscore); ok {
|
||||
append(&args, add_expr(parser, ast.Expr{
|
||||
kind=.Inference_Hole,
|
||||
span=hole.span,
|
||||
left=ast.INVALID_EXPR,
|
||||
right=ast.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
}))
|
||||
} else {
|
||||
append(&args, parse_expression_bp(parser, 0, nesting+1))
|
||||
}
|
||||
skip_newlines(parser)
|
||||
if _, ok := allow(parser, .Comma); ok {
|
||||
skip_newlines(parser)
|
||||
@@ -612,6 +622,74 @@ parse_keyed_initializers :: proc(
|
||||
return args[:], right_brace
|
||||
}
|
||||
|
||||
parse_positional_initializers :: proc(
|
||||
parser: ^Parser,
|
||||
left_brace: token.Token,
|
||||
nesting: int,
|
||||
close_message: string,
|
||||
) -> ([]ast.Expr_Id, token.Token) {
|
||||
parser.delimiter_depth += 1
|
||||
defer parser.delimiter_depth -= 1
|
||||
args: [dynamic]ast.Expr_Id
|
||||
args.allocator = parser.module.allocator
|
||||
skip_newlines(parser)
|
||||
for current(parser).kind != .Right_Brace && current(parser).kind != .Eof {
|
||||
append(&args, parse_expression_bp(parser, 0, nesting+1))
|
||||
skip_newlines(parser)
|
||||
if _, ok := allow(parser, .Comma); ok {
|
||||
skip_newlines(parser)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
right_brace, ok := allow(parser, .Right_Brace)
|
||||
if !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, close_message)
|
||||
right_brace = left_brace
|
||||
}
|
||||
return args[:], right_brace
|
||||
}
|
||||
|
||||
brace_starts_tuple :: proc(parser: ^Parser) -> bool {
|
||||
if current(parser).kind != .Left_Brace {
|
||||
return false
|
||||
}
|
||||
depth := 0
|
||||
for cursor := parser.cursor; cursor < len(parser.tokens.items); cursor += 1 {
|
||||
#partial switch parser.tokens.items[cursor].kind {
|
||||
case .Left_Brace, .Left_Paren, .Left_Bracket:
|
||||
depth += 1
|
||||
case .Right_Brace, .Right_Paren, .Right_Bracket:
|
||||
depth -= 1
|
||||
if depth == 0 {
|
||||
return cursor == parser.cursor+1
|
||||
}
|
||||
case .Comma:
|
||||
if depth == 1 {
|
||||
return true
|
||||
}
|
||||
case .Eof:
|
||||
return false
|
||||
case:
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
parse_tuple_literal :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
||||
left_brace := advance(parser)
|
||||
args, right_brace := parse_positional_initializers(parser, left_brace, nesting, "expected '}' after tuple literal")
|
||||
return add_expr(parser, ast.Expr{
|
||||
kind=.Struct_Literal,
|
||||
span=span_from(left_brace.span, right_brace.span),
|
||||
args=args,
|
||||
tuple=true,
|
||||
left=ast.INVALID_EXPR,
|
||||
right=ast.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
|
||||
parse_struct_literal :: proc(
|
||||
parser: ^Parser,
|
||||
qualifier: symbol.Id,
|
||||
@@ -619,13 +697,24 @@ parse_struct_literal :: proc(
|
||||
nesting: int,
|
||||
) -> ast.Expr_Id {
|
||||
left_brace := advance(parser)
|
||||
args, right_brace := parse_keyed_initializers(parser, left_brace, nesting, "expected '}' after struct literal")
|
||||
skip_newlines(parser)
|
||||
keyed_start := (current(parser).kind == .Identifier || token.is_keyword(current(parser).kind)) &&
|
||||
(peek(parser).kind == .Equal || peek(parser).kind == .Right_Brace || token.is_keyword(current(parser).kind))
|
||||
positional := current(parser).kind == .Right_Brace || !keyed_start
|
||||
args: []ast.Expr_Id
|
||||
right_brace: token.Token
|
||||
if positional {
|
||||
args, right_brace = parse_positional_initializers(parser, left_brace, nesting, "expected '}' after tuple literal")
|
||||
} else {
|
||||
args, right_brace = parse_keyed_initializers(parser, left_brace, nesting, "expected '}' after struct literal")
|
||||
}
|
||||
return add_expr(parser, ast.Expr{
|
||||
kind=.Struct_Literal,
|
||||
span=source.Span{file=name.span.file, start=first.span.start, end=right_brace.span.end},
|
||||
qualifier=qualifier,
|
||||
name=name.symbol,
|
||||
args=args[:],
|
||||
tuple=positional,
|
||||
left=ast.INVALID_EXPR,
|
||||
right=ast.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
@@ -636,7 +725,8 @@ parse_anonymous_struct_type_expr :: proc(parser: ^Parser) -> ast.Expr_Id {
|
||||
start := advance(parser)
|
||||
fields: [dynamic]types.Field
|
||||
fields.allocator = parser.module.allocator
|
||||
if !parse_record_body(parser, &fields, "expected '{' after anonymous struct type") {
|
||||
tuple := false
|
||||
if !parse_record_body(parser, &fields, "expected '{' after anonymous struct type", tuple_result=&tuple) {
|
||||
delete(fields)
|
||||
return invalid_expr(parser, start.span, "invalid anonymous struct type")
|
||||
}
|
||||
@@ -649,6 +739,7 @@ parse_anonymous_struct_type_expr :: proc(parser: ^Parser) -> ast.Expr_Id {
|
||||
kind=.Anonymous_Struct_Type,
|
||||
span=span_from(start.span, end.span),
|
||||
integer=u64(field_start)<<32 | u64(field_count),
|
||||
tuple=tuple,
|
||||
left=ast.INVALID_EXPR,
|
||||
right=ast.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
@@ -837,6 +928,8 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
||||
})
|
||||
}
|
||||
return parse_array_literal(parser, nesting)
|
||||
case .Left_Brace:
|
||||
return parse_tuple_literal(parser, nesting)
|
||||
case .Dot:
|
||||
start := advance(parser)
|
||||
member, member_ok := parse_member_name(parser)
|
||||
@@ -901,7 +994,8 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
||||
first := advance(parser)
|
||||
name := first
|
||||
qualifier := symbol.INVALID
|
||||
if _, ok := allow(parser, .Dot); ok {
|
||||
if current(parser).kind == .Dot && peek(parser).kind != .Integer {
|
||||
advance(parser)
|
||||
member, member_ok := parse_member_name(parser)
|
||||
if !member_ok {
|
||||
return invalid_expr(parser, current(parser).span, "expected a package member after '.'")
|
||||
@@ -914,11 +1008,22 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
||||
call := parse_call(parser, qualifier, first, name, nesting, intrinsic)
|
||||
if !intrinsic && current(parser).kind == .Left_Brace && !(parser.no_struct_literal && parser.delimiter_depth == 0) {
|
||||
left_brace := advance(parser)
|
||||
args, right_brace := parse_keyed_initializers(parser, left_brace, nesting, "expected '}' after struct literal")
|
||||
skip_newlines(parser)
|
||||
keyed_start := (current(parser).kind == .Identifier || token.is_keyword(current(parser).kind)) &&
|
||||
(peek(parser).kind == .Equal || peek(parser).kind == .Right_Brace || token.is_keyword(current(parser).kind))
|
||||
positional := current(parser).kind == .Right_Brace || !keyed_start
|
||||
args: []ast.Expr_Id
|
||||
right_brace: token.Token
|
||||
if positional {
|
||||
args, right_brace = parse_positional_initializers(parser, left_brace, nesting, "expected '}' after tuple literal")
|
||||
} else {
|
||||
args, right_brace = parse_keyed_initializers(parser, left_brace, nesting, "expected '}' after struct literal")
|
||||
}
|
||||
return add_expr(parser, ast.Expr{
|
||||
kind=.Struct_Literal,
|
||||
span=span_from(parser.module.exprs[call].span, right_brace.span),
|
||||
args=args,
|
||||
tuple=positional,
|
||||
left=call,
|
||||
right=ast.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
@@ -1113,6 +1218,26 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
|
||||
}
|
||||
if current(parser).kind == .Dot {
|
||||
advance(parser)
|
||||
if current(parser).kind == .Integer {
|
||||
field := advance(parser)
|
||||
text := token_text(parser, field)
|
||||
index, index_ok := parse_integer_magnitude(text)
|
||||
left_expr := parser.module.exprs[left]
|
||||
if !index_ok || (len(text) > 1 && text[0] == '0') {
|
||||
left = invalid_expr(parser, field.span, "tuple field indices must be canonical decimal integers")
|
||||
} else {
|
||||
left = add_expr(parser, ast.Expr{
|
||||
kind=.Field,
|
||||
span=span_from(left_expr.span, field.span),
|
||||
name=symbol.INVALID,
|
||||
integer=index,
|
||||
left=left,
|
||||
right=ast.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
continue
|
||||
}
|
||||
field, field_ok := parse_member_name(parser)
|
||||
if !field_ok {
|
||||
left = invalid_expr(parser, field.span, "expected a field name after '.'")
|
||||
@@ -1544,6 +1669,14 @@ parse_value_control_flow :: proc(parser: ^Parser) -> (ast.Stmt_Id, bool) {
|
||||
}
|
||||
|
||||
parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
if current(parser).kind == .Identifier && token_text(parser, current(parser)) == "inline" &&
|
||||
peek(parser).kind == .Keyword_For {
|
||||
start := advance(parser)
|
||||
id := parse_for(parser)
|
||||
parser.module.statements[id].inline = true
|
||||
parser.module.statements[id].span = span_from(start.span, parser.module.statements[id].span)
|
||||
return id
|
||||
}
|
||||
if current(parser).kind == .Keyword_Return {
|
||||
return parse_return(parser)
|
||||
}
|
||||
@@ -1623,7 +1756,7 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
}
|
||||
// A `{` on the right is a value block: parse its statements now; the
|
||||
// checker turns its final `yield` into the declared/assigned value.
|
||||
if current(parser).kind == .Left_Brace {
|
||||
if current(parser).kind == .Left_Brace && !brace_starts_tuple(parser) {
|
||||
brace := current(parser)
|
||||
body := parse_block(parser)
|
||||
id := ast.stmt_id(len(parser.module.statements))
|
||||
@@ -1697,7 +1830,7 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
return id
|
||||
}
|
||||
// A value block assigned to a complex target (`a[i] = { ... }`, `p.f = { ... }`).
|
||||
if current(parser).kind == .Left_Brace {
|
||||
if current(parser).kind == .Left_Brace && !brace_starts_tuple(parser) {
|
||||
brace := current(parser)
|
||||
body := parse_block(parser)
|
||||
id := ast.stmt_id(len(parser.module.statements))
|
||||
@@ -2339,26 +2472,51 @@ parse_record_body :: proc(
|
||||
expected_open: string,
|
||||
allow_anonymous_struct_payload := false,
|
||||
allow_keyword_names := false,
|
||||
tuple_result: ^bool = nil,
|
||||
) -> bool {
|
||||
if _, ok := allow(parser, .Left_Brace); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, expected_open)
|
||||
return false
|
||||
}
|
||||
skip_newlines(parser)
|
||||
mode := 0 // 0 unknown, 1 named, 2 tuple
|
||||
for current(parser).kind != .Right_Brace && current(parser).kind != .Eof {
|
||||
field_name, field_ok := parse_member_name(parser, allow_keyword_names)
|
||||
if !field_ok {
|
||||
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)
|
||||
unnamed := false
|
||||
if !allow_keyword_names {
|
||||
if current(parser).kind == .Identifier {
|
||||
next := peek(parser).kind
|
||||
unnamed = next == .Comma || next == .Newline || next == .Right_Brace ||
|
||||
next == .Dot || next == .Left_Paren
|
||||
} else {
|
||||
unnamed = is_type_token(current(parser).kind)
|
||||
}
|
||||
skip_newlines(parser)
|
||||
continue
|
||||
}
|
||||
field_type := parse_record_field_type(parser, allow_anonymous_struct_payload)
|
||||
append(fields, types.Field{name=u32(field_name.symbol), type=field_type})
|
||||
if unnamed {
|
||||
if mode == 1 {
|
||||
source.add(parser.diagnostics, current(parser).span, "struct fields cannot mix named and unnamed forms")
|
||||
}
|
||||
mode = 2
|
||||
field_type := parse_type(parser)
|
||||
append(fields, types.Field{name=0, type=field_type})
|
||||
} else {
|
||||
if mode == 2 {
|
||||
source.add(parser.diagnostics, current(parser).span, "struct fields cannot mix named and unnamed forms")
|
||||
}
|
||||
mode = 1
|
||||
field_name, field_ok := parse_member_name(parser, allow_keyword_names)
|
||||
if !field_ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected a struct field name or tuple element type")
|
||||
for current(parser).kind != .Newline &&
|
||||
current(parser).kind != .Right_Brace &&
|
||||
current(parser).kind != .Eof {
|
||||
advance(parser)
|
||||
}
|
||||
skip_newlines(parser)
|
||||
continue
|
||||
}
|
||||
field_type := parse_record_field_type(parser, allow_anonymous_struct_payload)
|
||||
append(fields, types.Field{name=u32(field_name.symbol), type=field_type})
|
||||
}
|
||||
if _, ok := allow(parser, .Comma); ok {
|
||||
skip_newlines(parser)
|
||||
continue
|
||||
@@ -2368,6 +2526,9 @@ parse_record_body :: proc(
|
||||
if _, ok := allow(parser, .Right_Brace); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '}' after struct fields")
|
||||
}
|
||||
if tuple_result != nil {
|
||||
tuple_result^ = mode == 2
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -2459,17 +2620,23 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout, file_hidden:
|
||||
fields.allocator = parser.module.allocator
|
||||
defer delete(fields)
|
||||
allow_anonymous_struct_payload := is_union && (inferred_tag || types.is_valid(declared_tag))
|
||||
tuple := false
|
||||
_ = parse_record_body(
|
||||
parser,
|
||||
&fields,
|
||||
"expected '{' after struct fields",
|
||||
allow_anonymous_struct_payload,
|
||||
allow_anonymous_struct_payload,
|
||||
&tuple,
|
||||
)
|
||||
if tuple && (c_layout || is_union) {
|
||||
source.add(parser.diagnostics, start.span, "unnamed fields are only supported by native structs")
|
||||
tuple = false
|
||||
}
|
||||
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, declared_tag=declared_tag) {
|
||||
if !types.define_record(&parser.module.type_store, id, fields[:], c_layout, false, is_union, tag=tag, declared_tag=declared_tag, tuple=tuple) {
|
||||
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||
}
|
||||
_ = finish_statement(parser)
|
||||
|
||||
Reference in New Issue
Block a user