2185 lines
70 KiB
Plaintext
2185 lines
70 KiB
Plaintext
#! grammar:
|
|
#! * type ..... -> type_sum ['!' type]; type_sum -> type_atom {'|' type_atom}; type_atom includes '(' type ')', identifier {'.' member | '(' [expr {',' expr} [',']] ')'}, '?' type_atom, ('@' | '*') ['mut'] type_atom, ['@'] 'proc' '(' parameters ')' type, '[' (expr | '_') ']' ['mut'] type_atom, '[' [';' expr] ']' ['mut'] type_atom, record/enum/union declarations, and ('distinct' | 'alias') type.
|
|
#! * binding ... -> :: | :=
|
|
#! * literal .. -> int | float | string | 'true' | 'false' | 'null'
|
|
#! * op ....... -> '+' | '-' | '*' | '/' | '==' | '!=' | '<' | '<=' | '>' | '>=' | 'and' | 'or'
|
|
#! * expr ..... -> identifier | literal | type | '.' member | '[' [expr {',' expr} [',']] ']' | [type] '{' [initializer {',' initializer} [',']] '}' | '(' expr ')' | ('-' | '!' | '&' | 'try') 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 '}' | expr 'catch' ['|' identifier '|'] (expr | block)
|
|
#! * decl ..... -> identifier [type] binding expr | identifier 'c_func' '(' parameters ')' type | ['test'] [identifier '::'] 'import' string
|
|
#! * parameters -> [['$'] identifier {',' ['$'] identifier} type {',' ['$'] identifier {',' ['$'] identifier} type} [',']]; '$' marks each compile-time parameter independently.
|
|
#! '$(' expr ')' is an explicit compile-time expression with primary-expression precedence.
|
|
#! * 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 | block | ('defer' | 'errdefer' ['|' identifier '|']) arm | 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 -/!/&/try higher than multiplication/division, addition/subtraction, relational comparisons, equality, and, or, ranges/catch. type composition | binds tighter than fallible !; pointer/optional/array prefixes bind to type atoms. compound range endpoints require their own parentheses.
|
|
#! binary operators associate left; unary operators associate right.
|
|
|
|
import "@std/debug"
|
|
import "@std/mem"
|
|
import "@std/arraylist"
|
|
import "@source/diagnostics"
|
|
import "@source/strpool"
|
|
|
|
Span :: alias diagnostics.Span
|
|
DiagnosticId :: alias diagnostics.DiagnosticId
|
|
no_diagnostic :: alias diagnostics.no_diagnostic
|
|
|
|
NodeId :: distinct u32
|
|
|
|
@hide:file
|
|
no_node :: maxval!(NodeId)
|
|
|
|
LiteralKind :: enum {
|
|
int, float, string, bool, null
|
|
}
|
|
|
|
ExprKind :: enum {
|
|
literal_int
|
|
literal_float
|
|
literal_string
|
|
literal_bool
|
|
identifier
|
|
unary
|
|
binary
|
|
call
|
|
range
|
|
array_type
|
|
pointer_type
|
|
function_type
|
|
array_literal
|
|
record_type
|
|
record_literal
|
|
enum_type
|
|
union_type
|
|
distinct_type
|
|
alias_expr
|
|
enum_literal
|
|
match_expr
|
|
index
|
|
selection
|
|
invalid
|
|
slice_type
|
|
optional_type
|
|
slice
|
|
builtin_call
|
|
literal_null
|
|
fallible_type
|
|
sum_type
|
|
try_expr
|
|
catch_expr
|
|
typed_constant
|
|
comptime_expr
|
|
}
|
|
|
|
DeclKind :: enum {
|
|
statement
|
|
function
|
|
import
|
|
invalid
|
|
}
|
|
|
|
StmtKind :: enum {
|
|
declaration
|
|
expression
|
|
assignment_stmt
|
|
return_stmt
|
|
break_stmt
|
|
continue_stmt
|
|
if_stmt
|
|
while_stmt
|
|
for_stmt
|
|
defer_stmt
|
|
errdefer_stmt
|
|
block_stmt
|
|
invalid
|
|
}
|
|
|
|
Mutability :: enum {
|
|
constant, variable
|
|
}
|
|
|
|
UnaryOperator :: enum {
|
|
negate
|
|
logical_not
|
|
address_of
|
|
dereference
|
|
unwrap
|
|
}
|
|
|
|
BinaryOperator :: enum {
|
|
add, subtract
|
|
multiply, divide
|
|
equal, not_equal
|
|
less, less_equal
|
|
greater, greater_equal
|
|
logical_and, logical_or
|
|
}
|
|
|
|
@hide:file
|
|
NodeKind :: enum {
|
|
literal_int
|
|
literal_float
|
|
literal_string
|
|
literal_bool
|
|
expr_identifier
|
|
expr_unary
|
|
expr_binary
|
|
expr_call
|
|
expr_range
|
|
expr_array_type
|
|
expr_pointer_type
|
|
expr_function_type
|
|
expr_array_literal
|
|
expr_record_type
|
|
expr_record_literal
|
|
record_field
|
|
expr_enum_type
|
|
expr_union_type
|
|
expr_distinct_type
|
|
expr_alias
|
|
expr_enum_literal
|
|
expr_match
|
|
match_arm
|
|
expr_index
|
|
expr_selection
|
|
stmt_expr
|
|
stmt_decl
|
|
stmt_return
|
|
stmt_break
|
|
stmt_continue
|
|
stmt_if
|
|
stmt_assignment
|
|
stmt_while
|
|
stmt_for
|
|
func_decl
|
|
param_decl
|
|
import_decl
|
|
block
|
|
package
|
|
invalid_expr
|
|
invalid_stmt
|
|
invalid_decl
|
|
expr_slice_type
|
|
expr_optional_type
|
|
expr_slice
|
|
expr_builtin_call
|
|
literal_null
|
|
expr_fallible_type
|
|
expr_sum_type
|
|
expr_try
|
|
expr_catch
|
|
stmt_defer
|
|
stmt_errdefer
|
|
stmt_block
|
|
expr_typed_constant
|
|
expr_comptime
|
|
literal_float_value
|
|
}
|
|
|
|
@hide:file
|
|
NodeData :: union {
|
|
node_id NodeId
|
|
extra u32
|
|
name_id strpool.StringId
|
|
diagnostic DiagnosticId
|
|
mutability Mutability
|
|
unary_operator UnaryOperator
|
|
binary_operator BinaryOperator
|
|
}
|
|
|
|
@hide:file
|
|
Node :: struct {
|
|
kind NodeKind
|
|
main NodeData = NodeData{ node_id = no_node }
|
|
data0 NodeData = NodeData{ node_id = no_node }
|
|
data1 NodeData = NodeData{ node_id = no_node }
|
|
}
|
|
|
|
@hide:file
|
|
Storage :: struct {
|
|
nodes arraylist.ArrayList(Node)
|
|
extra arraylist.ArrayList(u32)
|
|
spans arraylist.ArrayList(Span)
|
|
sources arraylist.ArrayList(diagnostics.Source)
|
|
generated arraylist.ArrayList([]mut u8)
|
|
}
|
|
|
|
#! owns node storage, source descriptors, and generated text; borrows registered source paths and text.
|
|
Builder :: struct {
|
|
storage Storage
|
|
source []u8
|
|
source_id diagnostics.SourceId = diagnostics.SourceId(0)
|
|
}
|
|
|
|
#! owns storage after finish, or borrows it after view; source paths and non-generated text remain borrowed.
|
|
Tree :: struct {
|
|
storage Storage
|
|
root NodeId
|
|
}
|
|
|
|
#! transient projection, not stored per import; test_only decodes an existing Node payload word.
|
|
ImportView :: struct {
|
|
span Span
|
|
path Span
|
|
namespace ?strpool.StringId
|
|
test_only bool
|
|
}
|
|
|
|
StatementDeclView :: struct {
|
|
span Span
|
|
name Span
|
|
type ?NodeId
|
|
mutability Mutability
|
|
initializer NodeId
|
|
}
|
|
|
|
FunctionDeclView :: struct {
|
|
span Span
|
|
name Span
|
|
return_type NodeId
|
|
body NodeId
|
|
}
|
|
|
|
ParamView :: struct {
|
|
span Span
|
|
name Span
|
|
type NodeId
|
|
comptime bool = false
|
|
}
|
|
|
|
UnaryExprView :: struct {
|
|
span Span
|
|
operator UnaryOperator
|
|
operand NodeId
|
|
}
|
|
|
|
BinaryExprView :: struct {
|
|
span Span
|
|
operator BinaryOperator
|
|
left NodeId
|
|
right NodeId
|
|
}
|
|
|
|
TypedConstantView :: struct {
|
|
type NodeId
|
|
value NodeId
|
|
}
|
|
|
|
CatchExprView :: struct {
|
|
span Span
|
|
operand NodeId
|
|
capture ?NodeId
|
|
handler NodeId
|
|
handler_is_block bool
|
|
}
|
|
|
|
CleanupView :: struct {
|
|
span Span
|
|
body NodeId
|
|
capture ?NodeId
|
|
}
|
|
|
|
CallExprView :: struct {
|
|
span Span
|
|
callee NodeId
|
|
}
|
|
|
|
ArrayTypeView :: struct {
|
|
span Span
|
|
count ?NodeId
|
|
element NodeId
|
|
mutable bool
|
|
}
|
|
|
|
PointerTypeView :: struct {
|
|
element NodeId
|
|
mutable bool
|
|
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
|
|
}
|
|
|
|
FieldView :: struct {
|
|
span Span
|
|
name ?NodeId
|
|
type ?NodeId
|
|
value ?NodeId
|
|
}
|
|
|
|
EnumTypeView :: struct { backing ?NodeId }
|
|
UnionTypeView :: struct { tag ?NodeId, auto_tag bool }
|
|
MatchView :: struct { subject NodeId }
|
|
MatchArmView :: struct {
|
|
capture ?NodeId
|
|
body NodeId
|
|
expression bool
|
|
catch_all bool
|
|
}
|
|
|
|
IndexView :: struct {
|
|
span Span
|
|
base NodeId
|
|
index NodeId
|
|
}
|
|
|
|
SelectionView :: struct {
|
|
span Span
|
|
base NodeId
|
|
member NodeId
|
|
}
|
|
|
|
ReturnView :: struct {
|
|
span Span
|
|
value ?NodeId
|
|
}
|
|
|
|
IfView :: struct {
|
|
span Span
|
|
condition NodeId
|
|
then_block NodeId
|
|
else_block ?NodeId
|
|
capture ?NodeId = null
|
|
}
|
|
|
|
AssignmentView :: struct {
|
|
span Span
|
|
target NodeId
|
|
value NodeId
|
|
}
|
|
|
|
WhileView :: struct {
|
|
span Span
|
|
condition NodeId
|
|
body NodeId
|
|
capture ?NodeId = null
|
|
}
|
|
|
|
RangeView :: struct {
|
|
span Span
|
|
start NodeId
|
|
end NodeId
|
|
inclusive bool
|
|
}
|
|
|
|
ForView :: struct {
|
|
span Span
|
|
iterable NodeId
|
|
capture ?NodeId
|
|
index ?NodeId
|
|
pointer_capture bool
|
|
index_capture bool
|
|
body NodeId
|
|
}
|
|
|
|
InvalidView :: struct {
|
|
span Span
|
|
diagnostic DiagnosticId
|
|
}
|
|
|
|
#! retains `allocator` for node storage and borrows `source`; release with deinit_builder.
|
|
init_builder proc(allocator mem.Allocator, source []u8) Builder {
|
|
return Builder{
|
|
storage = Storage{
|
|
nodes = arraylist.init(allocator),
|
|
extra = arraylist.init(allocator),
|
|
spans = arraylist.init(allocator),
|
|
sources = arraylist.init(allocator),
|
|
generated = arraylist.init(allocator),
|
|
},
|
|
source = source,
|
|
}
|
|
}
|
|
|
|
#! registers borrowed `path` and `text`; both must remain valid until deinit_builder or deinit.
|
|
set_source proc(
|
|
builder @mut Builder, path, text []u8,
|
|
) diagnostics.SourceId ! mem.AllocError {
|
|
id :: diagnostics.SourceId(builder.storage.sources.items.len)
|
|
try arraylist.append(&builder.storage.sources,
|
|
diagnostics.Source{
|
|
path = path,
|
|
text = text
|
|
})
|
|
builder.source = text
|
|
builder.source_id = id
|
|
return id
|
|
}
|
|
|
|
#! removes the latest source after its failed parse has rolled back all nodes.
|
|
remove_source proc(builder @mut Builder) void {
|
|
debug.assert(builder.storage.sources.items.len > 0)
|
|
_ = arraylist.pop(&builder.storage.sources)
|
|
if (builder.storage.sources.items.len == 0) {
|
|
builder.source = &[]
|
|
builder.source_id = diagnostics.SourceId(0)
|
|
return
|
|
}
|
|
builder.source_id = diagnostics.SourceId(builder.storage.sources.items.len - 1)
|
|
builder.source = builder.storage.sources.items[usize(builder.source_id)].text
|
|
}
|
|
|
|
#! borrows builder storage; refresh after builder mutation and never pass this view to deinit.
|
|
view proc(builder @Builder, root NodeId) Tree {
|
|
return Tree{ storage = builder.storage, root = root }
|
|
}
|
|
|
|
source_files proc(tree @Tree) []diagnostics.Source {
|
|
return tree.storage.sources.items
|
|
}
|
|
|
|
#! releases owned AST, generated text, and source descriptors, without freeing borrowed sources.
|
|
deinit_builder proc(builder @mut Builder) void {
|
|
for (builder.storage.generated.items) |text| mem.free(builder.storage.generated.allocator, text)
|
|
arraylist.deinit(&builder.storage.generated)
|
|
arraylist.deinit(&builder.storage.nodes)
|
|
arraylist.deinit(&builder.storage.extra)
|
|
arraylist.deinit(&builder.storage.spans)
|
|
arraylist.deinit(&builder.storage.sources)
|
|
}
|
|
|
|
#! transfers storage and source borrows to a caller-owned Tree; release with deinit.
|
|
finish proc(builder @mut Builder, root NodeId) Tree {
|
|
tree :: view(builder, root)
|
|
allocator :: builder.storage.nodes.allocator
|
|
builder.storage = Storage{
|
|
nodes = arraylist.init(allocator),
|
|
extra = arraylist.init(allocator),
|
|
spans = arraylist.init(allocator),
|
|
sources = arraylist.init(allocator),
|
|
generated = arraylist.init(allocator),
|
|
}
|
|
builder.source = &[]
|
|
builder.source_id = diagnostics.SourceId(0)
|
|
return tree
|
|
}
|
|
|
|
#! releases owned AST, generated text, and source descriptors, without freeing borrowed sources.
|
|
deinit proc(tree @mut Tree) void {
|
|
for (tree.storage.generated.items) |text| mem.free(tree.storage.generated.allocator, text)
|
|
arraylist.deinit(&tree.storage.generated)
|
|
arraylist.deinit(&tree.storage.nodes)
|
|
arraylist.deinit(&tree.storage.extra)
|
|
arraylist.deinit(&tree.storage.spans)
|
|
arraylist.deinit(&tree.storage.sources)
|
|
}
|
|
|
|
add_literal proc(builder @mut Builder, kind LiteralKind, span Span) NodeId ! mem.AllocError {
|
|
node_kind :: match kind {
|
|
.int: .literal_int
|
|
.float: .literal_float
|
|
.string: .literal_string
|
|
.bool: .literal_bool
|
|
.null: .literal_null
|
|
}
|
|
return try add_node(builder, Node{ kind = node_kind }, span)
|
|
}
|
|
|
|
add_identifier proc(
|
|
builder @mut Builder,
|
|
span Span,
|
|
interned_name strpool.StringId,
|
|
) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .expr_identifier,
|
|
main = NodeData{ name_id = interned_name },
|
|
}, span)
|
|
}
|
|
|
|
add_unary proc(
|
|
builder @mut Builder,
|
|
span Span,
|
|
operator UnaryOperator,
|
|
operand NodeId,
|
|
) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .expr_unary,
|
|
main = NodeData{ unary_operator = operator },
|
|
data0 = NodeData{ node_id = operand },
|
|
}, span)
|
|
}
|
|
|
|
add_binary proc(
|
|
builder @mut Builder,
|
|
span Span,
|
|
operator BinaryOperator,
|
|
left NodeId,
|
|
right NodeId,
|
|
) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .expr_binary,
|
|
main = NodeData{ binary_operator = operator },
|
|
data0 = NodeData{ node_id = left },
|
|
data1 = NodeData{ node_id = right },
|
|
}, span)
|
|
}
|
|
|
|
#! preserves a generated constant's declared type without introducing a runtime binding.
|
|
add_typed_constant proc(
|
|
builder @mut Builder, at Span, type, value NodeId,
|
|
) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .expr_typed_constant,
|
|
data0 = NodeData{ node_id = type },
|
|
data1 = NodeData{ node_id = value },
|
|
}, at)
|
|
}
|
|
|
|
add_comptime_expr proc(builder @mut Builder, at Span, operand NodeId) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .expr_comptime,
|
|
main = NodeData{ node_id = operand },
|
|
}, at)
|
|
}
|
|
|
|
#! retains the exact IEEE representation, including signed zero and NaN payloads.
|
|
add_float proc(builder @mut Builder, at Span, value f64) NodeId ! mem.AllocError {
|
|
bits :: ptrcast!(@u64, &value)^
|
|
return try add_node(builder, Node{
|
|
kind = .literal_float_value,
|
|
data0 = NodeData{ extra = u32(bits & 4294967295) },
|
|
data1 = NodeData{ extra = u32(bits >> 32) },
|
|
}, at)
|
|
}
|
|
|
|
add_binary_type proc(
|
|
builder @mut Builder, span Span, left, right NodeId, fallible bool,
|
|
) NodeId ! mem.AllocError {
|
|
kind NodeKind :: if (fallible) .expr_fallible_type else .expr_sum_type
|
|
return try add_node(builder, Node{
|
|
kind = kind,
|
|
main = NodeData{ binary_operator = .add },
|
|
data0 = NodeData{ node_id = left },
|
|
data1 = NodeData{ node_id = right },
|
|
}, span)
|
|
}
|
|
|
|
add_try_expr proc(builder @mut Builder, span Span, operand NodeId) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .expr_try,
|
|
main = NodeData{ node_id = operand },
|
|
}, span)
|
|
}
|
|
|
|
add_catch_expr proc(
|
|
builder @mut Builder, span Span, operand NodeId, capture ?NodeId,
|
|
handler NodeId, handler_is_block bool,
|
|
) NodeId ! mem.AllocError {
|
|
capture_id :: if (capture) |id| u32(id) else u32(no_node)
|
|
block_flag :: if (handler_is_block) u32(1) else u32(0)
|
|
extra :: try add_extra(builder, capture_id)
|
|
_ = try add_extra(builder, block_flag)
|
|
return try add_node(builder, Node{
|
|
kind = .expr_catch,
|
|
main = NodeData{ extra = extra },
|
|
data0 = NodeData{ node_id = operand },
|
|
data1 = NodeData{ node_id = handler },
|
|
}, span)
|
|
}
|
|
|
|
add_range proc(
|
|
builder @mut Builder, span Span, start NodeId, end NodeId, inclusive bool,
|
|
) NodeId ! mem.AllocError {
|
|
flag :: if (inclusive) u32(1) else u32(0)
|
|
return try add_node(builder, Node{
|
|
kind = .expr_range,
|
|
main = NodeData{ extra = flag },
|
|
data0 = NodeData{ node_id = start },
|
|
data1 = NodeData{ node_id = end },
|
|
}, span)
|
|
}
|
|
|
|
add_call proc(
|
|
builder @mut Builder,
|
|
span Span,
|
|
callee NodeId,
|
|
arguments []NodeId,
|
|
) NodeId ! mem.AllocError {
|
|
start :: builder.storage.extra.items.len
|
|
for (arguments) |argument| _ = try add_extra(builder, u32(argument))
|
|
return try add_node(builder, Node{
|
|
kind = .expr_call,
|
|
main = NodeData{ node_id = callee },
|
|
data0 = NodeData{ extra = u32(start) },
|
|
data1 = NodeData{ extra = u32(builder.storage.extra.items.len) },
|
|
}, 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 {
|
|
raw_count :: if (count) |id| id else no_node
|
|
flag :: if (mutable) u32(1) else u32(0)
|
|
return try add_node(builder, Node{
|
|
kind = .expr_array_type,
|
|
main = NodeData{ extra = flag },
|
|
data0 = NodeData{ node_id = raw_count },
|
|
data1 = NodeData{ node_id = element },
|
|
}, span)
|
|
}
|
|
|
|
add_pointer_type proc(
|
|
builder @mut Builder,
|
|
span Span,
|
|
element NodeId,
|
|
mutable, many bool,
|
|
) NodeId ! mem.AllocError {
|
|
mutable_flag :: if (mutable) u32(1) else u32(0)
|
|
many_flag :: if (many) u32(1) else u32(0)
|
|
return try add_node(builder, Node{
|
|
kind = .expr_pointer_type,
|
|
main = NodeData{ extra = mutable_flag },
|
|
data0 = NodeData{ extra = many_flag },
|
|
data1 = NodeData{ node_id = element },
|
|
}, span)
|
|
}
|
|
|
|
add_function_type proc(
|
|
builder @mut Builder, span Span, result NodeId, params []NodeId,
|
|
) NodeId ! mem.AllocError {
|
|
start :: builder.storage.extra.items.len
|
|
for (params) |parameter| _ = try add_extra(builder, u32(parameter))
|
|
return try add_node(builder, Node{
|
|
kind = .expr_function_type,
|
|
main = NodeData{ node_id = result },
|
|
data0 = NodeData{ extra = u32(start) },
|
|
data1 = NodeData{ extra = u32(builder.storage.extra.items.len) },
|
|
}, span)
|
|
}
|
|
|
|
add_array_literal proc(builder @mut Builder, span Span, elements []NodeId) NodeId ! mem.AllocError {
|
|
start :: builder.storage.extra.items.len
|
|
for (elements) |element| _ = try add_extra(builder, u32(element))
|
|
return try add_node(builder, Node{
|
|
kind = .expr_array_literal,
|
|
data0 = NodeData{ extra = u32(start) },
|
|
data1 = NodeData{ extra = u32(builder.storage.extra.items.len) },
|
|
}, span)
|
|
}
|
|
|
|
add_record_type proc(builder @mut Builder, span Span, fields []NodeId) NodeId ! mem.AllocError {
|
|
start :: builder.storage.extra.items.len
|
|
for (fields) |field_id| _ = try add_extra(builder, u32(field_id))
|
|
return try add_node(builder, Node{
|
|
kind = .expr_record_type,
|
|
data0 = NodeData{ extra = u32(start) },
|
|
data1 = NodeData{ extra = u32(builder.storage.extra.items.len) },
|
|
}, span)
|
|
}
|
|
|
|
add_enum_type proc(builder @mut Builder, span Span, backing ?NodeId, members []NodeId) NodeId ! mem.AllocError {
|
|
start :: builder.storage.extra.items.len
|
|
for (members) |member| _ = try add_extra(builder, u32(member))
|
|
raw_backing :: if (backing) |id| id else no_node
|
|
return try add_node(builder, Node{
|
|
kind = .expr_enum_type,
|
|
main = NodeData{ node_id = raw_backing },
|
|
data0 = NodeData{ extra = u32(start) },
|
|
data1 = NodeData{ extra = u32(builder.storage.extra.items.len) },
|
|
}, span)
|
|
}
|
|
|
|
add_union_type proc(builder @mut Builder, span Span, tag ?NodeId, auto_tag bool, fields []NodeId) NodeId ! mem.AllocError {
|
|
raw_tag :: if (tag) |id| u32(id) else u32(no_node)
|
|
tag_flag :: if (auto_tag) u32(1) else u32(0)
|
|
metadata :: try add_extra(builder, raw_tag)
|
|
_ = try add_extra(builder, tag_flag)
|
|
start :: builder.storage.extra.items.len
|
|
for (fields) |field_id| _ = try add_extra(builder, u32(field_id))
|
|
return try add_node(builder, Node{
|
|
kind = .expr_union_type,
|
|
main = NodeData{ extra = metadata },
|
|
data0 = NodeData{ extra = u32(start) },
|
|
data1 = NodeData{ extra = u32(builder.storage.extra.items.len) },
|
|
}, span)
|
|
}
|
|
|
|
add_type_operand proc(builder @mut Builder, span Span, operand NodeId, transparent bool) NodeId ! mem.AllocError {
|
|
kind :: if (transparent) NodeKind.expr_alias else NodeKind.expr_distinct_type
|
|
return try add_node(builder, Node{
|
|
kind = kind,
|
|
main = NodeData{ node_id = operand },
|
|
}, span)
|
|
}
|
|
|
|
add_enum_literal proc(builder @mut Builder, span Span, member NodeId) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .expr_enum_literal,
|
|
main = NodeData{ node_id = member },
|
|
}, span)
|
|
}
|
|
|
|
add_match proc(builder @mut Builder, span Span, subject NodeId, arms []NodeId) NodeId ! mem.AllocError {
|
|
start :: builder.storage.extra.items.len
|
|
for (arms) |arm| _ = try add_extra(builder, u32(arm))
|
|
return try add_node(builder, Node{
|
|
kind = .expr_match,
|
|
main = NodeData{ node_id = subject },
|
|
data0 = NodeData{ extra = u32(start) },
|
|
data1 = NodeData{ extra = u32(builder.storage.extra.items.len) },
|
|
}, span)
|
|
}
|
|
|
|
add_match_arm proc(
|
|
builder @mut Builder, span Span, patterns []NodeId, capture ?NodeId,
|
|
body NodeId, expression, catch_all bool,
|
|
) NodeId ! mem.AllocError {
|
|
raw_capture :: if (capture) |id| u32(id) else u32(no_node)
|
|
expression_flag :: if (expression) u32(1) else u32(0)
|
|
catch_all_flag :: if (catch_all) u32(1) else u32(0)
|
|
start :: try add_extra(builder, raw_capture)
|
|
_ = try add_extra(builder, expression_flag)
|
|
_ = try add_extra(builder, catch_all_flag)
|
|
for (patterns) |pattern| _ = try add_extra(builder, u32(pattern))
|
|
return try add_node(builder, Node{
|
|
kind = .match_arm,
|
|
main = NodeData{ node_id = body },
|
|
data0 = NodeData{ extra = start },
|
|
data1 = NodeData{ extra = u32(builder.storage.extra.items.len) },
|
|
}, span)
|
|
}
|
|
|
|
add_record_literal proc(
|
|
builder @mut Builder, span Span, type ?NodeId, fields []NodeId,
|
|
) NodeId ! mem.AllocError {
|
|
start :: builder.storage.extra.items.len
|
|
for (fields) |field_id| _ = try add_extra(builder, u32(field_id))
|
|
raw_type :: if (type) |id| id else no_node
|
|
return try add_node(builder, Node{
|
|
kind = .expr_record_literal,
|
|
main = NodeData{ node_id = raw_type },
|
|
data0 = NodeData{ extra = u32(start) },
|
|
data1 = NodeData{ extra = u32(builder.storage.extra.items.len) },
|
|
}, span)
|
|
}
|
|
|
|
add_field proc(
|
|
builder @mut Builder, span Span, name, type, value ?NodeId,
|
|
) NodeId ! mem.AllocError {
|
|
raw_name :: if (name) |id| id else no_node
|
|
raw_type :: if (type) |id| id else no_node
|
|
raw_value :: if (value) |id| id else no_node
|
|
return try add_node(builder, Node{
|
|
kind = .record_field,
|
|
main = NodeData{ node_id = raw_name },
|
|
data0 = NodeData{ node_id = raw_type },
|
|
data1 = NodeData{ node_id = raw_value },
|
|
}, span)
|
|
}
|
|
|
|
add_index proc(builder @mut Builder, span Span, base, index NodeId) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .expr_index,
|
|
data0 = NodeData{ node_id = base },
|
|
data1 = NodeData{ node_id = index },
|
|
}, span)
|
|
}
|
|
|
|
add_selection proc(
|
|
builder @mut Builder,
|
|
span Span,
|
|
base, member NodeId,
|
|
) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .expr_selection,
|
|
data0 = NodeData{ node_id = base },
|
|
data1 = NodeData{ node_id = member },
|
|
}, span)
|
|
}
|
|
|
|
add_expression_stmt proc(builder @mut Builder, expression NodeId) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .stmt_expr,
|
|
main = NodeData{ node_id = expression },
|
|
}, builder_span(builder, expression))
|
|
}
|
|
|
|
add_cleanup_stmt proc(
|
|
builder @mut Builder, span Span, body NodeId, capture ?NodeId, error_only bool,
|
|
) NodeId ! mem.AllocError {
|
|
kind NodeKind :: if (error_only) .stmt_errdefer else .stmt_defer
|
|
capture_id :: if (capture) |id| id else no_node
|
|
return try add_node(builder, Node{
|
|
kind = kind,
|
|
main = NodeData{ node_id = body },
|
|
data0 = NodeData{ node_id = capture_id },
|
|
}, span)
|
|
}
|
|
|
|
add_block_stmt proc(builder @mut Builder, body NodeId) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .stmt_block,
|
|
main = NodeData{ node_id = body },
|
|
}, builder_span(builder, body))
|
|
}
|
|
|
|
add_return_stmt proc(builder @mut Builder, span Span, value ?NodeId) NodeId ! mem.AllocError {
|
|
raw_value :: if (value) |id| id else no_node
|
|
return try add_node(builder, Node{
|
|
kind = .stmt_return,
|
|
main = NodeData{ node_id = raw_value },
|
|
}, span)
|
|
}
|
|
|
|
add_break_stmt proc(builder @mut Builder, span Span) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{ kind = .stmt_break }, span)
|
|
}
|
|
|
|
add_continue_stmt proc(builder @mut Builder, span Span) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{ kind = .stmt_continue }, span)
|
|
}
|
|
|
|
add_if_stmt proc(
|
|
builder @mut Builder,
|
|
span Span,
|
|
condition NodeId,
|
|
then_block NodeId,
|
|
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{ extra = extra },
|
|
data0 = NodeData{ node_id = then_block },
|
|
data1 = NodeData{ node_id = alternative },
|
|
}, span)
|
|
}
|
|
|
|
add_assignment_stmt proc(
|
|
builder @mut Builder,
|
|
span Span,
|
|
target NodeId,
|
|
value NodeId,
|
|
) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .stmt_assignment,
|
|
main = NodeData{ node_id = target },
|
|
data0 = NodeData{ node_id = value },
|
|
}, span)
|
|
}
|
|
|
|
add_while_stmt proc(
|
|
builder @mut Builder,
|
|
span Span,
|
|
condition NodeId,
|
|
body NodeId,
|
|
) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .stmt_while,
|
|
main = NodeData{ node_id = condition },
|
|
data0 = NodeData{ node_id = body },
|
|
}, 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,
|
|
) NodeId ! mem.AllocError {
|
|
raw_capture :: if (capture) |id| u32(id) else u32(no_node)
|
|
raw_index :: if (index) |id| u32(id) else u32(no_node)
|
|
pointer_flag :: if (pointer_capture) u32(1) else u32(0)
|
|
index_flag :: if (index_capture) u32(1) else u32(0)
|
|
extra :: try add_extra(builder, raw_capture)
|
|
_ = try add_extra(builder, raw_index)
|
|
_ = try add_extra(builder, pointer_flag)
|
|
_ = try add_extra(builder, index_flag)
|
|
return try add_node(builder, Node{
|
|
kind = .stmt_for,
|
|
main = NodeData{ node_id = iterable },
|
|
data0 = NodeData{ node_id = body },
|
|
data1 = NodeData{ extra = extra },
|
|
}, span)
|
|
}
|
|
|
|
add_statement_decl proc(
|
|
builder @mut Builder,
|
|
span Span,
|
|
name Span,
|
|
interned_name strpool.StringId,
|
|
type ?NodeId,
|
|
mutability Mutability,
|
|
initializer NodeId,
|
|
) NodeId ! mem.AllocError {
|
|
raw_type :: if (type) |id| u32(id) else u32(no_node)
|
|
extra :: try add_extra(builder, name.start)
|
|
_ = try add_extra(builder, name.end)
|
|
_ = try add_extra(builder, raw_type)
|
|
_ = try add_extra(builder, u32(initializer))
|
|
return try add_node(builder, Node{
|
|
kind = .stmt_decl,
|
|
main = NodeData{ extra = extra },
|
|
data0 = NodeData{ mutability = mutability },
|
|
data1 = NodeData{ name_id = interned_name },
|
|
}, span)
|
|
}
|
|
|
|
add_param proc(
|
|
builder @mut Builder,
|
|
span, name Span,
|
|
interned_name strpool.StringId,
|
|
type NodeId,
|
|
) NodeId ! mem.AllocError {
|
|
extra :: try add_extra(builder, name.start)
|
|
_ = try add_extra(builder, name.end)
|
|
_ = try add_extra(builder, 0)
|
|
return try add_node(builder, Node{
|
|
kind = .param_decl,
|
|
main = NodeData{ extra = extra },
|
|
data0 = NodeData{ node_id = type },
|
|
data1 = NodeData{ name_id = interned_name },
|
|
}, span)
|
|
}
|
|
|
|
set_param_comptime proc(builder @mut Builder, id NodeId) void {
|
|
node :: builder.storage.nodes.items[usize(id)]
|
|
debug.assert(node.kind == .param_decl)
|
|
builder.storage.extra.items[usize(node.main.extra) + 2] = 1
|
|
}
|
|
|
|
|
|
add_function_decl proc(
|
|
builder @mut Builder,
|
|
span Span,
|
|
name Span,
|
|
interned_name strpool.StringId,
|
|
return_type NodeId,
|
|
params []NodeId,
|
|
body NodeId,
|
|
) NodeId ! mem.AllocError {
|
|
start :: builder.storage.extra.items.len
|
|
_ = try add_extra(builder, name.start)
|
|
_ = try add_extra(builder, name.end)
|
|
_ = 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) },
|
|
data0 = NodeData{ node_id = body },
|
|
data1 = NodeData{ extra = u32(builder.storage.extra.items.len) },
|
|
}, 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
|
|
}
|
|
|
|
function_generic proc(tree @Tree, id NodeId) bool {
|
|
node :: get_node(&tree.storage, id)
|
|
if (node.kind != .func_decl and node.kind != .expr_function_type) return false
|
|
for 0..function_param_count(tree, id) |index| {
|
|
if (param(tree, function_param(tree, id, index)).comptime) return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
add_import_decl proc(
|
|
builder @mut Builder,
|
|
span, path Span,
|
|
namespace ?strpool.StringId,
|
|
test_only bool,
|
|
) NodeId ! mem.AllocError {
|
|
extra :: try add_extra(builder, path.start)
|
|
_ = try add_extra(builder, path.end)
|
|
alias_id :: if (namespace) |id| id else strpool.no_str
|
|
flag :: if (test_only) u32(1) else u32(0)
|
|
return try add_node(builder, Node{
|
|
kind = .import_decl,
|
|
main = NodeData{ extra = extra },
|
|
data0 = NodeData{ name_id = alias_id },
|
|
data1 = NodeData{ extra = flag },
|
|
}, span)
|
|
}
|
|
|
|
add_block proc(
|
|
builder @mut Builder,
|
|
span Span,
|
|
statements []NodeId,
|
|
) NodeId ! mem.AllocError {
|
|
start :: builder.storage.extra.items.len
|
|
for (statements) |statement| _ = try add_extra(builder, u32(statement))
|
|
return try add_node(builder, Node{
|
|
kind = .block,
|
|
data0 = NodeData{ extra = u32(start) },
|
|
data1 = NodeData{ extra = u32(builder.storage.extra.items.len) },
|
|
}, span)
|
|
}
|
|
|
|
add_package proc(builder @mut Builder, span Span, declarations []NodeId) NodeId ! mem.AllocError {
|
|
start :: builder.storage.extra.items.len
|
|
for (declarations) |declaration| _ = try add_extra(builder, u32(declaration))
|
|
return try add_node(builder, Node{
|
|
kind = .package,
|
|
data0 = NodeData{ extra = u32(start) },
|
|
data1 = NodeData{ extra = u32(builder.storage.extra.items.len) },
|
|
}, span)
|
|
}
|
|
|
|
add_invalid_expr proc(
|
|
builder @mut Builder,
|
|
span Span,
|
|
diagnostic DiagnosticId,
|
|
) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .invalid_expr,
|
|
main = NodeData{ diagnostic = diagnostic },
|
|
}, span)
|
|
}
|
|
|
|
add_invalid_stmt proc(
|
|
builder @mut Builder,
|
|
span Span,
|
|
diagnostic DiagnosticId,
|
|
) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .invalid_stmt,
|
|
main = NodeData{ diagnostic = diagnostic },
|
|
}, span)
|
|
}
|
|
|
|
add_invalid_decl proc(
|
|
builder @mut Builder,
|
|
span Span,
|
|
diagnostic DiagnosticId,
|
|
) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .invalid_decl,
|
|
main = NodeData{ diagnostic = diagnostic },
|
|
}, span)
|
|
}
|
|
|
|
node_count proc(tree @Tree) usize {
|
|
return tree.storage.nodes.items.len
|
|
}
|
|
|
|
#! returns the identifier's identity in the compilation-owned string pool.
|
|
#! accepts identifiers, declarations, and parameters; source spans remain available in typed views.
|
|
name_id proc(tree @Tree, id NodeId) strpool.StringId {
|
|
node :: get_node(&tree.storage, id)
|
|
return match node.kind {
|
|
.expr_identifier: node.main.name_id
|
|
.stmt_decl, .param_decl: node.data1.name_id
|
|
.func_decl: strpool.StringId(tree.storage.extra.items[usize(node.main.extra) + 3])
|
|
else: unreachable
|
|
}
|
|
}
|
|
|
|
package_decl_count proc(tree @Tree) usize {
|
|
return package_decl_count_at(tree, tree.root)
|
|
}
|
|
|
|
package_decl proc(tree @Tree, index usize) NodeId {
|
|
return package_decl_at(tree, tree.root, index)
|
|
}
|
|
|
|
package_decl_count_at proc(tree @Tree, package NodeId) usize {
|
|
root :: get_node(&tree.storage, package)
|
|
debug.assert(root.kind == .package)
|
|
return usize(root.data1.extra - root.data0.extra)
|
|
}
|
|
|
|
package_decl_at proc(tree @Tree, package NodeId, index usize) NodeId {
|
|
root :: get_node(&tree.storage, package)
|
|
debug.assert(root.kind == .package)
|
|
debug.assert(index < usize(root.data1.extra - root.data0.extra))
|
|
return NodeId(tree.storage.extra.items[usize(root.data0.extra) + index])
|
|
}
|
|
|
|
builder_package_decl_count proc(builder @Builder, package NodeId) usize {
|
|
tree :: view(builder, package)
|
|
return package_decl_count(&tree)
|
|
}
|
|
|
|
builder_package_decl proc(builder @Builder, package NodeId, index usize) NodeId {
|
|
tree :: view(builder, package)
|
|
return package_decl(&tree, index)
|
|
}
|
|
|
|
decl_kind proc(tree @Tree, id NodeId) DeclKind {
|
|
return match get_node(&tree.storage, id).kind {
|
|
.stmt_decl: .statement
|
|
.func_decl: .function
|
|
.import_decl: .import
|
|
.invalid_decl: .invalid
|
|
else: unreachable
|
|
}
|
|
}
|
|
|
|
stmt_kind proc(tree @Tree, id NodeId) StmtKind {
|
|
return match get_node(&tree.storage, id).kind {
|
|
.stmt_decl: .declaration
|
|
.stmt_expr: .expression
|
|
.stmt_return: .return_stmt
|
|
.stmt_break: .break_stmt
|
|
.stmt_continue: .continue_stmt
|
|
.stmt_if: .if_stmt
|
|
.stmt_assignment: .assignment_stmt
|
|
.stmt_while: .while_stmt
|
|
.stmt_for: .for_stmt
|
|
.stmt_defer: .defer_stmt
|
|
.stmt_errdefer: .errdefer_stmt
|
|
.stmt_block: .block_stmt
|
|
.invalid_stmt: .invalid
|
|
else: unreachable
|
|
}
|
|
}
|
|
|
|
statement_expr proc(tree @Tree, id NodeId) NodeId {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .stmt_expr)
|
|
return node.main.node_id
|
|
}
|
|
|
|
cleanup_stmt proc(tree @Tree, id NodeId) CleanupView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .stmt_defer or node.kind == .stmt_errdefer)
|
|
return CleanupView{
|
|
span = get_span(&tree.storage, id),
|
|
body = node.main.node_id,
|
|
capture = maybe_node_id(u32(node.data0.node_id)),
|
|
}
|
|
}
|
|
|
|
statement_block proc(tree @Tree, id NodeId) NodeId {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .stmt_block)
|
|
return node.main.node_id
|
|
}
|
|
|
|
return_stmt proc(tree @Tree, id NodeId) ReturnView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .stmt_return)
|
|
return ReturnView{
|
|
span = get_span(&tree.storage, id),
|
|
value = maybe_node_id(u32(node.main.node_id)),
|
|
}
|
|
}
|
|
|
|
if_stmt proc(tree @Tree, id NodeId) IfView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .stmt_if)
|
|
return IfView{
|
|
span = get_span(&tree.storage, 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]),
|
|
}
|
|
}
|
|
|
|
assignment_stmt proc(tree @Tree, id NodeId) AssignmentView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .stmt_assignment)
|
|
return AssignmentView{
|
|
span = get_span(&tree.storage, id),
|
|
target = node.main.node_id,
|
|
value = node.data0.node_id,
|
|
}
|
|
}
|
|
|
|
while_stmt proc(tree @Tree, id NodeId) WhileView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .stmt_while)
|
|
return 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)),
|
|
}
|
|
}
|
|
|
|
for_stmt proc(tree @Tree, id NodeId) ForView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .stmt_for)
|
|
extra :: usize(node.data1.extra)
|
|
return ForView{
|
|
span = get_span(&tree.storage, id),
|
|
iterable = node.main.node_id,
|
|
capture = maybe_node_id(tree.storage.extra.items[extra]),
|
|
index = maybe_node_id(tree.storage.extra.items[extra + 1]),
|
|
pointer_capture = tree.storage.extra.items[extra + 2] != 0,
|
|
index_capture = tree.storage.extra.items[extra + 3] != 0,
|
|
body = node.data0.node_id,
|
|
}
|
|
}
|
|
|
|
statement_decl proc(tree @Tree, id NodeId) StatementDeclView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .stmt_decl)
|
|
extra :: usize(node.main.extra)
|
|
raw_type :: tree.storage.extra.items[extra + 2]
|
|
return StatementDeclView{
|
|
span = get_span(&tree.storage, id),
|
|
name = span_from_extra(&tree.storage, extra, get_span(&tree.storage, id).source),
|
|
type = maybe_node_id(raw_type),
|
|
initializer = NodeId(tree.storage.extra.items[extra + 3]),
|
|
mutability = node.data0.mutability,
|
|
}
|
|
}
|
|
|
|
function_decl proc(tree @Tree, id NodeId) FunctionDeclView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .func_decl)
|
|
extra :: usize(node.main.extra)
|
|
return FunctionDeclView{
|
|
span = get_span(&tree.storage, id),
|
|
name = span_from_extra(&tree.storage, extra, get_span(&tree.storage, id).source),
|
|
return_type = NodeId(tree.storage.extra.items[extra + 2]),
|
|
body = node.data0.node_id,
|
|
}
|
|
}
|
|
|
|
callable_result proc(tree @Tree, id NodeId) NodeId {
|
|
node :: get_node(&tree.storage, id)
|
|
if (node.kind == .func_decl) return function_decl(tree, id).return_type
|
|
debug.assert(node.kind == .expr_function_type)
|
|
return node.main.node_id
|
|
}
|
|
|
|
import_decl proc(tree @Tree, id NodeId) ImportView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .import_decl)
|
|
span :: get_span(&tree.storage, id)
|
|
namespace ?strpool.StringId :: if (node.data0.name_id == strpool.no_str) null else node.data0.name_id
|
|
return ImportView{
|
|
span = span,
|
|
path = span_from_extra(&tree.storage, usize(node.main.extra), span.source),
|
|
namespace = namespace,
|
|
test_only = node.data1.extra != 0,
|
|
}
|
|
}
|
|
|
|
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
|
|
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 < function_param_count(tree, id))
|
|
return NodeId(tree.storage.extra.items[usize(start) + index])
|
|
}
|
|
|
|
param proc(tree @Tree, id NodeId) ParamView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .param_decl)
|
|
return ParamView{
|
|
span = get_span(&tree.storage, id),
|
|
name = span_from_extra(&tree.storage, usize(node.main.extra), get_span(&tree.storage, id).source),
|
|
type = node.data0.node_id,
|
|
comptime = tree.storage.extra.items[usize(node.main.extra) + 2] != 0,
|
|
}
|
|
}
|
|
|
|
block_statement_count proc(tree @Tree, id NodeId) usize {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .block)
|
|
return usize(node.data1.extra - node.data0.extra)
|
|
}
|
|
|
|
block_statement proc(tree @Tree, id NodeId, index usize) NodeId {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .block)
|
|
debug.assert(index < usize(node.data1.extra - node.data0.extra))
|
|
return NodeId(tree.storage.extra.items[usize(node.data0.extra) + index])
|
|
}
|
|
|
|
expr_kind proc(tree @Tree, id NodeId) ExprKind {
|
|
return expression_kind(get_node(&tree.storage, id).kind)
|
|
}
|
|
|
|
builder_expr_kind proc(builder @Builder, id NodeId) ExprKind {
|
|
return expression_kind(get_node(&builder.storage, id).kind)
|
|
}
|
|
|
|
@hide:file
|
|
expression_kind proc(kind NodeKind) ExprKind {
|
|
return match kind {
|
|
.literal_int: .literal_int
|
|
.literal_float: .literal_float
|
|
.literal_string: .literal_string
|
|
.literal_bool: .literal_bool
|
|
.expr_identifier: .identifier
|
|
.expr_unary: .unary
|
|
.expr_binary: .binary
|
|
.expr_call: .call
|
|
.expr_range: .range
|
|
.expr_array_type: .array_type
|
|
.expr_pointer_type: .pointer_type
|
|
.expr_function_type: .function_type
|
|
.expr_array_literal: .array_literal
|
|
.expr_record_type: .record_type
|
|
.expr_record_literal: .record_literal
|
|
.expr_enum_type: .enum_type
|
|
.expr_union_type: .union_type
|
|
.expr_distinct_type: .distinct_type
|
|
.expr_alias: .alias_expr
|
|
.expr_enum_literal: .enum_literal
|
|
.expr_match: .match_expr
|
|
.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
|
|
.expr_fallible_type: .fallible_type
|
|
.expr_sum_type: .sum_type
|
|
.expr_try: .try_expr
|
|
.expr_catch: .catch_expr
|
|
.expr_typed_constant: .typed_constant
|
|
.expr_comptime: .comptime_expr
|
|
.literal_float_value: .literal_float
|
|
else: unreachable
|
|
}
|
|
}
|
|
|
|
unary_expr proc(tree @Tree, id NodeId) UnaryExprView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_unary)
|
|
return UnaryExprView{
|
|
span = get_span(&tree.storage, id),
|
|
operator = node.main.unary_operator,
|
|
operand = node.data0.node_id,
|
|
}
|
|
}
|
|
|
|
binary_expr proc(tree @Tree, id NodeId) BinaryExprView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_binary or node.kind == .expr_fallible_type or node.kind == .expr_sum_type)
|
|
return BinaryExprView{
|
|
span = get_span(&tree.storage, id),
|
|
operator = node.main.binary_operator,
|
|
left = node.data0.node_id,
|
|
right = node.data1.node_id,
|
|
}
|
|
}
|
|
|
|
comptime_operand proc(tree @Tree, id NodeId) NodeId {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_comptime)
|
|
return node.main.node_id
|
|
}
|
|
|
|
#! generated IEEE literals have no textual spelling; parsed float literals return null.
|
|
float_value proc(tree @Tree, id NodeId) ?f64 {
|
|
node :: get_node(&tree.storage, id)
|
|
if (node.kind != .literal_float_value) return null
|
|
bits :: u64(node.data0.extra) | (u64(node.data1.extra) << 32)
|
|
return ptrcast!(@f64, &bits)^
|
|
}
|
|
|
|
typed_constant proc(tree @Tree, id NodeId) TypedConstantView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_typed_constant)
|
|
return TypedConstantView{
|
|
type = node.data0.node_id,
|
|
value = node.data1.node_id,
|
|
}
|
|
}
|
|
|
|
catch_expr proc(tree @Tree, id NodeId) CatchExprView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_catch)
|
|
extra :: usize(node.main.extra)
|
|
return CatchExprView{
|
|
span = get_span(&tree.storage, id),
|
|
operand = node.data0.node_id,
|
|
capture = maybe_node_id(tree.storage.extra.items[extra]),
|
|
handler = node.data1.node_id,
|
|
handler_is_block = tree.storage.extra.items[extra + 1] != 0,
|
|
}
|
|
}
|
|
|
|
range_expr proc(tree @Tree, id NodeId) RangeView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_range)
|
|
return RangeView{
|
|
span = get_span(&tree.storage, id),
|
|
start = node.data0.node_id,
|
|
end = node.data1.node_id,
|
|
inclusive = node.main.extra != 0,
|
|
}
|
|
}
|
|
|
|
call_expr proc(tree @Tree, id NodeId) CallExprView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_call)
|
|
return CallExprView{
|
|
span = get_span(&tree.storage, id),
|
|
callee = node.main.node_id,
|
|
}
|
|
}
|
|
|
|
call_arg_count proc(tree @Tree, id NodeId) usize {
|
|
node :: get_node(&tree.storage, id)
|
|
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 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])
|
|
}
|
|
|
|
array_type proc(tree @Tree, id NodeId) ArrayTypeView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_array_type)
|
|
return ArrayTypeView{
|
|
span = get_span(&tree.storage, id),
|
|
count = maybe_node_id(u32(node.data0.node_id)),
|
|
element = node.data1.node_id,
|
|
mutable = node.main.extra != 0,
|
|
}
|
|
}
|
|
|
|
pointer_type proc(tree @Tree, id NodeId) PointerTypeView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_pointer_type)
|
|
return PointerTypeView{
|
|
element = node.data1.node_id,
|
|
mutable = node.main.extra != 0,
|
|
many = node.data0.extra != 0,
|
|
}
|
|
}
|
|
|
|
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)
|
|
return usize(node.data1.extra - node.data0.extra)
|
|
}
|
|
|
|
array_element proc(tree @Tree, id NodeId, index usize) NodeId {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_array_literal)
|
|
debug.assert(index < usize(node.data1.extra - node.data0.extra))
|
|
return NodeId(tree.storage.extra.items[usize(node.data0.extra) + index])
|
|
}
|
|
|
|
record_literal proc(tree @Tree, id NodeId) RecordLiteralView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_record_literal)
|
|
return RecordLiteralView{
|
|
span = get_span(&tree.storage, id),
|
|
type = maybe_node_id(u32(node.main.node_id)),
|
|
}
|
|
}
|
|
|
|
field proc(tree @Tree, id NodeId) FieldView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .record_field)
|
|
return FieldView{
|
|
span = get_span(&tree.storage, id),
|
|
name = maybe_node_id(u32(node.main.node_id)),
|
|
type = maybe_node_id(u32(node.data0.node_id)),
|
|
value = maybe_node_id(u32(node.data1.node_id)),
|
|
}
|
|
}
|
|
|
|
enum_type proc(tree @Tree, id NodeId) EnumTypeView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_enum_type)
|
|
return EnumTypeView{ backing = maybe_node_id(u32(node.main.node_id)) }
|
|
}
|
|
|
|
enum_member_count proc(tree @Tree, id NodeId) usize {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_enum_type)
|
|
return usize(node.data1.extra - node.data0.extra)
|
|
}
|
|
|
|
enum_member proc(tree @Tree, id NodeId, index usize) NodeId {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(index < enum_member_count(tree, id))
|
|
return NodeId(tree.storage.extra.items[usize(node.data0.extra) + index])
|
|
}
|
|
|
|
union_type proc(tree @Tree, id NodeId) UnionTypeView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_union_type)
|
|
extra :: usize(node.main.extra)
|
|
return UnionTypeView{
|
|
tag = maybe_node_id(tree.storage.extra.items[extra]),
|
|
auto_tag = tree.storage.extra.items[extra + 1] != 0,
|
|
}
|
|
}
|
|
|
|
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 or node.kind == .expr_optional_type or node.kind == .expr_try)
|
|
return node.main.node_id
|
|
}
|
|
|
|
enum_literal proc(tree @Tree, id NodeId) NodeId {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_enum_literal)
|
|
return node.main.node_id
|
|
}
|
|
|
|
match_expr proc(tree @Tree, id NodeId) MatchView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_match)
|
|
return MatchView{ subject = node.main.node_id }
|
|
}
|
|
|
|
match_arm_count proc(tree @Tree, id NodeId) usize {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_match)
|
|
return usize(node.data1.extra - node.data0.extra)
|
|
}
|
|
|
|
match_arm proc(tree @Tree, id NodeId, index usize) NodeId {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(index < match_arm_count(tree, id))
|
|
return NodeId(tree.storage.extra.items[usize(node.data0.extra) + index])
|
|
}
|
|
|
|
match_arm_view proc(tree @Tree, id NodeId) MatchArmView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .match_arm)
|
|
extra :: usize(node.data0.extra)
|
|
return MatchArmView{
|
|
capture = maybe_node_id(tree.storage.extra.items[extra]),
|
|
body = node.main.node_id,
|
|
expression = tree.storage.extra.items[extra + 1] != 0,
|
|
catch_all = tree.storage.extra.items[extra + 2] != 0,
|
|
}
|
|
}
|
|
|
|
match_pattern_count proc(tree @Tree, id NodeId) usize {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .match_arm)
|
|
return usize(node.data1.extra - node.data0.extra - 3)
|
|
}
|
|
|
|
match_pattern proc(tree @Tree, id NodeId, index usize) NodeId {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(index < match_pattern_count(tree, id))
|
|
return NodeId(tree.storage.extra.items[usize(node.data0.extra) + 3 + index])
|
|
}
|
|
|
|
record_field_count proc(tree @Tree, id NodeId) usize {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_record_type or node.kind == .expr_record_literal or node.kind == .expr_union_type)
|
|
return usize(node.data1.extra - node.data0.extra)
|
|
}
|
|
|
|
record_field proc(tree @Tree, id NodeId, index usize) NodeId {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_record_type or node.kind == .expr_record_literal or node.kind == .expr_union_type)
|
|
debug.assert(index < usize(node.data1.extra - node.data0.extra))
|
|
return NodeId(tree.storage.extra.items[usize(node.data0.extra) + index])
|
|
}
|
|
|
|
index_expr proc(tree @Tree, id NodeId) IndexView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_index)
|
|
return IndexView{
|
|
span = get_span(&tree.storage, id),
|
|
base = node.data0.node_id,
|
|
index = node.data1.node_id,
|
|
}
|
|
}
|
|
|
|
selection_expr proc(tree @Tree, id NodeId) SelectionView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_selection)
|
|
return SelectionView{
|
|
span = get_span(&tree.storage, id),
|
|
base = node.data0.node_id,
|
|
member = node.data1.node_id,
|
|
}
|
|
}
|
|
|
|
node_span proc(tree @Tree, id NodeId) Span {
|
|
return get_span(&tree.storage, id)
|
|
}
|
|
|
|
invalid proc(tree @Tree, id NodeId) InvalidView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(
|
|
node.kind == .invalid_expr
|
|
or node.kind == .invalid_stmt
|
|
or node.kind == .invalid_decl
|
|
)
|
|
return InvalidView{
|
|
span = get_span(&tree.storage, id),
|
|
diagnostic = node.main.diagnostic,
|
|
}
|
|
}
|
|
|
|
#! returns text borrowed from the source buffer; the caller must not free it.
|
|
source_text proc(tree @Tree, span Span) []u8 {
|
|
debug.assert(usize(span.source) < tree.storage.sources.items.len)
|
|
text :: tree.storage.sources.items[usize(span.source)].text
|
|
debug.assert(span.start <= span.end and usize(span.end) <= text.len)
|
|
return text[usize(span.start)..usize(span.end)]
|
|
}
|
|
|
|
builder_span proc(builder @Builder, id NodeId) Span {
|
|
return get_span(&builder.storage, id)
|
|
}
|
|
|
|
@hide:file
|
|
span_from_extra proc(storage @Storage, start usize, source diagnostics.SourceId) Span {
|
|
return Span{
|
|
start = storage.extra.items[start],
|
|
end = storage.extra.items[start + 1],
|
|
source = source,
|
|
}
|
|
}
|
|
|
|
@hide:file
|
|
add_node proc(builder @mut Builder, node Node, span Span) NodeId ! mem.AllocError {
|
|
debug.assert(builder.storage.nodes.items.len == builder.storage.spans.items.len)
|
|
if (builder.storage.sources.items.len == 0)
|
|
_ = try set_source(builder, "", builder.source)
|
|
id :: node_id(builder.storage.nodes.items.len)
|
|
try arraylist.reserve(&builder.storage.nodes, builder.storage.nodes.items.len + 1)
|
|
try arraylist.reserve(&builder.storage.spans, builder.storage.spans.items.len + 1)
|
|
try arraylist.append(&builder.storage.nodes, node)
|
|
try arraylist.append(&builder.storage.spans, span)
|
|
return id
|
|
}
|
|
|
|
@hide:file
|
|
add_extra proc(builder @mut Builder, value u32) u32 ! mem.AllocError {
|
|
debug.assert(u64(builder.storage.extra.items.len) < u64(maxval!(u32)))
|
|
id :: u32(builder.storage.extra.items.len)
|
|
try arraylist.append(&builder.storage.extra, value)
|
|
return id
|
|
}
|
|
|
|
@hide:file
|
|
get_node proc(storage @Storage, id NodeId) Node {
|
|
debug.assert(usize(id) < storage.nodes.items.len)
|
|
return storage.nodes.items[usize(id)]
|
|
}
|
|
|
|
@hide:file
|
|
get_span proc(storage @Storage, id NodeId) Span {
|
|
debug.assert(usize(id) < storage.spans.items.len)
|
|
return storage.spans.items[usize(id)]
|
|
}
|
|
|
|
@hide:file
|
|
node_id proc(index usize) NodeId {
|
|
debug.assert(u64(index) < u64(no_node))
|
|
return NodeId(index)
|
|
}
|
|
|
|
@hide:file
|
|
maybe_node_id proc(value u32) ?NodeId {
|
|
if (value == u32(no_node)) return null
|
|
return NodeId(value)
|
|
}
|
|
|
|
NodeClass :: enum {
|
|
expression, statement, function, parameter, block, field, match_arm, package, import, invalid
|
|
}
|
|
|
|
@hide:file
|
|
NodeLayout :: struct {
|
|
class NodeClass = .expression
|
|
direct_children u32 = 0
|
|
extra_pointers u32 = 0
|
|
extra_start usize = 0
|
|
extra_count usize = 0
|
|
extra_children u32 = 0
|
|
list_start usize = 0
|
|
list_end usize = 0
|
|
}
|
|
|
|
#! describes owned extra storage and child slots, excluding names, spans, and flags.
|
|
@hide:file
|
|
node_layout proc(node Node) NodeLayout {
|
|
layout := NodeLayout{}
|
|
match node.kind {
|
|
.literal_int, .literal_float, .literal_float_value, .literal_string, .literal_bool, .literal_null,
|
|
.expr_identifier, .invalid_expr: {}
|
|
.expr_unary: layout.direct_children = 2
|
|
.expr_binary, .expr_range, .expr_index, .expr_selection, .expr_typed_constant,
|
|
.expr_fallible_type, .expr_sum_type, .expr_array_type, .expr_slice_type:
|
|
layout.direct_children = 6
|
|
.expr_pointer_type: layout.direct_children = 4
|
|
.expr_optional_type, .expr_distinct_type, .expr_alias, .expr_enum_literal,
|
|
.expr_try, .expr_comptime: layout.direct_children = 1
|
|
.expr_call, .expr_builtin_call, .expr_function_type, .expr_array_literal,
|
|
.expr_record_type, .expr_record_literal, .expr_enum_type, .expr_match,
|
|
.block, .package: {
|
|
layout.extra_pointers = 6
|
|
layout.extra_start = usize(node.data0.extra)
|
|
layout.extra_count = usize(node.data1.extra - node.data0.extra)
|
|
layout.list_end = layout.extra_count
|
|
if (node.kind == .block) layout.class = .block
|
|
else if (node.kind == .package) layout.class = .package
|
|
else if (node.kind != .expr_array_literal and node.kind != .expr_record_type)
|
|
layout.direct_children = 1
|
|
}
|
|
.expr_union_type: {
|
|
layout.extra_pointers = 7
|
|
layout.extra_start = usize(node.main.extra)
|
|
layout.extra_count = usize(node.data1.extra - node.main.extra)
|
|
layout.extra_children = 1
|
|
layout.list_start = 2
|
|
layout.list_end = layout.extra_count
|
|
}
|
|
.record_field: {
|
|
layout.class = .field
|
|
layout.direct_children = 7
|
|
}
|
|
.match_arm: {
|
|
layout.class = .match_arm
|
|
layout.direct_children = 1
|
|
layout.extra_pointers = 6
|
|
layout.extra_start = usize(node.data0.extra)
|
|
layout.extra_count = usize(node.data1.extra - node.data0.extra)
|
|
layout.extra_children = 1
|
|
layout.list_start = 3
|
|
layout.list_end = layout.extra_count
|
|
}
|
|
.expr_catch: {
|
|
layout.direct_children = 6
|
|
layout.extra_pointers = 1
|
|
layout.extra_start = usize(node.main.extra)
|
|
layout.extra_count = 2
|
|
layout.extra_children = 1
|
|
}
|
|
.expr_slice: {
|
|
layout.direct_children = 1
|
|
layout.extra_pointers = 2
|
|
layout.extra_start = usize(node.data0.extra)
|
|
layout.extra_count = 3
|
|
layout.extra_children = 7
|
|
}
|
|
.stmt_expr, .stmt_return, .stmt_block, .stmt_defer, .stmt_errdefer: {
|
|
layout.class = .statement
|
|
layout.direct_children = 1
|
|
if (node.kind == .stmt_defer or node.kind == .stmt_errdefer)
|
|
layout.direct_children = 3
|
|
}
|
|
.stmt_break, .stmt_continue, .invalid_stmt: layout.class = .statement
|
|
.stmt_assignment: {
|
|
layout.class = .statement
|
|
layout.direct_children = 3
|
|
}
|
|
.stmt_while: {
|
|
layout.class = .statement
|
|
layout.direct_children = 7
|
|
}
|
|
.stmt_if: {
|
|
layout.class = .statement
|
|
layout.direct_children = 6
|
|
layout.extra_pointers = 1
|
|
layout.extra_start = usize(node.main.extra)
|
|
layout.extra_count = 2
|
|
layout.extra_children = 3
|
|
}
|
|
.stmt_for: {
|
|
layout.class = .statement
|
|
layout.direct_children = 3
|
|
layout.extra_pointers = 4
|
|
layout.extra_start = usize(node.data1.extra)
|
|
layout.extra_count = 4
|
|
layout.extra_children = 3
|
|
}
|
|
.stmt_decl: {
|
|
layout.class = .statement
|
|
layout.extra_pointers = 1
|
|
layout.extra_start = usize(node.main.extra)
|
|
layout.extra_count = 4
|
|
layout.extra_children = 12
|
|
}
|
|
.func_decl: {
|
|
layout.class = .function
|
|
layout.direct_children = 2
|
|
layout.extra_pointers = 5
|
|
layout.extra_start = usize(node.main.extra)
|
|
layout.extra_count = usize(node.data1.extra - node.main.extra)
|
|
layout.extra_children = 4
|
|
layout.list_start = 4
|
|
layout.list_end = layout.extra_count - 1
|
|
}
|
|
.param_decl: {
|
|
layout.class = .parameter
|
|
layout.direct_children = 2
|
|
layout.extra_pointers = 1
|
|
layout.extra_start = usize(node.main.extra)
|
|
layout.extra_count = 3
|
|
}
|
|
.import_decl: {
|
|
layout.class = .import
|
|
layout.extra_pointers = 1
|
|
layout.extra_start = usize(node.main.extra)
|
|
layout.extra_count = 2
|
|
}
|
|
.invalid_decl: layout.class = .invalid
|
|
}
|
|
return layout
|
|
}
|
|
|
|
@hide:file
|
|
has_slot proc(mask u32, index usize) bool {
|
|
return (mask & (u32(1) << u32(index))) != 0
|
|
}
|
|
|
|
@hide:file
|
|
node_word proc(node Node, index usize) NodeData {
|
|
return match index {
|
|
0: node.main
|
|
1: node.data0
|
|
2: node.data1
|
|
else: unreachable
|
|
}
|
|
}
|
|
|
|
@hide:file
|
|
set_node_word proc(node @mut Node, index usize, word NodeData) void {
|
|
match index {
|
|
0: node.main = word
|
|
1: node.data0 = word
|
|
2: node.data1 = word
|
|
else: unreachable
|
|
}
|
|
}
|
|
|
|
node_class proc(tree @Tree, id NodeId) NodeClass {
|
|
return node_layout(get_node(&tree.storage, id)).class
|
|
}
|
|
|
|
#! counts actual AST children, excluding absent optional nodes and non-node metadata.
|
|
child_count proc(tree @Tree, id NodeId) usize {
|
|
node :: get_node(&tree.storage, id)
|
|
layout :: node_layout(node)
|
|
count := layout.list_end - layout.list_start
|
|
for (0..usize(3)) |i| {
|
|
if (has_slot(layout.direct_children, i) and node_word(node, i).node_id != no_node)
|
|
count += 1
|
|
}
|
|
for (0..usize(4)) |i| {
|
|
if (!has_slot(layout.extra_children, i)) continue
|
|
if (NodeId(tree.storage.extra.items[layout.extra_start + i]) != no_node) count += 1
|
|
}
|
|
return count
|
|
}
|
|
|
|
#! returns a child in stable payload order; `index` must be less than child_count.
|
|
child proc(tree @Tree, id NodeId, index usize) NodeId {
|
|
node :: get_node(&tree.storage, id)
|
|
layout :: node_layout(node)
|
|
remaining := index
|
|
for (0..usize(3)) |i| {
|
|
if (!has_slot(layout.direct_children, i)) continue
|
|
value :: node_word(node, i).node_id
|
|
if (value == no_node) continue
|
|
if (remaining == 0) return value
|
|
remaining -= 1
|
|
}
|
|
for (0..usize(4)) |i| {
|
|
if (!has_slot(layout.extra_children, i)) continue
|
|
value :: NodeId(tree.storage.extra.items[layout.extra_start + i])
|
|
if (value == no_node) continue
|
|
if (remaining == 0) return value
|
|
remaining -= 1
|
|
}
|
|
debug.assert(remaining < layout.list_end - layout.list_start)
|
|
return NodeId(tree.storage.extra.items[layout.extra_start + layout.list_start + remaining])
|
|
}
|
|
|
|
#! clones owned payload storage and preserves spans and interned names.
|
|
#! `mapping` covers original nodes, initially maxval!(NodeId), with substitutions prefilled.
|
|
#! nodes outside `mapping` are already concrete and returned unchanged.
|
|
clone_subtree proc(
|
|
builder @mut Builder, root NodeId, mapping []mut NodeId,
|
|
) NodeId ! mem.AllocError {
|
|
if (root == no_node or usize(root) >= mapping.len) return root
|
|
if (mapping[usize(root)] != no_node) return mapping[usize(root)]
|
|
original :: get_node(&builder.storage, root)
|
|
span :: get_span(&builder.storage, root)
|
|
layout :: node_layout(original)
|
|
cloned := original
|
|
start :: builder.storage.extra.items.len
|
|
for (0..layout.extra_count) |i| {
|
|
value :: builder.storage.extra.items[layout.extra_start + i]
|
|
_ = try add_extra(builder, value)
|
|
}
|
|
for (0..usize(3)) |i| {
|
|
if (has_slot(layout.extra_pointers, i)) {
|
|
offset :: usize(node_word(original, i).extra) - layout.extra_start
|
|
set_node_word(&cloned, i, NodeData{ extra = u32(start + offset) })
|
|
}
|
|
if (has_slot(layout.direct_children, i)) {
|
|
value :: try clone_subtree(builder, node_word(original, i).node_id, mapping)
|
|
set_node_word(&cloned, i, NodeData{ node_id = value })
|
|
}
|
|
}
|
|
for (0..layout.extra_count) |i| {
|
|
is_child :: (
|
|
(i < 4 and has_slot(layout.extra_children, i))
|
|
or (i >= layout.list_start and i < layout.list_end)
|
|
)
|
|
if (!is_child) continue
|
|
original_child :: NodeId(builder.storage.extra.items[layout.extra_start + i])
|
|
value :: try clone_subtree(builder, original_child, mapping)
|
|
builder.storage.extra.items[start + i] = u32(value)
|
|
}
|
|
result :: try add_node(builder, cloned, span)
|
|
mapping[usize(root)] = result
|
|
return result
|
|
}
|
|
|
|
#! shallow-copies the source kind and payload, retaining the target's diagnostic span.
|
|
replace_node proc(builder @mut Builder, target, source NodeId) void {
|
|
builder.storage.nodes.items[usize(target)] = get_node(&builder.storage, source)
|
|
}
|
|
|
|
#! selects a source node, retaining its literal spelling and diagnostic provenance.
|
|
replace_node_from_source proc(builder @mut Builder, target, source NodeId) void {
|
|
replace_node(builder, target, source)
|
|
builder.storage.spans.items[usize(target)] = get_span(&builder.storage, source)
|
|
}
|
|
|
|
#! shallow-copies kind, payload, and span; extra storage remains shared.
|
|
copy_node proc(builder @mut Builder, id NodeId) NodeId ! mem.AllocError {
|
|
node :: get_node(&builder.storage, id)
|
|
span :: get_span(&builder.storage, id)
|
|
return try add_node(builder, node, span)
|
|
}
|
|
|
|
#! replaces package children without changing its node identity or source span.
|
|
set_package_declarations proc(
|
|
builder @mut Builder, root NodeId, declarations []NodeId,
|
|
) void ! mem.AllocError {
|
|
debug.assert(get_node(&builder.storage, root).kind == .package)
|
|
start :: builder.storage.extra.items.len
|
|
for (declarations) |declaration| _ = try add_extra(builder, u32(declaration))
|
|
builder.storage.nodes.items[usize(root)].data0 = NodeData{ extra = u32(start) }
|
|
builder.storage.nodes.items[usize(root)].data1 = NodeData{ extra = u32(builder.storage.extra.items.len) }
|
|
}
|
|
|
|
#! retains generated decimal text until deinit_builder or ownership transfer through finish.
|
|
#! generated literals retain `at` for diagnostics; read their spelling through literal_text.
|
|
add_integer proc(builder @mut Builder, at Span, magnitude u64) NodeId ! mem.AllocError {
|
|
digits [20]mut u8 := undefined
|
|
start := digits.len
|
|
remaining := magnitude
|
|
while true {
|
|
start -= 1
|
|
digits[start] = '0' + u8(rem!(remaining, 10))
|
|
remaining = divtrunc!(remaining, 10)
|
|
if (remaining == 0) break
|
|
}
|
|
return try add_generated_literal(builder, at, .int, digits[start..])
|
|
}
|
|
|
|
#! retains generated boolean text with the same ownership and provenance rules as add_integer.
|
|
add_boolean proc(builder @mut Builder, at Span, value bool) NodeId ! mem.AllocError {
|
|
text :: if (value) "true" else "false"
|
|
return try add_generated_literal(builder, at, .bool, text)
|
|
}
|
|
|
|
#! copies arbitrary bytes into owned quoted spelling, retaining `at` for diagnostics.
|
|
add_string proc(builder @mut Builder, at Span, bytes []u8) NodeId ! mem.AllocError {
|
|
if (bytes.len > divtrunc!(maxval!(usize) - 2, usize(4))) return .out_of_memory
|
|
allocator :: builder.storage.generated.allocator
|
|
text :: try mem.alloc(u8, allocator, bytes.len * 4 + 2)
|
|
errdefer mem.free(allocator, text)
|
|
text[0] = '"'
|
|
digits :: "0123456789abcdef"
|
|
for (bytes) |byte, index| {
|
|
start :: index * 4 + 1
|
|
text[start] = '\\'
|
|
text[start + 1] = 'x'
|
|
text[start + 2] = digits[usize(divtrunc!(byte, u8(16)))]
|
|
text[start + 3] = digits[usize(rem!(byte, u8(16)))]
|
|
}
|
|
text[text.len - 1] = '"'
|
|
return try add_owned_literal(builder, at, .string, text)
|
|
}
|
|
|
|
@hide:file
|
|
add_generated_literal proc(
|
|
builder @mut Builder, at Span, kind LiteralKind, bytes []u8,
|
|
) NodeId ! mem.AllocError {
|
|
debug.assert(usize(at.source) < builder.storage.sources.items.len)
|
|
allocator :: builder.storage.generated.allocator
|
|
text :: try mem.alloc(u8, allocator, bytes.len)
|
|
errdefer mem.free(allocator, text)
|
|
memcopy!(text, bytes)
|
|
return try add_owned_literal(builder, at, kind, text)
|
|
}
|
|
|
|
@hide:file
|
|
add_owned_literal proc(
|
|
builder @mut Builder, at Span, kind LiteralKind, text []mut u8,
|
|
) NodeId ! mem.AllocError {
|
|
debug.assert(usize(at.source) < builder.storage.sources.items.len)
|
|
index :: builder.storage.generated.items.len
|
|
debug.assert(u64(index) < u64(maxval!(u32)))
|
|
try arraylist.append(&builder.storage.generated, text)
|
|
errdefer _ = arraylist.pop(&builder.storage.generated)
|
|
id :: try add_literal(builder, kind, at)
|
|
builder.storage.nodes.items[usize(id)].main = NodeData{ extra = u32(index) }
|
|
builder.storage.nodes.items[usize(id)].data0 = NodeData{ extra = 1 }
|
|
return id
|
|
}
|
|
|
|
#! borrows a literal's spelling, independently of its diagnostic source span.
|
|
#! query float_value first for generated IEEE literals.
|
|
literal_text proc(tree @Tree, id NodeId) []u8 {
|
|
node :: get_node(&tree.storage, id)
|
|
if (node.data0.extra == 1) return tree.storage.generated.items[usize(node.main.extra)]
|
|
return source_text(tree, get_span(&tree.storage, id))
|
|
}
|
|
|
|
StringError :: enum { invalid_escape }
|
|
|
|
#! decodes one byte from quoted literal text without allocation.
|
|
#! start `cursor` at one and call only while it is less than `text.len - 1`.
|
|
string_next proc(text []u8, cursor @mut usize) u8 ! StringError {
|
|
debug.assert(text.len >= 2 and cursor^ >= 1 and cursor^ < text.len - 1)
|
|
end :: text.len - 1
|
|
byte := text[cursor^]
|
|
cursor^ += 1
|
|
if (byte != '\\') return byte
|
|
if (cursor^ >= end) return .invalid_escape
|
|
escape :: text[cursor^]
|
|
cursor^ += 1
|
|
match escape {
|
|
'\\': byte = '\\'
|
|
'"': byte = '"'
|
|
'n': byte = '\n'
|
|
'r': byte = '\r'
|
|
't': byte = '\t'
|
|
'0': byte = 0
|
|
'x': {
|
|
if (end - cursor^ < 2) return .invalid_escape
|
|
high := u8(0)
|
|
low := u8(0)
|
|
if (hex_digit(text[cursor^])) |digit| high = digit
|
|
else return .invalid_escape
|
|
if (hex_digit(text[cursor^ + 1])) |digit| low = digit
|
|
else return .invalid_escape
|
|
byte = high * 16 + low
|
|
cursor^ += 2
|
|
}
|
|
else: return .invalid_escape
|
|
}
|
|
return byte
|
|
}
|
|
|
|
@hide:file
|
|
hex_digit proc(byte u8) ?u8 {
|
|
if (byte >= '0' and byte <= '9') return byte - '0'
|
|
if (byte >= 'a' and byte <= 'f') return byte - 'a' + 10
|
|
if (byte >= 'A' and byte <= 'F') return byte - 'A' + 10
|
|
return null
|
|
}
|