Compare commits
No commits in common. "eb9552c390d32f0c9e2874cf5629cbc636f8bff5" and "8f3a33a52b69c15419eac4fc21c4664662e0c275" have entirely different histories.
eb9552c390
...
8f3a33a52b
|
@ -533,8 +533,8 @@ pub const RuntimeEnv = struct {
|
|||
|
||||
if (arg_count < arity) {
|
||||
return self.raise(error.BadOperation, "expected `{expected}` {noun}, {provided} provided", .{
|
||||
.expected = arg_count,
|
||||
.provided = arity,
|
||||
.expected = arity,
|
||||
.provided = arg_count,
|
||||
.noun = if (arity == 1) "argument" else "arguments",
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
const Expr = @import("./Expr.zig");
|
||||
|
||||
const Stmt = @import("./Stmt.zig");
|
||||
|
||||
const app = @import("../app.zig");
|
||||
|
||||
const Compiler = @import("./Compiler.zig");
|
||||
|
||||
const coral = @import("coral");
|
||||
|
||||
const file = @import("../file.zig");
|
||||
|
@ -27,408 +25,6 @@ const Builtin = enum {
|
|||
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 LineList = coral.list.Stack(u32);
|
||||
|
|
|
@ -0,0 +1,413 @@
|
|||
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;
|
||||
}
|
Loading…
Reference in New Issue