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:
Luna 2019-06-02 22:52:19 -03:00
parent aa94396e51
commit 3a6df2d9ea
3 changed files with 97 additions and 4 deletions

View file

@ -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;

View file

@ -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 {
try self.statement();
if (try self.match(.VAR)) {
try self.varDecl();
} else {
try self.statement();
}
if (self.parser.panicMode) try self.synchronize();
}

View file

@ -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();