226 lines
5.8 KiB
Zig
226 lines
5.8 KiB
Zig
const Environment = @import("./Environment.zig");
|
|
|
|
const ast = @import("./ast.zig");
|
|
|
|
const coral = @import("coral");
|
|
|
|
const types = @import("./types.zig");
|
|
|
|
const tokens = @import("./tokens.zig");
|
|
|
|
env: *Environment,
|
|
message_name_len: usize,
|
|
message_buffer: Buffer,
|
|
bytecode_buffer: Buffer,
|
|
|
|
const Buffer = coral.list.Stack(u8);
|
|
|
|
const Opcode = enum (u8) {
|
|
ret,
|
|
|
|
push_nil,
|
|
push_true,
|
|
push_false,
|
|
push_zero,
|
|
push_integer,
|
|
push_float,
|
|
push_object,
|
|
push_table,
|
|
|
|
not,
|
|
neg,
|
|
|
|
add,
|
|
sub,
|
|
mul,
|
|
div,
|
|
};
|
|
|
|
const Self = @This();
|
|
|
|
fn clear_error_details(self: *Self) void {
|
|
coral.debug.assert(self.message_buffer.values.len >= self.message_name_len);
|
|
coral.debug.assert(self.message_buffer.drop(self.message_buffer.values.len - self.message_name_len));
|
|
}
|
|
|
|
pub fn compile(self: *Self, data: []const u8) types.RuntimeError!void {
|
|
var tokenizer = tokens.Tokenizer{.source = data};
|
|
var parsed_statements = try ast.ParsedStatements.init(self.env.allocator, &tokenizer);
|
|
|
|
switch (parsed_statements) {
|
|
.valid => |*statements| {
|
|
defer statements.deinit(self.env.allocator);
|
|
|
|
for (statements.list.values) |statement| {
|
|
switch (statement) {
|
|
.return_expression => |return_expression| {
|
|
try self.compile_expression(return_expression);
|
|
try self.emit_opcode(.ret);
|
|
},
|
|
|
|
.return_nothing => {
|
|
try self.emit_opcode(.push_nil);
|
|
try self.emit_opcode(.ret);
|
|
},
|
|
}
|
|
}
|
|
},
|
|
|
|
.invalid => |invalid| {
|
|
self.clear_error_details();
|
|
|
|
try self.message_buffer.push_all(self.env.allocator, "@(");
|
|
|
|
var writable_message = coral.list.Writable.from_stack(self.env.allocator, &self.message_buffer);
|
|
const message_writer = writable_message.as_writer();
|
|
|
|
coral.utf8.print_int(@typeInfo(usize).Int, message_writer, tokenizer.lines_stepped) catch {
|
|
return error.OutOfMemory;
|
|
};
|
|
|
|
coral.utf8.print(message_writer, "): ") catch {
|
|
return error.OutOfMemory;
|
|
};
|
|
|
|
coral.utf8.print(message_writer, invalid) catch {
|
|
return error.OutOfMemory;
|
|
};
|
|
|
|
return error.BadSyntax;
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn compile_expression(self: *Self, expression: ast.Expression) types.RuntimeError!void {
|
|
switch (expression) {
|
|
.nil_literal => try self.emit_opcode(.push_nil),
|
|
.true_literal => try self.emit_opcode(.push_true),
|
|
.false_literal => try self.emit_opcode(.push_false),
|
|
|
|
.integer_literal => |literal| {
|
|
if (literal == 0) {
|
|
try self.emit_opcode(.push_zero);
|
|
} else {
|
|
try self.emit_opcode(.push_integer);
|
|
try self.emit_float(0);
|
|
}
|
|
},
|
|
|
|
.float_literal => |literal| {
|
|
if (literal == 0) {
|
|
try self.emit_opcode(.push_zero);
|
|
} else {
|
|
try self.emit_opcode(.push_float);
|
|
try self.emit_float(literal);
|
|
}
|
|
},
|
|
|
|
.string_literal => |literal| {
|
|
try self.emit_opcode(.push_object);
|
|
try self.emit_object(try self.intern(literal));
|
|
},
|
|
|
|
.table_literal => |literal| {
|
|
if (literal.values.len > coral.math.max_int(@typeInfo(types.Integer).Int)) {
|
|
return error.OutOfMemory;
|
|
}
|
|
|
|
for (literal.values) |field| {
|
|
try self.compile_expression(field.expression.*);
|
|
try self.emit_opcode(.push_object);
|
|
try self.emit_object(try self.intern(field.identifier));
|
|
}
|
|
|
|
try self.emit_opcode(.push_table);
|
|
try self.emit_integer(@intCast(types.Integer, literal.values.len));
|
|
},
|
|
|
|
.binary_operation => |operation| {
|
|
try self.compile_expression(operation.lhs_expression.*);
|
|
try self.compile_expression(operation.rhs_expression.*);
|
|
|
|
try self.emit_opcode(switch (operation.kind) {
|
|
.addition => .add,
|
|
.subtraction => .sub,
|
|
.multiplication => .mul,
|
|
.division => .div,
|
|
});
|
|
},
|
|
|
|
.unary_operation => |operation| {
|
|
try self.compile_expression(operation.expression.*);
|
|
|
|
try self.emit_opcode(switch (operation.kind) {
|
|
.boolean_negation => .not,
|
|
.numeric_negation => .neg,
|
|
});
|
|
},
|
|
|
|
.grouped_expression => |grouped_expression| {
|
|
try self.compile_expression(grouped_expression.*);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn deinit(self: *Self) void {
|
|
self.bytecode_buffer.deinit(self.env.allocator);
|
|
self.message_buffer.deinit(self.env.allocator);
|
|
|
|
self.message_name_len = 0;
|
|
}
|
|
|
|
pub fn emit_float(self: *Self, float: types.Float) coral.io.AllocationError!void {
|
|
try self.bytecode_buffer.push_all(self.env.allocator, coral.io.bytes_of(&float));
|
|
}
|
|
|
|
pub fn emit_integer(self: *Self, integer: types.Integer) coral.io.AllocationError!void {
|
|
try self.bytecode_buffer.push_all(self.env.allocator, coral.io.bytes_of(&integer));
|
|
}
|
|
|
|
pub fn emit_object(self: *Self, object: types.Object) coral.io.AllocationError!void {
|
|
try self.bytecode_buffer.push_all(self.env.allocator, coral.io.bytes_of(&object));
|
|
}
|
|
|
|
pub fn emit_opcode(self: *Self, opcode: Opcode) coral.io.AllocationError!void {
|
|
try self.bytecode_buffer.push_one(self.env.allocator, @enumToInt(opcode));
|
|
}
|
|
|
|
pub fn error_details(self: Self) []const u8 {
|
|
coral.debug.assert(self.message_buffer.values.len >= self.message_name_len);
|
|
|
|
return self.message_buffer.values[self.message_name_len .. ];
|
|
}
|
|
|
|
pub fn init(env: *Environment, chunk_name: []const u8) coral.io.AllocationError!Self {
|
|
var bytecode_buffer = try Buffer.init(env.allocator, 0);
|
|
|
|
errdefer bytecode_buffer.deinit(env.allocator);
|
|
|
|
var message_buffer = try Buffer.init(env.allocator, chunk_name.len);
|
|
|
|
errdefer message_buffer.deinit(env.allocator);
|
|
|
|
message_buffer.push_all(env.allocator, chunk_name) catch unreachable;
|
|
|
|
return Self{
|
|
.env = env,
|
|
.message_buffer = message_buffer,
|
|
.bytecode_buffer = bytecode_buffer,
|
|
.message_name_len = chunk_name.len,
|
|
};
|
|
}
|
|
|
|
pub fn intern(self: *Self, string: []const u8) coral.io.AllocationError!types.Object {
|
|
const interned_string = try self.env.intern(string);
|
|
|
|
coral.debug.assert(interned_string == .object);
|
|
|
|
return interned_string.object;
|
|
}
|
|
|
|
pub fn name(self: Self) []const u8 {
|
|
coral.debug.assert(self.message_buffer.values.len >= self.message_name_len);
|
|
|
|
return self.message_buffer.values[0 .. self.message_name_len];
|
|
}
|