feat: complete memory representation gate

This commit is contained in:
2026-10-26 11:49:25 +01:00
parent 7856f4f963
commit cd64afbb40
16 changed files with 2103 additions and 159 deletions
+136 -15
View File
@@ -1,17 +1,17 @@
#! grammar:
#! * type ..... -> identifier {'.' member} | ('@' | '*') ['mut'] type | '@proc' '(' parameters ')' type | '[' (expr | '_') ']' ['mut'] type | 'struct' '{' fields '}' | 'enum' ['(' type ')'] '{' members '}' | 'union' ['(' (type | 'enum') ')'] '{' fields '}' | ('distinct' | 'alias') type
#! * type ..... -> identifier {'.' member} | '?' type | ('@' | '*') ['mut'] type | '@proc' '(' parameters ')' type | '[' (expr | '_') ']' ['mut'] type | '[' [';' expr] ']' ['mut'] type | 'struct' '{' fields '}' | 'enum' ['(' type ')'] '{' members '}' | 'union' ['(' (type | 'enum') ')'] '{' fields '}' | ('distinct' | 'alias') type
#! * binding ... -> :: | :=
#! * literal .. -> int | float | string | 'true' | 'false'
#! * literal .. -> int | float | string | 'true' | 'false' | 'null'
#! * op ....... -> '+' | '-' | '*' | '/' | '==' | '!=' | '<' | '<=' | '>' | '>=' | 'and' | 'or'
#! * expr ..... -> identifier | literal | type | '.' member | '[' [expr {',' expr} [',']] ']' | [type] '{' [initializer {',' initializer} [',']] '}' | '(' expr ')' | ('-' | '!' | '&') expr | expr '^' | expr op expr | expr '(' [expr {',' expr} [',']] ')' | expr '[' expr ']' | expr '.' (member | int) | expr ('..' | '..=') expr | 'match' expr '{' match_arms '}'
#! * decl ..... -> identifier [type] binding expr | ['test'] [identifier '::'] 'import' string
#! * expr ..... -> identifier | literal | type | '.' member | '[' [expr {',' expr} [',']] ']' | [type] '{' [initializer {',' initializer} [',']] '}' | '(' expr ')' | ('-' | '!' | '&') expr | expr ('^' | '?') | expr op expr | expr '(' [expr {',' expr} [',']] ')' | identifier '!' '(' [expr {',' expr} [',']] ')' | expr '[' expr ']' | expr '[' [expr] '..' [expr] [';' expr] ']' | expr '.' (member | int) | expr ('..' | '..=') expr | 'match' expr '{' match_arms '}'
#! * decl ..... -> identifier [type] binding expr | identifier 'c_func' '(' parameters ')' type | ['test'] [identifier '::'] 'import' string
#! * block .... -> '{' {stmt} '}'
#! * arm ...... -> block | stmt (single statements become canonical blocks)
#! * match_arm -> (expr {',' expr} ['|' identifier '|'] | 'else') ':' (block | stmt | expr); arms are comma/newline separated.
#! enum members and tagged-union variants accept keyword names; member names do not enter lexical scopes.
#! * stmt ..... -> decl | expr | expr '=' expr | 'return' [expr] | 'break' | 'continue' | 'if' expr arm ['else' arm] | 'while' expr arm | 'for' expr '|' ['@'] identifier [',' identifier] '|' arm
#! braceless guards/iterables require parentheses unless they are calls; else binds to the nearest if.
#! precedence (tightest first): calls/indices/selections/dereference, unary -/!/& higher than multiplication/division, addition/subtraction, relational comparisons, equality, and, or, ranges. compound range endpoints require their own parentheses.
#! * stmt ..... -> decl | expr | expr '=' expr | 'return' [expr] | 'break' | 'continue' | 'if' expr ['|' identifier '|'] arm ['else' arm] | 'while' expr ['|' identifier '|'] arm | 'for' expr '|' ['@'] identifier [',' identifier] '|' arm
#! braceless guards/iterables require parentheses unless they are calls or optional captures; else binds to the nearest if.
#! precedence (tightest first): calls/indices/selections/dereference/unwrap, unary -/!/& higher than multiplication/division, addition/subtraction, relational comparisons, equality, and, or, ranges. compound range endpoints require their own parentheses.
#! binary operators associate left; unary operators associate right.
import "@std/debug"
@@ -30,7 +30,7 @@ NodeId :: distinct u32
no_node :: maxval!(NodeId)
LiteralKind :: enum {
int, float, string, bool
int, float, string, bool, null
}
ExprKind :: enum {
@@ -58,6 +58,11 @@ ExprKind :: enum {
index
selection
invalid
slice_type
optional_type
slice
builtin_call
literal_null
}
DeclKind :: enum {
@@ -89,6 +94,7 @@ UnaryOperator :: enum {
logical_not
address_of
dereference
unwrap
}
BinaryOperator :: enum {
@@ -144,6 +150,11 @@ NodeKind :: enum {
invalid_expr
invalid_stmt
invalid_decl
expr_slice_type
expr_optional_type
expr_slice
expr_builtin_call
literal_null
}
@hide:file
@@ -246,6 +257,21 @@ PointerTypeView :: struct {
many bool
}
SliceTypeView :: struct {
element NodeId
mutable bool
sentinel ?NodeId
}
SliceView :: struct {
base NodeId
start ?NodeId
end ?NodeId
sentinel ?NodeId
}
BuiltinCallView :: struct { name NodeId }
RecordLiteralView :: struct {
span Span
type ?NodeId
@@ -290,6 +316,7 @@ IfView :: struct {
condition NodeId
then_block NodeId
else_block ?NodeId
capture ?NodeId = null
}
AssignmentView :: struct {
@@ -302,6 +329,7 @@ WhileView :: struct {
span Span
condition NodeId
body NodeId
capture ?NodeId = null
}
RangeView :: struct {
@@ -413,6 +441,7 @@ add_literal proc(builder @mut Builder, kind LiteralKind, span Span) NodeId ! mem
.float: .literal_float
.string: .literal_string
.bool: .literal_bool
.null: .literal_null
}
return try add_node(builder, Node{ kind = node_kind }, span)
}
@@ -484,6 +513,39 @@ add_call proc(
}, span)
}
add_builtin_call proc(builder @mut Builder, span Span, name NodeId, arguments []NodeId) NodeId ! mem.AllocError {
id :: try add_call(builder, span, name, arguments)
builder.storage.nodes.items[usize(id)].kind = .expr_builtin_call
return id
}
add_optional_type proc(builder @mut Builder, span Span, element NodeId) NodeId ! mem.AllocError {
return try add_node(builder, Node{ kind = .expr_optional_type, main = NodeData{ node_id = element } }, span)
}
add_slice_type proc(builder @mut Builder, span Span, element NodeId, mutable bool, sentinel ?NodeId) NodeId ! mem.AllocError {
flag u32 :: if (mutable) 1 else 0
raw_sentinel :: if (sentinel) |id| id else no_node
return try add_node(builder, Node{
kind = .expr_slice_type,
main = NodeData{ extra = flag },
data0 = NodeData{ node_id = raw_sentinel },
data1 = NodeData{ node_id = element },
}, span)
}
add_slice proc(builder @mut Builder, span Span, base NodeId, start, end, sentinel ?NodeId) NodeId ! mem.AllocError {
raw_start :: if (start) |id| u32(id) else u32(no_node)
raw_end :: if (end) |id| u32(id) else u32(no_node)
raw_sentinel :: if (sentinel) |id| u32(id) else u32(no_node)
extra :: try add_extra(builder, raw_start)
_ = try add_extra(builder, raw_end)
_ = try add_extra(builder, raw_sentinel)
return try add_node(builder, Node{
kind = .expr_slice, main = NodeData{ node_id = base }, data0 = NodeData{ extra = extra },
}, span)
}
add_array_type proc(
builder @mut Builder, span Span, count ?NodeId, element NodeId, mutable bool,
) NodeId ! mem.AllocError {
@@ -697,9 +759,11 @@ add_if_stmt proc(
else_block ?NodeId,
) NodeId ! mem.AllocError {
alternative :: if (else_block) |id| id else no_node
extra :: try add_extra(builder, u32(condition))
_ = try add_extra(builder, u32(no_node))
return try add_node(builder, Node{
kind = .stmt_if,
main = NodeData{ node_id = condition },
main = NodeData{ extra = extra },
data0 = NodeData{ node_id = then_block },
data1 = NodeData{ node_id = alternative },
}, span)
@@ -731,6 +795,15 @@ add_while_stmt proc(
}, span)
}
set_capture proc(builder @mut Builder, id NodeId, capture NodeId) void {
node :: &builder.storage.nodes.items[usize(id)]
if (node.kind == .stmt_if) builder.storage.extra.items[usize(node.main.extra) + 1] = u32(capture)
else {
debug.assert(node.kind == .stmt_while)
node.data1.node_id = capture
}
}
add_for_stmt proc(
builder @mut Builder, span Span, iterable NodeId, capture, index ?NodeId,
pointer_capture, index_capture bool, body NodeId,
@@ -804,6 +877,7 @@ add_function_decl proc(
_ = try add_extra(builder, u32(return_type))
_ = try add_extra(builder, u32(interned_name))
for (params) |parameter| _ = try add_extra(builder, u32(parameter))
_ = try add_extra(builder, 0)
return try add_node(builder, Node{
kind = .func_decl,
main = NodeData{ extra = u32(start) },
@@ -812,6 +886,18 @@ add_function_decl proc(
}, span)
}
set_function_external proc(builder @mut Builder, id NodeId) void {
node :: builder.storage.nodes.items[usize(id)]
debug.assert(node.kind == .func_decl)
builder.storage.extra.items[usize(node.data1.extra) - 1] = 1
}
function_external proc(tree @Tree, id NodeId) bool {
node :: get_node(&tree.storage, id)
if (node.kind != .func_decl) return false
return tree.storage.extra.items[usize(node.data1.extra) - 1] != 0
}
add_import_decl proc(
builder @mut Builder,
span, path Span,
@@ -980,9 +1066,10 @@ if_stmt proc(tree @Tree, id NodeId) IfView {
debug.assert(node.kind == .stmt_if)
return IfView{
span = get_span(&tree.storage, id),
condition = node.main.node_id,
condition = NodeId(tree.storage.extra.items[usize(node.main.extra)]),
then_block = node.data0.node_id,
else_block = maybe_node_id(u32(node.data1.node_id)),
capture = maybe_node_id(tree.storage.extra.items[usize(node.main.extra) + 1]),
}
}
@@ -1003,6 +1090,7 @@ while_stmt proc(tree @Tree, id NodeId) WhileView {
span = get_span(&tree.storage, id),
condition = node.main.node_id,
body = node.data0.node_id,
capture = maybe_node_id(u32(node.data1.node_id)),
}
}
@@ -1071,14 +1159,15 @@ function_param_count proc(tree @Tree, id NodeId) usize {
node :: get_node(&tree.storage, id)
debug.assert(node.kind == .func_decl or node.kind == .expr_function_type)
start :: if (node.kind == .func_decl) node.main.extra + 4 else node.data0.extra
return usize(node.data1.extra - start)
suffix :: if (node.kind == .func_decl) u32(1) else u32(0)
return usize(node.data1.extra - start - suffix)
}
function_param proc(tree @Tree, id NodeId, index usize) NodeId {
node :: get_node(&tree.storage, id)
debug.assert(node.kind == .func_decl or node.kind == .expr_function_type)
start :: if (node.kind == .func_decl) node.main.extra + 4 else node.data0.extra
debug.assert(index < usize(node.data1.extra - start))
debug.assert(index < function_param_count(tree, id))
return NodeId(tree.storage.extra.items[usize(start) + index])
}
@@ -1140,6 +1229,11 @@ expression_kind proc(kind NodeKind) ExprKind {
.expr_index: .index
.expr_selection: .selection
.invalid_expr: .invalid
.expr_slice_type: .slice_type
.expr_optional_type: .optional_type
.expr_slice: .slice
.expr_builtin_call: .builtin_call
.literal_null: .literal_null
else: unreachable
}
}
@@ -1187,13 +1281,13 @@ call_expr proc(tree @Tree, id NodeId) CallExprView {
call_arg_count proc(tree @Tree, id NodeId) usize {
node :: get_node(&tree.storage, id)
debug.assert(node.kind == .expr_call)
debug.assert(node.kind == .expr_call or node.kind == .expr_builtin_call)
return usize(node.data1.extra - node.data0.extra)
}
call_arg proc(tree @Tree, id NodeId, index usize) NodeId {
node :: get_node(&tree.storage, id)
debug.assert(node.kind == .expr_call)
debug.assert(node.kind == .expr_call or node.kind == .expr_builtin_call)
debug.assert(index < usize(node.data1.extra - node.data0.extra))
return NodeId(tree.storage.extra.items[usize(node.data0.extra) + index])
}
@@ -1219,6 +1313,33 @@ pointer_type proc(tree @Tree, id NodeId) PointerTypeView {
}
}
slice_type proc(tree @Tree, id NodeId) SliceTypeView {
node :: get_node(&tree.storage, id)
debug.assert(node.kind == .expr_slice_type)
return SliceTypeView{
element = node.data1.node_id, mutable = node.main.extra != 0,
sentinel = maybe_node_id(u32(node.data0.node_id)),
}
}
slice_expr proc(tree @Tree, id NodeId) SliceView {
node :: get_node(&tree.storage, id)
debug.assert(node.kind == .expr_slice)
extra :: usize(node.data0.extra)
return SliceView{
base = node.main.node_id,
start = maybe_node_id(tree.storage.extra.items[extra]),
end = maybe_node_id(tree.storage.extra.items[extra + 1]),
sentinel = maybe_node_id(tree.storage.extra.items[extra + 2]),
}
}
builtin_call proc(tree @Tree, id NodeId) BuiltinCallView {
node :: get_node(&tree.storage, id)
debug.assert(node.kind == .expr_builtin_call)
return BuiltinCallView{ name = node.main.node_id }
}
array_element_count proc(tree @Tree, id NodeId) usize {
node :: get_node(&tree.storage, id)
debug.assert(node.kind == .expr_array_literal)
@@ -1282,7 +1403,7 @@ union_type proc(tree @Tree, id NodeId) UnionTypeView {
type_operand proc(tree @Tree, id NodeId) NodeId {
node :: get_node(&tree.storage, id)
debug.assert(node.kind == .expr_distinct_type or node.kind == .expr_alias)
debug.assert(node.kind == .expr_distinct_type or node.kind == .expr_alias or node.kind == .expr_optional_type)
return node.main.node_id
}