reflection foundation, tuples, debug.print

This commit is contained in:
2026-07-14 23:45:30 +02:00
parent 267947e79d
commit 0b2055d64b
24 changed files with 126598 additions and 115394 deletions
+186 -19
View File
@@ -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)