Compare commits

...

4 commits

Author SHA1 Message Date
d7b78e09e3 finish impl for local vars 2019-06-03 16:24:54 -03:00
26d299cd23 add local/global "switch" 2019-06-03 16:10:49 -03:00
5138410be4 compiler: add scope support 2019-06-03 15:17:07 -03:00
25ee586acb compiler: add local scope basics 2019-06-03 15:10:12 -03:00
3 changed files with 153 additions and 26 deletions

View file

@ -35,6 +35,9 @@ const AllOpcodes = struct {
pub GetGlobalLong: u8 = 20, pub GetGlobalLong: u8 = 20,
pub SetGlobal: u8 = 21, pub SetGlobal: u8 = 21,
pub SetGlobalLong: u8 = 22, pub SetGlobalLong: u8 = 22,
pub GetLocal: u8 = 23,
pub SetLocal: u8 = 24,
}; };
pub const OpCode = AllOpcodes{}; pub const OpCode = AllOpcodes{};
@ -85,6 +88,17 @@ fn constantLongInstruction(
return offset + 4; return offset + 4;
} }
fn byteInstruction(
stdout: var,
name: []const u8,
chunk: *Chunk,
index: usize,
) !usize {
var slot: u8 = chunk.code[index + 1];
try stdout.print("{} {}", name, slot);
return index + 2;
}
pub const ConstantIndexTag = enum { pub const ConstantIndexTag = enum {
Small, Small,
Long, Long,
@ -251,6 +265,10 @@ pub const Chunk = struct {
return try constantInstruction(stdout, "OP_SETGLOBAL", self, index); return try constantInstruction(stdout, "OP_SETGLOBAL", self, index);
} else if (instruction == OpCode.SetGlobalLong) { } else if (instruction == OpCode.SetGlobalLong) {
return try constantLongInstruction(stdout, "OP_SETGLOBAL_LONG", self, index); return try constantLongInstruction(stdout, "OP_SETGLOBAL_LONG", self, index);
} else if (instruction == OpCode.GetLocal) {
return try byteInstruction(stdout, "OP_GETLOCAL", self, index);
} else if (instruction == OpCode.SetLocal) {
return try byteInstruction(stdout, "OP_GETLOCAL", self, index);
} else { } else {
try stdout.print("Unknown opcode: {}\n", instruction); try stdout.print("Unknown opcode: {}\n", instruction);
return index + 1; return index + 1;

View file

@ -113,6 +113,11 @@ var rules = []ParseRule{
ParseRule{}, ParseRule{},
}; };
pub const Local = struct {
name: tokens.Token,
depth: i32,
};
pub const Compiler = struct { pub const Compiler = struct {
src: []const u8, src: []const u8,
stdout: vm.StdOut, stdout: vm.StdOut,
@ -123,6 +128,10 @@ pub const Compiler = struct {
debug_flag: bool = false, debug_flag: bool = false,
vmach: *vm.VM, vmach: *vm.VM,
locals: [256]Local,
localCount: i32 = 0,
scopeDepth: i32 = 0,
pub fn init( pub fn init(
allocator: *Allocator, allocator: *Allocator,
chunk: *chunks.Chunk, chunk: *chunks.Chunk,
@ -139,6 +148,12 @@ pub const Compiler = struct {
.parser = Parser{}, .parser = Parser{},
.debug_flag = debug_flag, .debug_flag = debug_flag,
.vmach = vmach, .vmach = vmach,
// local variable resolution
.locals = []Local{Local{
.name = Token{},
.depth = -1,
}} ** 256,
}; };
} }
@ -229,6 +244,20 @@ pub const Compiler = struct {
} }
} }
fn beginScope(self: *Compiler) void {
self.scopeDepth += 1;
}
fn endScope(self: *Compiler) !void {
self.scopeDepth -= 1;
// clear the current scope in the stack
while (self.localCount > 0 and self.locals[@intCast(usize, self.localCount - 1)].depth > self.scopeDepth) {
try self.emitByte(chunks.OpCode.Pop);
self.localCount -= 1;
}
}
fn grouping(self: *Compiler, canAssign: bool) !void { fn grouping(self: *Compiler, canAssign: bool) !void {
try self.expression(); try self.expression();
try self.consume(.RIGHT_PAREN, "Expect ')' after expression."); try self.consume(.RIGHT_PAREN, "Expect ')' after expression.");
@ -252,27 +281,48 @@ pub const Compiler = struct {
))); )));
} }
fn resolveLocal(self: *Compiler, name: *Token) i32 {
var i = self.localCount - 1;
while (i >= 0) : (i -= 1) {
var idx = @intCast(usize, i);
var local = &self.locals[idx];
if (std.mem.eql(u8, name.lexeme, local.name.lexeme)) {
if (local.depth == -1) {
self.errorCurrent("Cannot read local variable in its own initializer.");
}
return i;
}
}
return -1;
}
fn namedVariable(self: *Compiler, tok: *Token, canAssign: bool) !void { fn namedVariable(self: *Compiler, tok: *Token, canAssign: bool) !void {
// writeConstant always writes OP_CODE which may be not // writeConstant always writes OP_CODE which may be not
// what we want, so. // what we want, so.
var idx = try self.currentChunk().writeConstantRaw(values.ObjVal(try objects.copyString( var getOp: u8 = undefined;
self.vmach, var setOp: u8 = undefined;
tok.lexeme,
)), tok.line); // we try to resolve the local. depending if it gets resolved
// or not, we select the necessary get/set op codes.
var arg: i32 = self.resolveLocal(tok);
if (arg != -1) {
getOp = chunks.OpCode.GetLocal;
setOp = chunks.OpCode.SetLocal;
} else {
arg = (try self.identifierConstant(tok)).Small;
getOp = chunks.OpCode.GetGlobal;
setOp = chunks.OpCode.SetGlobal;
}
var idx: u8 = @intCast(u8, arg);
if (canAssign and try self.match(.EQUAL)) { if (canAssign and try self.match(.EQUAL)) {
try self.expression(); try self.expression();
try self.emitConstWithIndex( try self.emitBytes(setOp, idx);
chunks.OpCode.SetGlobal,
chunks.OpCode.SetGlobalLong,
idx,
);
} else { } else {
try self.emitConstWithIndex( try self.emitBytes(getOp, idx);
chunks.OpCode.GetGlobal,
chunks.OpCode.GetGlobalLong,
idx,
);
} }
} }
@ -397,8 +447,44 @@ pub const Compiler = struct {
)), token.line); )), token.line);
} }
fn addLocal(self: *Compiler, name: Token) void {
if (self.localCount == 256) {
self.errorCurrent("Too many variables in function.");
return;
}
self.localCount += 1;
var local: *Local = &self.locals[@intCast(usize, self.localCount)];
local.name = name;
//local.depth = self.scopeDepth;
local.depth = -1;
}
fn declareVariable(self: *Compiler) void {
if (self.scopeDepth == 0) return;
var name: *Token = &self.parser.previous;
// check if we're redeclaring an existing variable
// in the *CURRENT* scope.
// go from current down to global
var i = self.localCount;
while (i >= 0) : (i -= 1) {
var local = self.locals[@intCast(usize, i)];
if (local.depth == -1 and local.depth < self.scopeDepth) break;
if (std.mem.eql(u8, name.lexeme, local.name.lexeme)) {
self.errorCurrent("Variable with this name already declared in this scope.");
}
}
self.addLocal(name.*);
}
fn parseVariable(self: *Compiler, msg: []const u8) !chunks.ConstantIndex { fn parseVariable(self: *Compiler, msg: []const u8) !chunks.ConstantIndex {
try self.consume(.IDENTIFIER, msg); try self.consume(.IDENTIFIER, msg);
self.declareVariable();
if (self.scopeDepth > 0) return chunks.ConstantIndex{ .Small = 0 };
return try self.identifierConstant(&self.parser.previous); return try self.identifierConstant(&self.parser.previous);
} }
@ -420,7 +506,18 @@ pub const Compiler = struct {
} }
} }
fn markInitialized(self: *Compiler) void {
if (self.scopeDepth == 0) return;
var idx = @intCast(usize, self.localCount);
self.locals[idx].depth = self.scopeDepth;
}
fn defineVariable(self: *Compiler, global: chunks.ConstantIndex) !void { fn defineVariable(self: *Compiler, global: chunks.ConstantIndex) !void {
if (self.scopeDepth > 0) {
self.markInitialized();
return;
}
try self.emitConstWithIndex( try self.emitConstWithIndex(
chunks.OpCode.DefineGlobal, chunks.OpCode.DefineGlobal,
chunks.OpCode.DefineGlobalLong, chunks.OpCode.DefineGlobalLong,
@ -437,11 +534,13 @@ pub const Compiler = struct {
try self.emitByte(chunks.OpCode.Nil); try self.emitByte(chunks.OpCode.Nil);
} }
// check scopeDepth here
try self.consume(.SEMICOLON, "Expect ';' after variable declaration."); try self.consume(.SEMICOLON, "Expect ';' after variable declaration.");
try self.defineVariable(global); try self.defineVariable(global);
} }
fn declaration(self: *Compiler) !void { fn declaration(self: *Compiler) anyerror!void {
if (try self.match(.VAR)) { if (try self.match(.VAR)) {
try self.varDecl(); try self.varDecl();
} else { } else {
@ -450,9 +549,21 @@ pub const Compiler = struct {
if (self.parser.panicMode) try self.synchronize(); if (self.parser.panicMode) try self.synchronize();
} }
fn block(self: *Compiler) anyerror!void {
while (!self.check(.RIGHT_BRACE) and !self.check(.EOF)) {
try self.declaration();
}
try self.consume(.RIGHT_BRACE, "Expect '}' after block.");
}
fn statement(self: *Compiler) !void { fn statement(self: *Compiler) !void {
if (try self.match(.PRINT)) { if (try self.match(.PRINT)) {
try self.printStmt(); try self.printStmt();
} else if (try self.match(.LEFT_BRACE)) {
self.beginScope();
try self.block();
try self.endScope();
} else { } else {
try self.exprStmt(); try self.exprStmt();
} }

View file

@ -294,26 +294,24 @@ pub const VM = struct {
chunk.OpCode.Pop => blk: { chunk.OpCode.Pop => blk: {
_ = self.pop(); _ = self.pop();
break :blk; },
chunk.OpCode.GetLocal => blk: {
var slot = self.readByte();
try self.push(self.stack[slot]);
},
chunk.OpCode.SetLocal => blk: {
var slot = self.readByte();
self.stack[slot] = self.peek(0);
}, },
chunk.OpCode.GetGlobal => blk: { chunk.OpCode.GetGlobal => blk: {
try self.doGetGlobal(self.readString()); try self.doGetGlobal(self.readString());
break :blk;
}, },
chunk.OpCode.GetGlobalLong => blk: {
try self.doGetGlobal(self.readStringLong());
break :blk;
},
chunk.OpCode.SetGlobal => blk: { chunk.OpCode.SetGlobal => blk: {
try self.doSetGlobal(self.readString()); try self.doSetGlobal(self.readString());
break :blk; break :blk;
}, },
chunk.OpCode.SetGlobalLong => blk: {
try self.doSetGlobal(self.readStringLong());
break :blk;
},
// extracting the name is different depending of the // extracting the name is different depending of the
// op code since one just uses a single byte, the other // op code since one just uses a single byte, the other