Merge compiler into Chunk.zig
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details

This commit is contained in:
kayomn 2023-11-06 20:53:24 +00:00
parent 83680607b7
commit eb9552c390
2 changed files with 406 additions and 415 deletions

View File

@ -1,6 +1,8 @@
const app = @import("../app.zig"); const Expr = @import("./Expr.zig");
const Compiler = @import("./Compiler.zig"); const Stmt = @import("./Stmt.zig");
const app = @import("../app.zig");
const coral = @import("coral"); const coral = @import("coral");
@ -25,6 +27,408 @@ const Builtin = enum {
vec3, vec3,
}; };
const Compiler = struct {
chunk: *Self,
env: *kym.RuntimeEnv,
fn compile_argument(self: *const Compiler, environment: *const tree.Environment, initial_argument: ?*const Expr) kym.RuntimeError!u8 {
// TODO: Exceeding 255 arguments will make the VM crash.
var maybe_argument = initial_argument;
var argument_count = @as(u8, 0);
while (maybe_argument) |argument| {
try self.compile_expression(environment, argument, null);
maybe_argument = argument.next;
argument_count += 1;
}
return argument_count;
}
fn compile_expression(self: *const Compiler, environment: *const tree.Environment, expression: *const Expr, name: ?[]const coral.io.Byte) kym.RuntimeError!void {
const number_format = coral.utf8.DecimalFormat{
.delimiter = "_",
.positive_prefix = .none,
};
switch (expression.kind) {
.nil_literal => try self.chunk.write(expression.line, .push_nil),
.true_literal => try self.chunk.write(expression.line, .push_true),
.false_literal => try self.chunk.write(expression.line, .push_false),
.number_literal => |literal| {
for (literal) |codepoint| {
if (codepoint == '.') {
return self.chunk.write(expression.line, .{
.push_const = try self.declare_float(number_format.parse(literal, kym.Float) orelse unreachable),
});
}
}
try self.chunk.write(expression.line, .{
.push_const = try self.declare_fixed(number_format.parse(literal, kym.Fixed) orelse unreachable),
});
},
.string_literal => |literal| {
try self.chunk.write(expression.line, .{.push_const = try self.declare_string(literal)});
},
.symbol_literal => |literal| {
try self.chunk.write(expression.line, .{.push_const = try self.declare_symbol(literal)});
},
.table_construct => |table_construct| {
var table_entry = table_construct.entry;
var field_count = @as(u32, 0);
while (table_entry) |entry| : (table_entry = entry.next) {
try self.compile_expression(environment, entry, null);
if (entry.kind != .key_value) {
try self.chunk.write(expression.line, .push_top);
}
field_count += 1;
}
try self.chunk.write(expression.line, .{.push_table = field_count});
},
.key_value => |key_value| {
try self.compile_expression(environment, key_value.value, null);
try self.compile_expression(environment, key_value.key, null);
},
.lambda_construct => |lambda_construct| {
var chunk = try Self.make(self.env, name orelse "<lambda>", lambda_construct.environment);
errdefer chunk.free(self.env);
if (lambda_construct.environment.capture_count == 0) {
try self.chunk.write(expression.line, .{.push_const = try self.declare_chunk(chunk)});
} else {
const lambda_captures = lambda_construct.environment.get_captures();
var index = lambda_captures.len;
while (index != 0) {
index -= 1;
try self.chunk.write(expression.line, switch (lambda_captures[index]) {
.declaration_index => |declaration_index| .{.push_local = declaration_index},
.capture_index => |capture_index| .{.push_binding = capture_index},
});
}
try self.chunk.write(expression.line, .{.push_const = try self.declare_chunk(chunk)});
try self.chunk.write(expression.line, .{.bind = lambda_construct.environment.capture_count});
}
},
.binary_op => |binary_op| {
try self.compile_expression(environment, binary_op.lhs_operand, null);
try self.compile_expression(environment, binary_op.rhs_operand, null);
try self.chunk.write(expression.line, switch (binary_op.operation) {
.addition => .add,
.subtraction => .sub,
.multiplication => .mul,
.divsion => .div,
.greater_equals_comparison => .cge,
.greater_than_comparison => .cgt,
.equals_comparison => .eql,
.less_than_comparison => .clt,
.less_equals_comparison => .cle,
});
},
.unary_op => |unary_op| {
try self.compile_expression(environment, unary_op.operand, null);
try self.chunk.write(expression.line, switch (unary_op.operation) {
.boolean_negation => .not,
.numeric_negation => .neg,
});
},
.invoke => |invoke| {
const argument_count = try self.compile_argument(environment, invoke.argument);
try self.compile_expression(environment, invoke.object, null);
try self.chunk.write(expression.line, .{.call = argument_count});
},
.group => |group| try self.compile_expression(environment, group, null),
.import_builtin => try self.chunk.write(expression.line, .{.push_builtin = .import}),
.print_builtin => try self.chunk.write(expression.line, .{.push_builtin = .print}),
.vec2_builtin => try self.chunk.write(expression.line, .{.push_builtin = .vec2}),
.vec3_builtin => try self.chunk.write(expression.line, .{.push_builtin = .vec3}),
.declaration_get => |declaration_get| {
if (get_local_index(environment, declaration_get.declaration)) |index| {
return self.chunk.write(expression.line, .{.push_local = index});
}
if (try self.get_binding_index(environment, declaration_get.declaration)) |index| {
try self.chunk.write(expression.line, .{.push_binding = index});
if (is_declaration_boxed(declaration_get.declaration)) {
try self.chunk.write(expression.line, .get_box);
}
return;
}
return self.env.raise(error.IllegalState, "local out of scope", .{});
},
.declaration_set => |declaration_set| {
if (get_local_index(environment, declaration_set.declaration)) |index| {
try self.compile_expression(environment, declaration_set.assign, null);
return self.chunk.write(expression.line, .{.set_local = index});
}
if (try self.get_binding_index(environment, declaration_set.declaration)) |index| {
try self.chunk.write(expression.line, .{.push_binding = index});
try self.compile_expression(environment, declaration_set.assign, null);
if (is_declaration_boxed(declaration_set.declaration)) {
try self.chunk.write(expression.line, .set_box);
}
return;
}
return self.env.raise(error.IllegalState, "local out of scope", .{});
},
.field_get => |field_get| {
try self.compile_expression(environment, field_get.object, null);
try self.chunk.write(expression.line, .{.push_const = try self.declare_symbol(field_get.identifier)});
try self.chunk.write(expression.line, .get_dynamic);
},
.field_set => |field_set| {
try self.compile_expression(environment, field_set.object, null);
try self.chunk.write(expression.line, .{.push_const = try self.declare_symbol(field_set.identifier)});
try self.compile_expression(environment, field_set.assign, null);
try self.chunk.write(expression.line, .set_dynamic);
},
.subscript_get => |subscript_get| {
try self.compile_expression(environment, subscript_get.object, null);
try self.compile_expression(environment, subscript_get.index, null);
try self.chunk.write(expression.line, .get_dynamic);
},
.subscript_set => |subscript_set| {
try self.compile_expression(environment, subscript_set.object, null);
try self.compile_expression(environment, subscript_set.index, null);
try self.compile_expression(environment, subscript_set.assign, null);
try self.chunk.write(expression.line, .set_dynamic);
},
}
}
pub fn compile_environment(self: *const Compiler, environment: *const tree.Environment) kym.RuntimeError!void {
if (environment.statement) |statement| {
const last_statement = try self.compile_statement(environment, statement);
if (last_statement.kind != .@"return") {
try self.chunk.write(last_statement.line, .push_nil);
}
}
}
fn compile_statement(self: *const Compiler, environment: *const tree.Environment, initial_statement: *const Stmt) kym.RuntimeError!*const Stmt {
var current_statement = initial_statement;
while (true) {
switch (current_statement.kind) {
.@"return" => |@"return"| {
if (@"return".returned_expression) |expression| {
try self.compile_expression(environment, expression, null);
} else {
try self.chunk.write(current_statement.line, .push_nil);
}
// TODO: Omit ret calls at ends of chunk.
try self.chunk.write(current_statement.line, .ret);
},
.@"while" => |@"while"| {
try self.compile_expression(environment, @"while".loop_expression, null);
try self.chunk.write(current_statement.line, .{.jf = 0});
const origin_index = @as(u32, @intCast(self.chunk.opcodes.values.len - 1));
_ = try self.compile_statement(environment, @"while".loop);
self.chunk.opcodes.values[origin_index].jf = @intCast(self.chunk.opcodes.values.len - 1);
try self.compile_expression(environment, @"while".loop_expression, null);
try self.chunk.write(current_statement.line, .{.jt = origin_index});
},
.@"if" => |@"if"| {
try self.compile_expression(environment, @"if".then_expression, null);
try self.chunk.write(current_statement.line, .{.jf = 0});
const origin_index = @as(u32, @intCast(self.chunk.opcodes.values.len - 1));
_ = try self.compile_statement(environment, @"if".@"then");
self.chunk.opcodes.values[origin_index].jf = @intCast(self.chunk.opcodes.values.len - 1);
if (@"if".@"else") |@"else"| {
_ = try self.compile_statement(environment, @"else");
}
},
.declare => |declare| {
try self.compile_expression(environment, declare.initial_expression, declare.declaration.identifier);
if (is_declaration_boxed(declare.declaration)) {
try self.chunk.write(current_statement.line, .push_boxed);
}
},
.top_expression => |top_expression| {
try self.compile_expression(environment, top_expression, null);
if (top_expression.kind == .invoke) {
try self.chunk.write(current_statement.line, .pop);
}
},
}
current_statement = current_statement.next orelse return current_statement;
}
}
const constants_max = @as(usize, coral.math.max_int(@typeInfo(u16).Int));
fn declare_chunk(self: *const Compiler, chunk: Self) kym.RuntimeError!u16 {
if (self.chunk.constants.values.len == coral.math.max_int(@typeInfo(u16).Int)) {
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
.max = @as(usize, coral.math.max_int(@typeInfo(u16).Int)),
});
}
const constant = try self.env.new_dynamic(coral.io.bytes_of(&chunk), typeinfo);
errdefer self.env.discard(constant);
try self.chunk.constants.push_one(constant);
return @intCast(self.chunk.constants.values.len - 1);
}
fn declare_fixed(self: *const Compiler, fixed: kym.Fixed) kym.RuntimeError!u16 {
if (self.chunk.constants.values.len == constants_max) {
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
.max = constants_max,
});
}
const constant = try self.env.new_fixed(fixed);
errdefer self.env.discard(constant);
try self.chunk.constants.push_one(constant);
return @intCast(self.chunk.constants.values.len - 1);
}
fn declare_float(self: *const Compiler, float: kym.Float) kym.RuntimeError!u16 {
if (self.chunk.constants.values.len == constants_max) {
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
.max = constants_max,
});
}
const constant = try self.env.new_float(float);
errdefer self.env.discard(constant);
try self.chunk.constants.push_one(constant);
return @intCast(self.chunk.constants.values.len - 1);
}
fn declare_string(self: *const Compiler, string: []const coral.io.Byte) kym.RuntimeError!u16 {
if (self.chunk.constants.values.len == constants_max) {
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
.max = constants_max,
});
}
const constant = try self.env.new_string(string);
errdefer self.env.discard(constant);
try self.chunk.constants.push_one(constant);
return @intCast(self.chunk.constants.values.len - 1);
}
fn declare_symbol(self: *const Compiler, symbol: []const coral.io.Byte) kym.RuntimeError!u16 {
if (self.chunk.constants.values.len == constants_max) {
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
.max = constants_max,
});
}
const constant = try self.env.new_symbol(symbol);
errdefer self.env.discard(constant);
try self.chunk.constants.push_one(constant);
return @intCast(self.chunk.constants.values.len - 1);
}
fn get_binding_index(self: *const Compiler, environment: *const tree.Environment, declaration: *const tree.Declaration) kym.RuntimeError!?u8 {
var binding_index = @as(u8, 0);
while (binding_index < environment.capture_count) : (binding_index += 1) {
var capture = &environment.captures[binding_index];
var target_environment = environment.enclosing orelse return null;
while (capture.* == .capture_index) {
capture = &target_environment.captures[capture.capture_index];
target_environment = target_environment.enclosing orelse return null;
}
try kym.assert(self.env, capture.* == .declaration_index);
if (&target_environment.declarations[capture.declaration_index] == declaration) {
return binding_index;
}
}
return null;
}
fn get_local_index(environment: *const tree.Environment, declaration: *const tree.Declaration) ?u8 {
var remaining = environment.declaration_count;
while (remaining != 0) {
remaining -= 1;
if (&environment.declarations[remaining] == declaration) {
return remaining;
}
}
return null;
}
fn is_declaration_boxed(declaration: *const tree.Declaration) bool {
return declaration.is.captured and !declaration.is.readonly;
}
};
const ConstList = coral.list.Stack(*kym.RuntimeRef); const ConstList = coral.list.Stack(*kym.RuntimeRef);
const LineList = coral.list.Stack(u32); const LineList = coral.list.Stack(u32);

