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
|
@ -28,6 +28,9 @@ const AllOpcodes = struct {
|
||||||
|
|
||||||
pub Print: u8 = 15,
|
pub Print: u8 = 15,
|
||||||
pub Pop: u8 = 16,
|
pub Pop: u8 = 16,
|
||||||
|
|
||||||
|
pub DefineGlobal: u8 = 17,
|
||||||
|
pub DefineGlobalLong: u8 = 18,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const OpCode = AllOpcodes{};
|
pub const OpCode = AllOpcodes{};
|
||||||
|
@ -78,6 +81,16 @@ fn constantLongInstruction(
|
||||||
return offset + 4;
|
return offset + 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const ConstantIndexTag = enum {
|
||||||
|
Small,
|
||||||
|
Long,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ConstantIndex = union(ConstantIndexTag) {
|
||||||
|
Small: u8,
|
||||||
|
Long: [3]u8,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Chunk = struct {
|
pub const Chunk = struct {
|
||||||
count: usize,
|
count: usize,
|
||||||
lines: []usize,
|
lines: []usize,
|
||||||
|
@ -119,13 +132,20 @@ pub const Chunk = struct {
|
||||||
return self.constants.count - 1;
|
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);
|
try self.constants.write(val);
|
||||||
var constant_idx = self.constants.count - 1;
|
var constant_idx = self.constants.count - 1;
|
||||||
|
|
||||||
if (constant_idx < 256) {
|
if (constant_idx < 256) {
|
||||||
try self.write(OpCode.Constant, line);
|
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 {
|
} else {
|
||||||
var idx_u24: u24 = @intCast(u24, constant_idx);
|
var idx_u24: u24 = @intCast(u24, constant_idx);
|
||||||
|
|
||||||
|
@ -139,6 +159,7 @@ pub const Chunk = struct {
|
||||||
try self.write(v3, line);
|
try self.write(v3, line);
|
||||||
try self.write(v2, line);
|
try self.write(v2, line);
|
||||||
try self.write(v1, 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);
|
return try simpleInstruction(stdout, "OP_PRINT", index);
|
||||||
} else if (instruction == OpCode.Pop) {
|
} else if (instruction == OpCode.Pop) {
|
||||||
return try simpleInstruction(stdout, "OP_POP", index);
|
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 {
|
} else {
|
||||||
try stdout.print("Unknown opcode: {}\n", instruction);
|
try stdout.print("Unknown opcode: {}\n", instruction);
|
||||||
return index + 1;
|
return index + 1;
|
||||||
|
|
|
@ -215,7 +215,7 @@ pub const Compiler = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emitConstant(self: *Compiler, value: Value) !void {
|
fn emitConstant(self: *Compiler, value: Value) !void {
|
||||||
try self.currentChunk().writeConstant(
|
_ = try self.currentChunk().writeConstant(
|
||||||
value,
|
value,
|
||||||
self.parser.previous.line,
|
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 {
|
fn declaration(self: *Compiler) !void {
|
||||||
try self.statement();
|
if (try self.match(.VAR)) {
|
||||||
|
try self.varDecl();
|
||||||
|
} else {
|
||||||
|
try self.statement();
|
||||||
|
}
|
||||||
if (self.parser.panicMode) try self.synchronize();
|
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 {
|
pub const VM = struct {
|
||||||
chk: *Chunk = undefined,
|
chk: *Chunk = undefined,
|
||||||
src: []const u8,
|
src: []const u8,
|
||||||
|
@ -49,6 +51,7 @@ pub const VM = struct {
|
||||||
pub allocator: *std.mem.Allocator,
|
pub allocator: *std.mem.Allocator,
|
||||||
|
|
||||||
objs: ?*objects.Object = null,
|
objs: ?*objects.Object = null,
|
||||||
|
globals: ValueMap,
|
||||||
|
|
||||||
fn resetStack(self: *VM) void {
|
fn resetStack(self: *VM) void {
|
||||||
self.stackTop = 0;
|
self.stackTop = 0;
|
||||||
|
@ -67,6 +70,8 @@ pub const VM = struct {
|
||||||
.stdout = stdout,
|
.stdout = stdout,
|
||||||
.debug_flag = debug_flag,
|
.debug_flag = debug_flag,
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
|
|
||||||
|
.globals = ValueMap.init(allocator),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.resetStack();
|
self.resetStack();
|
||||||
|
@ -219,6 +224,11 @@ pub const VM = struct {
|
||||||
self.resetStack();
|
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 {
|
fn run(self: *VM) !void {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (self.debug_flag) {
|
if (self.debug_flag) {
|
||||||
|
@ -260,6 +270,18 @@ pub const VM = struct {
|
||||||
break :blk;
|
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: {
|
chunk.OpCode.Equal => blk: {
|
||||||
var a = self.pop();
|
var a = self.pop();
|
||||||
var b = self.pop();
|
var b = self.pop();
|
||||||
|
|
Loading…
Reference in New Issue