ona/source/ona/kym/Chunk.zig
kayomn 5642f399b9
Some checks failed
continuous-integration/drone/push Build is failing
Add AST generation stage to Kym parser
2023-05-28 01:19:46 +00:00

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];
}