From 26d299cd2341e1af5ad2a3bfb6eab6abe0b98086 Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 3 Jun 2019 16:10:49 -0300 Subject: [PATCH] add local/global "switch" --- src/chunk.zig | 3 ++ src/compiler.zig | 99 +++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 84 insertions(+), 18 deletions(-) diff --git a/src/chunk.zig b/src/chunk.zig index 5ce7062..bcfc23e 100644 --- a/src/chunk.zig +++ b/src/chunk.zig @@ -35,6 +35,9 @@ const AllOpcodes = struct { pub GetGlobalLong: u8 = 20, pub SetGlobal: u8 = 21, pub SetGlobalLong: u8 = 22, + + pub GetLocal: u8 = 23, + pub SetLocal: u8 = 24, }; pub const OpCode = AllOpcodes{}; diff --git a/src/compiler.zig b/src/compiler.zig index f468ee8..7b51ca4 100644 --- a/src/compiler.zig +++ b/src/compiler.zig @@ -129,8 +129,8 @@ pub const Compiler = struct { vmach: *vm.VM, locals: [256]Local, - localCount: u8 = 0, - scopeDepth: u8 = 0, + localCount: i32 = 0, + scopeDepth: i32 = 0, pub fn init( allocator: *Allocator, @@ -248,8 +248,14 @@ pub const Compiler = struct { self.scopeDepth += 1; } - fn endScope(self: *Compiler) void { + 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 { @@ -275,27 +281,45 @@ 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)) { + return i; + } + } + + return -1; + } + fn namedVariable(self: *Compiler, tok: *Token, canAssign: bool) !void { // writeConstant always writes OP_CODE which may be not // what we want, so. - var idx = try self.currentChunk().writeConstantRaw(values.ObjVal(try objects.copyString( - self.vmach, - tok.lexeme, - )), tok.line); + var getOp: u8 = undefined; + var setOp: u8 = undefined; + + // 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)) { try self.expression(); - try self.emitConstWithIndex( - chunks.OpCode.SetGlobal, - chunks.OpCode.SetGlobalLong, - idx, - ); + try self.emitBytes(setOp, idx); } else { - try self.emitConstWithIndex( - chunks.OpCode.GetGlobal, - chunks.OpCode.GetGlobalLong, - idx, - ); + try self.emitBytes(getOp, idx); } } @@ -420,8 +444,43 @@ pub const Compiler = struct { )), 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; + } + + 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 { try self.consume(.IDENTIFIER, msg); + self.declareVariable(); + if (self.scopeDepth > 0) return chunks.ConstantIndex{ .Small = 0 }; return try self.identifierConstant(&self.parser.previous); } @@ -444,6 +503,8 @@ pub const Compiler = struct { } fn defineVariable(self: *Compiler, global: chunks.ConstantIndex) !void { + if (self.scopeDepth > 0) return; + try self.emitConstWithIndex( chunks.OpCode.DefineGlobal, chunks.OpCode.DefineGlobalLong, @@ -460,6 +521,8 @@ pub const Compiler = struct { try self.emitByte(chunks.OpCode.Nil); } + // check scopeDepth here + try self.consume(.SEMICOLON, "Expect ';' after variable declaration."); try self.defineVariable(global); } @@ -487,7 +550,7 @@ pub const Compiler = struct { } else if (try self.match(.LEFT_BRACE)) { self.beginScope(); try self.block(); - self.endScope(); + try self.endScope(); } else { try self.exprStmt(); }