forked from luna/jorts
add basic global variable support
- chunk: make writeConstant return a ConstantIndex for better integration with the (optional) OP_CONST_LONG - compiler: quickfix emitConstant() - vm: add a global ValueMap
This commit is contained in:
parent
aa94396e51
commit
3a6df2d9ea
3 changed files with 97 additions and 4 deletions
|
@ -28,6 +28,9 @@ const AllOpcodes = struct {
|
|||
|
||||
pub Print: u8 = 15,
|
||||
pub Pop: u8 = 16,
|
||||
|
||||
pub DefineGlobal: u8 = 17,
|
||||
pub DefineGlobalLong: u8 = 18,
|
||||
};
|
||||
|
||||
pub const OpCode = AllOpcodes{};
|
||||
|
@ -78,6 +81,16 @@ fn constantLongInstruction(
|
|||
return offset + 4;
|
||||
}
|
||||
|
||||
pub const ConstantIndexTag = enum {
|
||||
Small,
|
||||
Long,
|
||||
};
|
||||
|
||||
pub const ConstantIndex = union(ConstantIndexTag) {
|
||||
Small: u8,
|
||||
Long: [3]u8,
|
||||
};
|
||||
|
||||
pub const Chunk = struct {
|
||||
count: usize,
|
||||
lines: []usize,
|
||||
|
@ -119,13 +132,20 @@ pub const Chunk = struct {
|
|||
return self.constants.count - 1;
|
||||
}
|
||||
|
||||
pub fn writeConstant(self: *Chunk, val: value.Value, line: usize) !void {
|
||||
pub fn writeConstant(
|
||||
self: *Chunk,
|
||||
val: value.Value,
|
||||
line: usize,
|
||||
) !ConstantIndex {
|
||||
try self.constants.write(val);
|
||||
var constant_idx = self.constants.count - 1;
|
||||
|
||||
if (constant_idx < 256) {
|
||||
try self.write(OpCode.Constant, line);
|
||||
try self.write(@intCast(u8, constant_idx), line);
|
||||
|
||||
var idx_small = @intCast(u8, constant_idx);
|
||||
try self.write(idx_small, line);
|
||||
return ConstantIndex{ .Small = idx_small };
|
||||
} else {
|
||||
var idx_u24: u24 = @intCast(u24, constant_idx);
|
||||
|
||||
|
@ -139,6 +159,7 @@ pub const Chunk = struct {
|
|||
try self.write(v3, line);
|
||||
try self.write(v2, line);
|
||||
try self.write(v1, line);
|
||||
return ConstantIndex{ .Long = []u8{ v3, v2, v1 } };
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,6 +217,10 @@ pub const Chunk = struct {
|
|||
return try simpleInstruction(stdout, "OP_PRINT", index);
|
||||
} else if (instruction == OpCode.Pop) {
|
||||
return try simpleInstruction(stdout, "OP_POP", index);
|
||||
} else if (instruction == OpCode.DefineGlobal) {
|
||||
return try simpleInstruction(stdout, "OP_DEFGLOBAL", index);
|
||||
} else if (instruction == OpCode.DefineGlobalLong) {
|
||||
return try simpleInstruction(stdout, "OP_DEFGLOBAL_LONG", index);
|
||||
} else {
|
||||
try stdout.print("Unknown opcode: {}\n", instruction);
|
||||
return index + 1;
|
||||
|
|
|
@ -215,7 +215,7 @@ pub const Compiler = struct {
|
|||
}
|
||||
|
||||
fn emitConstant(self: *Compiler, value: Value) !void {
|
||||
try self.currentChunk().writeConstant(
|
||||
_ = try self.currentChunk().writeConstant(
|
||||
value,
|
||||
self.parser.previous.line,
|
||||
);
|
||||
|
@ -352,8 +352,54 @@ pub const Compiler = struct {
|
|||
}
|
||||
}
|
||||
|
||||
/// Write an identifier constant to the bytecode.
|
||||
fn identifierConstant(
|
||||
self: *Compiler,
|
||||
token: *Token,
|
||||
) !chunks.ConstantIndex {
|
||||
return try self.currentChunk().writeConstant(values.ObjVal(try objects.copyString(
|
||||
self.vmach,
|
||||
token.lexeme,
|
||||
)), token.line);
|
||||
}
|
||||
|
||||
fn parseVariable(self: *Compiler, msg: []const u8) !chunks.ConstantIndex {
|
||||
try self.consume(.IDENTIFIER, msg);
|
||||
return try self.identifierConstant(&self.parser.previous);
|
||||
}
|
||||
|
||||
fn defineVariable(self: *Compiler, global: chunks.ConstantIndex) !void {
|
||||
switch (global) {
|
||||
.Small => |val| try self.emitBytes(chunks.OpCode.DefineGlobal, val),
|
||||
.Long => |val| blk: {
|
||||
try self.emitByte(chunks.OpCode.DefineGlobalLong);
|
||||
try self.emitByte(val[0]);
|
||||
try self.emitByte(val[1]);
|
||||
try self.emitByte(val[2]);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn varDecl(self: *Compiler) !void {
|
||||
var global = try self.parseVariable("Expect variable name.");
|
||||
|
||||
if (try self.match(.EQUAL)) {
|
||||
try self.expression();
|
||||
} else {
|
||||
try self.emitByte(chunks.OpCode.Nil);
|
||||
}
|
||||
|
||||
try self.consume(.SEMICOLON, "Expect ';' after variable declaration.");
|
||||
try self.defineVariable(global);
|
||||
}
|
||||
|
||||
fn declaration(self: *Compiler) !void {
|
||||
if (try self.match(.VAR)) {
|
||||
try self.varDecl();
|
||||
} else {
|
||||
try self.statement();
|
||||
}
|
||||
if (self.parser.panicMode) try self.synchronize();
|
||||
}
|
||||
|
||||
|
|
22
src/vm.zig
22
src/vm.zig
|
@ -36,6 +36,8 @@ fn valuesEqual(a: value.Value, b: value.Value) bool {
|
|||
}
|
||||
}
|
||||
|
||||
pub const ValueMap = std.AutoHashMap([]const u8, values.Value);
|
||||
|
||||
pub const VM = struct {
|
||||
chk: *Chunk = undefined,
|
||||
src: []const u8,
|
||||
|
@ -49,6 +51,7 @@ pub const VM = struct {
|
|||
pub allocator: *std.mem.Allocator,
|
||||
|
||||
objs: ?*objects.Object = null,
|
||||
globals: ValueMap,
|
||||
|
||||
fn resetStack(self: *VM) void {
|
||||
self.stackTop = 0;
|
||||
|
@ -67,6 +70,8 @@ pub const VM = struct {
|
|||
.stdout = stdout,
|
||||
.debug_flag = debug_flag,
|
||||
.allocator = allocator,
|
||||
|
||||
.globals = ValueMap.init(allocator),
|
||||
};
|
||||
|
||||
self.resetStack();
|
||||
|
@ -219,6 +224,11 @@ pub const VM = struct {
|
|||
self.resetStack();
|
||||
}
|
||||
|
||||
fn defGlobal(self: *VM, name: []const u8) !void {
|
||||
_ = try self.globals.put(name, self.peek(0));
|
||||
_ = self.pop();
|
||||
}
|
||||
|
||||
fn run(self: *VM) !void {
|
||||
while (true) {
|
||||
if (self.debug_flag) {
|
||||
|
@ -260,6 +270,18 @@ pub const VM = struct {
|
|||
break :blk;
|
||||
},
|
||||
|
||||
// extracting the name is different depending of the
|
||||
// op code since one just uses a single byte, the other
|
||||
// uses three bytes since its a u24.
|
||||
chunk.OpCode.DefineGlobal => blk: {
|
||||
try self.defGlobal(self.readConst().as.Object.value.String);
|
||||
break :blk;
|
||||
},
|
||||
chunk.OpCode.DefineGlobalLong => blk: {
|
||||
try self.defGlobal(self.readConstLong().as.Object.value.String);
|
||||
break :blk;
|
||||
},
|
||||
|
||||
chunk.OpCode.Equal => blk: {
|
||||
var a = self.pop();
|
||||
var b = self.pop();
|
||||
|
|
Loading…
Reference in a new issue