add local/global "switch"
This commit is contained in:
parent
5138410be4
commit
26d299cd23
2 changed files with 84 additions and 18 deletions
|
@ -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{};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue