String Literal Interpolation #46
|
@ -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);
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
Loading…
Reference in New Issue