849 lines
24 KiB
Plaintext
849 lines
24 KiB
Plaintext
#! grammar:
|
|
#! * type ..... -> identifier
|
|
#! * binding ... -> :: | :=
|
|
#! * literal .. -> int | float | string | 'true' | 'false'
|
|
#! * op ....... -> '+' | '-' | '*' | '/' | '==' | '!=' | '<' | '<=' | '>' | '>=' | 'and' | 'or'
|
|
#! * expr ..... -> identifier | literal | '(' expr ')' | ('-' | '!') expr | expr op expr | expr '(' [expr {',' expr} [',']] ')' | expr ('..' | '..=') expr
|
|
#! * decl ..... -> identifier [type] binding expr
|
|
#! * block .... -> '{' {stmt} '}'
|
|
#! * arm ...... -> block | stmt (single statements become canonical blocks)
|
|
#! * stmt ..... -> decl | expr | identifier '=' expr | 'return' [expr] | 'break' | 'continue' | 'if' expr arm ['else' arm] | 'while' expr arm | 'for' expr '|' ['@'] identifier [',' identifier] '|' arm
|
|
#! braceless guards/iterables require parentheses unless they are calls; else binds to the nearest if.
|
|
#! precedence (tightest first): calls, unary -/!, multiplication/division, addition/subtraction, relational comparisons, equality, and, or, ranges. compound range endpoints require their own parentheses.
|
|
#! binary operators associate left; unary operators associate right.
|
|
|
|
import "@std/debug"
|
|
import "@std/mem"
|
|
import "@std/arraylist"
|
|
import "@source/diagnostics"
|
|
|
|
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
|
|
}
|
|
|
|
ExprKind :: enum {
|
|
literal_int
|
|
literal_float
|
|
literal_string
|
|
literal_bool
|
|
identifier
|
|
unary
|
|
binary
|
|
call
|
|
range
|
|
invalid
|
|
}
|
|
|
|
DeclKind :: enum {
|
|
statement, function, invalid
|
|
}
|
|
|
|
StmtKind :: enum {
|
|
declaration,
|
|
expression,
|
|
assignment_stmt,
|
|
return_stmt,
|
|
break_stmt,
|
|
continue_stmt,
|
|
if_stmt,
|
|
while_stmt,
|
|
for_stmt,
|
|
invalid
|
|
}
|
|
|
|
Mutability :: enum {
|
|
constant, variable
|
|
}
|
|
|
|
UnaryOperator :: enum { negate, logical_not }
|
|
|
|
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,
|
|
stmt_expr,
|
|
stmt_decl,
|
|
stmt_return,
|
|
stmt_break,
|
|
stmt_continue,
|
|
stmt_if,
|
|
stmt_assignment,
|
|
stmt_while,
|
|
stmt_for,
|
|
func_decl,
|
|
param_decl,
|
|
block,
|
|
package,
|
|
invalid_expr,
|
|
invalid_stmt,
|
|
invalid_decl,
|
|
}
|
|
|
|
@hide:file
|
|
NodeData :: union {
|
|
node_id NodeId
|
|
extra u32
|
|
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)
|
|
}
|
|
|
|
#! owns node storage, retains its allocator, and borrows source until finish or deinit_builder.
|
|
Builder :: struct {
|
|
storage Storage
|
|
source []u8
|
|
}
|
|
|
|
#! owns node storage and borrows source; source must remain valid until deinit.
|
|
Tree :: struct {
|
|
storage Storage
|
|
source []u8
|
|
root NodeId
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
UnaryExprView :: struct {
|
|
span Span
|
|
operator UnaryOperator
|
|
operand NodeId
|
|
}
|
|
|
|
BinaryExprView :: struct {
|
|
span Span
|
|
operator BinaryOperator
|
|
left NodeId
|
|
right NodeId
|
|
}
|
|
|
|
CallExprView :: struct {
|
|
span Span
|
|
callee NodeId
|
|
}
|
|
|
|
ReturnView :: struct {
|
|
span Span
|
|
value ?NodeId
|
|
}
|
|
|
|
IfView :: struct {
|
|
span Span
|
|
condition NodeId
|
|
then_block NodeId
|
|
else_block ?NodeId
|
|
}
|
|
|
|
AssignmentView :: struct {
|
|
span Span
|
|
target NodeId
|
|
value NodeId
|
|
}
|
|
|
|
WhileView :: struct {
|
|
span Span
|
|
condition NodeId
|
|
body NodeId
|
|
}
|
|
|
|
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),
|
|
},
|
|
source = source,
|
|
}
|
|
}
|
|
|
|
#! releases builder-owned node storage with its retained allocator, without freeing source.
|
|
deinit_builder proc(builder @mut Builder) void {
|
|
arraylist.deinit(&builder.storage.nodes)
|
|
arraylist.deinit(&builder.storage.extra)
|
|
arraylist.deinit(&builder.storage.spans)
|
|
}
|
|
|
|
#! transfers node storage and the source borrow to a caller-owned Tree; release with deinit.
|
|
finish proc(builder @mut Builder, root NodeId) Tree {
|
|
tree :: Tree{ storage = builder.storage, source = builder.source, root = root }
|
|
allocator :: builder.storage.nodes.allocator
|
|
builder.storage = Storage{
|
|
nodes = arraylist.init(allocator),
|
|
extra = arraylist.init(allocator),
|
|
spans = arraylist.init(allocator),
|
|
}
|
|
builder.source = &[]
|
|
return tree
|
|
}
|
|
|
|
#! releases owned node storage with its retained allocator, without freeing source.
|
|
deinit proc(tree @mut Tree) void {
|
|
arraylist.deinit(&tree.storage.nodes)
|
|
arraylist.deinit(&tree.storage.extra)
|
|
arraylist.deinit(&tree.storage.spans)
|
|
}
|
|
|
|
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
|
|
}
|
|
return try add_node(builder, Node{ kind = node_kind }, span)
|
|
}
|
|
|
|
add_identifier proc(builder @mut Builder, span Span) NodeId ! mem.AllocError {
|
|
return try add_node(builder, Node{ kind = .expr_identifier }, 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)
|
|
}
|
|
|
|
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_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_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
|
|
return try add_node(builder, Node{
|
|
kind = .stmt_if,
|
|
main = NodeData{ node_id = condition },
|
|
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)
|
|
}
|
|
|
|
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,
|
|
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 },
|
|
}, span)
|
|
}
|
|
|
|
add_param proc(builder @mut Builder, span, name Span, type NodeId) NodeId ! mem.AllocError {
|
|
extra :: try add_extra(builder, name.start)
|
|
_ = try add_extra(builder, name.end)
|
|
return try add_node(builder, Node{
|
|
kind = .param_decl,
|
|
main = NodeData{ extra = extra },
|
|
data0 = NodeData{ node_id = type },
|
|
}, span)
|
|
}
|
|
|
|
add_function_decl proc(
|
|
builder @mut Builder,
|
|
span Span,
|
|
name Span,
|
|
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))
|
|
for (params) |parameter| _ = try add_extra(builder, u32(parameter))
|
|
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)
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
package_decl_count proc(tree @Tree) usize {
|
|
root :: get_node(&tree.storage, tree.root)
|
|
debug.assert(root.kind == .package)
|
|
return usize(root.data1.extra - root.data0.extra)
|
|
}
|
|
|
|
package_decl proc(tree @Tree, index usize) NodeId {
|
|
root :: get_node(&tree.storage, tree.root)
|
|
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])
|
|
}
|
|
|
|
decl_kind proc(tree @Tree, id NodeId) DeclKind {
|
|
return match get_node(&tree.storage, id).kind {
|
|
.stmt_decl: .statement
|
|
.func_decl: .function
|
|
.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
|
|
.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
|
|
}
|
|
|
|
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 = node.main.node_id,
|
|
then_block = node.data0.node_id,
|
|
else_block = maybe_node_id(u32(node.data1.node_id)),
|
|
}
|
|
}
|
|
|
|
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,
|
|
}
|
|
}
|
|
|
|
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),
|
|
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),
|
|
return_type = NodeId(tree.storage.extra.items[extra + 2]),
|
|
body = node.data0.node_id,
|
|
}
|
|
}
|
|
|
|
function_param_count proc(tree @Tree, id NodeId) usize {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .func_decl)
|
|
return usize(node.data1.extra - node.main.extra - 3)
|
|
}
|
|
|
|
function_param proc(tree @Tree, id NodeId, index usize) NodeId {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .func_decl)
|
|
debug.assert(index < usize(node.data1.extra - node.main.extra - 3))
|
|
return NodeId(tree.storage.extra.items[usize(node.main.extra) + 3 + 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)),
|
|
type = node.data0.node_id,
|
|
}
|
|
}
|
|
|
|
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
|
|
.invalid_expr: .invalid
|
|
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)
|
|
return BinaryExprView{
|
|
span = get_span(&tree.storage, id),
|
|
operator = node.main.binary_operator,
|
|
left = node.data0.node_id,
|
|
right = node.data1.node_id,
|
|
}
|
|
}
|
|
|
|
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)
|
|
return usize(node.data1.extra - node.data0.extra)
|
|
}
|
|
|
|
call_arg proc(tree @Tree, id NodeId, index usize) NodeId {
|
|
node :: get_node(&tree.storage, id)
|
|
debug.assert(node.kind == .expr_call)
|
|
debug.assert(index < usize(node.data1.extra - node.data0.extra))
|
|
return NodeId(tree.storage.extra.items[usize(node.data0.extra) + index])
|
|
}
|
|
|
|
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(span.start <= span.end and usize(span.end) <= tree.source.len)
|
|
return tree.source[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) Span {
|
|
return Span{
|
|
start = storage.extra.items[start],
|
|
end = storage.extra.items[start + 1],
|
|
}
|
|
}
|
|
|
|
@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)
|
|
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)
|
|
}
|