2019-06-01 19:12:39 +00:00
|
|
|
const std = @import("std");
|
2019-06-02 16:17:32 +00:00
|
|
|
const scanner = @import("scanner.zig");
|
2019-06-01 19:37:24 +00:00
|
|
|
const vm = @import("vm.zig");
|
2019-06-01 23:33:43 +00:00
|
|
|
const chunks = @import("chunk.zig");
|
|
|
|
const tokens = @import("token.zig");
|
2019-06-01 23:48:26 +00:00
|
|
|
const values = @import("value.zig");
|
2019-06-02 17:01:54 +00:00
|
|
|
const objects = @import("object.zig");
|
2019-06-01 19:12:39 +00:00
|
|
|
|
|
|
|
const Allocator = std.mem.Allocator;
|
2019-06-01 23:33:43 +00:00
|
|
|
const Scanner = scanner.Scanner;
|
|
|
|
const Chunk = chunks.Chunk;
|
|
|
|
const Token = tokens.Token;
|
|
|
|
const TokenType = tokens.TokenType;
|
2019-06-01 23:48:26 +00:00
|
|
|
const Value = values.Value;
|
|
|
|
const OpCode = chunks.OpCode;
|
2019-06-01 23:33:43 +00:00
|
|
|
|
2019-06-02 00:32:25 +00:00
|
|
|
/// Holds parser state for the compiler.
|
|
|
|
const Parser = struct {
|
2019-06-01 23:33:43 +00:00
|
|
|
previous: Token = undefined,
|
|
|
|
current: Token = undefined,
|
|
|
|
|
|
|
|
// TODO are those needed
|
|
|
|
hadError: bool = false,
|
|
|
|
panicMode: bool = false,
|
|
|
|
};
|
2019-06-01 04:20:06 +00:00
|
|
|
|
2019-06-02 00:32:25 +00:00
|
|
|
/// Represents the order of operations in the parser.
|
|
|
|
const Precedence = enum(u5) {
|
|
|
|
None,
|
|
|
|
Assignment, // =
|
|
|
|
Or, // or
|
|
|
|
And, // and
|
|
|
|
Equality, // == !=
|
|
|
|
Comparison, // < > <= >=
|
|
|
|
Term, // + -
|
|
|
|
Factor, // * /
|
|
|
|
Unary, // ! -
|
|
|
|
Call, // . () []
|
|
|
|
Primary,
|
|
|
|
};
|
|
|
|
|
2019-06-03 04:41:22 +00:00
|
|
|
const ParseFn = fn (*Compiler, bool) anyerror!void;
|
2019-06-02 00:32:25 +00:00
|
|
|
|
|
|
|
const ParseRule = struct {
|
|
|
|
prefix: ?ParseFn = null,
|
|
|
|
infix: ?ParseFn = null,
|
|
|
|
precedence: Precedence = Precedence.None,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// For each token, this defines a parse rule for it.
|
|
|
|
var rules = []ParseRule{
|
|
|
|
// for LEFT_PAREN, we determine it as a call precedence
|
|
|
|
// plus a prefix parse function of grouping
|
|
|
|
ParseRule{ .prefix = Compiler.grouping, .precedence = .Call },
|
|
|
|
ParseRule{},
|
|
|
|
ParseRule{},
|
|
|
|
ParseRule{},
|
|
|
|
ParseRule{},
|
|
|
|
|
|
|
|
// dot token, means a call too, for things like a.b
|
|
|
|
ParseRule{ .precedence = .Call },
|
|
|
|
|
|
|
|
// specific to -, as it can be an unary operator when its a prefix
|
|
|
|
// of something, or a binary one, when its a infix or another thing.
|
|
|
|
ParseRule{
|
|
|
|
.prefix = Compiler.unary,
|
|
|
|
.infix = Compiler.binary,
|
|
|
|
.precedence = .Term,
|
|
|
|
},
|
|
|
|
|
|
|
|
ParseRule{ .infix = Compiler.binary, .precedence = .Term },
|
|
|
|
ParseRule{},
|
|
|
|
|
|
|
|
// slash is a binary operator, as well as star.
|
|
|
|
ParseRule{ .infix = Compiler.binary, .precedence = .Factor },
|
|
|
|
ParseRule{ .infix = Compiler.binary, .precedence = .Factor },
|
|
|
|
|
|
|
|
// as the token enum says, those are 1/2 char tokens.
|
2019-06-02 03:02:37 +00:00
|
|
|
ParseRule{ .prefix = Compiler.unary },
|
2019-06-02 00:32:25 +00:00
|
|
|
// this is specifically for the != operator
|
2019-06-02 03:16:33 +00:00
|
|
|
ParseRule{ .infix = Compiler.binary, .precedence = .Equality },
|
2019-06-02 00:32:25 +00:00
|
|
|
ParseRule{},
|
|
|
|
// this is specifically for the == operator
|
2019-06-02 03:16:33 +00:00
|
|
|
ParseRule{ .infix = Compiler.binary, .precedence = .Equality },
|
2019-06-02 00:32:25 +00:00
|
|
|
|
|
|
|
// all the comparison ones
|
2019-06-02 03:16:33 +00:00
|
|
|
ParseRule{ .infix = Compiler.binary, .precedence = .Comparison },
|
|
|
|
ParseRule{ .infix = Compiler.binary, .precedence = .Comparison },
|
|
|
|
ParseRule{ .infix = Compiler.binary, .precedence = .Comparison },
|
|
|
|
ParseRule{ .infix = Compiler.binary, .precedence = .Comparison },
|
2019-06-02 00:32:25 +00:00
|
|
|
|
2019-06-03 02:43:12 +00:00
|
|
|
ParseRule{ .prefix = Compiler.variable },
|
2019-06-02 17:01:54 +00:00
|
|
|
ParseRule{ .prefix = Compiler.string },
|
2019-06-02 00:32:25 +00:00
|
|
|
ParseRule{ .prefix = Compiler.number },
|
|
|
|
ParseRule{ .precedence = .And },
|
|
|
|
ParseRule{},
|
|
|
|
ParseRule{},
|
2019-06-02 02:44:59 +00:00
|
|
|
|
|
|
|
// false
|
|
|
|
ParseRule{ .prefix = Compiler.literal },
|
2019-06-02 00:32:25 +00:00
|
|
|
ParseRule{},
|
|
|
|
ParseRule{},
|
|
|
|
ParseRule{},
|
2019-06-02 02:44:59 +00:00
|
|
|
ParseRule{ .prefix = Compiler.literal },
|
2019-06-02 00:32:25 +00:00
|
|
|
ParseRule{ .precedence = .Or },
|
|
|
|
ParseRule{},
|
|
|
|
ParseRule{},
|
|
|
|
ParseRule{},
|
|
|
|
ParseRule{},
|
2019-06-02 02:44:59 +00:00
|
|
|
ParseRule{ .prefix = Compiler.literal },
|
2019-06-02 00:32:25 +00:00
|
|
|
ParseRule{},
|
|
|
|
ParseRule{},
|
|
|
|
ParseRule{},
|
|
|
|
};
|
|
|
|
|
2019-06-03 18:10:12 +00:00
|
|
|
pub const Local = struct {
|
|
|
|
name: tokens.Token,
|
|
|
|
depth: i32,
|
|
|
|
};
|
|
|
|
|
2019-06-01 04:20:06 +00:00
|
|
|
pub const Compiler = struct {
|
2019-06-01 19:12:39 +00:00
|
|
|
src: []const u8,
|
2019-06-01 19:37:24 +00:00
|
|
|
stdout: vm.StdOut,
|
2019-06-01 19:12:39 +00:00
|
|
|
allocator: *Allocator,
|
2019-06-01 23:33:43 +00:00
|
|
|
parser: Parser,
|
|
|
|
scanr: Scanner = undefined,
|
|
|
|
chunk: *chunks.Chunk,
|
2019-06-02 00:32:25 +00:00
|
|
|
debug_flag: bool = false,
|
2019-06-02 17:52:19 +00:00
|
|
|
vmach: *vm.VM,
|
2019-06-01 04:20:06 +00:00
|
|
|
|
2019-06-03 18:10:12 +00:00
|
|
|
locals: [256]Local,
|
2019-06-03 19:10:49 +00:00
|
|
|
localCount: i32 = 0,
|
|
|
|
scopeDepth: i32 = 0,
|
2019-06-03 18:10:12 +00:00
|
|
|
|
2019-06-01 19:37:24 +00:00
|
|
|
pub fn init(
|
|
|
|
allocator: *Allocator,
|
2019-06-01 23:33:43 +00:00
|
|
|
chunk: *chunks.Chunk,
|
2019-06-01 19:37:24 +00:00
|
|
|
stdout: vm.StdOut,
|
|
|
|
source: []const u8,
|
2019-06-02 00:32:25 +00:00
|
|
|
debug_flag: bool,
|
2019-06-02 17:52:19 +00:00
|
|
|
vmach: *vm.VM,
|
2019-06-01 19:37:24 +00:00
|
|
|
) Compiler {
|
|
|
|
return Compiler{
|
|
|
|
.src = source,
|
2019-06-01 23:33:43 +00:00
|
|
|
.chunk = chunk,
|
2019-06-01 19:37:24 +00:00
|
|
|
.allocator = allocator,
|
|
|
|
.stdout = stdout,
|
2019-06-01 23:33:43 +00:00
|
|
|
.parser = Parser{},
|
2019-06-02 00:32:25 +00:00
|
|
|
.debug_flag = debug_flag,
|
2019-06-02 17:52:19 +00:00
|
|
|
.vmach = vmach,
|
2019-06-03 18:10:12 +00:00
|
|
|
|
|
|
|
// local variable resolution
|
|
|
|
.locals = []Local{Local{
|
|
|
|
.name = Token{},
|
|
|
|
.depth = -1,
|
|
|
|
}} ** 256,
|
2019-06-01 19:37:24 +00:00
|
|
|
};
|
2019-06-01 04:20:06 +00:00
|
|
|
}
|
|
|
|
|
2019-06-01 23:33:43 +00:00
|
|
|
fn errorAt(self: *Compiler, token: Token, msg: []const u8) void {
|
|
|
|
if (self.parser.panicMode) return;
|
|
|
|
self.parser.panicMode = true;
|
|
|
|
|
|
|
|
std.debug.warn("[line {}] Error", token.line);
|
|
|
|
if (token.ttype == TokenType.EOF) {
|
|
|
|
std.debug.warn(" at end");
|
|
|
|
} else {
|
|
|
|
std.debug.warn(" at '{}'", token.lexeme);
|
|
|
|
}
|
|
|
|
|
|
|
|
std.debug.warn(": {}\n", msg);
|
|
|
|
self.parser.hadError = true;
|
|
|
|
}
|
2019-06-01 19:37:24 +00:00
|
|
|
|
2019-06-01 23:33:43 +00:00
|
|
|
fn errorCurrent(self: *Compiler, msg: []const u8) void {
|
|
|
|
self.errorAt(self.parser.current, msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn errorPrevious(self: *Compiler, msg: []const u8) void {
|
|
|
|
self.errorAt(self.parser.previous, msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn advance(self: *Compiler) !void {
|
|
|
|
self.parser.previous = self.parser.current;
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
var token_opt = try self.scanr.scanToken();
|
2019-06-01 20:07:22 +00:00
|
|
|
if (token_opt) |token| {
|
2019-06-01 23:33:43 +00:00
|
|
|
self.parser.current = token;
|
2019-06-01 20:07:22 +00:00
|
|
|
break;
|
2019-06-01 19:37:24 +00:00
|
|
|
}
|
|
|
|
}
|
2019-06-01 19:21:36 +00:00
|
|
|
}
|
2019-06-01 23:33:43 +00:00
|
|
|
|
|
|
|
fn consume(self: *Compiler, ttype: TokenType, msg: []const u8) !void {
|
|
|
|
if (self.parser.current.ttype == ttype) {
|
|
|
|
try self.advance();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.errorCurrent(msg);
|
|
|
|
}
|
|
|
|
|
2019-06-02 20:28:54 +00:00
|
|
|
fn check(self: *Compiler, ttype: TokenType) bool {
|
|
|
|
return self.parser.current.ttype == ttype;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn match(self: *Compiler, ttype: TokenType) !bool {
|
|
|
|
if (!(self.check(ttype))) return false;
|
|
|
|
|
|
|
|
try self.advance();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-06-01 23:33:43 +00:00
|
|
|
fn currentChunk(self: *Compiler) *chunks.Chunk {
|
|
|
|
return self.chunk;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn emitByte(self: *Compiler, byte: u8) !void {
|
|
|
|
try self.currentChunk().write(byte, self.parser.previous.line);
|
|
|
|
}
|
|
|
|
|
2019-06-02 03:16:33 +00:00
|
|
|
fn emitBytes(self: *Compiler, byte1: u8, byte2: u8) !void {
|
2019-06-01 23:33:43 +00:00
|
|
|
try self.emitByte(byte1);
|
|
|
|
try self.emitByte(byte2);
|
|
|
|
}
|
|
|
|
|
2019-06-01 23:48:26 +00:00
|
|
|
fn emitReturn(self: *Compiler) !void {
|
|
|
|
try self.emitByte(OpCode.Return);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn emitConstant(self: *Compiler, value: Value) !void {
|
2019-06-03 01:52:19 +00:00
|
|
|
_ = try self.currentChunk().writeConstant(
|
2019-06-01 23:48:26 +00:00
|
|
|
value,
|
|
|
|
self.parser.previous.line,
|
|
|
|
);
|
2019-06-01 23:33:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn end(self: *Compiler) !void {
|
2019-06-01 23:48:26 +00:00
|
|
|
try self.emitReturn();
|
2019-06-02 00:32:25 +00:00
|
|
|
|
|
|
|
if (self.debug_flag and !self.parser.hadError) {
|
|
|
|
try self.currentChunk().disassemble(self.stdout, "code");
|
|
|
|
}
|
2019-06-01 23:48:26 +00:00
|
|
|
}
|
|
|
|
|
2019-06-03 18:17:07 +00:00
|
|
|
fn beginScope(self: *Compiler) void {
|
|
|
|
self.scopeDepth += 1;
|
|
|
|
}
|
|
|
|
|
2019-06-03 19:10:49 +00:00
|
|
|
fn endScope(self: *Compiler) !void {
|
2019-06-03 18:17:07 +00:00
|
|
|
self.scopeDepth -= 1;
|
2019-06-03 19:10:49 +00:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
2019-06-03 18:17:07 +00:00
|
|
|
}
|
|
|
|
|
2019-06-03 04:41:22 +00:00
|
|
|
fn grouping(self: *Compiler, canAssign: bool) !void {
|
2019-06-01 23:48:26 +00:00
|
|
|
try self.expression();
|
|
|
|
try self.consume(.RIGHT_PAREN, "Expect ')' after expression.");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Emits bytecode for a number being loaded into the code.
|
2019-06-03 04:41:22 +00:00
|
|
|
fn number(self: *Compiler, canAssign: bool) !void {
|
2019-06-02 00:32:25 +00:00
|
|
|
var value: f64 = try std.fmt.parseFloat(
|
|
|
|
f64,
|
|
|
|
self.parser.previous.lexeme,
|
|
|
|
);
|
2019-06-02 02:33:53 +00:00
|
|
|
try self.emitConstant(values.NumberVal(value));
|
2019-06-01 23:33:43 +00:00
|
|
|
}
|
|
|
|
|
2019-06-03 04:41:22 +00:00
|
|
|
fn string(self: *Compiler, canAssign: bool) !void {
|
2019-06-02 17:18:01 +00:00
|
|
|
const lexeme_len = self.parser.previous.lexeme.len;
|
2019-06-02 17:52:19 +00:00
|
|
|
|
2019-06-02 17:01:54 +00:00
|
|
|
try self.emitConstant(values.ObjVal(try objects.copyString(
|
2019-06-02 17:52:19 +00:00
|
|
|
self.vmach,
|
2019-06-02 17:18:01 +00:00
|
|
|
self.parser.previous.lexeme[1 .. lexeme_len - 1],
|
2019-06-02 17:01:54 +00:00
|
|
|
)));
|
|
|
|
}
|
|
|
|
|
2019-06-03 19:10:49 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-06-03 04:41:22 +00:00
|
|
|
fn namedVariable(self: *Compiler, tok: *Token, canAssign: bool) !void {
|
2019-06-03 03:55:50 +00:00
|
|
|
// writeConstant always writes OP_CODE which may be not
|
|
|
|
// what we want, so.
|
2019-06-03 19:10:49 +00:00
|
|
|
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);
|
2019-06-03 03:55:50 +00:00
|
|
|
|
2019-06-03 04:41:22 +00:00
|
|
|
if (canAssign and try self.match(.EQUAL)) {
|
|
|
|
try self.expression();
|
2019-06-03 19:10:49 +00:00
|
|
|
try self.emitBytes(setOp, idx);
|
2019-06-03 04:41:22 +00:00
|
|
|
} else {
|
2019-06-03 19:10:49 +00:00
|
|
|
try self.emitBytes(getOp, idx);
|
2019-06-03 04:41:22 +00:00
|
|
|
}
|
2019-06-03 02:43:12 +00:00
|
|
|
}
|
|
|
|
|
2019-06-03 04:41:22 +00:00
|
|
|
fn variable(self: *Compiler, canAssign: bool) !void {
|
|
|
|
try self.namedVariable(&self.parser.previous, canAssign);
|
2019-06-03 02:43:12 +00:00
|
|
|
}
|
|
|
|
|
2019-06-01 23:48:26 +00:00
|
|
|
/// Emits bytecode for a given unary.
|
2019-06-03 04:41:22 +00:00
|
|
|
fn unary(self: *Compiler, canAssign: bool) !void {
|
2019-06-01 23:48:26 +00:00
|
|
|
var ttype = self.parser.previous.ttype;
|
2019-06-02 00:32:25 +00:00
|
|
|
try self.parsePrecedence(.Unary);
|
|
|
|
|
2019-06-01 23:48:26 +00:00
|
|
|
switch (ttype) {
|
|
|
|
.MINUS => try self.emitByte(OpCode.Negate),
|
2019-06-02 03:02:37 +00:00
|
|
|
.BANG => try self.emitByte(OpCode.Not),
|
2019-06-01 23:48:26 +00:00
|
|
|
else => unreachable,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-03 04:41:22 +00:00
|
|
|
fn binary(self: *Compiler, canAssign: bool) !void {
|
2019-06-02 00:32:25 +00:00
|
|
|
var op_type = self.parser.previous.ttype;
|
|
|
|
var rule: *ParseRule = self.getRule(op_type);
|
|
|
|
try self.parsePrecedence(@intToEnum(Precedence, @enumToInt(rule.precedence) + 1));
|
|
|
|
|
|
|
|
switch (op_type) {
|
|
|
|
.PLUS => try self.emitByte(OpCode.Add),
|
|
|
|
.MINUS => try self.emitByte(OpCode.Subtract),
|
|
|
|
.STAR => try self.emitByte(OpCode.Multiply),
|
|
|
|
.SLASH => try self.emitByte(OpCode.Divide),
|
2019-06-02 03:16:33 +00:00
|
|
|
|
|
|
|
.EQUAL_EQUAL => try self.emitByte(OpCode.Equal),
|
|
|
|
.GREATER => try self.emitByte(OpCode.Greater),
|
|
|
|
.LESS => try self.emitByte(OpCode.Less),
|
|
|
|
|
|
|
|
.BANG_EQUAL => try self.emitBytes(OpCode.Equal, OpCode.Not),
|
|
|
|
.GREATER_EQUAL => try self.emitBytes(OpCode.Less, OpCode.Not),
|
|
|
|
.LESS_EQUAL => try self.emitBytes(OpCode.Greater, OpCode.Not),
|
|
|
|
|
2019-06-02 00:32:25 +00:00
|
|
|
else => unreachable,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-03 04:41:22 +00:00
|
|
|
fn literal(self: *Compiler, canAssign: bool) !void {
|
2019-06-02 02:44:59 +00:00
|
|
|
switch (self.parser.previous.ttype) {
|
|
|
|
.FALSE => try self.emitByte(OpCode.False),
|
|
|
|
.NIL => try self.emitByte(OpCode.Nil),
|
|
|
|
.TRUE => try self.emitByte(OpCode.True),
|
|
|
|
else => unreachable,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-03 04:41:22 +00:00
|
|
|
fn parsePrecedence(self: *Compiler, precedence: Precedence) anyerror!void {
|
2019-06-02 00:32:25 +00:00
|
|
|
try self.advance();
|
|
|
|
var as_int = @enumToInt(precedence);
|
|
|
|
var prefix_rule_opt = self.getRule(self.parser.previous.ttype).prefix;
|
|
|
|
|
|
|
|
if (prefix_rule_opt) |prefix_rule| {
|
2019-06-03 04:41:22 +00:00
|
|
|
var canAssign: bool = as_int <= @enumToInt(Precedence.Assignment);
|
|
|
|
try prefix_rule(self, canAssign);
|
2019-06-02 00:32:25 +00:00
|
|
|
|
|
|
|
while (as_int <= @enumToInt(self.getRule(self.parser.current.ttype).precedence)) {
|
|
|
|
try self.advance();
|
|
|
|
var infix_rule_opt = self.getRule(self.parser.previous.ttype).infix;
|
|
|
|
if (infix_rule_opt) |infix_rule| {
|
2019-06-03 04:41:22 +00:00
|
|
|
try infix_rule(self, canAssign);
|
2019-06-02 00:32:25 +00:00
|
|
|
}
|
|
|
|
}
|
2019-06-03 04:41:22 +00:00
|
|
|
|
|
|
|
if (canAssign and try self.match(.EQUAL)) {
|
|
|
|
self.errorPrevious("Invalid assignment target.");
|
|
|
|
try self.expression();
|
|
|
|
}
|
2019-06-02 00:32:25 +00:00
|
|
|
} else {
|
|
|
|
self.errorPrevious("Expect expression.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn getRule(self: *Compiler, ttype: TokenType) *ParseRule {
|
|
|
|
return &rules[@enumToInt(ttype)];
|
|
|
|
}
|
|
|
|
|
2019-06-03 04:41:22 +00:00
|
|
|
fn expression(self: *Compiler) anyerror!void {
|
2019-06-02 00:32:25 +00:00
|
|
|
try self.parsePrecedence(.Assignment);
|
|
|
|
}
|
2019-06-01 23:48:26 +00:00
|
|
|
|
2019-06-02 20:28:54 +00:00
|
|
|
fn printStmt(self: *Compiler) !void {
|
|
|
|
try self.expression();
|
|
|
|
try self.consume(.SEMICOLON, "Expect ';' after value.");
|
|
|
|
try self.emitByte(OpCode.Print);
|
|
|
|
}
|
|
|
|
|
2019-06-02 21:04:36 +00:00
|
|
|
fn exprStmt(self: *Compiler) !void {
|
|
|
|
try self.expression();
|
|
|
|
try self.consume(.SEMICOLON, "Expect ';' after expression.");
|
|
|
|
try self.emitByte(OpCode.Pop);
|
|
|
|
}
|
|
|
|
|
2019-06-02 21:11:23 +00:00
|
|
|
fn synchronize(self: *Compiler) !void {
|
|
|
|
self.parser.panicMode = false;
|
|
|
|
|
|
|
|
while (self.parser.current.ttype != .EOF) {
|
|
|
|
if (self.parser.previous.ttype == .SEMICOLON) return;
|
|
|
|
|
|
|
|
switch (self.parser.current.ttype) {
|
|
|
|
.CLASS, .FUN, .VAR, .FOR, .IF, .WHILE, .PRINT, .RETURN => return,
|
|
|
|
else => {},
|
|
|
|
}
|
|
|
|
|
|
|
|
try self.advance();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-03 01:52:19 +00:00
|
|
|
/// Write an identifier constant to the bytecode.
|
|
|
|
fn identifierConstant(
|
|
|
|
self: *Compiler,
|
|
|
|
token: *Token,
|
|
|
|
) !chunks.ConstantIndex {
|
2019-06-03 04:41:22 +00:00
|
|
|
return try self.currentChunk().writeConstantRaw(values.ObjVal(try objects.copyString(
|
2019-06-03 01:52:19 +00:00
|
|
|
self.vmach,
|
|
|
|
token.lexeme,
|
|
|
|
)), token.line);
|
|
|
|
}
|
|
|
|
|
2019-06-03 19:10:49 +00:00
|
|
|
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.*);
|
|
|
|
}
|
|
|
|
|
2019-06-03 01:52:19 +00:00
|
|
|
fn parseVariable(self: *Compiler, msg: []const u8) !chunks.ConstantIndex {
|
|
|
|
try self.consume(.IDENTIFIER, msg);
|
2019-06-03 19:10:49 +00:00
|
|
|
self.declareVariable();
|
|
|
|
if (self.scopeDepth > 0) return chunks.ConstantIndex{ .Small = 0 };
|
2019-06-03 01:52:19 +00:00
|
|
|
return try self.identifierConstant(&self.parser.previous);
|
|
|
|
}
|
|
|
|
|
2019-06-03 02:43:12 +00:00
|
|
|
fn emitConstWithIndex(
|
|
|
|
self: *Compiler,
|
|
|
|
op_short: u8,
|
|
|
|
op_long: u8,
|
|
|
|
idx: chunks.ConstantIndex,
|
|
|
|
) !void {
|
|
|
|
switch (idx) {
|
|
|
|
.Small => |val| try self.emitBytes(op_short, val),
|
2019-06-03 01:52:19 +00:00
|
|
|
.Long => |val| blk: {
|
2019-06-03 02:43:12 +00:00
|
|
|
try self.emitByte(op_long);
|
2019-06-03 01:52:19 +00:00
|
|
|
try self.emitByte(val[0]);
|
|
|
|
try self.emitByte(val[1]);
|
|
|
|
try self.emitByte(val[2]);
|
|
|
|
},
|
|
|
|
else => unreachable,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-03 02:43:12 +00:00
|
|
|
fn defineVariable(self: *Compiler, global: chunks.ConstantIndex) !void {
|
2019-06-03 19:10:49 +00:00
|
|
|
if (self.scopeDepth > 0) return;
|
|
|
|
|
2019-06-03 02:43:12 +00:00
|
|
|
try self.emitConstWithIndex(
|
|
|
|
chunks.OpCode.DefineGlobal,
|
|
|
|
chunks.OpCode.DefineGlobalLong,
|
|
|
|
global,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-06-03 01:52:19 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-06-03 19:10:49 +00:00
|
|
|
// check scopeDepth here
|
|
|
|
|
2019-06-03 01:52:19 +00:00
|
|
|
try self.consume(.SEMICOLON, "Expect ';' after variable declaration.");
|
|
|
|
try self.defineVariable(global);
|
|
|
|
}
|
|
|
|
|
2019-06-03 18:17:07 +00:00
|
|
|
fn declaration(self: *Compiler) anyerror!void {
|
2019-06-03 01:52:19 +00:00
|
|
|
if (try self.match(.VAR)) {
|
|
|
|
try self.varDecl();
|
|
|
|
} else {
|
|
|
|
try self.statement();
|
|
|
|
}
|
2019-06-02 21:11:23 +00:00
|
|
|
if (self.parser.panicMode) try self.synchronize();
|
2019-06-02 20:28:54 +00:00
|
|
|
}
|
|
|
|
|
2019-06-03 18:17:07 +00:00
|
|
|
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.");
|
|
|
|
}
|
|
|
|
|
2019-06-02 20:28:54 +00:00
|
|
|
fn statement(self: *Compiler) !void {
|
|
|
|
if (try self.match(.PRINT)) {
|
|
|
|
try self.printStmt();
|
2019-06-03 18:17:07 +00:00
|
|
|
} else if (try self.match(.LEFT_BRACE)) {
|
|
|
|
self.beginScope();
|
|
|
|
try self.block();
|
2019-06-03 19:10:49 +00:00
|
|
|
try self.endScope();
|
2019-06-02 21:04:36 +00:00
|
|
|
} else {
|
|
|
|
try self.exprStmt();
|
2019-06-02 20:28:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-01 23:33:43 +00:00
|
|
|
/// Compile the source given when initializing the compiler
|
|
|
|
/// into the given chunk.
|
|
|
|
pub fn compile(self: *Compiler, chunk: *Chunk) !bool {
|
|
|
|
self.scanr = try scanner.Scanner.init(self.allocator, self.src);
|
|
|
|
|
|
|
|
try self.advance();
|
2019-06-02 20:28:54 +00:00
|
|
|
while (!(try self.match(.EOF))) {
|
|
|
|
try self.declaration();
|
|
|
|
}
|
|
|
|
// try self.expression();
|
|
|
|
// try self.consume(.EOF, "Expect end of expression.");
|
2019-06-01 23:33:43 +00:00
|
|
|
try self.end();
|
|
|
|
|
|
|
|
return !self.parser.hadError;
|
|
|
|
}
|
2019-06-01 04:20:06 +00:00
|
|
|
};
|