3097 lines
100 KiB
V
3097 lines
100 KiB
V
// Copyright (c) 2020-2024 Joe Conigliaro. All rights reserved.
|
|
// Use of this source code is governed by an MIT license
|
|
// that can be found in the LICENSE file.
|
|
module ast
|
|
|
|
import os
|
|
import v2.token
|
|
|
|
// FlatAst is a contiguous, index-based AST representation. Every node lives
|
|
// in a single `nodes[]` table; relationships are encoded via `edges[]` ranges
|
|
// instead of heap-allocated sumtype payloads. The schema is designed to be
|
|
// LOSSLESS: every field of every legacy AST variant is preserved either as a
|
|
// kind-specific bit/enum in the cell, or as a child node reachable through
|
|
// edges. token.Pos is stored inline (8 bytes) per node to preserve diagnostics
|
|
// without an extra interning layer.
|
|
|
|
pub type FlatNodeId = int
|
|
|
|
pub const invalid_flat_node_id = FlatNodeId(-1)
|
|
|
|
// flag bit positions used by FlatNode.flags. Bits are reused per variant;
|
|
// see add_* helpers below for the exact mapping.
|
|
pub const flag_is_public = u8(1) << 0
|
|
pub const flag_is_mut = u8(1) << 1
|
|
pub const flag_is_method = u8(1) << 2
|
|
pub const flag_is_static = u8(1) << 3
|
|
pub const flag_is_union = u8(1) << 4
|
|
pub const flag_is_gated = u8(1) << 5
|
|
pub const flag_is_aliased = u8(1) << 6 // ImportStmt
|
|
pub const flag_defer_func = u8(1) << 7 // DeferStmt.mode == .function
|
|
pub const flag_field_is_module_mut = u8(1) << 2 // FieldDecl (reuse method bit)
|
|
pub const flag_field_is_interface_method = u8(1) << 3 // FieldDecl (reuse static bit)
|
|
pub const flag_is_count = u8(1) << 6 // SqlExpr (reuse aliased bit)
|
|
pub const flag_is_create = u8(1) << 7 // SqlExpr (reuse defer bit)
|
|
|
|
// FlatNodeKind is a dense tag for every stored AST node variant.
|
|
pub enum FlatNodeKind {
|
|
file
|
|
stmt_asm
|
|
stmt_assert
|
|
stmt_assign
|
|
stmt_block
|
|
stmt_comptime
|
|
stmt_const_decl
|
|
stmt_defer
|
|
stmt_directive
|
|
stmt_empty
|
|
stmt_enum_decl
|
|
stmt_expr
|
|
stmt_flow_control
|
|
stmt_fn_decl
|
|
stmt_for_in
|
|
stmt_for
|
|
stmt_global_decl
|
|
stmt_import
|
|
stmt_interface_decl
|
|
stmt_label
|
|
stmt_module
|
|
stmt_return
|
|
stmt_struct_decl
|
|
stmt_type_decl
|
|
stmt_attributes
|
|
expr_array_init
|
|
expr_as_cast
|
|
expr_assoc
|
|
expr_basic_literal
|
|
expr_call
|
|
expr_call_or_cast
|
|
expr_cast
|
|
expr_comptime
|
|
expr_empty
|
|
expr_fn_literal
|
|
expr_generic_arg_or_index
|
|
expr_generic_args
|
|
expr_ident
|
|
expr_if
|
|
expr_if_guard
|
|
expr_index
|
|
expr_infix
|
|
expr_init
|
|
expr_keyword
|
|
expr_keyword_operator
|
|
expr_lambda
|
|
expr_lifetime
|
|
expr_lock
|
|
expr_map_init
|
|
expr_match
|
|
expr_modifier
|
|
expr_or
|
|
expr_paren
|
|
expr_postfix
|
|
expr_prefix
|
|
expr_range
|
|
expr_select
|
|
expr_selector
|
|
expr_sql
|
|
expr_string_inter
|
|
expr_string
|
|
expr_tuple
|
|
expr_unsafe
|
|
typ_anon_struct
|
|
typ_array_fixed
|
|
typ_array
|
|
typ_channel
|
|
typ_fn
|
|
typ_generic
|
|
typ_map
|
|
typ_nil
|
|
typ_none
|
|
typ_option
|
|
typ_pointer
|
|
typ_result
|
|
typ_thread
|
|
typ_tuple
|
|
aux_attribute
|
|
aux_field_init
|
|
aux_field_decl
|
|
aux_parameter
|
|
aux_match_branch
|
|
aux_string_inter
|
|
aux_list // ordered list grouping: edges are the items
|
|
aux_string // single interned string carrier (used where a node needs an extra string slot)
|
|
aux_int // single integer carrier (used where a node needs an extra int slot)
|
|
}
|
|
|
|
// FlatEdge represents a parent->child relationship.
|
|
pub struct FlatEdge {
|
|
pub:
|
|
child_id FlatNodeId
|
|
}
|
|
|
|
// FlatNode is the compiler-grade flat cell.
|
|
// 28 bytes of payload, 32-byte aligned. Carries inline pos for diagnostics
|
|
// and four side-channel slots (flags, aux, name_id, extra) for variant data.
|
|
pub struct FlatNode {
|
|
pub mut:
|
|
kind FlatNodeKind // 1 byte: variant tag
|
|
flags u8 // 1 byte: bit-packed booleans (see flag_* consts)
|
|
aux u16 // 2 bytes: token.Token or sub-kind enum (variant-specific)
|
|
name_id int // 4 bytes: primary interned string id (-1 if none)
|
|
extra int // 4 bytes: secondary slot (interned string id, packed ints, or list boundary)
|
|
pos token.Pos // 8 bytes: inline source position
|
|
first_edge int // 4 bytes: start offset into FlatAst.edges
|
|
edge_count int // 4 bytes: number of child edges
|
|
}
|
|
|
|
// FlatFile maps a source file to its flat root node and original selector map.
|
|
pub struct FlatFile {
|
|
pub:
|
|
file_id FlatNodeId
|
|
name_idx int
|
|
mod_idx int
|
|
selector_names map[int]string
|
|
}
|
|
|
|
// FlatAst is a contiguous AST graph representation.
|
|
pub struct FlatAst {
|
|
pub mut:
|
|
files []FlatFile
|
|
nodes []FlatNode
|
|
edges []FlatEdge
|
|
strings []string
|
|
}
|
|
|
|
// FlatAstStats captures high-level memory and shape metrics.
|
|
pub struct FlatAstStats {
|
|
pub:
|
|
file_roots int
|
|
nodes int
|
|
edges int
|
|
strings int
|
|
string_bytes u64
|
|
bytes_estimate u64
|
|
}
|
|
|
|
// LegacyAstStats captures estimated dynamic memory and node shape for the existing AST.
|
|
pub struct LegacyAstStats {
|
|
pub mut:
|
|
files int
|
|
expr_nodes int
|
|
stmt_nodes int
|
|
type_nodes int
|
|
aux_nodes int
|
|
node_bytes u64
|
|
array_bytes u64
|
|
string_entries int
|
|
string_bytes u64
|
|
// allocs counts distinct heap allocations the GC must track: every
|
|
// sumtype variant payload, every dynamic array (spine), and every
|
|
// string buffer. Flat AST collapses these to a handful of arenas.
|
|
allocs u64
|
|
bytes_estimate u64
|
|
}
|
|
|
|
// flatten_files converts recursive v2 AST files into a flat, index-based graph.
|
|
pub fn flatten_files(files []File) FlatAst {
|
|
mut total_bytes := i64(0)
|
|
for file in files {
|
|
if file.name == '' {
|
|
continue
|
|
}
|
|
total_bytes += os.file_size(file.name)
|
|
}
|
|
nodes_cap, edges_cap, strings_cap := arena_caps_for_bytes(total_bytes)
|
|
mut b := new_flat_builder_with_capacity(nodes_cap, edges_cap, strings_cap)
|
|
for file in files {
|
|
b.append_file(file)
|
|
}
|
|
return b.flat
|
|
}
|
|
|
|
// arena_caps_for_bytes returns (nodes_cap, edges_cap, strings_cap) sized from
|
|
// total source-byte estimate. Calibrated against the v2 self-host parse:
|
|
// ~150 nodes/KB, ~170 edges/KB, ~5 unique strings/KB. Floors match the
|
|
// default seed caps so tiny inputs don't waste arena space.
|
|
pub fn arena_caps_for_bytes(total_bytes i64) (int, int, int) {
|
|
kb := int(total_bytes / 1024) + 1
|
|
mut nodes_cap := 150 * kb
|
|
mut edges_cap := 170 * kb
|
|
mut strings_cap := 5 * kb
|
|
if nodes_cap < 1024 {
|
|
nodes_cap = 1024
|
|
}
|
|
if edges_cap < 2048 {
|
|
edges_cap = 2048
|
|
}
|
|
if strings_cap < 512 {
|
|
strings_cap = 512
|
|
}
|
|
return nodes_cap, edges_cap, strings_cap
|
|
}
|
|
|
|
// legacy_ast_stats estimates dynamic memory and node counts for the recursive AST.
|
|
pub fn legacy_ast_stats(files []File) LegacyAstStats {
|
|
mut w := LegacyAstWalker{}
|
|
w.walk_files(files)
|
|
return w.stats
|
|
}
|
|
|
|
// count_legacy_nodes returns the total legacy node count used by the walker.
|
|
pub fn count_legacy_nodes(files []File) int {
|
|
stats := legacy_ast_stats(files)
|
|
return stats.files + stats.expr_nodes + stats.stmt_nodes + stats.type_nodes + stats.aux_nodes
|
|
}
|
|
|
|
// stats returns aggregate shape and estimated memory usage for FlatAst.
|
|
pub fn (flat &FlatAst) stats() FlatAstStats {
|
|
mut string_bytes := u64(0)
|
|
for s in flat.strings {
|
|
string_bytes += u64(s.len)
|
|
}
|
|
mut bytes := u64(flat.files.len) * u64(sizeof(FlatFile))
|
|
bytes += u64(flat.nodes.len) * u64(sizeof(FlatNode))
|
|
bytes += u64(flat.edges.len) * u64(sizeof(FlatEdge))
|
|
bytes += u64(flat.strings.len) * u64(sizeof(string))
|
|
bytes += string_bytes
|
|
return FlatAstStats{
|
|
file_roots: flat.files.len
|
|
nodes: flat.nodes.len
|
|
edges: flat.edges.len
|
|
strings: flat.strings.len
|
|
string_bytes: string_bytes
|
|
bytes_estimate: bytes
|
|
}
|
|
}
|
|
|
|
// count_nodes_by_kind returns a tally of how many nodes exist of each kind.
|
|
// Useful for spotting structural overhead (e.g., aux_list wrappers).
|
|
pub fn (flat &FlatAst) count_nodes_by_kind() map[string]int {
|
|
mut hist := map[string]int{}
|
|
for n in flat.nodes {
|
|
hist[n.kind.str()]++
|
|
}
|
|
return hist
|
|
}
|
|
|
|
// count_reachable_nodes traverses from file roots and counts unique reachable nodes.
|
|
pub fn (flat &FlatAst) count_reachable_nodes() int {
|
|
if flat.nodes.len == 0 || flat.files.len == 0 {
|
|
return 0
|
|
}
|
|
mut seen := []bool{len: flat.nodes.len}
|
|
mut stack := []int{cap: flat.files.len}
|
|
for file in flat.files {
|
|
stack << file.file_id
|
|
}
|
|
mut count := 0
|
|
for stack.len > 0 {
|
|
node_id := stack.pop()
|
|
if node_id < 0 || node_id >= flat.nodes.len {
|
|
continue
|
|
}
|
|
if seen[node_id] {
|
|
continue
|
|
}
|
|
seen[node_id] = true
|
|
count++
|
|
node := flat.nodes[node_id]
|
|
for i in 0 .. node.edge_count {
|
|
edge := flat.edges[node.first_edge + i]
|
|
stack << edge.child_id
|
|
}
|
|
}
|
|
return count
|
|
}
|
|
|
|
// node_type_name returns the short type-name tag associated with a flat node.
|
|
pub fn (flat &FlatAst) node_type_name(node_id FlatNodeId) string {
|
|
if node_id < 0 || node_id >= flat.nodes.len {
|
|
return ''
|
|
}
|
|
return flat.nodes[node_id].kind.str()
|
|
}
|
|
|
|
// child_at returns the i-th child of `parent` (0-indexed), or invalid_flat_node_id.
|
|
@[inline]
|
|
pub fn (flat &FlatAst) child_at(parent FlatNodeId, i int) FlatNodeId {
|
|
if parent < 0 || parent >= flat.nodes.len {
|
|
return invalid_flat_node_id
|
|
}
|
|
node := flat.nodes[parent]
|
|
if i < 0 || i >= node.edge_count {
|
|
return invalid_flat_node_id
|
|
}
|
|
return flat.edges[node.first_edge + i].child_id
|
|
}
|
|
|
|
// string_at returns the interned string at `idx`, or empty when out of range.
|
|
@[inline]
|
|
pub fn (flat &FlatAst) string_at(idx int) string {
|
|
if idx < 0 || idx >= flat.strings.len {
|
|
return ''
|
|
}
|
|
return flat.strings[idx]
|
|
}
|
|
|
|
// FlatBuilder is the incremental builder behind FlatAst. It is public so
|
|
// front-ends (parser, type checker, ...) can append nodes directly without
|
|
// going through the legacy recursive AST. The legacy converters (add_expr,
|
|
// add_stmt, ...) remain internal to this module and are exposed via the
|
|
// `append_file(File)` shim used during the Phase 2 transition.
|
|
pub struct FlatBuilder {
|
|
pub mut:
|
|
flat FlatAst
|
|
mut:
|
|
string_ids map[string]int
|
|
empty_list_id FlatNodeId = invalid_flat_node_id
|
|
empty_expr_id FlatNodeId = invalid_flat_node_id
|
|
empty_stmt_id FlatNodeId = invalid_flat_node_id
|
|
}
|
|
|
|
// new_flat_builder returns a fresh FlatBuilder seeded with reasonable arena
|
|
// capacities. Callers may resize after measuring (b.flat.nodes.grow_cap, ...).
|
|
pub fn new_flat_builder() FlatBuilder {
|
|
return new_flat_builder_with_capacity(1024, 2048, 512)
|
|
}
|
|
|
|
// new_flat_builder_with_capacity lets callers pre-size the three arenas up
|
|
// front when an estimate is available (e.g. derived from total source size).
|
|
// Empirically the v2 self-host produces ~150 nodes/KB, ~165 edges/KB and ~5
|
|
// unique strings/KB; supplying tight caps avoids the geometric realloc churn
|
|
// that dominates the streaming parse path.
|
|
pub fn new_flat_builder_with_capacity(nodes_cap int, edges_cap int, strings_cap int) FlatBuilder {
|
|
return FlatBuilder{
|
|
flat: FlatAst{
|
|
files: []FlatFile{}
|
|
nodes: []FlatNode{cap: nodes_cap}
|
|
edges: []FlatEdge{cap: edges_cap}
|
|
strings: []string{cap: strings_cap}
|
|
}
|
|
string_ids: map[string]int{}
|
|
}
|
|
}
|
|
|
|
// take_flat moves the built flat graph out and releases transient builder-only
|
|
// indexes. The returned FlatAst keeps owning its arena arrays.
|
|
pub fn (mut b FlatBuilder) take_flat() FlatAst {
|
|
flat := b.flat
|
|
unsafe {
|
|
b.string_ids.free()
|
|
}
|
|
b.flat = FlatAst{}
|
|
b.string_ids = map[string]int{}
|
|
b.empty_list_id = invalid_flat_node_id
|
|
b.empty_expr_id = invalid_flat_node_id
|
|
b.empty_stmt_id = invalid_flat_node_id
|
|
return flat
|
|
}
|
|
|
|
// append_file converts one legacy File into flat nodes and registers it as a
|
|
// root. Designed for streaming use: the caller can drop the legacy `file`
|
|
// immediately after this returns, capping peak memory at ~one file's legacy
|
|
// AST plus the cumulative flat representation.
|
|
pub fn (mut b FlatBuilder) append_file(file File) FlatNodeId {
|
|
file_id := b.add_file(file)
|
|
b.flat.files << FlatFile{
|
|
file_id: file_id
|
|
name_idx: b.intern(file.name)
|
|
mod_idx: b.intern(file.mod)
|
|
selector_names: file.selector_names
|
|
}
|
|
return file_id
|
|
}
|
|
|
|
// append_file_with_stmt_ids registers a file root whose stmt list is built
|
|
// from FlatNodeIds that were already emitted into this builder by a previous
|
|
// per-stmt pass (e.g. the transformer's flat-write path). Attributes and
|
|
// imports are still consumed from the legacy `file` because they are not
|
|
// subject to transformer rewrites; emitting them here keeps the file shape
|
|
// bit-equal to `add_file`'s output.
|
|
pub fn (mut b FlatBuilder) append_file_with_stmt_ids(file File, stmt_ids []FlatNodeId) FlatNodeId {
|
|
mut edges := []FlatEdge{}
|
|
edges = b.push_edge(edges, b.make_list_attribute(file.attributes))
|
|
edges = b.push_edge(edges, b.make_list_imports(file.imports))
|
|
edges = b.push_edge(edges, b.make_list_from_stmt_ids(stmt_ids))
|
|
file_id := b.emit(.file, token.Pos{}, b.intern(file.name), b.intern(file.mod), 0, 0, edges)
|
|
b.flat.files << FlatFile{
|
|
file_id: file_id
|
|
name_idx: b.intern(file.name)
|
|
mod_idx: b.intern(file.mod)
|
|
selector_names: file.selector_names
|
|
}
|
|
return file_id
|
|
}
|
|
|
|
// append_file_with_flat_attrs_and_stmt_ids registers a file root whose
|
|
// attributes and stmt list were already emitted into this builder. Imports are
|
|
// still accepted as legacy values because import handling is not transformed.
|
|
pub fn (mut b FlatBuilder) append_file_with_flat_attrs_and_stmt_ids(name string, mod string, selector_names map[int]string, attrs_id FlatNodeId, imports []ImportStmt, stmt_ids []FlatNodeId) FlatNodeId {
|
|
mut edges := []FlatEdge{}
|
|
edges = b.push_edge(edges, attrs_id)
|
|
edges = b.push_edge(edges, b.make_list_imports(imports))
|
|
edges = b.push_edge(edges, b.make_list_from_stmt_ids(stmt_ids))
|
|
file_id := b.emit(.file, token.Pos{}, b.intern(name), b.intern(mod), 0, 0, edges)
|
|
b.flat.files << FlatFile{
|
|
file_id: file_id
|
|
name_idx: b.intern(name)
|
|
mod_idx: b.intern(mod)
|
|
selector_names: selector_names
|
|
}
|
|
return file_id
|
|
}
|
|
|
|
// append_file_with_flat_lists_and_stmt_ids registers a file root whose
|
|
// attributes, imports, and stmt list were already emitted into this builder.
|
|
pub fn (mut b FlatBuilder) append_file_with_flat_lists_and_stmt_ids(name string, mod string, selector_names map[int]string, attrs_id FlatNodeId, imports_id FlatNodeId, stmt_ids []FlatNodeId) FlatNodeId {
|
|
mut edges := []FlatEdge{}
|
|
edges = b.push_edge(edges, attrs_id)
|
|
edges = b.push_edge(edges, imports_id)
|
|
edges = b.push_edge(edges, b.make_list_from_stmt_ids(stmt_ids))
|
|
file_id := b.emit(.file, token.Pos{}, b.intern(name), b.intern(mod), 0, 0, edges)
|
|
b.flat.files << FlatFile{
|
|
file_id: file_id
|
|
name_idx: b.intern(name)
|
|
mod_idx: b.intern(mod)
|
|
selector_names: selector_names
|
|
}
|
|
return file_id
|
|
}
|
|
|
|
// append_flat copies an entire `src` FlatAst (nodes, edges, strings, file roots)
|
|
// into this builder, relocating every node id / edge target and re-interning
|
|
// every string so the merged result is structurally identical to `src` decoded
|
|
// standalone. Returns the node-id offset applied to all of `src`'s nodes (a src
|
|
// node id `x` maps to merged id `x + offset`).
|
|
//
|
|
// This is the merge primitive behind the flat parallel transform: each worker
|
|
// emits into its own FlatBuilder, then the main thread concatenates the
|
|
// workers' outputs via append_flat (replacing the legacy per-worker `ast.File`
|
|
// rehydrate-then-flatten). Relocation rules — verified against the canonical
|
|
// decoder (flat_reader.v):
|
|
// - edge.child_id : + node_offset (invalid_flat_node_id stays invalid)
|
|
// - node.first_edge: + edge_offset
|
|
// - node.name_id : re-interned via the string remap (-1 stays -1)
|
|
// - node.extra : re-interned ONLY for the three kinds that store a string
|
|
// id there (file mod, stmt_directive value, stmt_import
|
|
// alias); every other kind packs ints/counts/flags/list-
|
|
// boundaries in extra (lhs_len, cap_len, keys_len, width/
|
|
// precision, aux_int value, ...) → copied verbatim
|
|
// (relocation-invariant)
|
|
// - node.aux / pos : copied verbatim (aux is a token/sub-kind enum; pos.id is
|
|
// globally unique across the merged inputs, and
|
|
// selector_names is keyed by pos.id so it needs no remap)
|
|
// - file.file_id : + node_offset; name_idx/mod_idx re-interned;
|
|
// selector_names copied (cloned for ownership)
|
|
pub fn (mut b FlatBuilder) append_flat(src &FlatAst) int {
|
|
node_offset := b.flat.nodes.len
|
|
edge_offset := b.flat.edges.len
|
|
// Remap src string ids -> dst string ids (dedups into b's intern table).
|
|
mut string_remap := []int{len: src.strings.len}
|
|
for i, s in src.strings {
|
|
string_remap[i] = b.intern(s)
|
|
}
|
|
// Relocate + append edges.
|
|
b.flat.edges.grow_cap(src.edges.len)
|
|
for e in src.edges {
|
|
child := if e.child_id >= 0 { e.child_id + node_offset } else { e.child_id }
|
|
b.flat.edges << FlatEdge{
|
|
child_id: child
|
|
}
|
|
}
|
|
// Relocate + append nodes.
|
|
b.flat.nodes.grow_cap(src.nodes.len)
|
|
for n in src.nodes {
|
|
mut nn := n
|
|
nn.name_id = if n.name_id >= 0 { string_remap[n.name_id] } else { n.name_id }
|
|
if n.extra >= 0 && (n.kind == .file || n.kind == .stmt_directive || n.kind == .stmt_import) {
|
|
nn.extra = string_remap[n.extra]
|
|
}
|
|
nn.first_edge = n.first_edge + edge_offset
|
|
b.flat.nodes << nn
|
|
}
|
|
// Relocate + append file roots.
|
|
for ff in src.files {
|
|
b.flat.files << FlatFile{
|
|
file_id: ff.file_id + node_offset
|
|
name_idx: if ff.name_idx >= 0 { string_remap[ff.name_idx] } else { ff.name_idx }
|
|
mod_idx: if ff.mod_idx >= 0 { string_remap[ff.mod_idx] } else { ff.mod_idx }
|
|
selector_names: ff.selector_names.clone()
|
|
}
|
|
}
|
|
return node_offset
|
|
}
|
|
|
|
// copy_subtree_from copies the subtree rooted at `root` from `src` into this
|
|
// builder, re-interning strings and rebuilding child edges recursively.
|
|
pub fn (mut b FlatBuilder) copy_subtree_from(src &FlatAst, root FlatNodeId) FlatNodeId {
|
|
if root < 0 || root >= src.nodes.len {
|
|
return invalid_flat_node_id
|
|
}
|
|
n := src.nodes[root]
|
|
mut edges := []FlatEdge{cap: n.edge_count}
|
|
for i in 0 .. n.edge_count {
|
|
child := src.child_at(root, i)
|
|
copied_child := if child >= 0 {
|
|
b.copy_subtree_from(src, child)
|
|
} else {
|
|
child
|
|
}
|
|
edges << FlatEdge{
|
|
child_id: copied_child
|
|
}
|
|
}
|
|
name_id := b.copy_string_id_from(src, n.name_id)
|
|
extra := if n.extra >= 0
|
|
&& (n.kind == .file || n.kind == .stmt_directive || n.kind == .stmt_import) {
|
|
b.copy_string_id_from(src, n.extra)
|
|
} else {
|
|
n.extra
|
|
}
|
|
return b.emit(n.kind, n.pos, name_id, extra, n.aux, n.flags, edges)
|
|
}
|
|
|
|
fn (mut b FlatBuilder) copy_string_id_from(src &FlatAst, id int) int {
|
|
if id < 0 || id >= src.strings.len {
|
|
return id
|
|
}
|
|
return b.intern(src.strings[id])
|
|
}
|
|
|
|
// emit_stmt is the public wrapper around the legacy stmt-to-flat conversion.
|
|
// Future sessions of the transformer-writes-flat port reach for this when a
|
|
// stmt variant still falls back to legacy emission; the long-term goal is for
|
|
// transform_stmt_to_flat (in v2.transformer) to stop calling it as each stmt
|
|
// arm gets a direct-emit replacement.
|
|
pub fn (mut b FlatBuilder) emit_stmt(stmt Stmt) FlatNodeId {
|
|
return b.add_stmt(stmt)
|
|
}
|
|
|
|
// append_file_stmts re-emits the file root for `flat.files[file_idx]` with
|
|
// `extra_stmt_ids` appended to the existing stmts list. The file's existing
|
|
// attrs and imports edges are reused as-is (no re-emit) — only the stmts
|
|
// list (file root edge 2) is rebuilt. Updates `flat.files[file_idx]` in place
|
|
// to point at the new file root and returns its FlatNodeId.
|
|
//
|
|
// Append-only: the old file root remains in `flat.nodes` as garbage (never
|
|
// reachable from `flat.files`). Callers must not retain the previous file_id.
|
|
//
|
|
// Designed for the post-pass mutation pattern (s144..s149 `_parts` extracts).
|
|
// `inject_embed_file_helper`-equivalent + `inject_test_main`-equivalent +
|
|
// `generated_fns_parts` core/module/user splices all need to append a list
|
|
// of pre-emitted stmt nodes to a specific file's stmt list. The transformer's
|
|
// upcoming `post_pass_to_flat` wires each of those into one call of this
|
|
// helper.
|
|
//
|
|
// Bit-equal to `append_file(file_with_extended_stmts)` w.r.t. the
|
|
// `signature()` walk: the file root's edge list (attrs_list_id,
|
|
// imports_list_id, new_stmts_list_id) names the same logical children, and
|
|
// `signature()` only follows reachable edges from the file root.
|
|
pub fn (mut b FlatBuilder) append_file_stmts(file_idx int, extra_stmt_ids []FlatNodeId) FlatNodeId {
|
|
if file_idx < 0 || file_idx >= b.flat.files.len {
|
|
return invalid_flat_node_id
|
|
}
|
|
old_file_id := b.flat.files[file_idx].file_id
|
|
if extra_stmt_ids.len == 0 {
|
|
return old_file_id
|
|
}
|
|
name_idx := b.flat.files[file_idx].name_idx
|
|
mod_idx := b.flat.files[file_idx].mod_idx
|
|
old_attrs_list_id := b.flat.child_at(old_file_id, 0)
|
|
old_imports_list_id := b.flat.child_at(old_file_id, 1)
|
|
old_stmts_list_id := b.flat.child_at(old_file_id, 2)
|
|
old_stmts_count := if old_stmts_list_id < 0 || old_stmts_list_id >= b.flat.nodes.len {
|
|
0
|
|
} else {
|
|
b.flat.nodes[old_stmts_list_id].edge_count
|
|
}
|
|
mut stmt_ids := []FlatNodeId{cap: old_stmts_count + extra_stmt_ids.len}
|
|
for i in 0 .. old_stmts_count {
|
|
stmt_ids << b.flat.child_at(old_stmts_list_id, i)
|
|
}
|
|
for id in extra_stmt_ids {
|
|
stmt_ids << id
|
|
}
|
|
new_stmts_list_id := b.make_list_from_stmt_ids(stmt_ids)
|
|
mut edges := []FlatEdge{cap: 3}
|
|
edges = b.push_edge(edges, old_attrs_list_id)
|
|
edges = b.push_edge(edges, old_imports_list_id)
|
|
edges = b.push_edge(edges, new_stmts_list_id)
|
|
new_file_id := b.emit(.file, token.Pos{}, name_idx, mod_idx, 0, 0, edges)
|
|
b.flat.files[file_idx] = FlatFile{
|
|
file_id: new_file_id
|
|
name_idx: name_idx
|
|
mod_idx: mod_idx
|
|
selector_names: b.flat.files[file_idx].selector_names
|
|
}
|
|
return new_file_id
|
|
}
|
|
|
|
// prepend_file_stmts re-emits the file root for `flat.files[file_idx]` with
|
|
// `prepended_stmt_ids` placed BEFORE the existing stmts list. The file's
|
|
// existing attrs and imports edges are reused as-is (no re-emit) — only the
|
|
// stmts list (file root edge 2) is rebuilt. Updates `flat.files[file_idx]`
|
|
// in place to point at the new file root and returns its FlatNodeId.
|
|
//
|
|
// Sibling of `append_file_stmts` — same shape, opposite order. Designed for
|
|
// `inject_live_reload`'s file-level prepend (c_decls + global_decls go BEFORE
|
|
// the existing file stmts).
|
|
//
|
|
// Append-only: the old file root remains in `flat.nodes` as garbage (never
|
|
// reachable from `flat.files`). Callers must not retain the previous file_id.
|
|
pub fn (mut b FlatBuilder) prepend_file_stmts(file_idx int, prepended_stmt_ids []FlatNodeId) FlatNodeId {
|
|
if file_idx < 0 || file_idx >= b.flat.files.len {
|
|
return invalid_flat_node_id
|
|
}
|
|
old_file_id := b.flat.files[file_idx].file_id
|
|
if prepended_stmt_ids.len == 0 {
|
|
return old_file_id
|
|
}
|
|
name_idx := b.flat.files[file_idx].name_idx
|
|
mod_idx := b.flat.files[file_idx].mod_idx
|
|
old_attrs_list_id := b.flat.child_at(old_file_id, 0)
|
|
old_imports_list_id := b.flat.child_at(old_file_id, 1)
|
|
old_stmts_list_id := b.flat.child_at(old_file_id, 2)
|
|
old_stmts_count := if old_stmts_list_id < 0 || old_stmts_list_id >= b.flat.nodes.len {
|
|
0
|
|
} else {
|
|
b.flat.nodes[old_stmts_list_id].edge_count
|
|
}
|
|
mut stmt_ids := []FlatNodeId{cap: old_stmts_count + prepended_stmt_ids.len}
|
|
for id in prepended_stmt_ids {
|
|
stmt_ids << id
|
|
}
|
|
for i in 0 .. old_stmts_count {
|
|
stmt_ids << b.flat.child_at(old_stmts_list_id, i)
|
|
}
|
|
new_stmts_list_id := b.make_list_from_stmt_ids(stmt_ids)
|
|
mut edges := []FlatEdge{cap: 3}
|
|
edges = b.push_edge(edges, old_attrs_list_id)
|
|
edges = b.push_edge(edges, old_imports_list_id)
|
|
edges = b.push_edge(edges, new_stmts_list_id)
|
|
new_file_id := b.emit(.file, token.Pos{}, name_idx, mod_idx, 0, 0, edges)
|
|
b.flat.files[file_idx] = FlatFile{
|
|
file_id: new_file_id
|
|
name_idx: name_idx
|
|
mod_idx: mod_idx
|
|
selector_names: b.flat.files[file_idx].selector_names
|
|
}
|
|
return new_file_id
|
|
}
|
|
|
|
// replace_file_stmt re-emits the file root for `flat.files[file_idx]` with
|
|
// the stmt at `stmt_idx` replaced by `new_stmt_id`. The file's existing
|
|
// attrs and imports edges are reused as-is (no re-emit); only the stmts
|
|
// list (file root edge 2) is rebuilt with the substitution applied.
|
|
// Updates `flat.files[file_idx]` in place to point at the new file root and
|
|
// returns its FlatNodeId.
|
|
//
|
|
// Companion primitive to `prepend_to_fn_body`: that primitive emits a NEW
|
|
// fn_decl_id (the old fn is unreachable garbage) but cannot rewire the
|
|
// surrounding file. `replace_file_stmt` is the cross-stitching call that
|
|
// puts the new id back into the file's stmts list. Together they support
|
|
// the prepend-style post_pass mutations (`inject_main_runtime_const_init_calls`
|
|
// is the first user, s155).
|
|
//
|
|
// Append-only: the old file root remains in `flat.nodes` as garbage (never
|
|
// reachable from `flat.files`). Callers must not retain the previous file_id.
|
|
pub fn (mut b FlatBuilder) replace_file_stmt(file_idx int, stmt_idx int, new_stmt_id FlatNodeId) FlatNodeId {
|
|
if file_idx < 0 || file_idx >= b.flat.files.len {
|
|
return invalid_flat_node_id
|
|
}
|
|
if new_stmt_id < 0 || new_stmt_id >= b.flat.nodes.len {
|
|
return invalid_flat_node_id
|
|
}
|
|
old_file_id := b.flat.files[file_idx].file_id
|
|
old_attrs_list_id := b.flat.child_at(old_file_id, 0)
|
|
old_imports_list_id := b.flat.child_at(old_file_id, 1)
|
|
old_stmts_list_id := b.flat.child_at(old_file_id, 2)
|
|
old_stmts_count := if old_stmts_list_id < 0 || old_stmts_list_id >= b.flat.nodes.len {
|
|
0
|
|
} else {
|
|
b.flat.nodes[old_stmts_list_id].edge_count
|
|
}
|
|
if stmt_idx < 0 || stmt_idx >= old_stmts_count {
|
|
return invalid_flat_node_id
|
|
}
|
|
mut stmt_ids := []FlatNodeId{cap: old_stmts_count}
|
|
for i in 0 .. old_stmts_count {
|
|
if i == stmt_idx {
|
|
stmt_ids << new_stmt_id
|
|
} else {
|
|
stmt_ids << b.flat.child_at(old_stmts_list_id, i)
|
|
}
|
|
}
|
|
new_stmts_list_id := b.make_list_from_stmt_ids(stmt_ids)
|
|
mut edges := []FlatEdge{cap: 3}
|
|
edges = b.push_edge(edges, old_attrs_list_id)
|
|
edges = b.push_edge(edges, old_imports_list_id)
|
|
edges = b.push_edge(edges, new_stmts_list_id)
|
|
name_idx := b.flat.files[file_idx].name_idx
|
|
mod_idx := b.flat.files[file_idx].mod_idx
|
|
new_file_id := b.emit(.file, token.Pos{}, name_idx, mod_idx, 0, 0, edges)
|
|
b.flat.files[file_idx] = FlatFile{
|
|
file_id: new_file_id
|
|
name_idx: name_idx
|
|
mod_idx: mod_idx
|
|
selector_names: b.flat.files[file_idx].selector_names
|
|
}
|
|
return new_file_id
|
|
}
|
|
|
|
// prepend_to_fn_body re-emits a stmt_fn_decl with `extra_stmt_ids` prepended
|
|
// to its body. The original FnDecl's receiver/typ/attrs edges (0/1/2) and its
|
|
// metadata (name_id, extra, aux, flags, pos) are reused verbatim; only the
|
|
// stmts list (edge 3) is rebuilt as `extras + old_children`. Returns the
|
|
// FlatNodeId of the NEW stmt_fn_decl — the old one remains in `flat.nodes`
|
|
// as garbage. Callers must thread the returned id back into wherever the
|
|
// old FnDecl was referenced (typically a file's stmts list).
|
|
//
|
|
// Designed for the prepend-style post_pass mutations:
|
|
// - `inject_main_runtime_const_init_calls` (prepends `__v_init_consts_*()`
|
|
// calls to the top of `main`)
|
|
// - `inject_live_reload` (prepends a reload-check to nested ForStmt bodies;
|
|
// the same body-rebuild shape applies to non-FnDecl callsites too, but
|
|
// the FnDecl case is the immediate need so this primitive lands first).
|
|
//
|
|
// Bit-equal to `add_stmt(FnDecl{... stmts: extras_then_old})` w.r.t.
|
|
// `signature()`: the new node has identical name_id/extra/aux/pos/flags and
|
|
// edges = [old_receiver, old_typ, old_attrs, new_stmts_list].
|
|
pub fn (mut b FlatBuilder) prepend_to_fn_body(fn_decl_id FlatNodeId, extra_stmt_ids []FlatNodeId) FlatNodeId {
|
|
if fn_decl_id < 0 || fn_decl_id >= b.flat.nodes.len {
|
|
return invalid_flat_node_id
|
|
}
|
|
old_node := b.flat.nodes[fn_decl_id]
|
|
if old_node.kind != .stmt_fn_decl {
|
|
return invalid_flat_node_id
|
|
}
|
|
if extra_stmt_ids.len == 0 {
|
|
return fn_decl_id
|
|
}
|
|
old_receiver_id := b.flat.child_at(fn_decl_id, 0)
|
|
old_typ_id := b.flat.child_at(fn_decl_id, 1)
|
|
old_attrs_id := b.flat.child_at(fn_decl_id, 2)
|
|
old_stmts_list_id := b.flat.child_at(fn_decl_id, 3)
|
|
old_stmts_count := if old_stmts_list_id < 0 || old_stmts_list_id >= b.flat.nodes.len {
|
|
0
|
|
} else {
|
|
b.flat.nodes[old_stmts_list_id].edge_count
|
|
}
|
|
mut stmt_ids := []FlatNodeId{cap: extra_stmt_ids.len + old_stmts_count}
|
|
for id in extra_stmt_ids {
|
|
stmt_ids << id
|
|
}
|
|
for i in 0 .. old_stmts_count {
|
|
stmt_ids << b.flat.child_at(old_stmts_list_id, i)
|
|
}
|
|
new_stmts_list_id := b.make_list_from_stmt_ids(stmt_ids)
|
|
mut edges := []FlatEdge{cap: 4}
|
|
edges = b.push_edge(edges, old_receiver_id)
|
|
edges = b.push_edge(edges, old_typ_id)
|
|
edges = b.push_edge(edges, old_attrs_id)
|
|
edges = b.push_edge(edges, new_stmts_list_id)
|
|
return b.emit(.stmt_fn_decl, old_node.pos, old_node.name_id, old_node.extra, old_node.aux,
|
|
old_node.flags, edges)
|
|
}
|
|
|
|
// prepend_to_for_body re-emits a stmt_for with `extra_stmt_ids` prepended to
|
|
// its body. The original ForStmt's init/cond/post edges (0/1/2) and pos are
|
|
// reused verbatim; the body stmts (edges 3..edge_count) are rebuilt as
|
|
// `extras + old_body_stmts`. Returns the FlatNodeId of the NEW stmt_for —
|
|
// the old one remains in `flat.nodes` as garbage. Callers must thread the
|
|
// returned id back into wherever the old ForStmt was referenced.
|
|
//
|
|
// Body-stmt encoding difference vs `prepend_to_fn_body`: ForStmt encodes
|
|
// body stmts as INLINE edges starting at index 3 (no list child), whereas
|
|
// FnDecl's body stmts live inside a `.aux_list` at edge 3. So `prepend_to_for_body`
|
|
// rebuilds the for's edge list directly — no intermediate list node.
|
|
//
|
|
// Designed for `inject_live_reload`, which prepends a reload-check stmt to
|
|
// the body of every ForStmt found in every function. Combined with
|
|
// `prepend_to_fn_body` (s154) and `replace_file_stmt` (s155), this primitive
|
|
// closes the cross-stitching chain needed for the recursive live-reload
|
|
// splice: for each ForStmt change, rebuild the for, rebuild the enclosing
|
|
// FnDecl, rewire the enclosing file's stmts list.
|
|
pub fn (mut b FlatBuilder) prepend_to_for_body(for_stmt_id FlatNodeId, extra_stmt_ids []FlatNodeId) FlatNodeId {
|
|
if for_stmt_id < 0 || for_stmt_id >= b.flat.nodes.len {
|
|
return invalid_flat_node_id
|
|
}
|
|
old_node := b.flat.nodes[for_stmt_id]
|
|
if old_node.kind != .stmt_for {
|
|
return invalid_flat_node_id
|
|
}
|
|
if extra_stmt_ids.len == 0 {
|
|
return for_stmt_id
|
|
}
|
|
old_init_id := b.flat.child_at(for_stmt_id, 0)
|
|
old_cond_id := b.flat.child_at(for_stmt_id, 1)
|
|
old_post_id := b.flat.child_at(for_stmt_id, 2)
|
|
old_body_count := old_node.edge_count - 3
|
|
mut body_ids := []FlatNodeId{cap: extra_stmt_ids.len + old_body_count}
|
|
for id in extra_stmt_ids {
|
|
body_ids << id
|
|
}
|
|
for i in 0 .. old_body_count {
|
|
body_ids << b.flat.child_at(for_stmt_id, 3 + i)
|
|
}
|
|
return b.emit_for_stmt_by_ids(old_init_id, old_cond_id, old_post_id, body_ids)
|
|
}
|
|
|
|
// replace_fn_body_stmts re-emits a stmt_fn_decl with its body stmts list
|
|
// fully REPLACED by `new_body_stmt_ids`. The original FnDecl's receiver/typ/
|
|
// attrs edges (0/1/2) and metadata (name_id, extra, aux, flags, pos) are
|
|
// reused verbatim; only the stmts list (edge 3) is rebuilt as a fresh
|
|
// aux_list over `new_body_stmt_ids`. Returns the FlatNodeId of the NEW
|
|
// stmt_fn_decl — the old one remains in `flat.nodes` as garbage. Callers
|
|
// must thread the returned id back into wherever the old FnDecl was
|
|
// referenced (typically a file's stmts list).
|
|
//
|
|
// Companion primitive to `prepend_to_fn_body` (s154). Where `prepend_to_fn_body`
|
|
// adds extras to the head of the existing body, `replace_fn_body_stmts`
|
|
// gives callers full control over the new body — used by `inject_live_reload`
|
|
// which combines preamble prepend (main fn only) + per-ForStmt prepend
|
|
// (every fn) + per-stmt call rewriting (every fn) into ONE new body. The
|
|
// caller computes the new body stmt ids first (mixing freshly-emitted stmts
|
|
// with ids returned by `prepend_to_for_body` and unchanged legacy stmt ids),
|
|
// then hands them off here for a one-shot FnDecl rebuild.
|
|
//
|
|
// Bit-equal to `add_stmt(FnDecl{... stmts: new_body})` w.r.t. `signature()`:
|
|
// the new node has identical name_id/extra/aux/pos/flags and edges =
|
|
// [old_receiver, old_typ, old_attrs, new_stmts_list].
|
|
pub fn (mut b FlatBuilder) replace_fn_body_stmts(fn_decl_id FlatNodeId, new_body_stmt_ids []FlatNodeId) FlatNodeId {
|
|
if fn_decl_id < 0 || fn_decl_id >= b.flat.nodes.len {
|
|
return invalid_flat_node_id
|
|
}
|
|
old_node := b.flat.nodes[fn_decl_id]
|
|
if old_node.kind != .stmt_fn_decl {
|
|
return invalid_flat_node_id
|
|
}
|
|
old_receiver_id := b.flat.child_at(fn_decl_id, 0)
|
|
old_typ_id := b.flat.child_at(fn_decl_id, 1)
|
|
old_attrs_id := b.flat.child_at(fn_decl_id, 2)
|
|
new_stmts_list_id := b.make_list_from_stmt_ids(new_body_stmt_ids)
|
|
mut edges := []FlatEdge{cap: 4}
|
|
edges = b.push_edge(edges, old_receiver_id)
|
|
edges = b.push_edge(edges, old_typ_id)
|
|
edges = b.push_edge(edges, old_attrs_id)
|
|
edges = b.push_edge(edges, new_stmts_list_id)
|
|
return b.emit(.stmt_fn_decl, old_node.pos, old_node.name_id, old_node.extra, old_node.aux,
|
|
old_node.flags, edges)
|
|
}
|
|
|
|
// emit_expr is the public wrapper around the legacy expr-to-flat conversion.
|
|
// The flat-write port reaches for this when an expr variant still falls back
|
|
// to legacy emission. Phase 4 sessions replace non-leaf arms in
|
|
// transform_expr_to_flat one at a time with direct-emit logic that no longer
|
|
// reaches for this helper.
|
|
pub fn (mut b FlatBuilder) emit_expr(expr Expr) FlatNodeId {
|
|
return b.add_expr(expr)
|
|
}
|
|
|
|
// emit_attribute_list emits an aux_list of attribute flat nodes, bit-equal
|
|
// to the encoding produced by add_stmt for stmts that carry attributes.
|
|
// Empty inputs return the shared empty-list sentinel.
|
|
pub fn (mut b FlatBuilder) emit_attribute_list(attrs []Attribute) FlatNodeId {
|
|
return b.make_list_attribute(attrs)
|
|
}
|
|
|
|
// emit_aux_list_from_ids builds an aux_list whose children are FlatNodeIds
|
|
// already present in this builder. Empty inputs return the shared empty-list
|
|
// sentinel.
|
|
pub fn (mut b FlatBuilder) emit_aux_list_from_ids(ids []FlatNodeId) FlatNodeId {
|
|
if ids.len == 0 {
|
|
return b.get_empty_list()
|
|
}
|
|
mut edges := []FlatEdge{cap: ids.len}
|
|
for id in ids {
|
|
edges = b.push_edge(edges, id)
|
|
}
|
|
return b.emit_simple(.aux_list, token.Pos{}, edges)
|
|
}
|
|
|
|
// emit_field_decl_by_ids emits an aux_field_decl node from already-flat
|
|
// child FlatNodeIds. Used by the flat-write port for stmts that carry
|
|
// FieldDecls (GlobalDecl, StructDecl, ...) once their typ/value expressions
|
|
// have been emitted directly. Mirrors the add_field_decl encoding exactly.
|
|
pub fn (mut b FlatBuilder) emit_field_decl_by_ids(field FieldDecl, typ_id FlatNodeId, value_id FlatNodeId, attrs_id FlatNodeId) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: 3}
|
|
edges = b.push_edge(edges, typ_id)
|
|
edges = b.push_edge(edges, value_id)
|
|
edges = b.push_edge(edges, attrs_id)
|
|
return b.emit(.aux_field_decl, token.Pos{}, b.intern(field.name), -1, 0,
|
|
field_decl_flags(field), edges)
|
|
}
|
|
|
|
// emit_global_decl_by_ids emits a stmt_global_decl node from already-flat
|
|
// child FlatNodeIds (the attribute list and field-decl list). Mirrors the
|
|
// add_stmt(GlobalDecl) encoding exactly, including the flag_is_public bit.
|
|
pub fn (mut b FlatBuilder) emit_global_decl_by_ids(is_public bool, attrs_id FlatNodeId, fields_id FlatNodeId) FlatNodeId {
|
|
mut flags := u8(0)
|
|
if is_public {
|
|
flags |= flag_is_public
|
|
}
|
|
return b.emit_simple_with_flags(.stmt_global_decl, token.Pos{}, flags, [
|
|
FlatEdge{
|
|
child_id: attrs_id
|
|
},
|
|
FlatEdge{
|
|
child_id: fields_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_field_init_by_id emits an aux_field_init node from an already-flat
|
|
// value FlatNodeId. Used by the flat-write port for ConstDecl when the
|
|
// field's value expression has been emitted directly. Mirrors the
|
|
// add_field_init encoding exactly.
|
|
pub fn (mut b FlatBuilder) emit_field_init_by_id(name string, value_id FlatNodeId) FlatNodeId {
|
|
return b.emit(.aux_field_init, token.Pos{}, b.intern(name), -1, 0, 0, [
|
|
FlatEdge{
|
|
child_id: value_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_const_decl_by_ids emits a stmt_const_decl node from an already-flat
|
|
// fields list FlatNodeId. Mirrors the add_stmt(ConstDecl) encoding exactly,
|
|
// including the flag_is_public bit when `is_public` is true.
|
|
pub fn (mut b FlatBuilder) emit_const_decl_by_ids(is_public bool, fields_id FlatNodeId) FlatNodeId {
|
|
mut flags := u8(0)
|
|
if is_public {
|
|
flags |= flag_is_public
|
|
}
|
|
return b.emit_simple_with_flags(.stmt_const_decl, token.Pos{}, flags, [
|
|
FlatEdge{
|
|
child_id: fields_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_return_stmt_by_ids emits a stmt_return node from a slice of
|
|
// already-flat returned-expression FlatNodeIds. Mirrors the
|
|
// add_stmt(ReturnStmt) encoding exactly: pos is the zero `token.Pos{}` the
|
|
// legacy add_stmt arm uses, edges are the returned exprs in order. Used by
|
|
// the flat-write port to direct-emit the outer `ast.ReturnStmt` wrapper.
|
|
pub fn (mut b FlatBuilder) emit_return_stmt_by_ids(expr_ids []FlatNodeId) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: expr_ids.len}
|
|
for eid in expr_ids {
|
|
edges << FlatEdge{
|
|
child_id: eid
|
|
}
|
|
}
|
|
return b.emit_simple(.stmt_return, token.Pos{}, edges)
|
|
}
|
|
|
|
// emit_expr_stmt_by_id emits a stmt_expr node from an already-flat
|
|
// FlatNodeId child. Mirrors the add_stmt(ExprStmt) encoding exactly: pos is
|
|
// the zero `token.Pos{}` the legacy add_stmt arm uses, single edge to the
|
|
// expression. Used by the flat-write port to direct-emit the
|
|
// `ast.ExprStmt` wrapper at every expression-statement site.
|
|
pub fn (mut b FlatBuilder) emit_expr_stmt_by_id(expr_id FlatNodeId) FlatNodeId {
|
|
return b.emit_simple(.stmt_expr, token.Pos{}, [
|
|
FlatEdge{
|
|
child_id: expr_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_label_stmt_by_id emits a stmt_label node from an already-flat
|
|
// FlatNodeId child stmt. Mirrors the add_stmt(LabelStmt) encoding exactly:
|
|
// pos is the zero `token.Pos{}` the legacy add_stmt arm uses, name is
|
|
// interned, single edge to the labelled stmt. Used by the flat-write port
|
|
// to direct-emit the `ast.LabelStmt` wrapper.
|
|
pub fn (mut b FlatBuilder) emit_label_stmt_by_id(name string, stmt_id FlatNodeId) FlatNodeId {
|
|
return b.emit(.stmt_label, token.Pos{}, b.intern(name), -1, 0, 0, [
|
|
FlatEdge{
|
|
child_id: stmt_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_block_stmt_by_ids emits a stmt_block node from a slice of
|
|
// already-flat child stmt FlatNodeIds. Mirrors the add_stmt(BlockStmt)
|
|
// encoding exactly: pos is the zero `token.Pos{}` the legacy add_stmt arm
|
|
// uses, edges are the inner stmts in order. Used by the flat-write port to
|
|
// direct-emit the `ast.BlockStmt` wrapper.
|
|
pub fn (mut b FlatBuilder) emit_block_stmt_by_ids(stmt_ids []FlatNodeId) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: stmt_ids.len}
|
|
for sid in stmt_ids {
|
|
edges << FlatEdge{
|
|
child_id: sid
|
|
}
|
|
}
|
|
return b.emit_simple(.stmt_block, token.Pos{}, edges)
|
|
}
|
|
|
|
// emit_assert_stmt_by_id emits a stmt_assert node from an already-flat child
|
|
// expr FlatNodeId. Mirrors the add_stmt(AssertStmt) encoding exactly: pos is
|
|
// the zero `token.Pos{}` the legacy add_stmt arm uses, edges are
|
|
// [expr_id, empty_expr_id]. The legacy fallback path in `transform_stmt`
|
|
// rebuilds `AssertStmt{expr: transformed}` without setting `extra`, so the
|
|
// extra slot is always the default `empty_expr` — emit that via the cached
|
|
// `add_expr(empty_expr)`. Used by the flat-write port to direct-emit the
|
|
// `ast.AssertStmt` fallback wrapper (most assert stmts are expanded in
|
|
// `transform_stmts` and never reach the dispatch arm).
|
|
pub fn (mut b FlatBuilder) emit_assert_stmt_by_id(expr_id FlatNodeId) FlatNodeId {
|
|
extra_id := b.add_expr(empty_expr)
|
|
return b.emit_simple(.stmt_assert, token.Pos{}, [
|
|
FlatEdge{
|
|
child_id: expr_id
|
|
},
|
|
FlatEdge{
|
|
child_id: extra_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_for_stmt_by_ids emits a stmt_for node from already-flat init/post
|
|
// stmt FlatNodeIds, cond expr FlatNodeId, and body stmt FlatNodeIds.
|
|
// Mirrors the add_stmt(ForStmt) encoding exactly: pos = `token.Pos{}` the
|
|
// legacy arm uses, edges = [init, cond, post, body_stmts...].
|
|
pub fn (mut b FlatBuilder) emit_for_stmt_by_ids(init_id FlatNodeId, cond_id FlatNodeId, post_id FlatNodeId, stmt_ids []FlatNodeId) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: 3 + stmt_ids.len}
|
|
edges << FlatEdge{
|
|
child_id: init_id
|
|
}
|
|
edges << FlatEdge{
|
|
child_id: cond_id
|
|
}
|
|
edges << FlatEdge{
|
|
child_id: post_id
|
|
}
|
|
for sid in stmt_ids {
|
|
edges << FlatEdge{
|
|
child_id: sid
|
|
}
|
|
}
|
|
return b.emit_simple(.stmt_for, token.Pos{}, edges)
|
|
}
|
|
|
|
// emit_for_in_stmt_by_ids emits a stmt_for_in node from already-flat key,
|
|
// value, and iterable expression FlatNodeIds. Mirrors add_stmt(ForInStmt).
|
|
pub fn (mut b FlatBuilder) emit_for_in_stmt_by_ids(key_id FlatNodeId, value_id FlatNodeId, expr_id FlatNodeId) FlatNodeId {
|
|
return b.emit_simple(.stmt_for_in, token.Pos{}, [
|
|
FlatEdge{
|
|
child_id: key_id
|
|
},
|
|
FlatEdge{
|
|
child_id: value_id
|
|
},
|
|
FlatEdge{
|
|
child_id: expr_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_flow_control_stmt emits a stmt_flow_control node. Mirrors
|
|
// add_stmt(FlowControlStmt): label in name_id, op in aux, no edges.
|
|
pub fn (mut b FlatBuilder) emit_flow_control_stmt(op token.Token, label string) FlatNodeId {
|
|
return b.emit(.stmt_flow_control, token.Pos{}, b.intern(label), -1, u16(int(op)), 0, [])
|
|
}
|
|
|
|
// emit_map_init_expr_by_ids emits an expr_map_init node from already-flat
|
|
// type FlatNodeId and parallel slices of key/value expression FlatNodeIds
|
|
// (with `key_ids.len == val_ids.len`). Mirrors the add_expr(MapInitExpr)
|
|
// encoding exactly: aux1=-1, aux2=keys.len, edges = [typ, keys..., vals...].
|
|
// The flat-write port for `transform_map_init_expr`'s identity branch
|
|
// (eval-backend map literal) uses this to skip the `ast.MapInitExpr`
|
|
// wrapper struct allocation.
|
|
pub fn (mut b FlatBuilder) emit_map_init_expr_by_ids(typ_id FlatNodeId, key_ids []FlatNodeId, val_ids []FlatNodeId, pos token.Pos) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: 1 + key_ids.len + val_ids.len}
|
|
edges << FlatEdge{
|
|
child_id: typ_id
|
|
}
|
|
for kid in key_ids {
|
|
edges << FlatEdge{
|
|
child_id: kid
|
|
}
|
|
}
|
|
for vid in val_ids {
|
|
edges << FlatEdge{
|
|
child_id: vid
|
|
}
|
|
}
|
|
return b.emit(.expr_map_init, pos, -1, key_ids.len, 0, 0, edges)
|
|
}
|
|
|
|
// emit_if_expr_by_ids emits an expr_if node from already-flat cond, else,
|
|
// and stmt FlatNodeIds. Mirrors the add_expr(IfExpr) encoding exactly:
|
|
// edges = [cond, else_expr, stmts...]. The flat-write port for
|
|
// `transform_if_expr`'s identity branch (no value-position lowering: any
|
|
// `ast.IfExpr` returned by the helper — recursive normalisation, smartcast
|
|
// rewrite, default transformed_if rebuild) uses this to skip the
|
|
// `ast.IfExpr` wrapper struct allocation.
|
|
pub fn (mut b FlatBuilder) emit_if_expr_by_ids(cond_id FlatNodeId, else_id FlatNodeId, stmt_ids []FlatNodeId, pos token.Pos) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: 2 + stmt_ids.len}
|
|
edges << FlatEdge{
|
|
child_id: cond_id
|
|
}
|
|
edges << FlatEdge{
|
|
child_id: else_id
|
|
}
|
|
for sid in stmt_ids {
|
|
edges << FlatEdge{
|
|
child_id: sid
|
|
}
|
|
}
|
|
return b.emit_simple(.expr_if, pos, edges)
|
|
}
|
|
|
|
// emit_call_expr_by_ids emits an expr_call node from already-flat lhs and
|
|
// args expression FlatNodeIds. Mirrors the add_expr(CallExpr) encoding
|
|
// exactly: edges = [lhs, args...]. The flat-write port for
|
|
// `transform_call_expr`'s many identity-shape rebuild branches (default
|
|
// fallback, generic-math inline result, smartcast method, etc.) uses this
|
|
// to skip the `ast.CallExpr` wrapper struct allocation.
|
|
pub fn (mut b FlatBuilder) emit_call_expr_by_ids(lhs_id FlatNodeId, arg_ids []FlatNodeId, pos token.Pos) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: 1 + arg_ids.len}
|
|
edges << FlatEdge{
|
|
child_id: lhs_id
|
|
}
|
|
for aid in arg_ids {
|
|
edges << FlatEdge{
|
|
child_id: aid
|
|
}
|
|
}
|
|
return b.emit_simple(.expr_call, pos, edges)
|
|
}
|
|
|
|
// emit_infix_expr_by_ids emits an expr_infix node from already-flat lhs and
|
|
// rhs expression FlatNodeIds. Mirrors the add_expr(InfixExpr) encoding
|
|
// exactly: aux1=-1, aux2=-1, meta=u16(op), edges = [lhs, rhs]. The
|
|
// flat-write port for `transform_infix_expr`'s identity branch
|
|
// (no rewrite triggered: just `transform(lhs)`/`transform(rhs)` rebuild)
|
|
// uses this to skip the `ast.InfixExpr` wrapper struct allocation.
|
|
pub fn (mut b FlatBuilder) emit_infix_expr_by_ids(op token.Token, lhs_id FlatNodeId, rhs_id FlatNodeId, pos token.Pos) FlatNodeId {
|
|
return b.emit(.expr_infix, pos, -1, -1, u16(int(op)), 0, [
|
|
FlatEdge{
|
|
child_id: lhs_id
|
|
},
|
|
FlatEdge{
|
|
child_id: rhs_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_keyword_operator_by_ids emits an expr_keyword_operator node from
|
|
// already-flat operand expression FlatNodeIds. Mirrors add_expr(KeywordOperator)
|
|
// exactly: aux1=-1, aux2=-1, meta=u16(op), edges = exprs.
|
|
pub fn (mut b FlatBuilder) emit_keyword_operator_by_ids(op token.Token, expr_ids []FlatNodeId, pos token.Pos) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: expr_ids.len}
|
|
for id in expr_ids {
|
|
edges << FlatEdge{
|
|
child_id: id
|
|
}
|
|
}
|
|
return b.emit(.expr_keyword_operator, pos, -1, -1, u16(int(op)), 0, edges)
|
|
}
|
|
|
|
// emit_array_init_expr_by_ids emits an expr_array_init node from already-flat
|
|
// type/init/cap/len/update expression FlatNodeIds and a slice of already-flat
|
|
// element expression FlatNodeIds. Mirrors the add_expr(ArrayInitExpr)
|
|
// encoding exactly: edges = [typ, init, cap, len, update_expr, exprs...].
|
|
// The flat-write port for `transform_array_init_expr`'s identity branches
|
|
// (invalid data, fixed array, eval-backend dynamic array) uses this to skip
|
|
// the `ast.ArrayInitExpr` wrapper struct allocation.
|
|
pub fn (mut b FlatBuilder) emit_array_init_expr_by_ids(typ_id FlatNodeId, init_id FlatNodeId, cap_id FlatNodeId, len_id FlatNodeId, update_expr_id FlatNodeId, expr_ids []FlatNodeId, pos token.Pos) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: 5 + expr_ids.len}
|
|
edges << FlatEdge{
|
|
child_id: typ_id
|
|
}
|
|
edges << FlatEdge{
|
|
child_id: init_id
|
|
}
|
|
edges << FlatEdge{
|
|
child_id: cap_id
|
|
}
|
|
edges << FlatEdge{
|
|
child_id: len_id
|
|
}
|
|
edges << FlatEdge{
|
|
child_id: update_expr_id
|
|
}
|
|
for eid in expr_ids {
|
|
edges << FlatEdge{
|
|
child_id: eid
|
|
}
|
|
}
|
|
return b.emit_simple(.expr_array_init, pos, edges)
|
|
}
|
|
|
|
// emit_array_type_by_elem_id emits a typ_array node from an already-flat
|
|
// element type expression FlatNodeId. Mirrors add_type(ArrayType).
|
|
pub fn (mut b FlatBuilder) emit_array_type_by_elem_id(elem_type_id FlatNodeId) FlatNodeId {
|
|
return b.emit_simple(.typ_array, token.Pos{}, [
|
|
FlatEdge{
|
|
child_id: elem_type_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_assign_stmt_by_ids emits a stmt_assign node from already-flat
|
|
// lhs/rhs expression FlatNodeIds. Mirrors the add_stmt(AssignStmt)
|
|
// encoding exactly: aux1=-1, aux2=lhs.len (lhs/rhs boundary in edges),
|
|
// meta=u16(op), edges = lhs in order followed by rhs in order.
|
|
pub fn (mut b FlatBuilder) emit_assign_stmt_by_ids(op token.Token, lhs_ids []FlatNodeId, rhs_ids []FlatNodeId, pos token.Pos) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: lhs_ids.len + rhs_ids.len}
|
|
for lid in lhs_ids {
|
|
edges << FlatEdge{
|
|
child_id: lid
|
|
}
|
|
}
|
|
for rid in rhs_ids {
|
|
edges << FlatEdge{
|
|
child_id: rid
|
|
}
|
|
}
|
|
return b.emit(.stmt_assign, pos, -1, lhs_ids.len, u16(int(op)), 0, edges)
|
|
}
|
|
|
|
// emit_comptime_stmt_by_id emits a stmt_comptime node wrapping an
|
|
// already-flat child stmt FlatNodeId. Mirrors the add_stmt(ComptimeStmt)
|
|
// encoding exactly: pos is the zero `token.Pos{}` the legacy add_stmt arm
|
|
// uses, single edge to the inner stmt. Used by the flat-write port to
|
|
// direct-emit the `ast.ComptimeStmt` wrapper (the `$for` branch — the
|
|
// non-`$for` branch drops the wrapper entirely and recurses).
|
|
pub fn (mut b FlatBuilder) emit_comptime_stmt_by_id(stmt_id FlatNodeId) FlatNodeId {
|
|
return b.emit_simple(.stmt_comptime, token.Pos{}, [
|
|
FlatEdge{
|
|
child_id: stmt_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_defer_stmt_by_ids emits a stmt_defer node from a slice of already-flat
|
|
// child stmt FlatNodeIds. Mirrors the add_stmt(DeferStmt) encoding exactly:
|
|
// pos is the zero `token.Pos{}` the legacy add_stmt arm uses, flags carry
|
|
// `flag_defer_func` when mode is `.function`, edges are the inner stmts in
|
|
// order. Used by the flat-write port to direct-emit the `ast.DeferStmt`
|
|
// wrapper.
|
|
pub fn (mut b FlatBuilder) emit_defer_stmt_by_ids(mode DeferMode, stmt_ids []FlatNodeId) FlatNodeId {
|
|
mut flags := u8(0)
|
|
if mode == .function {
|
|
flags |= flag_defer_func
|
|
}
|
|
mut edges := []FlatEdge{cap: stmt_ids.len}
|
|
for sid in stmt_ids {
|
|
edges << FlatEdge{
|
|
child_id: sid
|
|
}
|
|
}
|
|
return b.emit_simple_with_flags(.stmt_defer, token.Pos{}, flags, edges)
|
|
}
|
|
|
|
// emit_parameter is the pub wrapper over the private add_parameter, used by
|
|
// the flat-write port when an FnDecl's receiver is emitted directly.
|
|
pub fn (mut b FlatBuilder) emit_parameter(param Parameter) FlatNodeId {
|
|
return b.add_parameter(param)
|
|
}
|
|
|
|
// emit_type is the pub wrapper over the private add_type, used by the
|
|
// flat-write port when an FnDecl's signature is emitted directly. The
|
|
// FnDecl encoding wraps `FnType` as `Type(stmt.typ)` so callers convert.
|
|
pub fn (mut b FlatBuilder) emit_type(typ Type) FlatNodeId {
|
|
return b.add_type(typ)
|
|
}
|
|
|
|
// emit_ident_by_name emits an expr_ident node from a name string and pos.
|
|
// Mirrors the add_expr(Ident) encoding exactly: name is interned into the
|
|
// data_id slot, extra is -1, no edges. Used by the flat-write port to
|
|
// direct-emit synthesised `ast.Ident{name, pos}` wrappers (smartcast
|
|
// rewrites, module/enum lookups, `unsafe { nil }`, ...).
|
|
pub fn (mut b FlatBuilder) emit_ident_by_name(name string, pos token.Pos) FlatNodeId {
|
|
return b.emit(.expr_ident, pos, b.intern(name), -1, 0, 0, []FlatEdge{})
|
|
}
|
|
|
|
// emit_basic_literal_by_value emits an expr_basic_literal node from a kind/value/pos.
|
|
// Mirrors the add_expr(BasicLiteral) encoding exactly: value is interned into
|
|
// the data_id slot, extra is -1, meta packs the kind token, no edges. Used by
|
|
// the flat-write port to direct-emit synthesised `ast.BasicLiteral{kind,
|
|
// value, pos}` wrappers (`$if res {...}` → `false`, etc.).
|
|
pub fn (mut b FlatBuilder) emit_basic_literal_by_value(kind token.Token, value string, pos token.Pos) FlatNodeId {
|
|
return b.emit(.expr_basic_literal, pos, b.intern(value), -1, u16(int(kind)), 0, []FlatEdge{})
|
|
}
|
|
|
|
// emit_string_literal_by_value emits an expr_string node from a kind/value/pos.
|
|
// Mirrors the add_expr(StringLiteral) encoding exactly: value is interned
|
|
// into the data_id slot, extra is -1, meta packs the StringLiteralKind, no
|
|
// edges. Used by the flat-write port to direct-emit synthesised
|
|
// `ast.StringLiteral{kind, value, pos}` wrappers (`$typeof(x)` → V type name
|
|
// string literal, etc.).
|
|
pub fn (mut b FlatBuilder) emit_string_literal_by_value(kind StringLiteralKind, value string, pos token.Pos) FlatNodeId {
|
|
return b.emit(.expr_string, pos, b.intern(value), -1, u16(int(kind)), 0, []FlatEdge{})
|
|
}
|
|
|
|
// emit_paren_expr_by_id emits an expr_paren node from an already-flat
|
|
// inner expression FlatNodeId. Mirrors the add_expr(ParenExpr) encoding
|
|
// exactly.
|
|
pub fn (mut b FlatBuilder) emit_paren_expr_by_id(inner_id FlatNodeId, pos token.Pos) FlatNodeId {
|
|
return b.emit_simple(.expr_paren, pos, [
|
|
FlatEdge{
|
|
child_id: inner_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_prefix_expr_by_id emits an expr_prefix node from an already-flat
|
|
// inner expression FlatNodeId. Mirrors the add_expr(PrefixExpr) encoding
|
|
// exactly: the operator token is packed into the meta u16.
|
|
pub fn (mut b FlatBuilder) emit_prefix_expr_by_id(op token.Token, inner_id FlatNodeId, pos token.Pos) FlatNodeId {
|
|
return b.emit(.expr_prefix, pos, -1, -1, u16(int(op)), 0, [
|
|
FlatEdge{
|
|
child_id: inner_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_modifier_expr_by_id emits an expr_modifier node from an already-flat
|
|
// inner expression FlatNodeId. Mirrors the add_expr(ModifierExpr) encoding
|
|
// exactly: the modifier kind token is packed into the meta u16.
|
|
pub fn (mut b FlatBuilder) emit_modifier_expr_by_id(kind token.Token, inner_id FlatNodeId, pos token.Pos) FlatNodeId {
|
|
return b.emit(.expr_modifier, pos, -1, -1, u16(int(kind)), 0, [
|
|
FlatEdge{
|
|
child_id: inner_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_postfix_expr_by_id emits an expr_postfix node from an already-flat
|
|
// inner expression FlatNodeId. Mirrors the add_expr(PostfixExpr) encoding
|
|
// exactly: the operator token is packed into the meta u16.
|
|
pub fn (mut b FlatBuilder) emit_postfix_expr_by_id(op token.Token, inner_id FlatNodeId, pos token.Pos) FlatNodeId {
|
|
return b.emit(.expr_postfix, pos, -1, -1, u16(int(op)), 0, [
|
|
FlatEdge{
|
|
child_id: inner_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_cast_expr_by_ids emits an expr_cast node from already-flat type
|
|
// expression and inner expression FlatNodeIds. Mirrors the
|
|
// add_expr(CastExpr) encoding exactly: edge[0] = typ, edge[1] = expr.
|
|
pub fn (mut b FlatBuilder) emit_cast_expr_by_ids(typ_id FlatNodeId, expr_id FlatNodeId, pos token.Pos) FlatNodeId {
|
|
return b.emit_simple(.expr_cast, pos, [
|
|
FlatEdge{
|
|
child_id: typ_id
|
|
},
|
|
FlatEdge{
|
|
child_id: expr_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_as_cast_expr_by_ids emits an expr_as_cast node from already-flat
|
|
// inner expression and type expression FlatNodeIds. Mirrors the
|
|
// add_expr(AsCastExpr) encoding exactly: edge[0] = expr, edge[1] = typ.
|
|
pub fn (mut b FlatBuilder) emit_as_cast_expr_by_ids(expr_id FlatNodeId, typ_id FlatNodeId, pos token.Pos) FlatNodeId {
|
|
return b.emit_simple(.expr_as_cast, pos, [
|
|
FlatEdge{
|
|
child_id: expr_id
|
|
},
|
|
FlatEdge{
|
|
child_id: typ_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_sql_expr_by_id emits an expr_sql node from an already-flat inner
|
|
// expression FlatNodeId. Mirrors the add_expr(SqlExpr) encoding exactly:
|
|
// table_name interned into aux1, is_count/is_create packed into flags,
|
|
// single edge to inner expr.
|
|
pub fn (mut b FlatBuilder) emit_sql_expr_by_id(table_name string, is_count bool, is_create bool, expr_id FlatNodeId, pos token.Pos) FlatNodeId {
|
|
mut flags := u8(0)
|
|
if is_count {
|
|
flags |= flag_is_count
|
|
}
|
|
if is_create {
|
|
flags |= flag_is_create
|
|
}
|
|
return b.emit(.expr_sql, pos, b.intern(table_name), -1, 0, flags, [
|
|
FlatEdge{
|
|
child_id: expr_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_unsafe_expr_by_ids emits an expr_unsafe node from already-flat
|
|
// stmt FlatNodeIds. Mirrors the add_expr(UnsafeExpr) encoding exactly:
|
|
// edges are the body stmts in order.
|
|
pub fn (mut b FlatBuilder) emit_unsafe_expr_by_ids(stmt_ids []FlatNodeId, pos token.Pos) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: stmt_ids.len}
|
|
for sid in stmt_ids {
|
|
edges << FlatEdge{
|
|
child_id: sid
|
|
}
|
|
}
|
|
return b.emit_simple(.expr_unsafe, pos, edges)
|
|
}
|
|
|
|
// emit_lambda_expr_by_ids emits an expr_lambda node from an already-flat
|
|
// inner expression FlatNodeId and a slice of already-flat arg FlatNodeIds
|
|
// (each arg is an Ident). Mirrors the add_expr(LambdaExpr) encoding exactly:
|
|
// edge[0] is the inner expression, edge[1..] are the args.
|
|
pub fn (mut b FlatBuilder) emit_lambda_expr_by_ids(inner_id FlatNodeId, arg_ids []FlatNodeId, pos token.Pos) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: 1 + arg_ids.len}
|
|
edges << FlatEdge{
|
|
child_id: inner_id
|
|
}
|
|
for aid in arg_ids {
|
|
edges << FlatEdge{
|
|
child_id: aid
|
|
}
|
|
}
|
|
return b.emit_simple(.expr_lambda, pos, edges)
|
|
}
|
|
|
|
// emit_comptime_expr_by_id emits an expr_comptime node from an already-flat
|
|
// inner expression FlatNodeId. Mirrors the add_expr(ComptimeExpr) encoding
|
|
// exactly: a single edge to the inner expression.
|
|
pub fn (mut b FlatBuilder) emit_comptime_expr_by_id(inner_id FlatNodeId, pos token.Pos) FlatNodeId {
|
|
return b.emit_simple(.expr_comptime, pos, [
|
|
FlatEdge{
|
|
child_id: inner_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_index_expr_by_ids emits an expr_index node from already-flat lhs
|
|
// and index expression FlatNodeIds. Mirrors the add_expr(IndexExpr) encoding
|
|
// exactly: edge[0] is the lhs, edge[1] is the index expression, and the
|
|
// `is_gated` flag is packed into the flags byte.
|
|
pub fn (mut b FlatBuilder) emit_index_expr_by_ids(lhs_id FlatNodeId, expr_id FlatNodeId, is_gated bool, pos token.Pos) FlatNodeId {
|
|
flags := if is_gated { flag_is_gated } else { u8(0) }
|
|
return b.emit(.expr_index, pos, -1, -1, 0, flags, [
|
|
FlatEdge{
|
|
child_id: lhs_id
|
|
},
|
|
FlatEdge{
|
|
child_id: expr_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_selector_expr_by_ids emits an expr_selector node from an already-flat
|
|
// lhs expression and an already-flat rhs Ident expression. Mirrors the
|
|
// add_expr(SelectorExpr) encoding exactly: edge[0] is the lhs expr,
|
|
// edge[1] is the rhs (an Ident emitted as expr_ident).
|
|
pub fn (mut b FlatBuilder) emit_selector_expr_by_ids(lhs_id FlatNodeId, rhs_id FlatNodeId, pos token.Pos) FlatNodeId {
|
|
return b.emit_simple(.expr_selector, pos, [
|
|
FlatEdge{
|
|
child_id: lhs_id
|
|
},
|
|
FlatEdge{
|
|
child_id: rhs_id
|
|
},
|
|
])
|
|
}
|
|
|
|
// emit_init_expr_by_ids emits an expr_init node from an already-flat type
|
|
// FlatNodeId and a slice of already-flat aux_field_init FlatNodeIds. Mirrors
|
|
// the add_expr(InitExpr) encoding exactly: edge[0] is the type, edge[1..]
|
|
// are the field-init aux nodes. The flat-write port for struct literals uses
|
|
// this together with `emit_field_init_by_id` to avoid materialising an
|
|
// `ast.InitExpr` wrapper on the default path.
|
|
pub fn (mut b FlatBuilder) emit_init_expr_by_ids(typ_id FlatNodeId, field_ids []FlatNodeId, pos token.Pos) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: 1 + field_ids.len}
|
|
edges << FlatEdge{
|
|
child_id: typ_id
|
|
}
|
|
for fid in field_ids {
|
|
edges << FlatEdge{
|
|
child_id: fid
|
|
}
|
|
}
|
|
return b.emit_simple(.expr_init, pos, edges)
|
|
}
|
|
|
|
// emit_fn_literal_by_ids emits an expr_fn_literal node from an already-flat
|
|
// FnType FlatNodeId, slice of already-flat captured_var FlatNodeIds, and
|
|
// slice of already-flat stmt FlatNodeIds. Mirrors the add_expr(FnLiteral)
|
|
// encoding exactly: edge[0] is the type, edge[1..1+captured.len] are the
|
|
// captured vars, edge[1+captured.len..] are the stmts; `captured_vars.len`
|
|
// is packed into `extra` so the boundary is recoverable.
|
|
pub fn (mut b FlatBuilder) emit_fn_literal_by_ids(typ_id FlatNodeId, captured_var_ids []FlatNodeId, stmt_ids []FlatNodeId, pos token.Pos) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: 1 + captured_var_ids.len + stmt_ids.len}
|
|
edges << FlatEdge{
|
|
child_id: typ_id
|
|
}
|
|
for cv_id in captured_var_ids {
|
|
edges << FlatEdge{
|
|
child_id: cv_id
|
|
}
|
|
}
|
|
for sid in stmt_ids {
|
|
edges << FlatEdge{
|
|
child_id: sid
|
|
}
|
|
}
|
|
return b.emit(.expr_fn_literal, pos, -1, captured_var_ids.len, 0, 0, edges)
|
|
}
|
|
|
|
// emit_string_inter_by_ids emits an aux_string_inter node from already-flat
|
|
// inter expression and format_expr FlatNodeIds. Mirrors add_string_inter
|
|
// exactly: edge[0] = expr, edge[1] = format_expr, edge[2] = width,
|
|
// edge[3] = precision; the format token packs into the meta u16;
|
|
// resolved_fmt interns into name.
|
|
pub fn (mut b FlatBuilder) emit_string_inter_by_ids(format StringInterFormat, width int, precision int, expr_id FlatNodeId, format_expr_id FlatNodeId, resolved_fmt string) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: 4}
|
|
edges = b.push_edge(edges, expr_id)
|
|
edges = b.push_edge(edges, format_expr_id)
|
|
edges = b.push_edge(edges, b.emit_int(width))
|
|
edges = b.push_edge(edges, b.emit_int(precision))
|
|
return b.emit(.aux_string_inter, token.Pos{}, b.intern(resolved_fmt), -1, u16(int(format)), 0,
|
|
edges)
|
|
}
|
|
|
|
// emit_string_inter_literal_by_ids emits an expr_string_inter node from a
|
|
// verbatim values []string and a slice of already-flat StringInter FlatNodeIds.
|
|
// Mirrors the add_expr(StringInterLiteral) encoding exactly: edge[0] = values
|
|
// list (built via make_list_strings), edge[1] = inters list (built from the
|
|
// supplied FlatNodeIds via the standard aux_list shape).
|
|
pub fn (mut b FlatBuilder) emit_string_inter_literal_by_ids(kind StringLiteralKind, values []string, inter_ids []FlatNodeId, pos token.Pos) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: 2}
|
|
edges = b.push_edge(edges, b.make_list_strings(values))
|
|
edges = b.push_edge(edges, b.emit_aux_list_from_ids(inter_ids))
|
|
return b.emit(.expr_string_inter, pos, -1, -1, u16(int(kind)), 0, edges)
|
|
}
|
|
|
|
// emit_fn_decl_by_ids emits a stmt_fn_decl node from already-flat child
|
|
// FlatNodeIds (receiver parameter, FnType, attribute list, stmt list).
|
|
// Mirrors the add_stmt(FnDecl) encoding exactly, including the
|
|
// flag_is_public / flag_is_method / flag_is_static bits and the language
|
|
// enum carried in the meta u16.
|
|
pub fn (mut b FlatBuilder) emit_fn_decl_by_ids(name string, is_public bool, is_method bool, is_static bool, language Language, pos token.Pos, receiver_id FlatNodeId, typ_id FlatNodeId, attrs_id FlatNodeId, stmts_id FlatNodeId) FlatNodeId {
|
|
mut flags := u8(0)
|
|
if is_public {
|
|
flags |= flag_is_public
|
|
}
|
|
if is_method {
|
|
flags |= flag_is_method
|
|
}
|
|
if is_static {
|
|
flags |= flag_is_static
|
|
}
|
|
return b.emit(.stmt_fn_decl, pos, b.intern(name), -1, u16(int(language)), flags, [
|
|
FlatEdge{
|
|
child_id: receiver_id
|
|
},
|
|
FlatEdge{
|
|
child_id: typ_id
|
|
},
|
|
FlatEdge{
|
|
child_id: attrs_id
|
|
},
|
|
FlatEdge{
|
|
child_id: stmts_id
|
|
},
|
|
])
|
|
}
|
|
|
|
fn (mut b FlatBuilder) make_list_from_stmt_ids(stmt_ids []FlatNodeId) FlatNodeId {
|
|
if stmt_ids.len == 0 {
|
|
return b.get_empty_list()
|
|
}
|
|
mut edges := []FlatEdge{cap: stmt_ids.len}
|
|
for id in stmt_ids {
|
|
edges = b.push_edge(edges, id)
|
|
}
|
|
return b.emit_simple(.aux_list, token.Pos{}, edges)
|
|
}
|
|
|
|
fn (mut b FlatBuilder) intern(s string) int {
|
|
if s.len == 0 {
|
|
return -1
|
|
}
|
|
if idx := b.string_ids[s] {
|
|
return idx
|
|
}
|
|
idx := b.flat.strings.len
|
|
b.flat.strings << s
|
|
b.string_ids[s] = idx
|
|
return idx
|
|
}
|
|
|
|
// emit appends a node with the supplied cell payload + edges, returning its id.
|
|
fn (mut b FlatBuilder) emit(kind FlatNodeKind, pos token.Pos, name_id int, extra int, aux u16, flags u8, edges []FlatEdge) FlatNodeId {
|
|
first_edge := b.flat.edges.len
|
|
if edges.len > 0 {
|
|
b.flat.edges << edges
|
|
}
|
|
node_id := FlatNodeId(b.flat.nodes.len)
|
|
b.flat.nodes << FlatNode{
|
|
kind: kind
|
|
flags: flags
|
|
aux: aux
|
|
name_id: name_id
|
|
extra: extra
|
|
pos: pos
|
|
first_edge: first_edge
|
|
edge_count: edges.len
|
|
}
|
|
return node_id
|
|
}
|
|
|
|
// emit_simple wraps emit() for nodes with no scalar payload beyond kind/pos/edges.
|
|
@[inline]
|
|
fn (mut b FlatBuilder) emit_simple(kind FlatNodeKind, pos token.Pos, edges []FlatEdge) FlatNodeId {
|
|
return b.emit(kind, pos, -1, -1, 0, 0, edges)
|
|
}
|
|
|
|
fn (mut b FlatBuilder) push_edge(edges []FlatEdge, child FlatNodeId) []FlatEdge {
|
|
// Append-and-reassign pattern (callers always do `edges = b.push_edge(edges, ...)`),
|
|
// so skipping the defensive clone is safe and avoids one copy per edge.
|
|
mut updated := unsafe { edges }
|
|
updated << FlatEdge{
|
|
child_id: child
|
|
}
|
|
return updated
|
|
}
|
|
|
|
fn (mut b FlatBuilder) push_expr(edges []FlatEdge, expr Expr) []FlatEdge {
|
|
return b.push_edge(edges, b.add_expr(expr))
|
|
}
|
|
|
|
fn (mut b FlatBuilder) push_stmt(edges []FlatEdge, stmt Stmt) []FlatEdge {
|
|
return b.push_edge(edges, b.add_stmt(stmt))
|
|
}
|
|
|
|
fn (mut b FlatBuilder) push_type(edges []FlatEdge, typ Type) []FlatEdge {
|
|
return b.push_edge(edges, b.add_type(typ))
|
|
}
|
|
|
|
// get_empty_list returns a shared aux_list node id used as a canonical
|
|
// "empty list". Many AST variants have multiple optional sub-lists; reusing
|
|
// one node avoids emitting thousands of zero-edge aux_list cells.
|
|
fn (mut b FlatBuilder) get_empty_list() FlatNodeId {
|
|
if b.empty_list_id == invalid_flat_node_id {
|
|
b.empty_list_id = b.emit_simple(.aux_list, token.Pos{}, []FlatEdge{})
|
|
}
|
|
return b.empty_list_id
|
|
}
|
|
|
|
// make_list creates an aux_list node containing the supplied edges.
|
|
fn (mut b FlatBuilder) make_list_expr(items []Expr) FlatNodeId {
|
|
if items.len == 0 {
|
|
return b.get_empty_list()
|
|
}
|
|
mut edges := []FlatEdge{cap: items.len}
|
|
for it in items {
|
|
edges = b.push_expr(edges, it)
|
|
}
|
|
return b.emit_simple(.aux_list, token.Pos{}, edges)
|
|
}
|
|
|
|
fn (mut b FlatBuilder) make_list_stmt(items []Stmt) FlatNodeId {
|
|
if items.len == 0 {
|
|
return b.get_empty_list()
|
|
}
|
|
mut edges := []FlatEdge{cap: items.len}
|
|
for it in items {
|
|
edges = b.push_stmt(edges, it)
|
|
}
|
|
return b.emit_simple(.aux_list, token.Pos{}, edges)
|
|
}
|
|
|
|
fn (mut b FlatBuilder) make_list_attribute(items []Attribute) FlatNodeId {
|
|
if items.len == 0 {
|
|
return b.get_empty_list()
|
|
}
|
|
mut edges := []FlatEdge{cap: items.len}
|
|
for it in items {
|
|
edges = b.push_edge(edges, b.add_attribute(it))
|
|
}
|
|
return b.emit_simple(.aux_list, token.Pos{}, edges)
|
|
}
|
|
|
|
fn (mut b FlatBuilder) make_list_field_init(items []FieldInit) FlatNodeId {
|
|
if items.len == 0 {
|
|
return b.get_empty_list()
|
|
}
|
|
mut edges := []FlatEdge{cap: items.len}
|
|
for it in items {
|
|
edges = b.push_edge(edges, b.add_field_init(it))
|
|
}
|
|
return b.emit_simple(.aux_list, token.Pos{}, edges)
|
|
}
|
|
|
|
fn (mut b FlatBuilder) make_list_field_decl(items []FieldDecl) FlatNodeId {
|
|
if items.len == 0 {
|
|
return b.get_empty_list()
|
|
}
|
|
mut edges := []FlatEdge{cap: items.len}
|
|
for it in items {
|
|
edges = b.push_edge(edges, b.add_field_decl(it))
|
|
}
|
|
return b.emit_simple(.aux_list, token.Pos{}, edges)
|
|
}
|
|
|
|
fn (mut b FlatBuilder) make_list_parameter(items []Parameter) FlatNodeId {
|
|
if items.len == 0 {
|
|
return b.get_empty_list()
|
|
}
|
|
mut edges := []FlatEdge{cap: items.len}
|
|
for it in items {
|
|
edges = b.push_edge(edges, b.add_parameter(it))
|
|
}
|
|
return b.emit_simple(.aux_list, token.Pos{}, edges)
|
|
}
|
|
|
|
fn (mut b FlatBuilder) make_list_match_branch(items []MatchBranch) FlatNodeId {
|
|
if items.len == 0 {
|
|
return b.get_empty_list()
|
|
}
|
|
mut edges := []FlatEdge{cap: items.len}
|
|
for it in items {
|
|
edges = b.push_edge(edges, b.add_match_branch(it))
|
|
}
|
|
return b.emit_simple(.aux_list, token.Pos{}, edges)
|
|
}
|
|
|
|
fn (mut b FlatBuilder) make_list_string_inter(items []StringInter) FlatNodeId {
|
|
if items.len == 0 {
|
|
return b.get_empty_list()
|
|
}
|
|
mut edges := []FlatEdge{cap: items.len}
|
|
for it in items {
|
|
edges = b.push_edge(edges, b.add_string_inter(it))
|
|
}
|
|
return b.emit_simple(.aux_list, token.Pos{}, edges)
|
|
}
|
|
|
|
fn (mut b FlatBuilder) make_list_strings(items []string) FlatNodeId {
|
|
if items.len == 0 {
|
|
return b.get_empty_list()
|
|
}
|
|
mut edges := []FlatEdge{cap: items.len}
|
|
for s in items {
|
|
id := b.emit(.aux_string, token.Pos{}, b.intern(s), -1, 0, 0, []FlatEdge{})
|
|
edges = b.push_edge(edges, id)
|
|
}
|
|
return b.emit_simple(.aux_list, token.Pos{}, edges)
|
|
}
|
|
|
|
fn (mut b FlatBuilder) emit_int(value int) FlatNodeId {
|
|
return b.emit(.aux_int, token.Pos{}, -1, value, 0, 0, []FlatEdge{})
|
|
}
|
|
|
|
fn (mut b FlatBuilder) make_list_imports(items []ImportStmt) FlatNodeId {
|
|
if items.len == 0 {
|
|
return b.get_empty_list()
|
|
}
|
|
mut edges := []FlatEdge{cap: items.len}
|
|
for it in items {
|
|
edges = b.push_edge(edges, b.add_stmt(Stmt(it)))
|
|
}
|
|
return b.emit_simple(.aux_list, token.Pos{}, edges)
|
|
}
|
|
|
|
// add_file emits the file root and returns its FlatNodeId.
|
|
fn (mut b FlatBuilder) add_file(file File) FlatNodeId {
|
|
mut edges := []FlatEdge{}
|
|
edges = b.push_edge(edges, b.make_list_attribute(file.attributes))
|
|
edges = b.push_edge(edges, b.make_list_imports(file.imports))
|
|
edges = b.push_edge(edges, b.make_list_stmt(file.stmts))
|
|
return b.emit(.file, token.Pos{}, b.intern(file.name), b.intern(file.mod), 0, 0, edges)
|
|
}
|
|
|
|
fn (mut b FlatBuilder) add_stmt(stmt Stmt) FlatNodeId {
|
|
if stmt is []Attribute {
|
|
return b.emit_simple(.stmt_attributes, token.Pos{}, [
|
|
FlatEdge{
|
|
child_id: b.make_list_attribute(stmt)
|
|
},
|
|
])
|
|
}
|
|
match stmt {
|
|
AsmStmt {
|
|
return b.emit(.stmt_asm, token.Pos{}, b.intern(stmt.arch), -1, 0, 0, []FlatEdge{})
|
|
}
|
|
AssertStmt {
|
|
mut edges := []FlatEdge{}
|
|
edges = b.push_expr(edges, stmt.expr)
|
|
edges = b.push_expr(edges, stmt.extra)
|
|
return b.emit_simple(.stmt_assert, token.Pos{}, edges)
|
|
}
|
|
AssignStmt {
|
|
mut edges := []FlatEdge{cap: stmt.lhs.len + stmt.rhs.len}
|
|
for e in stmt.lhs {
|
|
edges = b.push_expr(edges, e)
|
|
}
|
|
for e in stmt.rhs {
|
|
edges = b.push_expr(edges, e)
|
|
}
|
|
return b.emit(.stmt_assign, stmt.pos, -1, stmt.lhs.len, u16(int(stmt.op)), 0, edges)
|
|
}
|
|
BlockStmt {
|
|
mut edges := []FlatEdge{cap: stmt.stmts.len}
|
|
for s in stmt.stmts {
|
|
edges = b.push_stmt(edges, s)
|
|
}
|
|
return b.emit_simple(.stmt_block, token.Pos{}, edges)
|
|
}
|
|
ComptimeStmt {
|
|
return b.emit_simple(.stmt_comptime, token.Pos{}, [
|
|
FlatEdge{
|
|
child_id: b.add_stmt(stmt.stmt)
|
|
},
|
|
])
|
|
}
|
|
ConstDecl {
|
|
mut flags := u8(0)
|
|
if stmt.is_public {
|
|
flags |= flag_is_public
|
|
}
|
|
return b.emit_simple_with_flags(.stmt_const_decl, token.Pos{}, flags, [
|
|
FlatEdge{
|
|
child_id: b.make_list_field_init(stmt.fields)
|
|
},
|
|
])
|
|
}
|
|
DeferStmt {
|
|
mut flags := u8(0)
|
|
if stmt.mode == .function {
|
|
flags |= flag_defer_func
|
|
}
|
|
mut edges := []FlatEdge{cap: stmt.stmts.len}
|
|
for s in stmt.stmts {
|
|
edges = b.push_stmt(edges, s)
|
|
}
|
|
return b.emit_simple_with_flags(.stmt_defer, token.Pos{}, flags, edges)
|
|
}
|
|
Directive {
|
|
mut edges := []FlatEdge{}
|
|
// ct_cond carried as a child aux_string node when non-empty
|
|
if stmt.ct_cond.len > 0 {
|
|
id := b.emit(.aux_string, token.Pos{}, b.intern(stmt.ct_cond), -1, 0, 0,
|
|
[]FlatEdge{})
|
|
edges = b.push_edge(edges, id)
|
|
}
|
|
return b.emit(.stmt_directive, token.Pos{}, b.intern(stmt.name), b.intern(stmt.value),
|
|
0, 0, edges)
|
|
}
|
|
EmptyStmt {
|
|
if int(stmt) == 0 {
|
|
if b.empty_stmt_id == invalid_flat_node_id {
|
|
b.empty_stmt_id = b.emit(.stmt_empty, token.Pos{}, -1, 0, 0, 0, []FlatEdge{})
|
|
}
|
|
return b.empty_stmt_id
|
|
}
|
|
return b.emit(.stmt_empty, token.Pos{}, -1, int(stmt), 0, 0, []FlatEdge{})
|
|
}
|
|
EnumDecl {
|
|
mut flags := u8(0)
|
|
if stmt.is_public {
|
|
flags |= flag_is_public
|
|
}
|
|
mut edges := []FlatEdge{}
|
|
edges = b.push_expr(edges, stmt.as_type)
|
|
edges = b.push_edge(edges, b.make_list_attribute(stmt.attributes))
|
|
edges = b.push_edge(edges, b.make_list_field_decl(stmt.fields))
|
|
return b.emit_simple_with_flags_name(.stmt_enum_decl, token.Pos{}, flags,
|
|
b.intern(stmt.name), edges)
|
|
}
|
|
ExprStmt {
|
|
return b.emit_simple(.stmt_expr, token.Pos{}, [
|
|
FlatEdge{
|
|
child_id: b.add_expr(stmt.expr)
|
|
},
|
|
])
|
|
}
|
|
FlowControlStmt {
|
|
return b.emit(.stmt_flow_control, token.Pos{}, b.intern(stmt.label), -1,
|
|
u16(int(stmt.op)), 0, []FlatEdge{})
|
|
}
|
|
FnDecl {
|
|
mut flags := u8(0)
|
|
if stmt.is_public {
|
|
flags |= flag_is_public
|
|
}
|
|
if stmt.is_method {
|
|
flags |= flag_is_method
|
|
}
|
|
if stmt.is_static {
|
|
flags |= flag_is_static
|
|
}
|
|
mut edges := []FlatEdge{}
|
|
if stmt.is_method {
|
|
edges = b.push_edge(edges, b.add_parameter(stmt.receiver))
|
|
} else {
|
|
// s251: a non-method FnDecl keeps the parser's zero `ast.Parameter{}`
|
|
// receiver, whose `typ` is a zero-valued Expr (invalid sum-type tag,
|
|
// null payload). Routing that through add_expr is unsafe on the arm64
|
|
// self-host: an exhaustive match on an unmatched tag falls into the
|
|
// first arm (ArrayInitExpr) and derefs the null payload. The receiver
|
|
// edge is only read for methods, so emit a clean empty receiver
|
|
// (typ = empty_expr, a valid EmptyExpr) for non-methods.
|
|
edges = b.push_edge(edges, b.add_parameter(Parameter{
|
|
typ: empty_expr
|
|
}))
|
|
}
|
|
edges = b.push_edge(edges, b.add_type(Type(stmt.typ)))
|
|
edges = b.push_edge(edges, b.make_list_attribute(stmt.attributes))
|
|
edges = b.push_edge(edges, b.make_list_stmt(stmt.stmts))
|
|
return b.emit(.stmt_fn_decl, stmt.pos, b.intern(stmt.name), -1,
|
|
u16(int(stmt.language)), flags, edges)
|
|
}
|
|
ForInStmt {
|
|
mut edges := []FlatEdge{cap: 3}
|
|
edges = b.push_expr(edges, stmt.key)
|
|
edges = b.push_expr(edges, stmt.value)
|
|
edges = b.push_expr(edges, stmt.expr)
|
|
return b.emit_simple(.stmt_for_in, token.Pos{}, edges)
|
|
}
|
|
ForStmt {
|
|
mut edges := []FlatEdge{cap: 3 + stmt.stmts.len}
|
|
edges = b.push_stmt(edges, stmt.init)
|
|
edges = b.push_expr(edges, stmt.cond)
|
|
edges = b.push_stmt(edges, stmt.post)
|
|
for s in stmt.stmts {
|
|
edges = b.push_stmt(edges, s)
|
|
}
|
|
return b.emit_simple(.stmt_for, token.Pos{}, edges)
|
|
}
|
|
GlobalDecl {
|
|
mut flags := u8(0)
|
|
if stmt.is_public {
|
|
flags |= flag_is_public
|
|
}
|
|
mut edges := []FlatEdge{}
|
|
edges = b.push_edge(edges, b.make_list_attribute(stmt.attributes))
|
|
edges = b.push_edge(edges, b.make_list_field_decl(stmt.fields))
|
|
return b.emit_simple_with_flags(.stmt_global_decl, token.Pos{}, flags, edges)
|
|
}
|
|
ImportStmt {
|
|
mut flags := u8(0)
|
|
if stmt.is_aliased {
|
|
flags |= flag_is_aliased
|
|
}
|
|
mut edges := []FlatEdge{cap: stmt.symbols.len}
|
|
for sym in stmt.symbols {
|
|
edges = b.push_expr(edges, sym)
|
|
}
|
|
return b.emit(.stmt_import, token.Pos{}, b.intern(stmt.name), b.intern(stmt.alias), 0,
|
|
flags, edges)
|
|
}
|
|
InterfaceDecl {
|
|
mut flags := u8(0)
|
|
if stmt.is_public {
|
|
flags |= flag_is_public
|
|
}
|
|
mut edges := []FlatEdge{cap: 4}
|
|
edges = b.push_edge(edges, b.make_list_attribute(stmt.attributes))
|
|
edges = b.push_edge(edges, b.make_list_expr(stmt.generic_params))
|
|
edges = b.push_edge(edges, b.make_list_expr(stmt.embedded))
|
|
edges = b.push_edge(edges, b.make_list_field_decl(stmt.fields))
|
|
return b.emit_simple_with_flags_name(.stmt_interface_decl, token.Pos{}, flags,
|
|
b.intern(stmt.name), edges)
|
|
}
|
|
LabelStmt {
|
|
return b.emit(.stmt_label, token.Pos{}, b.intern(stmt.name), -1, 0, 0, [
|
|
FlatEdge{
|
|
child_id: b.add_stmt(stmt.stmt)
|
|
},
|
|
])
|
|
}
|
|
ModuleStmt {
|
|
return b.emit(.stmt_module, token.Pos{}, b.intern(stmt.name), -1, 0, 0, []FlatEdge{})
|
|
}
|
|
ReturnStmt {
|
|
mut edges := []FlatEdge{cap: stmt.exprs.len}
|
|
for e in stmt.exprs {
|
|
edges = b.push_expr(edges, e)
|
|
}
|
|
return b.emit_simple(.stmt_return, token.Pos{}, edges)
|
|
}
|
|
StructDecl {
|
|
mut flags := u8(0)
|
|
if stmt.is_public {
|
|
flags |= flag_is_public
|
|
}
|
|
if stmt.is_union {
|
|
flags |= flag_is_union
|
|
}
|
|
mut edges := []FlatEdge{cap: 5}
|
|
edges = b.push_edge(edges, b.make_list_attribute(stmt.attributes))
|
|
edges = b.push_edge(edges, b.make_list_expr(stmt.implements))
|
|
edges = b.push_edge(edges, b.make_list_expr(stmt.embedded))
|
|
edges = b.push_edge(edges, b.make_list_expr(stmt.generic_params))
|
|
edges = b.push_edge(edges, b.make_list_field_decl(stmt.fields))
|
|
return b.emit(.stmt_struct_decl, stmt.pos, b.intern(stmt.name), -1,
|
|
u16(int(stmt.language)), flags, edges)
|
|
}
|
|
TypeDecl {
|
|
mut flags := u8(0)
|
|
if stmt.is_public {
|
|
flags |= flag_is_public
|
|
}
|
|
mut edges := []FlatEdge{cap: 4}
|
|
edges = b.push_expr(edges, stmt.base_type)
|
|
edges = b.push_edge(edges, b.make_list_attribute([]Attribute{}))
|
|
edges = b.push_edge(edges, b.make_list_expr(stmt.generic_params))
|
|
edges = b.push_edge(edges, b.make_list_expr(stmt.variants))
|
|
return b.emit(.stmt_type_decl, token.Pos{}, b.intern(stmt.name), -1,
|
|
u16(int(stmt.language)), flags, edges)
|
|
}
|
|
[]Attribute {
|
|
// handled at top of function
|
|
return invalid_flat_node_id
|
|
}
|
|
}
|
|
}
|
|
|
|
@[inline]
|
|
fn (mut b FlatBuilder) emit_simple_with_flags(kind FlatNodeKind, pos token.Pos, flags u8, edges []FlatEdge) FlatNodeId {
|
|
return b.emit(kind, pos, -1, -1, 0, flags, edges)
|
|
}
|
|
|
|
@[inline]
|
|
fn (mut b FlatBuilder) emit_simple_with_flags_name(kind FlatNodeKind, pos token.Pos, flags u8, name_id int, edges []FlatEdge) FlatNodeId {
|
|
return b.emit(kind, pos, name_id, -1, 0, flags, edges)
|
|
}
|
|
|
|
fn (mut b FlatBuilder) add_expr(expr Expr) FlatNodeId {
|
|
if expr_is_zero_value(expr) {
|
|
if b.empty_expr_id == invalid_flat_node_id {
|
|
b.empty_expr_id = b.emit(.expr_empty, token.Pos{}, -1, 0, 0, 0, []FlatEdge{})
|
|
}
|
|
return b.empty_expr_id
|
|
}
|
|
match expr {
|
|
Type {
|
|
return b.add_type(expr)
|
|
}
|
|
FieldInit {
|
|
return b.add_field_init(expr)
|
|
}
|
|
else {}
|
|
}
|
|
|
|
match expr {
|
|
ArrayInitExpr {
|
|
mut edges := []FlatEdge{cap: 5 + expr.exprs.len}
|
|
edges = b.push_expr(edges, expr.typ)
|
|
edges = b.push_expr(edges, expr.init)
|
|
edges = b.push_expr(edges, expr.cap)
|
|
edges = b.push_expr(edges, expr.len)
|
|
edges = b.push_expr(edges, expr.update_expr)
|
|
for e in expr.exprs {
|
|
edges = b.push_expr(edges, e)
|
|
}
|
|
return b.emit_simple(.expr_array_init, expr.pos, edges)
|
|
}
|
|
AsCastExpr {
|
|
mut edges := []FlatEdge{cap: 2}
|
|
edges = b.push_expr(edges, expr.expr)
|
|
edges = b.push_expr(edges, expr.typ)
|
|
return b.emit_simple(.expr_as_cast, expr.pos, edges)
|
|
}
|
|
AssocExpr {
|
|
mut edges := []FlatEdge{cap: 2 + expr.fields.len}
|
|
edges = b.push_expr(edges, expr.typ)
|
|
edges = b.push_expr(edges, expr.expr)
|
|
for f in expr.fields {
|
|
edges = b.push_edge(edges, b.add_field_init(f))
|
|
}
|
|
return b.emit_simple(.expr_assoc, expr.pos, edges)
|
|
}
|
|
BasicLiteral {
|
|
return b.emit(.expr_basic_literal, expr.pos, b.intern(expr.value), -1,
|
|
u16(int(expr.kind)), 0, []FlatEdge{})
|
|
}
|
|
CallExpr {
|
|
mut edges := []FlatEdge{cap: 1 + expr.args.len}
|
|
edges = b.push_expr(edges, expr.lhs)
|
|
for a in expr.args {
|
|
edges = b.push_expr(edges, a)
|
|
}
|
|
return b.emit_simple(.expr_call, expr.pos, edges)
|
|
}
|
|
CallOrCastExpr {
|
|
mut edges := []FlatEdge{cap: 2}
|
|
edges = b.push_expr(edges, expr.lhs)
|
|
edges = b.push_expr(edges, expr.expr)
|
|
return b.emit_simple(.expr_call_or_cast, expr.pos, edges)
|
|
}
|
|
CastExpr {
|
|
mut edges := []FlatEdge{cap: 2}
|
|
edges = b.push_expr(edges, expr.typ)
|
|
edges = b.push_expr(edges, expr.expr)
|
|
return b.emit_simple(.expr_cast, expr.pos, edges)
|
|
}
|
|
ComptimeExpr {
|
|
return b.emit_simple(.expr_comptime, expr.pos, [
|
|
FlatEdge{
|
|
child_id: b.add_expr(expr.expr)
|
|
},
|
|
])
|
|
}
|
|
EmptyExpr {
|
|
// All EmptyExpr instances in the parser are `EmptyExpr(0)` (see
|
|
// ast.empty_expr). Share one node — 65k+ duplicates per build.
|
|
if int(expr) == 0 {
|
|
if b.empty_expr_id == invalid_flat_node_id {
|
|
b.empty_expr_id = b.emit(.expr_empty, token.Pos{}, -1, 0, 0, 0, []FlatEdge{})
|
|
}
|
|
return b.empty_expr_id
|
|
}
|
|
return b.emit(.expr_empty, token.Pos{}, -1, int(expr), 0, 0, []FlatEdge{})
|
|
}
|
|
FnLiteral {
|
|
mut edges := []FlatEdge{}
|
|
edges = b.push_edge(edges, b.add_type(Type(expr.typ)))
|
|
for cv in expr.captured_vars {
|
|
edges = b.push_expr(edges, cv)
|
|
}
|
|
for s in expr.stmts {
|
|
edges = b.push_stmt(edges, s)
|
|
}
|
|
// extra stores captured_vars.len so the boundary between captured
|
|
// vars and stmts is recoverable.
|
|
return b.emit(.expr_fn_literal, expr.pos, -1, expr.captured_vars.len, 0, 0, edges)
|
|
}
|
|
GenericArgOrIndexExpr {
|
|
mut edges := []FlatEdge{cap: 2}
|
|
edges = b.push_expr(edges, expr.lhs)
|
|
edges = b.push_expr(edges, expr.expr)
|
|
return b.emit_simple(.expr_generic_arg_or_index, expr.pos, edges)
|
|
}
|
|
GenericArgs {
|
|
mut edges := []FlatEdge{cap: 1 + expr.args.len}
|
|
edges = b.push_expr(edges, expr.lhs)
|
|
for a in expr.args {
|
|
edges = b.push_expr(edges, a)
|
|
}
|
|
return b.emit_simple(.expr_generic_args, expr.pos, edges)
|
|
}
|
|
Ident {
|
|
return b.emit(.expr_ident, expr.pos, b.intern(expr.name), -1, 0, 0, []FlatEdge{})
|
|
}
|
|
IfExpr {
|
|
mut edges := []FlatEdge{cap: 2 + expr.stmts.len}
|
|
edges = b.push_expr(edges, expr.cond)
|
|
edges = b.push_expr(edges, expr.else_expr)
|
|
for s in expr.stmts {
|
|
edges = b.push_stmt(edges, s)
|
|
}
|
|
return b.emit_simple(.expr_if, expr.pos, edges)
|
|
}
|
|
IfGuardExpr {
|
|
return b.emit_simple(.expr_if_guard, expr.pos, [
|
|
FlatEdge{
|
|
child_id: b.add_stmt(Stmt(expr.stmt))
|
|
},
|
|
])
|
|
}
|
|
IndexExpr {
|
|
mut flags := u8(0)
|
|
if expr.is_gated {
|
|
flags |= flag_is_gated
|
|
}
|
|
mut edges := []FlatEdge{cap: 2}
|
|
edges = b.push_expr(edges, expr.lhs)
|
|
edges = b.push_expr(edges, expr.expr)
|
|
return b.emit(.expr_index, expr.pos, -1, -1, 0, flags, edges)
|
|
}
|
|
InfixExpr {
|
|
mut edges := []FlatEdge{cap: 2}
|
|
edges = b.push_expr(edges, expr.lhs)
|
|
edges = b.push_expr(edges, expr.rhs)
|
|
return b.emit(.expr_infix, expr.pos, -1, -1, u16(int(expr.op)), 0, edges)
|
|
}
|
|
InitExpr {
|
|
mut edges := []FlatEdge{cap: 1 + expr.fields.len}
|
|
edges = b.push_expr(edges, expr.typ)
|
|
for f in expr.fields {
|
|
edges = b.push_edge(edges, b.add_field_init(f))
|
|
}
|
|
return b.emit_simple(.expr_init, expr.pos, edges)
|
|
}
|
|
Keyword {
|
|
return b.emit(.expr_keyword, token.Pos{}, -1, -1, u16(int(expr.tok)), 0, []FlatEdge{})
|
|
}
|
|
KeywordOperator {
|
|
mut edges := []FlatEdge{cap: expr.exprs.len}
|
|
for e in expr.exprs {
|
|
edges = b.push_expr(edges, e)
|
|
}
|
|
return b.emit(.expr_keyword_operator, expr.pos, -1, -1, u16(int(expr.op)), 0, edges)
|
|
}
|
|
LambdaExpr {
|
|
mut edges := []FlatEdge{cap: 1 + expr.args.len}
|
|
edges = b.push_expr(edges, expr.expr)
|
|
for a in expr.args {
|
|
edges = b.push_expr(edges, Expr(a))
|
|
}
|
|
return b.emit_simple(.expr_lambda, expr.pos, edges)
|
|
}
|
|
LifetimeExpr {
|
|
return b.emit(.expr_lifetime, expr.pos, b.intern(expr.name), -1, 0, 0, []FlatEdge{})
|
|
}
|
|
LockExpr {
|
|
mut edges := []FlatEdge{cap: expr.lock_exprs.len + expr.rlock_exprs.len + expr.stmts.len}
|
|
for e in expr.lock_exprs {
|
|
edges = b.push_expr(edges, e)
|
|
}
|
|
for e in expr.rlock_exprs {
|
|
edges = b.push_expr(edges, e)
|
|
}
|
|
for s in expr.stmts {
|
|
edges = b.push_stmt(edges, s)
|
|
}
|
|
// Pack (lock.len, rlock.len) into extra; stmts.len = edge_count - lock - rlock.
|
|
lock_len := u32(expr.lock_exprs.len) & 0xFFFF
|
|
rlock_len := u32(expr.rlock_exprs.len) & 0xFFFF
|
|
packed := int(lock_len | (rlock_len << 16))
|
|
return b.emit(.expr_lock, expr.pos, -1, packed, 0, 0, edges)
|
|
}
|
|
MapInitExpr {
|
|
mut edges := []FlatEdge{cap: 1 + expr.keys.len + expr.vals.len}
|
|
edges = b.push_expr(edges, expr.typ)
|
|
for k in expr.keys {
|
|
edges = b.push_expr(edges, k)
|
|
}
|
|
for v in expr.vals {
|
|
edges = b.push_expr(edges, v)
|
|
}
|
|
return b.emit(.expr_map_init, expr.pos, -1, expr.keys.len, 0, 0, edges)
|
|
}
|
|
MatchExpr {
|
|
mut edges := []FlatEdge{cap: 1 + expr.branches.len}
|
|
edges = b.push_expr(edges, expr.expr)
|
|
for br in expr.branches {
|
|
edges = b.push_edge(edges, b.add_match_branch(br))
|
|
}
|
|
return b.emit_simple(.expr_match, expr.pos, edges)
|
|
}
|
|
ModifierExpr {
|
|
mut edges := []FlatEdge{cap: 1}
|
|
edges = b.push_expr(edges, expr.expr)
|
|
return b.emit(.expr_modifier, expr.pos, -1, -1, u16(int(expr.kind)), 0, edges)
|
|
}
|
|
OrExpr {
|
|
mut edges := []FlatEdge{cap: 1 + expr.stmts.len}
|
|
edges = b.push_expr(edges, expr.expr)
|
|
for s in expr.stmts {
|
|
edges = b.push_stmt(edges, s)
|
|
}
|
|
return b.emit_simple(.expr_or, expr.pos, edges)
|
|
}
|
|
ParenExpr {
|
|
return b.emit_simple(.expr_paren, expr.pos, [
|
|
FlatEdge{
|
|
child_id: b.add_expr(expr.expr)
|
|
},
|
|
])
|
|
}
|
|
PostfixExpr {
|
|
return b.emit(.expr_postfix, expr.pos, -1, -1, u16(int(expr.op)), 0, [
|
|
FlatEdge{
|
|
child_id: b.add_expr(expr.expr)
|
|
},
|
|
])
|
|
}
|
|
PrefixExpr {
|
|
return b.emit(.expr_prefix, expr.pos, -1, -1, u16(int(expr.op)), 0, [
|
|
FlatEdge{
|
|
child_id: b.add_expr(expr.expr)
|
|
},
|
|
])
|
|
}
|
|
RangeExpr {
|
|
mut edges := []FlatEdge{cap: 2}
|
|
edges = b.push_expr(edges, expr.start)
|
|
edges = b.push_expr(edges, expr.end)
|
|
return b.emit(.expr_range, expr.pos, -1, -1, u16(int(expr.op)), 0, edges)
|
|
}
|
|
SelectExpr {
|
|
mut edges := []FlatEdge{cap: 2 + expr.stmts.len}
|
|
edges = b.push_stmt(edges, expr.stmt)
|
|
edges = b.push_expr(edges, expr.next)
|
|
for s in expr.stmts {
|
|
edges = b.push_stmt(edges, s)
|
|
}
|
|
return b.emit_simple(.expr_select, expr.pos, edges)
|
|
}
|
|
SelectorExpr {
|
|
mut edges := []FlatEdge{cap: 2}
|
|
edges = b.push_expr(edges, expr.lhs)
|
|
edges = b.push_expr(edges, Expr(expr.rhs))
|
|
return b.emit_simple(.expr_selector, expr.pos, edges)
|
|
}
|
|
SqlExpr {
|
|
mut flags := u8(0)
|
|
if expr.is_count {
|
|
flags |= flag_is_count
|
|
}
|
|
if expr.is_create {
|
|
flags |= flag_is_create
|
|
}
|
|
return b.emit(.expr_sql, expr.pos, b.intern(expr.table_name), -1, 0, flags, [
|
|
FlatEdge{
|
|
child_id: b.add_expr(expr.expr)
|
|
},
|
|
])
|
|
}
|
|
StringInterLiteral {
|
|
mut edges := []FlatEdge{cap: 2}
|
|
edges = b.push_edge(edges, b.make_list_strings(expr.values))
|
|
edges = b.push_edge(edges, b.make_list_string_inter(expr.inters))
|
|
return b.emit(.expr_string_inter, expr.pos, -1, -1, u16(int(expr.kind)), 0, edges)
|
|
}
|
|
StringLiteral {
|
|
return b.emit(.expr_string, expr.pos, b.intern(expr.value), -1, u16(int(expr.kind)), 0,
|
|
[]FlatEdge{})
|
|
}
|
|
Tuple {
|
|
mut edges := []FlatEdge{cap: expr.exprs.len}
|
|
for e in expr.exprs {
|
|
edges = b.push_expr(edges, e)
|
|
}
|
|
return b.emit_simple(.expr_tuple, expr.pos, edges)
|
|
}
|
|
UnsafeExpr {
|
|
mut edges := []FlatEdge{cap: expr.stmts.len}
|
|
for s in expr.stmts {
|
|
edges = b.push_stmt(edges, s)
|
|
}
|
|
return b.emit_simple(.expr_unsafe, expr.pos, edges)
|
|
}
|
|
FieldInit, Type {
|
|
// handled at top of function
|
|
return invalid_flat_node_id
|
|
}
|
|
}
|
|
}
|
|
|
|
@[inline]
|
|
fn expr_is_zero_value(expr Expr) bool {
|
|
tag_word := unsafe { (&u64(&expr))[0] }
|
|
data_word := unsafe { (&u64(&expr))[1] }
|
|
return tag_word == 0 && data_word == 0
|
|
}
|
|
|
|
fn (mut b FlatBuilder) add_type(typ Type) FlatNodeId {
|
|
match typ {
|
|
AnonStructType {
|
|
mut edges := []FlatEdge{cap: 3}
|
|
edges = b.push_edge(edges, b.make_list_expr(typ.generic_params))
|
|
edges = b.push_edge(edges, b.make_list_expr(typ.embedded))
|
|
edges = b.push_edge(edges, b.make_list_field_decl(typ.fields))
|
|
return b.emit_simple(.typ_anon_struct, token.Pos{}, edges)
|
|
}
|
|
ArrayFixedType {
|
|
mut edges := []FlatEdge{cap: 2}
|
|
edges = b.push_expr(edges, typ.len)
|
|
edges = b.push_expr(edges, typ.elem_type)
|
|
return b.emit_simple(.typ_array_fixed, token.Pos{}, edges)
|
|
}
|
|
ArrayType {
|
|
mut edges := []FlatEdge{cap: 1}
|
|
edges = b.push_expr(edges, typ.elem_type)
|
|
return b.emit_simple(.typ_array, token.Pos{}, edges)
|
|
}
|
|
ChannelType {
|
|
mut edges := []FlatEdge{cap: 2}
|
|
edges = b.push_expr(edges, typ.cap)
|
|
edges = b.push_expr(edges, typ.elem_type)
|
|
return b.emit_simple(.typ_channel, token.Pos{}, edges)
|
|
}
|
|
FnType {
|
|
mut edges := []FlatEdge{cap: 3}
|
|
edges = b.push_edge(edges, b.make_list_expr(typ.generic_params))
|
|
edges = b.push_edge(edges, b.make_list_parameter(typ.params))
|
|
edges = b.push_expr(edges, typ.return_type)
|
|
return b.emit_simple(.typ_fn, token.Pos{}, edges)
|
|
}
|
|
GenericType {
|
|
mut edges := []FlatEdge{cap: 1 + typ.params.len}
|
|
edges = b.push_expr(edges, typ.name)
|
|
for p in typ.params {
|
|
edges = b.push_expr(edges, p)
|
|
}
|
|
return b.emit_simple(.typ_generic, token.Pos{}, edges)
|
|
}
|
|
MapType {
|
|
mut edges := []FlatEdge{cap: 2}
|
|
edges = b.push_expr(edges, typ.key_type)
|
|
edges = b.push_expr(edges, typ.value_type)
|
|
return b.emit_simple(.typ_map, token.Pos{}, edges)
|
|
}
|
|
NilType {
|
|
return b.emit_simple(.typ_nil, token.Pos{}, []FlatEdge{})
|
|
}
|
|
NoneType {
|
|
return b.emit_simple(.typ_none, token.Pos{}, []FlatEdge{})
|
|
}
|
|
OptionType {
|
|
mut edges := []FlatEdge{cap: 1}
|
|
edges = b.push_expr(edges, typ.base_type)
|
|
return b.emit_simple(.typ_option, token.Pos{}, edges)
|
|
}
|
|
PointerType {
|
|
mut edges := []FlatEdge{cap: 1}
|
|
edges = b.push_expr(edges, typ.base_type)
|
|
return b.emit(.typ_pointer, token.Pos{}, b.intern(typ.lifetime), -1, 0, 0, edges)
|
|
}
|
|
ResultType {
|
|
mut edges := []FlatEdge{cap: 1}
|
|
edges = b.push_expr(edges, typ.base_type)
|
|
return b.emit_simple(.typ_result, token.Pos{}, edges)
|
|
}
|
|
ThreadType {
|
|
mut edges := []FlatEdge{cap: 1}
|
|
edges = b.push_expr(edges, typ.elem_type)
|
|
return b.emit_simple(.typ_thread, token.Pos{}, edges)
|
|
}
|
|
TupleType {
|
|
mut edges := []FlatEdge{cap: typ.types.len}
|
|
for t in typ.types {
|
|
edges = b.push_expr(edges, t)
|
|
}
|
|
return b.emit_simple(.typ_tuple, token.Pos{}, edges)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn (mut b FlatBuilder) add_attribute(attr Attribute) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: 2}
|
|
edges = b.push_expr(edges, attr.value)
|
|
edges = b.push_expr(edges, attr.comptime_cond)
|
|
return b.emit(.aux_attribute, token.Pos{}, b.intern(attr.name), -1, 0, 0, edges)
|
|
}
|
|
|
|
fn (mut b FlatBuilder) add_field_init(field FieldInit) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: 1}
|
|
edges = b.push_expr(edges, field.value)
|
|
return b.emit(.aux_field_init, token.Pos{}, b.intern(field.name), -1, 0, 0, edges)
|
|
}
|
|
|
|
fn field_decl_flags(field FieldDecl) u8 {
|
|
mut flags := u8(0)
|
|
if field.is_public {
|
|
flags |= flag_is_public
|
|
}
|
|
if field.is_mut {
|
|
flags |= flag_is_mut
|
|
}
|
|
if field.is_module_mut {
|
|
flags |= flag_field_is_module_mut
|
|
}
|
|
if field.is_interface_method {
|
|
flags |= flag_field_is_interface_method
|
|
}
|
|
return flags
|
|
}
|
|
|
|
fn (mut b FlatBuilder) add_field_decl(field FieldDecl) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: 3}
|
|
edges = b.push_expr(edges, field.typ)
|
|
edges = b.push_expr(edges, field.value)
|
|
edges = b.push_edge(edges, b.make_list_attribute(field.attributes))
|
|
return b.emit(.aux_field_decl, token.Pos{}, b.intern(field.name), -1, 0,
|
|
field_decl_flags(field), edges)
|
|
}
|
|
|
|
fn (mut b FlatBuilder) add_parameter(param Parameter) FlatNodeId {
|
|
mut flags := u8(0)
|
|
if param.is_mut {
|
|
flags |= flag_is_mut
|
|
}
|
|
mut edges := []FlatEdge{cap: 1}
|
|
edges = b.push_expr(edges, param.typ)
|
|
return b.emit(.aux_parameter, param.pos, b.intern(param.name), -1, 0, flags, edges)
|
|
}
|
|
|
|
fn (mut b FlatBuilder) add_match_branch(branch MatchBranch) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: 2}
|
|
edges = b.push_edge(edges, b.make_list_expr(branch.cond))
|
|
edges = b.push_edge(edges, b.make_list_stmt(branch.stmts))
|
|
return b.emit_simple(.aux_match_branch, branch.pos, edges)
|
|
}
|
|
|
|
fn (mut b FlatBuilder) add_string_inter(inter StringInter) FlatNodeId {
|
|
mut edges := []FlatEdge{cap: 4}
|
|
edges = b.push_expr(edges, inter.expr)
|
|
edges = b.push_expr(edges, inter.format_expr)
|
|
edges = b.push_edge(edges, b.emit_int(inter.width))
|
|
edges = b.push_edge(edges, b.emit_int(inter.precision))
|
|
return b.emit(.aux_string_inter, token.Pos{}, b.intern(inter.resolved_fmt), -1,
|
|
u16(int(inter.format)), 0, edges)
|
|
}
|
|
|
|
// LegacyAstWalker estimates dynamic memory usage of the recursive AST for diagnostics.
|
|
struct LegacyAstWalker {
|
|
mut:
|
|
stats LegacyAstStats
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_files(files []File) {
|
|
w.stats.files = files.len
|
|
w.stats.node_bytes += u64(files.len) * u64(sizeof(File))
|
|
w.add_array_storage(sizeof(File), files.len)
|
|
for file in files {
|
|
w.walk_file(file)
|
|
}
|
|
w.stats.bytes_estimate = w.stats.node_bytes + w.stats.array_bytes + w.stats.string_bytes
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) add_array_storage(elem_size u32, len int) {
|
|
if len <= 0 || elem_size == 0 {
|
|
return
|
|
}
|
|
w.stats.array_bytes += u64(elem_size) * u64(len)
|
|
w.stats.allocs++
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) add_string(s string) {
|
|
w.stats.string_entries++
|
|
w.stats.string_bytes += u64(s.len)
|
|
if s.len > 0 {
|
|
w.stats.allocs++
|
|
}
|
|
}
|
|
|
|
// add_variant_payload accounts for one heap allocation behind a sumtype slot
|
|
// (e.g., AssignStmt, CallExpr, ArrayType) plus its struct bytes.
|
|
fn (mut w LegacyAstWalker) add_variant_payload(size u32) {
|
|
w.stats.node_bytes += u64(size)
|
|
w.stats.allocs++
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_file(file File) {
|
|
w.add_string(file.mod)
|
|
w.add_string(file.name)
|
|
w.walk_attribute_array(file.attributes)
|
|
w.walk_import_array(file.imports)
|
|
w.walk_stmt_array(file.stmts)
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_stmt(stmt Stmt) {
|
|
if stmt is []Attribute {
|
|
w.walk_attribute_array(stmt)
|
|
return
|
|
}
|
|
w.stats.stmt_nodes++
|
|
// Sumtype slot is counted by parent (array storage / field sizeof).
|
|
// Add only the heap-allocated variant payload.
|
|
match stmt {
|
|
AssignStmt {
|
|
w.add_variant_payload(sizeof(AssignStmt))
|
|
w.walk_expr_array(stmt.lhs)
|
|
w.walk_expr_array(stmt.rhs)
|
|
}
|
|
AssertStmt {
|
|
w.add_variant_payload(sizeof(AssertStmt))
|
|
w.walk_expr(stmt.expr)
|
|
w.walk_expr(stmt.extra)
|
|
}
|
|
AsmStmt {
|
|
w.add_variant_payload(sizeof(AsmStmt))
|
|
w.add_string(stmt.arch)
|
|
}
|
|
BlockStmt {
|
|
w.add_variant_payload(sizeof(BlockStmt))
|
|
w.walk_stmt_array(stmt.stmts)
|
|
}
|
|
ComptimeStmt {
|
|
w.add_variant_payload(sizeof(ComptimeStmt))
|
|
w.walk_stmt(stmt.stmt)
|
|
}
|
|
ConstDecl {
|
|
w.add_variant_payload(sizeof(ConstDecl))
|
|
w.walk_field_init_array(stmt.fields)
|
|
}
|
|
DeferStmt {
|
|
w.add_variant_payload(sizeof(DeferStmt))
|
|
w.walk_stmt_array(stmt.stmts)
|
|
}
|
|
Directive {
|
|
w.add_variant_payload(sizeof(Directive))
|
|
w.add_string(stmt.name)
|
|
w.add_string(stmt.value)
|
|
w.add_string(stmt.ct_cond)
|
|
}
|
|
EmptyStmt {}
|
|
EnumDecl {
|
|
w.add_variant_payload(sizeof(EnumDecl))
|
|
w.walk_attribute_array(stmt.attributes)
|
|
w.add_string(stmt.name)
|
|
w.walk_expr(stmt.as_type)
|
|
w.walk_field_decl_array(stmt.fields)
|
|
}
|
|
ExprStmt {
|
|
w.add_variant_payload(sizeof(ExprStmt))
|
|
w.walk_expr(stmt.expr)
|
|
}
|
|
FlowControlStmt {
|
|
w.add_variant_payload(sizeof(FlowControlStmt))
|
|
w.add_string(stmt.label)
|
|
}
|
|
FnDecl {
|
|
w.add_variant_payload(sizeof(FnDecl))
|
|
w.walk_attribute_array(stmt.attributes)
|
|
if stmt.is_method {
|
|
w.walk_parameter(stmt.receiver)
|
|
} else {
|
|
w.walk_parameter(Parameter{
|
|
typ: empty_expr
|
|
})
|
|
}
|
|
w.add_string(stmt.name)
|
|
w.walk_type(Type(stmt.typ))
|
|
w.walk_stmt_array(stmt.stmts)
|
|
}
|
|
ForInStmt {
|
|
w.add_variant_payload(sizeof(ForInStmt))
|
|
w.walk_expr(stmt.key)
|
|
w.walk_expr(stmt.value)
|
|
w.walk_expr(stmt.expr)
|
|
}
|
|
ForStmt {
|
|
w.add_variant_payload(sizeof(ForStmt))
|
|
w.walk_stmt(stmt.init)
|
|
w.walk_expr(stmt.cond)
|
|
w.walk_stmt(stmt.post)
|
|
w.walk_stmt_array(stmt.stmts)
|
|
}
|
|
GlobalDecl {
|
|
w.add_variant_payload(sizeof(GlobalDecl))
|
|
w.walk_attribute_array(stmt.attributes)
|
|
w.walk_field_decl_array(stmt.fields)
|
|
}
|
|
ImportStmt {
|
|
w.add_variant_payload(sizeof(ImportStmt))
|
|
w.add_string(stmt.name)
|
|
w.add_string(stmt.alias)
|
|
w.walk_expr_array(stmt.symbols)
|
|
}
|
|
InterfaceDecl {
|
|
w.add_variant_payload(sizeof(InterfaceDecl))
|
|
w.walk_attribute_array(stmt.attributes)
|
|
w.add_string(stmt.name)
|
|
w.walk_expr_array(stmt.generic_params)
|
|
w.walk_expr_array(stmt.embedded)
|
|
w.walk_field_decl_array(stmt.fields)
|
|
}
|
|
LabelStmt {
|
|
w.add_variant_payload(sizeof(LabelStmt))
|
|
w.add_string(stmt.name)
|
|
w.walk_stmt(stmt.stmt)
|
|
}
|
|
ModuleStmt {
|
|
w.add_variant_payload(sizeof(ModuleStmt))
|
|
w.add_string(stmt.name)
|
|
}
|
|
ReturnStmt {
|
|
w.add_variant_payload(sizeof(ReturnStmt))
|
|
w.walk_expr_array(stmt.exprs)
|
|
}
|
|
StructDecl {
|
|
w.add_variant_payload(sizeof(StructDecl))
|
|
w.walk_attribute_array(stmt.attributes)
|
|
w.add_string(stmt.name)
|
|
w.walk_expr_array(stmt.implements)
|
|
w.walk_expr_array(stmt.embedded)
|
|
w.walk_expr_array(stmt.generic_params)
|
|
w.walk_field_decl_array(stmt.fields)
|
|
}
|
|
TypeDecl {
|
|
w.add_variant_payload(sizeof(TypeDecl))
|
|
w.add_string(stmt.name)
|
|
w.walk_expr(stmt.base_type)
|
|
w.walk_expr_array(stmt.generic_params)
|
|
w.walk_expr_array(stmt.variants)
|
|
}
|
|
[]Attribute {}
|
|
}
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_expr(expr Expr) {
|
|
match expr {
|
|
Type {
|
|
w.walk_type(expr)
|
|
return
|
|
}
|
|
FieldInit {
|
|
w.walk_field_init(expr)
|
|
return
|
|
}
|
|
else {}
|
|
}
|
|
|
|
w.stats.expr_nodes++
|
|
// Sumtype slot is counted by parent; add only heap variant payload.
|
|
match expr {
|
|
ArrayInitExpr {
|
|
w.add_variant_payload(sizeof(ArrayInitExpr))
|
|
w.walk_expr(expr.typ)
|
|
w.walk_expr_array(expr.exprs)
|
|
w.walk_expr(expr.init)
|
|
w.walk_expr(expr.cap)
|
|
w.walk_expr(expr.len)
|
|
w.walk_expr(expr.update_expr)
|
|
}
|
|
AsCastExpr {
|
|
w.add_variant_payload(sizeof(AsCastExpr))
|
|
w.walk_expr(expr.expr)
|
|
w.walk_expr(expr.typ)
|
|
}
|
|
AssocExpr {
|
|
w.add_variant_payload(sizeof(AssocExpr))
|
|
w.walk_expr(expr.typ)
|
|
w.walk_expr(expr.expr)
|
|
w.walk_field_init_array(expr.fields)
|
|
}
|
|
BasicLiteral {
|
|
w.add_variant_payload(sizeof(BasicLiteral))
|
|
w.add_string(expr.value)
|
|
}
|
|
CallExpr {
|
|
w.add_variant_payload(sizeof(CallExpr))
|
|
w.walk_expr(expr.lhs)
|
|
w.walk_expr_array(expr.args)
|
|
}
|
|
CallOrCastExpr {
|
|
w.add_variant_payload(sizeof(CallOrCastExpr))
|
|
w.walk_expr(expr.lhs)
|
|
w.walk_expr(expr.expr)
|
|
}
|
|
CastExpr {
|
|
w.add_variant_payload(sizeof(CastExpr))
|
|
w.walk_expr(expr.typ)
|
|
w.walk_expr(expr.expr)
|
|
}
|
|
ComptimeExpr {
|
|
w.add_variant_payload(sizeof(ComptimeExpr))
|
|
w.walk_expr(expr.expr)
|
|
}
|
|
EmptyExpr {}
|
|
FnLiteral {
|
|
w.add_variant_payload(sizeof(FnLiteral))
|
|
w.walk_type(Type(expr.typ))
|
|
w.walk_expr_array(expr.captured_vars)
|
|
w.walk_stmt_array(expr.stmts)
|
|
}
|
|
GenericArgOrIndexExpr {
|
|
w.add_variant_payload(sizeof(GenericArgOrIndexExpr))
|
|
w.walk_expr(expr.lhs)
|
|
w.walk_expr(expr.expr)
|
|
}
|
|
GenericArgs {
|
|
w.add_variant_payload(sizeof(GenericArgs))
|
|
w.walk_expr(expr.lhs)
|
|
w.walk_expr_array(expr.args)
|
|
}
|
|
Ident {
|
|
w.add_variant_payload(sizeof(Ident))
|
|
w.add_string(expr.name)
|
|
}
|
|
IfExpr {
|
|
w.add_variant_payload(sizeof(IfExpr))
|
|
w.walk_expr(expr.cond)
|
|
w.walk_expr(expr.else_expr)
|
|
w.walk_stmt_array(expr.stmts)
|
|
}
|
|
IfGuardExpr {
|
|
w.add_variant_payload(sizeof(IfGuardExpr))
|
|
w.walk_stmt(Stmt(expr.stmt))
|
|
}
|
|
IndexExpr {
|
|
w.add_variant_payload(sizeof(IndexExpr))
|
|
w.walk_expr(expr.lhs)
|
|
w.walk_expr(expr.expr)
|
|
}
|
|
InfixExpr {
|
|
w.add_variant_payload(sizeof(InfixExpr))
|
|
w.walk_expr(expr.lhs)
|
|
w.walk_expr(expr.rhs)
|
|
}
|
|
InitExpr {
|
|
w.add_variant_payload(sizeof(InitExpr))
|
|
w.walk_expr(expr.typ)
|
|
w.walk_field_init_array(expr.fields)
|
|
}
|
|
Keyword {}
|
|
KeywordOperator {
|
|
w.add_variant_payload(sizeof(KeywordOperator))
|
|
w.walk_expr_array(expr.exprs)
|
|
}
|
|
LambdaExpr {
|
|
w.add_variant_payload(sizeof(LambdaExpr))
|
|
w.walk_ident_array(expr.args)
|
|
w.walk_expr(expr.expr)
|
|
}
|
|
LifetimeExpr {
|
|
w.add_variant_payload(sizeof(LifetimeExpr))
|
|
w.add_string(expr.name)
|
|
}
|
|
LockExpr {
|
|
w.add_variant_payload(sizeof(LockExpr))
|
|
w.walk_expr_array(expr.lock_exprs)
|
|
w.walk_expr_array(expr.rlock_exprs)
|
|
w.walk_stmt_array(expr.stmts)
|
|
}
|
|
MapInitExpr {
|
|
w.add_variant_payload(sizeof(MapInitExpr))
|
|
w.walk_expr(expr.typ)
|
|
w.walk_expr_array(expr.keys)
|
|
w.walk_expr_array(expr.vals)
|
|
}
|
|
MatchExpr {
|
|
w.add_variant_payload(sizeof(MatchExpr))
|
|
w.walk_expr(expr.expr)
|
|
w.walk_match_branch_array(expr.branches)
|
|
}
|
|
ModifierExpr {
|
|
w.add_variant_payload(sizeof(ModifierExpr))
|
|
w.walk_expr(expr.expr)
|
|
}
|
|
OrExpr {
|
|
w.add_variant_payload(sizeof(OrExpr))
|
|
w.walk_expr(expr.expr)
|
|
w.walk_stmt_array(expr.stmts)
|
|
}
|
|
ParenExpr {
|
|
w.add_variant_payload(sizeof(ParenExpr))
|
|
w.walk_expr(expr.expr)
|
|
}
|
|
PostfixExpr {
|
|
w.add_variant_payload(sizeof(PostfixExpr))
|
|
w.walk_expr(expr.expr)
|
|
}
|
|
PrefixExpr {
|
|
w.add_variant_payload(sizeof(PrefixExpr))
|
|
w.walk_expr(expr.expr)
|
|
}
|
|
RangeExpr {
|
|
w.add_variant_payload(sizeof(RangeExpr))
|
|
w.walk_expr(expr.start)
|
|
w.walk_expr(expr.end)
|
|
}
|
|
SelectExpr {
|
|
w.add_variant_payload(sizeof(SelectExpr))
|
|
w.walk_stmt(expr.stmt)
|
|
w.walk_stmt_array(expr.stmts)
|
|
w.walk_expr(expr.next)
|
|
}
|
|
SelectorExpr {
|
|
w.add_variant_payload(sizeof(SelectorExpr))
|
|
w.walk_expr(expr.lhs)
|
|
w.walk_ident(expr.rhs)
|
|
}
|
|
SqlExpr {
|
|
w.add_variant_payload(sizeof(SqlExpr))
|
|
w.walk_expr(expr.expr)
|
|
w.add_string(expr.table_name)
|
|
}
|
|
StringInterLiteral {
|
|
w.add_variant_payload(sizeof(StringInterLiteral))
|
|
w.walk_string_array(expr.values)
|
|
w.walk_string_inter_array(expr.inters)
|
|
}
|
|
StringLiteral {
|
|
w.add_variant_payload(sizeof(StringLiteral))
|
|
w.add_string(expr.value)
|
|
}
|
|
Tuple {
|
|
w.add_variant_payload(sizeof(Tuple))
|
|
w.walk_expr_array(expr.exprs)
|
|
}
|
|
UnsafeExpr {
|
|
w.add_variant_payload(sizeof(UnsafeExpr))
|
|
w.walk_stmt_array(expr.stmts)
|
|
}
|
|
FieldInit, Type {}
|
|
}
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_type(typ Type) {
|
|
w.stats.type_nodes++
|
|
// Sumtype slot is counted by parent; add only heap variant payload.
|
|
match typ {
|
|
AnonStructType {
|
|
w.add_variant_payload(sizeof(AnonStructType))
|
|
w.walk_expr_array(typ.generic_params)
|
|
w.walk_expr_array(typ.embedded)
|
|
w.walk_field_decl_array(typ.fields)
|
|
}
|
|
ArrayFixedType {
|
|
w.add_variant_payload(sizeof(ArrayFixedType))
|
|
w.walk_expr(typ.len)
|
|
w.walk_expr(typ.elem_type)
|
|
}
|
|
ArrayType {
|
|
w.add_variant_payload(sizeof(ArrayType))
|
|
w.walk_expr(typ.elem_type)
|
|
}
|
|
ChannelType {
|
|
w.add_variant_payload(sizeof(ChannelType))
|
|
w.walk_expr(typ.cap)
|
|
w.walk_expr(typ.elem_type)
|
|
}
|
|
FnType {
|
|
w.add_variant_payload(sizeof(FnType))
|
|
w.walk_expr_array(typ.generic_params)
|
|
w.walk_parameter_array(typ.params)
|
|
w.walk_expr(typ.return_type)
|
|
}
|
|
GenericType {
|
|
w.add_variant_payload(sizeof(GenericType))
|
|
w.walk_expr(typ.name)
|
|
w.walk_expr_array(typ.params)
|
|
}
|
|
MapType {
|
|
w.add_variant_payload(sizeof(MapType))
|
|
w.walk_expr(typ.key_type)
|
|
w.walk_expr(typ.value_type)
|
|
}
|
|
NilType {}
|
|
NoneType {}
|
|
OptionType {
|
|
w.add_variant_payload(sizeof(OptionType))
|
|
w.walk_expr(typ.base_type)
|
|
}
|
|
PointerType {
|
|
w.add_variant_payload(sizeof(PointerType))
|
|
w.walk_expr(typ.base_type)
|
|
w.add_string(typ.lifetime)
|
|
}
|
|
ResultType {
|
|
w.add_variant_payload(sizeof(ResultType))
|
|
w.walk_expr(typ.base_type)
|
|
}
|
|
ThreadType {
|
|
w.add_variant_payload(sizeof(ThreadType))
|
|
w.walk_expr(typ.elem_type)
|
|
}
|
|
TupleType {
|
|
w.add_variant_payload(sizeof(TupleType))
|
|
w.walk_expr_array(typ.types)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Plain-struct walkers: storage is counted at parent (inline field) or array
|
|
// level. Only scan for dynamic content here.
|
|
|
|
fn (mut w LegacyAstWalker) walk_attribute(attr Attribute) {
|
|
w.stats.aux_nodes++
|
|
w.add_string(attr.name)
|
|
w.walk_expr(attr.value)
|
|
w.walk_expr(attr.comptime_cond)
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_field_init(field FieldInit) {
|
|
w.stats.aux_nodes++
|
|
w.add_string(field.name)
|
|
w.walk_expr(field.value)
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_field_decl(field FieldDecl) {
|
|
w.stats.aux_nodes++
|
|
w.add_string(field.name)
|
|
w.walk_expr(field.typ)
|
|
w.walk_expr(field.value)
|
|
w.walk_attribute_array(field.attributes)
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_parameter(param Parameter) {
|
|
w.stats.aux_nodes++
|
|
w.add_string(param.name)
|
|
w.walk_expr(param.typ)
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_match_branch(branch MatchBranch) {
|
|
w.stats.aux_nodes++
|
|
w.walk_expr_array(branch.cond)
|
|
w.walk_stmt_array(branch.stmts)
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_string_inter(inter StringInter) {
|
|
w.stats.aux_nodes++
|
|
w.walk_expr(inter.expr)
|
|
w.walk_expr(inter.format_expr)
|
|
w.add_string(inter.resolved_fmt)
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_ident(ident Ident) {
|
|
w.walk_expr(Expr(ident))
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_expr_array(items []Expr) {
|
|
w.add_array_storage(sizeof(Expr), items.len)
|
|
for item in items {
|
|
w.walk_expr(item)
|
|
}
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_stmt_array(items []Stmt) {
|
|
w.add_array_storage(sizeof(Stmt), items.len)
|
|
for item in items {
|
|
w.walk_stmt(item)
|
|
}
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_attribute_array(items []Attribute) {
|
|
w.add_array_storage(sizeof(Attribute), items.len)
|
|
for item in items {
|
|
w.walk_attribute(item)
|
|
}
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_field_init_array(items []FieldInit) {
|
|
w.add_array_storage(sizeof(FieldInit), items.len)
|
|
for item in items {
|
|
w.walk_field_init(item)
|
|
}
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_field_decl_array(items []FieldDecl) {
|
|
w.add_array_storage(sizeof(FieldDecl), items.len)
|
|
for item in items {
|
|
w.walk_field_decl(item)
|
|
}
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_parameter_array(items []Parameter) {
|
|
w.add_array_storage(sizeof(Parameter), items.len)
|
|
for item in items {
|
|
w.walk_parameter(item)
|
|
}
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_match_branch_array(items []MatchBranch) {
|
|
w.add_array_storage(sizeof(MatchBranch), items.len)
|
|
for item in items {
|
|
w.walk_match_branch(item)
|
|
}
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_string_inter_array(items []StringInter) {
|
|
w.add_array_storage(sizeof(StringInter), items.len)
|
|
for item in items {
|
|
w.walk_string_inter(item)
|
|
}
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_ident_array(items []Ident) {
|
|
w.add_array_storage(sizeof(Ident), items.len)
|
|
for item in items {
|
|
w.walk_ident(item)
|
|
}
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_import_array(items []ImportStmt) {
|
|
w.add_array_storage(sizeof(ImportStmt), items.len)
|
|
for item in items {
|
|
w.walk_stmt(Stmt(item))
|
|
}
|
|
}
|
|
|
|
fn (mut w LegacyAstWalker) walk_string_array(items []string) {
|
|
w.add_array_storage(sizeof(string), items.len)
|
|
for item in items {
|
|
w.add_string(item)
|
|
}
|
|
}
|