tiny.chant.ParserNodeStorage
Defined in tiny.chant.
The storage divides one buffer the caller supplies into slots for up to two expression nodes, one statement node and two type nodes per admitted token, along with the source token and cached inferred type of each node.
API (36)
Actions
Public operations.
activateadmitcreateExprcreateExprAtcreateStmtcreateTypedeinitexpressionOrigininferredTypeinitsetInferredTypestatustypeOrigin
Types and contracts
Public types and contracts.
Fields and members
Public fields and members.
admitted_tokens_lencapacityexpression_countexpression_originsexpression_typesexpressionsphasestatement_countstatementsstoragetype_chargestype_counttype_originstypes
Source
Source: lib/chant/src/parse/state/storage.zig:43
zig
/// The storage divides one buffer the caller supplies into slots for up to two/// expression nodes, one statement node and two type nodes per admitted token,/// along with the source token and cached inferred type of each node. A caller/// allocates one aligned buffer of `Capacity.storage_bytes`, hands it to/// `init`, activates it, and passes it to `parse`, and every node of the/// resulting syntax tree (the parsed translation unit) lives here. The buffer/// belongs to the caller, and `deinit` hands it back. Each parse starts by/// clearing the node counts, so a second parse into the same storage/// invalidates every node pointer from the first, and `deinit` invalidates them/// as well. A request for a node when its slots are full returns/// `error.NodeCapacityExceeded` before anything is written. The order of use is/// `init`, `activate`, any number of parses, then `deinit`.pub const Storage = struct { phase: alloc_phase.capacity.Phase, capacity: capacity_mod.Capacity, storage: @This().Storage, expressions: []ast.Expr, statements: []ast.Stmt, types: []ast.Type, type_origins: []origin.TypeOrigin, expression_origins: []usize, expression_types: []?*const ast.Type, type_charges: []u8, admitted_tokens_len: usize, expression_count: usize, statement_count: usize, type_count: usize, pub const storage_alignment: usize = capacity_mod.storage_alignment; pub const Storage = []align(storage_alignment) u8; pub const Limits: type = capacity_mod.Limits; pub const Capacity: type = capacity_mod.Capacity; pub const Exhaustion: type = NodeExhaustion; pub const InitError = capacity_mod.DeriveError || error{StorageTooShort}; pub const work_limits: alloc_phase.capacity.WorkLimits = .{ .transition_steps_max = std.math.maxInt(usize), .cleanup_steps_per_call_max = 0, .cleanup_calls_at_capacity_max = 0, }; pub const claim: alloc_phase.capacity.Declaration = .{ .source = .{ .id = "chant.parser_node_storage", .kind = .phase_static, .limit_source = .caller, .storage = .{ .covered = &.{ .{ .id = "at_most_two_expression_nodes_per_lexed_token", .lifetime = .steady, .detail = "at most two expression nodes per lexed token", }, .{ .id = "at_most_one_statement_node_per_lexed_token", .lifetime = .steady, .detail = "at most one statement node per lexed token", }, .{ .id = "at_most_two_dynamic_type_nodes_per_lexed_token", .lifetime = .steady, .detail = "at most two dynamic type nodes per lexed token", }, .{ .id = "node_origin_inferred_type_cache_and_two_charge_toke_1bf7f8a5a1ad", .lifetime = .steady, .detail = "node origin, inferred-type cache, and two-charge token ledger metadata", }, }, .excluded = &.{ "list and map backing bytes plus their container metadata", "object bindings, scopes, and decoded token-text bytes", "caller-owned lexed tokens and their borrowed source and file bytes", "allocator metadata for caller acquisition and excluded parser allocations", "Choir context and lowered IR storage", }, }, .capacity = .{ .inputs = &.{ alloc_phase.capacity.bindInput(capacity_mod.Limits, "tokens", "tokens"), }, .type_selectors = &.{ alloc_phase.capacity.bindType(ast.Expr, "expr"), alloc_phase.capacity.bindType(ast.Stmt, "stmt"), alloc_phase.capacity.bindType(ast.Type, "type"), alloc_phase.capacity.bindType(origin.TypeOrigin, "typeorigin"), alloc_phase.capacity.bindType(usize, "usize"), alloc_phase.capacity.bindType(?*const ast.Type, "type_type"), alloc_phase.capacity.bindType(u8, "u8"), }, .nodes = &.{ .{ .input = 0 }, .{ .scale = .{ .node = 0, .coefficient = .{ .literal = 2 } } }, .{ .scale = .{ .node = 1, .coefficient = .{ .size_of_concrete_type = 0 } } }, .{ .scale = .{ .node = 0, .coefficient = .{ .size_of_concrete_type = 1 } } }, .{ .scale = .{ .node = 0, .coefficient = .{ .literal = 2 } } }, .{ .scale = .{ .node = 4, .coefficient = .{ .size_of_concrete_type = 2 } } }, .{ .scale = .{ .node = 0, .coefficient = .{ .literal = 2 } } }, .{ .scale = .{ .node = 6, .coefficient = .{ .size_of_concrete_type = 3 } } }, .{ .scale = .{ .node = 0, .coefficient = .{ .literal = 2 } } }, .{ .scale = .{ .node = 8, .coefficient = .{ .size_of_concrete_type = 4 } } }, .{ .scale = .{ .node = 0, .coefficient = .{ .literal = 2 } } }, .{ .scale = .{ .node = 10, .coefficient = .{ .size_of_concrete_type = 5 } } }, .{ .scale = .{ .node = 0, .coefficient = .{ .size_of_concrete_type = 6 } } }, .{ .constant = 0 }, .{ .alignment = .{ .node = 13, .alignment = .{ .concrete_type = 0 } } }, .{ .add = .{ .left = 14, .right = 2 } }, .{ .alignment = .{ .node = 15, .alignment = .{ .concrete_type = 1 } } }, .{ .add = .{ .left = 16, .right = 3 } }, .{ .alignment = .{ .node = 17, .alignment = .{ .concrete_type = 2 } } }, .{ .add = .{ .left = 18, .right = 5 } }, .{ .alignment = .{ .node = 19, .alignment = .{ .concrete_type = 3 } } }, .{ .add = .{ .left = 20, .right = 7 } }, .{ .alignment = .{ .node = 21, .alignment = .{ .concrete_type = 4 } } }, .{ .add = .{ .left = 22, .right = 9 } }, .{ .alignment = .{ .node = 23, .alignment = .{ .concrete_type = 5 } } }, .{ .add = .{ .left = 24, .right = 11 } }, .{ .alignment = .{ .node = 25, .alignment = .{ .concrete_type = 6 } } }, .{ .add = .{ .left = 26, .right = 12 } }, .{ .alignment = .{ .node = 27, .alignment = .{ .literal = 1 } } }, }, .assertions = &.{.{ .scope = .closure_total, .measure = .retained, .relation = .exact, .expression = 28, }}, }, .overload = .{ .kind = .reject_before_mutation, .detail = "short regions and mismatched or over-limit token surveys reject before node placement", }, .risks = .{ .transitive = .{ .status = .witnessed, .detail = "all parser node creation dispatches exhaustively into the three typed slices", }, .foreign = .{ .status = .excluded, .detail = "node placement reads borrowed memory and crosses no foreign boundary", }, }, .work = .{ .equation = "each node placement is one checked slice write bounded by lexed tokens" }, .dependencies = &.{"chant.lexed_token_storage"}, .obligations = &.{ .{ .key = "chant_parser_nodes_capacity", .role = .capacity_model }, .{ .key = "chant_parser_nodes_boundary", .role = .overload }, .{ .key = "chant_parser_nodes_oom", .role = .custom }, .{ .key = "chant_parser_nodes_identity", .role = .overload }, .{ .key = "chant_parser_nodes_prepublication", .role = .overload }, .{ .key = "chant_parser_nodes_sealed_transitive_risk", .role = .transitive_risk }, .{ .key = "chant_parser_nodes_sealed_foreign_risk", .role = .foreign_risk }, .{ .key = "chant_parser_nodes_sealed_work_bound", .role = .work_bound }, .{ .key = "chant_parser_nodes_differential", .role = .transitive_risk }, .{ .key = "chant_parser_nodes_adversarial", .role = .transitive_risk }, .{ .key = "chant_parser_type_charge", .role = .transitive_risk }, .{ .key = "chant_parser_type_cache", .role = .transitive_risk }, }, }, .bindings = .{ .owner = @This(), .seal = .{ .family = alloc_phase.capacity.selector(@This().activate), .premise = .{ .class = .checked_semantic_fact, .authority = .checker, }, }, .teardown = .{ .family = alloc_phase.capacity.selector(@This().deinit), .premise = .{ .class = .checked_semantic_fact, .authority = .checker, }, }, }, }; pub const Status = struct { expressions: usize, statements: usize, types: usize, }; pub fn init( storage: @This().Storage, limits: capacity_mod.Limits, ) InitError!@This() { const capacity = try capacity_mod.Capacity.derive(limits); if (storage.len < capacity.storage_bytes) return error.StorageTooShort; const owned = storage[0..capacity.storage_bytes]; return .{ .phase = .initialization, .capacity = capacity, .storage = owned, .expressions = typedSlice( ast.Expr, owned, capacity.expressions_offset, capacity.expressions, ), .statements = typedSlice( ast.Stmt, owned, capacity.statements_offset, capacity.statements, ), .types = typedSlice( ast.Type, owned, capacity.types_offset, capacity.types, ), .type_origins = typedSlice( origin.TypeOrigin, owned, capacity.type_origins_offset, capacity.types, ), .expression_origins = typedSlice( usize, owned, capacity.expression_origins_offset, capacity.expressions, ), .expression_types = typedSlice( ?*const ast.Type, owned, capacity.expression_types_offset, capacity.expressions, ), .type_charges = typedSlice( u8, owned, capacity.type_charges_offset, capacity.limits.tokens, ), .admitted_tokens_len = 0, .expression_count = 0, .statement_count = 0, .type_count = 0, }; } pub fn activate(self: *@This()) void { std.debug.assert(self.phase == .initialization); self.assertStorage(); self.phase = .steady; } pub fn admit( self: *@This(), token_survey: Survey, tokens: []const Token, ) NodeExhaustion!void { std.debug.assert(self.phase == .steady); try requireIdentity(token_survey, tokens); if (token_survey.limits.tokens > self.capacity.limits.tokens) { return error.NodeCapacityExceeded; } @memset(self.type_charges, 0); self.admitted_tokens_len = tokens.len; self.expression_count = 0; self.statement_count = 0; self.type_count = 0; } pub fn createExpr(self: *@This(), value: ast.Expr) PlacementExhaustion!*ast.Expr { return self.createExprAt(value, null); } pub fn createExprAt( self: *@This(), value: ast.Expr, token_index: ?usize, ) PlacementExhaustion!*ast.Expr { std.debug.assert(self.phase == .steady); if (self.expression_count >= self.expressions.len) { return error.NodeCapacityExceeded; } if (token_index) |index| { if (index >= self.admitted_tokens_len) { return error.NodeCapacityExceeded; } } const result = &self.expressions[self.expression_count]; self.expression_origins[self.expression_count] = token_index orelse origin.no_token; self.expression_types[self.expression_count] = null; self.expression_count += 1; result.* = value; return result; } pub fn createStmt(self: *@This(), value: ast.Stmt) PlacementExhaustion!*ast.Stmt { std.debug.assert(self.phase == .steady); if (self.statement_count >= self.statements.len) { return error.NodeCapacityExceeded; } const result = &self.statements[self.statement_count]; self.statement_count += 1; result.* = value; return result; } pub fn createType( self: *@This(), value: ast.Type, placement: origin.TypePlacement, ) PlacementExhaustion!*ast.Type { std.debug.assert(self.phase == .steady); if (self.type_count >= self.types.len) return error.NodeCapacityExceeded; if (!self.validTypePlacement(placement)) { return error.NodeCapacityExceeded; } if (self.type_charges[placement.charged] >= 2) { return error.NodeCapacityExceeded; } const result = &self.types[self.type_count]; self.type_charges[placement.charged] += 1; self.type_origins[self.type_count] = placement.origin; self.type_count += 1; result.* = value; return result; } pub fn typeOrigin( self: *const @This(), value: *const ast.Type, ) ?origin.TypeOrigin { const index = pointerIndex(ast.Type, self.types, value) orelse return null; std.debug.assert(index < self.type_count); return self.type_origins[index]; } pub fn expressionOrigin( self: *const @This(), value: *const ast.Expr, ) usize { const index = pointerIndex(ast.Expr, self.expressions, value) orelse unreachable; std.debug.assert(index < self.expression_count); const token_index = self.expression_origins[index]; std.debug.assert(token_index != origin.no_token); return token_index; } pub fn inferredType( self: *const @This(), value: *const ast.Expr, ) ?*const ast.Type { const index = pointerIndex(ast.Expr, self.expressions, value) orelse unreachable; std.debug.assert(index < self.expression_count); return self.expression_types[index]; } pub fn setInferredType( self: *@This(), value: *const ast.Expr, inferred: *const ast.Type, ) void { const index = pointerIndex(ast.Expr, self.expressions, value) orelse unreachable; std.debug.assert(index < self.expression_count); std.debug.assert(self.expression_types[index] == null); self.expression_types[index] = inferred; } pub fn status(self: *const @This()) Status { std.debug.assert(self.phase == .steady); return .{ .expressions = self.expression_count, .statements = self.statement_count, .types = self.type_count, }; } pub fn deinit(self: *@This()) @This().Storage { std.debug.assert(self.phase == .steady); self.phase = .teardown; const storage = self.storage; self.* = undefined; return storage; } fn assertStorage(self: *const @This()) void { const expected = capacity_mod.Capacity.derive( self.capacity.limits, ) catch unreachable; std.debug.assert(std.meta.eql(expected, self.capacity)); std.debug.assert(self.storage.len == self.capacity.storage_bytes); std.debug.assert(self.expressions.len == self.capacity.expressions); std.debug.assert(self.statements.len == self.capacity.statements); std.debug.assert(self.types.len == self.capacity.types); std.debug.assert(self.type_origins.len == self.capacity.types); std.debug.assert(self.expression_origins.len == self.capacity.expressions); std.debug.assert(self.expression_types.len == self.capacity.expressions); std.debug.assert(self.type_charges.len == self.capacity.limits.tokens); } fn validTypePlacement( self: *const @This(), placement: origin.TypePlacement, ) bool { if (placement.charged >= self.admitted_tokens_len) return false; if (placement.origin.primary >= self.admitted_tokens_len) return false; if (placement.origin.secondary != origin.no_token and placement.origin.secondary >= self.admitted_tokens_len) { return false; } return placement.charged == placement.origin.primary or placement.charged == placement.origin.secondary; }};Source: lib/chant/src/root.zig:79
zig
pub const ParserNodeStorage = parse.Storage;Also reachable as
parse.Storage, parse.state.Storage.
Audit
| Definitions | 23 |
|---|---|
| Public names | 69 |
| Members | 17 |
| Version | 26.7.0 |
| Revision | daab053ee433 |