cc39c96fc0
Snapshot full union storage rather than choosing an overlapping field. Preserve structural union source syntax independently of nominal identity so direct literal arguments materialize and share normalized specialization keys. Reject uninitialized bytes and address-bearing member layouts.
2412 lines
79 KiB
Plaintext
2412 lines
79 KiB
Plaintext
#! grammar:
|
|
#! * type ..... -> type_sum ['!' type]; type_sum -> type_atom {'|' type_atom}; type_atom includes '(' type ')', identifier {'.' member | '(' [expr {',' 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 ....... -> '+' | '-' | '*' | '/' | '&' | 'xor' | '|' | '<<' | '>>' | '<<|' | '==' | '!=' | '<' | '<=' | '>' | '>=' | '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.
|
|
#! value expressions also accept 'if' guard ['|' identifier '|'] expr 'else' expr, canonicalized to matches; else is required.
|
|
#! bare braces containing statement syntax form a value block; contextual record literals keep their initializer grammar.
|
|
#! 'yield' requires an expression on the same line and targets the nearest enclosing value construct, not a statement block.
|
|
#! * 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 assignment_op expr | 'return' [expr] | 'yield' expr | 'break' | 'continue' | 'unreachable' | 'if' expr ['|' identifier '|'] arm ['else' arm] | 'while' expr ['|' identifier '|'] arm | ['inline'] 'for' expr '|' ['@'] identifier [',' identifier] '|' arm
|
|
#! assignment_op is '=' or a contiguous compound: '+=', '-=', '*=', '/=', '&=', '|=', 'xor=', '<<=', '>>=', '<<|='.
|
|
#! braceless guards/iterables require parentheses unless they are calls, explicit comptime expressions or optional captures; else binds to the nearest if. bitwise-OR headers require parentheses before captures.
|
|
#! precedence (tightest first): calls/indices/selections/dereference/unwrap, unary -, !, ~, &, try higher than multiplication/division, addition/subtraction, shifts << / >> / <<|, bitwise &/xor/|, 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.
|
|
#! compound assignments share their target with the canonical binary value's left operand; lowering evaluates the place and loads its old value once before the RHS.
|
|
#! 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)
|
|
|
|
#! void literals are generated constants only; there is no source void literal.
|
|
LiteralKind :: enum {
|
|
int
|
|
float
|
|
string
|
|
bool
|
|
null
|
|
void
|
|
undefined
|
|
}
|
|
|
|
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
|
|
union_tag_type
|
|
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
|
|
union_constant
|
|
comptime_expr
|
|
literal_void
|
|
literal_undefined
|
|
block_expr
|
|
}
|
|
|
|
DeclKind :: enum {
|
|
statement
|
|
function
|
|
import
|
|
invalid
|
|
}
|
|
|
|
StmtKind :: enum {
|
|
declaration
|
|
expression
|
|
assignment_stmt
|
|
return_stmt
|
|
yield_stmt
|
|
break_stmt
|
|
continue_stmt
|
|
unreachable_stmt
|
|
if_stmt
|
|
while_stmt
|
|
for_stmt
|
|
defer_stmt
|
|
errdefer_stmt
|
|
block_stmt
|
|
invalid
|
|
}
|
|
|
|
Mutability :: enum {
|
|
constant, variable
|
|
}
|
|
|
|
UnaryOperator :: enum {
|
|
negate
|
|
logical_not
|
|
bit_not
|
|
address_of
|
|
dereference
|
|
unwrap
|
|
}
|
|
|
|
BinaryOperator :: enum {
|
|
add, subtract
|
|
multiply, divide
|
|
bit_and, bit_or, bit_xor
|
|
shift_left, shift_right, shift_left_saturating
|
|
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_union_tag_type
|
|
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_union_constant
|
|
expr_comptime
|
|
literal_float_value
|
|
literal_void
|
|
literal_undefined
|
|
expr_block
|
|
stmt_yield
|
|
stmt_unreachable
|
|
}
|
|
|
|
@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 file_local_flag u8 :: 0b0001
|
|
@hide:file foreign_type_flag u8 :: 0b0010
|
|
@hide:file variadic_flag u8 :: 0b0100
|
|
@hide:file opaque_flag u8 :: 0b1000
|
|
@hide:file optional_match_flag u8 :: 0b00010000
|
|
@hide:file compound_assignment_flag u8 :: 0b00100000
|
|
|
|
@hide:file
|
|
Node :: struct {
|
|
kind NodeKind
|
|
# use the padding before the payload words rather than enlarging every node.
|
|
flags u8 = 0
|
|
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,
|
|
optional_capture bool = false,
|
|
}
|
|
|
|
MatchArmView :: struct {
|
|
capture ?NodeId
|
|
body NodeId
|
|
expression bool
|
|
catch_all bool
|
|
is_inline bool = false
|
|
tag_capture ?NodeId = null
|
|
tag_capture_present bool = false
|
|
}
|
|
|
|
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
|
|
compound bool = false
|
|
}
|
|
|
|
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
|
|
is_inline bool = false
|
|
}
|
|
|
|
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
|
|
.void: .literal_void
|
|
.undefined: .literal_undefined
|
|
}
|
|
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)
|
|
}
|
|
|
|
#! copies selector spelling into AST-owned storage, independently of its diagnostic span.
|
|
add_generated_identifier proc(
|
|
builder @mut Builder,
|
|
at Span,
|
|
interned_name strpool.StringId,
|
|
text []u8,
|
|
) NodeId ! mem.AllocError {
|
|
id :: try add_generated_literal(builder, at, .string, text)
|
|
node :: &builder.storage.nodes.items[usize(id)]
|
|
node.kind = .expr_identifier
|
|
node.data1 = node.main
|
|
node.main = NodeData{ name_id = interned_name }
|
|
return id
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
#! retains an untagged union's complete native-layout bytes as a string literal.
|
|
add_union_constant proc(
|
|
builder @mut Builder, at Span, type, bytes NodeId,
|
|
) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .expr_union_constant,
|
|
data0 = NodeData{ node_id = type },
|
|
data1 = NodeData{ node_id = bytes },
|
|
}, 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)
|
|
}
|
|
|
|
set_record_c_layout proc(builder @mut Builder, id NodeId) void {
|
|
node :: &builder.storage.nodes.items[usize(id)]
|
|
debug.assert(node.kind == .expr_record_type)
|
|
node.main = NodeData{ extra = 1 }
|
|
}
|
|
|
|
record_c_layout proc(tree @Tree, id NodeId) bool {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_record_type)
|
|
return node.main.extra == 1
|
|
}
|
|
|
|
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_union_tag_type proc(builder @mut Builder, span Span, operand NodeId) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .expr_union_tag_type,
|
|
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)
|
|
}
|
|
|
|
#! marks the canonical presence match emitted for an optional-capture conditional.
|
|
set_match_optional proc(builder @mut Builder, id NodeId) void {
|
|
node :: &builder.storage.nodes.items[usize(id)]
|
|
debug.assert(node.kind == .expr_match)
|
|
node.flags = node.flags | optional_match_flag
|
|
}
|
|
|
|
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)
|
|
_ = try add_extra(builder, u32(no_node))
|
|
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)
|
|
}
|
|
|
|
set_match_arm_inline proc(
|
|
builder @mut Builder, id NodeId, tag_capture ?NodeId, tag_capture_present bool,
|
|
) void {
|
|
node :: get_node(&builder.storage, id)
|
|
debug.assert(node.kind == .match_arm)
|
|
extra :: usize(node.data0.extra)
|
|
builder.storage.extra.items[extra + 1] = builder.storage.extra.items[extra + 1] | 2
|
|
if (tag_capture_present)
|
|
builder.storage.extra.items[extra + 1] = builder.storage.extra.items[extra + 1] | 4
|
|
builder.storage.extra.items[extra + 3] = if (tag_capture) |capture| u32(capture) else u32(no_node)
|
|
}
|
|
|
|
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_block_expr proc(builder @mut Builder, span Span, body NodeId) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .expr_block,
|
|
main = NodeData{ node_id = body },
|
|
}, span)
|
|
}
|
|
|
|
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_yield_stmt proc(builder @mut Builder, span Span, value NodeId) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{
|
|
kind = .stmt_yield,
|
|
main = NodeData{ node_id = 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_unreachable_stmt proc(builder @mut Builder, span Span) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{ kind = .stmt_unreachable }, 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)
|
|
}
|
|
|
|
set_assignment_compound proc(builder @mut Builder, id NodeId) void {
|
|
node :: &builder.storage.nodes.items[usize(id)]
|
|
debug.assert(node.kind == .stmt_assignment)
|
|
node.flags = node.flags | compound_assignment_flag
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
set_for_inline proc(builder @mut Builder, id NodeId) void {
|
|
node :: &builder.storage.nodes.items[usize(id)]
|
|
debug.assert(node.kind == .stmt_for)
|
|
flags :: &builder.storage.extra.items[usize(node.data1.extra) + 3]
|
|
flags^ = flags^ | 2
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
set_file_local proc(builder @mut Builder, id NodeId) void {
|
|
node :: &builder.storage.nodes.items[usize(id)]
|
|
node.flags = node.flags | file_local_flag
|
|
}
|
|
|
|
file_local proc(tree @Tree, id NodeId) bool {
|
|
return (tree.storage.nodes.items[usize(id)].flags & file_local_flag) != 0
|
|
}
|
|
|
|
set_callable_foreign proc(builder @mut Builder, id NodeId) void {
|
|
node :: &builder.storage.nodes.items[usize(id)]
|
|
node.flags = node.flags | foreign_type_flag
|
|
}
|
|
|
|
callable_foreign proc(tree @Tree, id NodeId) bool {
|
|
return function_external(tree, id) or (tree.storage.nodes.items[usize(id)].flags & foreign_type_flag) != 0
|
|
}
|
|
|
|
set_callable_variadic proc(builder @mut Builder, id NodeId) void {
|
|
node :: &builder.storage.nodes.items[usize(id)]
|
|
node.flags = node.flags | variadic_flag
|
|
}
|
|
|
|
callable_variadic proc(tree @Tree, id NodeId) bool {
|
|
return (tree.storage.nodes.items[usize(id)].flags & variadic_flag) != 0
|
|
}
|
|
|
|
set_record_opaque proc(builder @mut Builder, id NodeId) void {
|
|
node :: &builder.storage.nodes.items[usize(id)]
|
|
node.flags = node.flags | opaque_flag
|
|
}
|
|
|
|
record_opaque proc(tree @Tree, id NodeId) bool {
|
|
return (tree.storage.nodes.items[usize(id)].flags & opaque_flag) != 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_yield: .yield_stmt
|
|
.stmt_break: .break_stmt
|
|
.stmt_continue: .continue_stmt
|
|
.stmt_unreachable: .unreachable_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
|
|
}
|
|
|
|
block_expr proc(tree @Tree, id NodeId) NodeId {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_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)),
|
|
}
|
|
}
|
|
|
|
yield_stmt proc(tree @Tree, id NodeId) ReturnView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .stmt_yield)
|
|
return ReturnView{
|
|
span = get_span(&tree.storage, id),
|
|
value = 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,
|
|
compound = (node.flags & compound_assignment_flag) != 0,
|
|
}
|
|
}
|
|
|
|
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] & 1) != 0,
|
|
body = node.data0.node_id,
|
|
is_inline = (tree.storage.extra.items[extra + 3] & 2) != 0,
|
|
}
|
|
}
|
|
|
|
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_union_tag_type: .union_tag_type
|
|
.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_union_constant: .union_constant
|
|
.expr_comptime: .comptime_expr
|
|
.expr_block: .block_expr
|
|
.literal_float_value: .literal_float
|
|
.literal_void: .literal_void
|
|
.literal_undefined: .literal_undefined
|
|
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,
|
|
}
|
|
}
|
|
|
|
union_constant proc(tree @Tree, id NodeId) TypedConstantView {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_union_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 or node.kind == .expr_union_tag_type)
|
|
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,
|
|
optional_capture = (node.flags & optional_match_flag) != 0,
|
|
}
|
|
}
|
|
|
|
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] & 1) != 0,
|
|
catch_all = tree.storage.extra.items[extra + 2] != 0,
|
|
is_inline = (tree.storage.extra.items[extra + 1] & 2) != 0,
|
|
tag_capture = maybe_node_id(tree.storage.extra.items[extra + 3]),
|
|
tag_capture_present = (tree.storage.extra.items[extra + 1] & 4) != 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 - 4)
|
|
}
|
|
|
|
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) + 4 + 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,
|
|
.literal_void, .literal_undefined, .expr_identifier, .invalid_expr: {}
|
|
.expr_unary: layout.direct_children = 2
|
|
.expr_binary, .expr_range, .expr_index, .expr_selection, .expr_typed_constant, .expr_union_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_union_tag_type, .expr_enum_literal,
|
|
.expr_try, .expr_comptime, .expr_block: 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 = 9
|
|
layout.list_start = 4
|
|
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_yield, .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, .stmt_unreachable, .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 literal or selector 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.kind == .expr_identifier and node.data0.extra == 1)
|
|
return tree.storage.generated.items[usize(node.data1.extra)]
|
|
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
|
|
}
|