View File

@ -1,413 +0,0 @@
const Chunk = @import("./Chunk.zig");
const Expr = @import("./Expr.zig");
const Stmt = @import("./Stmt.zig");
const coral = @import("coral");
const kym = @import("../kym.zig");
const tree = @import("./tree.zig");
chunk: *Chunk,
env: *kym.RuntimeEnv,
const Self = @This();
fn compile_argument(self: Self, environment: *const tree.Environment, initial_argument: ?*const Expr) kym.RuntimeError!u8 {
// TODO: Exceeding 255 arguments will make the VM crash.
var maybe_argument = initial_argument;
var argument_count = @as(u8, 0);
while (maybe_argument) |argument| {
try self.compile_expression(environment, argument, null);
maybe_argument = argument.next;
argument_count += 1;
}
return argument_count;
}
fn compile_expression(self: Self, environment: *const tree.Environment, expression: *const Expr, name: ?[]const coral.io.Byte) kym.RuntimeError!void {
const number_format = coral.utf8.DecimalFormat{
.delimiter = "_",
.positive_prefix = .none,
};
switch (expression.kind) {
.nil_literal => try self.chunk.write(expression.line, .push_nil),
.true_literal => try self.chunk.write(expression.line, .push_true),
.false_literal => try self.chunk.write(expression.line, .push_false),
.number_literal => |literal| {
for (literal) |codepoint| {
if (codepoint == '.') {
return self.chunk.write(expression.line, .{
.push_const = try self.declare_float(number_format.parse(literal, kym.Float) orelse unreachable),
});
}
}
try self.chunk.write(expression.line, .{
.push_const = try self.declare_fixed(number_format.parse(literal, kym.Fixed) orelse unreachable),
});
},
.string_literal => |literal| {
try self.chunk.write(expression.line, .{.push_const = try self.declare_string(literal)});
},
.symbol_literal => |literal| {
try self.chunk.write(expression.line, .{.push_const = try self.declare_symbol(literal)});
},
.table_construct => |table_construct| {
var table_entry = table_construct.entry;
var field_count = @as(u32, 0);
while (table_entry) |entry| : (table_entry = entry.next) {
try self.compile_expression(environment, entry, null);
if (entry.kind != .key_value) {
try self.chunk.write(expression.line, .push_top);
}
field_count += 1;
}
try self.chunk.write(expression.line, .{.push_table = field_count});
},
.key_value => |key_value| {
try self.compile_expression(environment, key_value.value, null);
try self.compile_expression(environment, key_value.key, null);
},
.lambda_construct => |lambda_construct| {
var chunk = try Chunk.make(self.env, name orelse "<lambda>", lambda_construct.environment);
errdefer chunk.free(self.env);
if (lambda_construct.environment.capture_count == 0) {
try self.chunk.write(expression.line, .{.push_const = try self.declare_chunk(chunk)});
} else {
const lambda_captures = lambda_construct.environment.get_captures();
var index = lambda_captures.len;
while (index != 0) {
index -= 1;
try self.chunk.write(expression.line, switch (lambda_captures[index]) {
.declaration_index => |declaration_index| .{.push_local = declaration_index},
.capture_index => |capture_index| .{.push_binding = capture_index},
});
}
try self.chunk.write(expression.line, .{.push_const = try self.declare_chunk(chunk)});
try self.chunk.write(expression.line, .{.bind = lambda_construct.environment.capture_count});
}
},
.binary_op => |binary_op| {
try self.compile_expression(environment, binary_op.lhs_operand, null);
try self.compile_expression(environment, binary_op.rhs_operand, null);
try self.chunk.write(expression.line, switch (binary_op.operation) {
.addition => .add,
.subtraction => .sub,
.multiplication => .mul,
.divsion => .div,
.greater_equals_comparison => .cge,
.greater_than_comparison => .cgt,
.equals_comparison => .eql,
.less_than_comparison => .clt,
.less_equals_comparison => .cle,
});
},
.unary_op => |unary_op| {
try self.compile_expression(environment, unary_op.operand, null);
try self.chunk.write(expression.line, switch (unary_op.operation) {
.boolean_negation => .not,
.numeric_negation => .neg,
});
},
.invoke => |invoke| {
const argument_count = try self.compile_argument(environment, invoke.argument);
try self.compile_expression(environment, invoke.object, null);
try self.chunk.write(expression.line, .{.call = argument_count});
},
.group => |group| try self.compile_expression(environment, group, null),
.import_builtin => try self.chunk.write(expression.line, .{.push_builtin = .import}),
.print_builtin => try self.chunk.write(expression.line, .{.push_builtin = .print}),
.vec2_builtin => try self.chunk.write(expression.line, .{.push_builtin = .vec2}),
.vec3_builtin => try self.chunk.write(expression.line, .{.push_builtin = .vec3}),
.declaration_get => |declaration_get| {
if (get_local_index(environment, declaration_get.declaration)) |index| {
return self.chunk.write(expression.line, .{.push_local = index});
}
if (try self.get_binding_index(environment, declaration_get.declaration)) |index| {
try self.chunk.write(expression.line, .{.push_binding = index});
if (is_declaration_boxed(declaration_get.declaration)) {
try self.chunk.write(expression.line, .get_box);
}
return;
}
return self.env.raise(error.IllegalState, "local out of scope", .{});
},
.declaration_set => |declaration_set| {
if (get_local_index(environment, declaration_set.declaration)) |index| {
try self.compile_expression(environment, declaration_set.assign, null);
return self.chunk.write(expression.line, .{.set_local = index});
}
if (try self.get_binding_index(environment, declaration_set.declaration)) |index| {
try self.chunk.write(expression.line, .{.push_binding = index});
try self.compile_expression(environment, declaration_set.assign, null);
if (is_declaration_boxed(declaration_set.declaration)) {
try self.chunk.write(expression.line, .set_box);
}
return;
}
return self.env.raise(error.IllegalState, "local out of scope", .{});
},
.field_get => |field_get| {
try self.compile_expression(environment, field_get.object, null);
try self.chunk.write(expression.line, .{.push_const = try self.declare_symbol(field_get.identifier)});
try self.chunk.write(expression.line, .get_dynamic);
},
.field_set => |field_set| {
try self.compile_expression(environment, field_set.object, null);
try self.chunk.write(expression.line, .{.push_const = try self.declare_symbol(field_set.identifier)});
try self.compile_expression(environment, field_set.assign, null);
try self.chunk.write(expression.line, .set_dynamic);
},
.subscript_get => |subscript_get| {
try self.compile_expression(environment, subscript_get.object, null);
try self.compile_expression(environment, subscript_get.index, null);
try self.chunk.write(expression.line, .get_dynamic);
},
.subscript_set => |subscript_set| {
try self.compile_expression(environment, subscript_set.object, null);
try self.compile_expression(environment, subscript_set.index, null);
try self.compile_expression(environment, subscript_set.assign, null);
try self.chunk.write(expression.line, .set_dynamic);
},
}
}
pub fn compile_environment(self: Self, environment: *const tree.Environment) kym.RuntimeError!void {
if (environment.statement) |statement| {
const last_statement = try self.compile_statement(environment, statement);
if (last_statement.kind != .@"return") {
try self.chunk.write(last_statement.line, .push_nil);
}
}
}
fn compile_statement(self: Self, environment: *const tree.Environment, initial_statement: *const Stmt) kym.RuntimeError!*const Stmt {
var current_statement = initial_statement;
while (true) {
switch (current_statement.kind) {
.@"return" => |@"return"| {
if (@"return".returned_expression) |expression| {
try self.compile_expression(environment, expression, null);
} else {
try self.chunk.write(current_statement.line, .push_nil);
}
// TODO: Omit ret calls at ends of chunk.
try self.chunk.write(current_statement.line, .ret);
},
.@"while" => |@"while"| {
try self.compile_expression(environment, @"while".loop_expression, null);
try self.chunk.write(current_statement.line, .{.jf = 0});
const origin_index = @as(u32, @intCast(self.chunk.opcodes.values.len - 1));
_ = try self.compile_statement(environment, @"while".loop);
self.chunk.opcodes.values[origin_index].jf = @intCast(self.chunk.opcodes.values.len - 1);
try self.compile_expression(environment, @"while".loop_expression, null);
try self.chunk.write(current_statement.line, .{.jt = origin_index});
},
.@"if" => |@"if"| {
try self.compile_expression(environment, @"if".then_expression, null);
try self.chunk.write(current_statement.line, .{.jf = 0});
const origin_index = @as(u32, @intCast(self.chunk.opcodes.values.len - 1));
_ = try self.compile_statement(environment, @"if".@"then");
self.chunk.opcodes.values[origin_index].jf = @intCast(self.chunk.opcodes.values.len - 1);
if (@"if".@"else") |@"else"| {
_ = try self.compile_statement(environment, @"else");
}
},
.declare => |declare| {
try self.compile_expression(environment, declare.initial_expression, declare.declaration.identifier);
if (is_declaration_boxed(declare.declaration)) {
try self.chunk.write(current_statement.line, .push_boxed);
}
},
.top_expression => |top_expression| {
try self.compile_expression(environment, top_expression, null);
if (top_expression.kind == .invoke) {
try self.chunk.write(current_statement.line, .pop);
}
},
}
current_statement = current_statement.next orelse return current_statement;
}
}
const constants_max = @as(usize, coral.math.max_int(@typeInfo(u16).Int));
fn declare_chunk(self: Self, chunk: Chunk) kym.RuntimeError!u16 {
if (self.chunk.constants.values.len == coral.math.max_int(@typeInfo(u16).Int)) {
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
.max = @as(usize, coral.math.max_int(@typeInfo(u16).Int)),
});
}
const constant = try self.env.new_dynamic(coral.io.bytes_of(&chunk), Chunk.typeinfo);
errdefer self.env.discard(constant);
try self.chunk.constants.push_one(constant);
return @intCast(self.chunk.constants.values.len - 1);
}
fn declare_fixed(self: Self, fixed: kym.Fixed) kym.RuntimeError!u16 {
if (self.chunk.constants.values.len == constants_max) {
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
.max = constants_max,
});
}
const constant = try self.env.new_fixed(fixed);
errdefer self.env.discard(constant);
try self.chunk.constants.push_one(constant);
return @intCast(self.chunk.constants.values.len - 1);
}
fn declare_float(self: Self, float: kym.Float) kym.RuntimeError!u16 {
if (self.chunk.constants.values.len == constants_max) {
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
.max = constants_max,
});
}
const constant = try self.env.new_float(float);
errdefer self.env.discard(constant);
try self.chunk.constants.push_one(constant);
return @intCast(self.chunk.constants.values.len - 1);
}
fn declare_string(self: Self, string: []const coral.io.Byte) kym.RuntimeError!u16 {
if (self.chunk.constants.values.len == constants_max) {
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
.max = constants_max,
});
}
const constant = try self.env.new_string(string);
errdefer self.env.discard(constant);
try self.chunk.constants.push_one(constant);
return @intCast(self.chunk.constants.values.len - 1);
}
fn declare_symbol(self: Self, symbol: []const coral.io.Byte) kym.RuntimeError!u16 {
if (self.chunk.constants.values.len == constants_max) {
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
.max = constants_max,
});
}
const constant = try self.env.new_symbol(symbol);
errdefer self.env.discard(constant);
try self.chunk.constants.push_one(constant);
return @intCast(self.chunk.constants.values.len - 1);
}
pub fn get_binding_index(self: *const Self, environment: *const tree.Environment, declaration: *const tree.Declaration) kym.RuntimeError!?u8 {
var binding_index = @as(u8, 0);
while (binding_index < environment.capture_count) : (binding_index += 1) {
var capture = &environment.captures[binding_index];
var target_environment = environment.enclosing orelse return null;
while (capture.* == .capture_index) {
capture = &target_environment.captures[capture.capture_index];
target_environment = target_environment.enclosing orelse return null;
}
try kym.assert(self.env, capture.* == .declaration_index);
if (&target_environment.declarations[capture.declaration_index] == declaration) {
return binding_index;
}
}
return null;
}
pub fn get_local_index(environment: *const tree.Environment, declaration: *const tree.Declaration) ?u8 {
var remaining = environment.declaration_count;
while (remaining != 0) {
remaining -= 1;
if (&environment.declarations[remaining] == declaration) {
return remaining;
}
}
return null;
}
fn is_declaration_boxed(declaration: *const tree.Declaration) bool {
return declaration.is.captured and !declaration.is.readonly;
}