jorts/src/compiler.zig

368 lines
10 KiB
Zig
Raw Normal View History

const std = @import("std");
2019-06-02 16:17:32 +00:00
const scanner = @import("scanner.zig");
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");
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
/// 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,
};
/// 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,
};
const ParseFn = fn (*Compiler) anyerror!void;
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 },
// this is specifically for the != operator
2019-06-02 03:16:33 +00:00
ParseRule{ .infix = Compiler.binary, .precedence = .Equality },
ParseRule{},
// this is specifically for the == operator
2019-06-02 03:16:33 +00:00
ParseRule{ .infix = Compiler.binary, .precedence = .Equality },
// 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 },
ParseRule{},
2019-06-02 17:01:54 +00:00
ParseRule{ .prefix = Compiler.string },
ParseRule{ .prefix = Compiler.number },
ParseRule{ .precedence = .And },
ParseRule{},
ParseRule{},
// false
ParseRule{ .prefix = Compiler.literal },
ParseRule{},
ParseRule{},
ParseRule{},
ParseRule{ .prefix = Compiler.literal },
ParseRule{ .precedence = .Or },
ParseRule{},
ParseRule{},
ParseRule{},
ParseRule{},
ParseRule{ .prefix = Compiler.literal },
ParseRule{},
ParseRule{},
ParseRule{},
};
pub const Compiler = struct {
src: []const u8,
stdout: vm.StdOut,
allocator: *Allocator,
2019-06-01 23:33:43 +00:00
parser: Parser,
scanr: Scanner = undefined,
chunk: *chunks.Chunk,
debug_flag: bool = false,
vmach: *vm.VM,
pub fn init(
allocator: *Allocator,
2019-06-01 23:33:43 +00:00
chunk: *chunks.Chunk,
stdout: vm.StdOut,
source: []const u8,
debug_flag: bool,
vmach: *vm.VM,
) Compiler {
return Compiler{
.src = source,
2019-06-01 23:33:43 +00:00
.chunk = chunk,
.allocator = allocator,
.stdout = stdout,
2019-06-01 23:33:43 +00:00
.parser = Parser{},
.debug_flag = debug_flag,
.vmach = vmach,
};
}
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 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();
if (token_opt) |token| {
2019-06-01 23:33:43 +00:00
self.parser.current = token;
break;
}
}
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);
}
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 {
try self.currentChunk().writeConstant(
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();
if (self.debug_flag and !self.parser.hadError) {
try self.currentChunk().disassemble(self.stdout, "code");
}
2019-06-01 23:48:26 +00:00
}
fn grouping(self: *Compiler) !void {
try self.expression();
try self.consume(.RIGHT_PAREN, "Expect ')' after expression.");
}
/// Emits bytecode for a number being loaded into the code.
fn number(self: *Compiler) !void {
var value: f64 = try std.fmt.parseFloat(
f64,
self.parser.previous.lexeme,
);
try self.emitConstant(values.NumberVal(value));
2019-06-01 23:33:43 +00:00
}
2019-06-02 17:01:54 +00:00
fn string(self: *Compiler) !void {
const lexeme_len = self.parser.previous.lexeme.len;
2019-06-02 17:01:54 +00:00
try self.emitConstant(values.ObjVal(try objects.copyString(
self.vmach,
self.parser.previous.lexeme[1 .. lexeme_len - 1],
2019-06-02 17:01:54 +00:00
)));
}
2019-06-01 23:48:26 +00:00
/// Emits bytecode for a given unary.
fn unary(self: *Compiler) !void {
var ttype = self.parser.previous.ttype;
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,
}
}
fn binary(self: *Compiler) !void {
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),
else => unreachable,
}
}
fn literal(self: *Compiler) !void {
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,
}
}
fn parsePrecedence(self: *Compiler, precedence: Precedence) !void {
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| {
try prefix_rule(self);
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| {
try infix_rule(self);
}
}
} else {
self.errorPrevious("Expect expression.");
return;
}
}
fn getRule(self: *Compiler, ttype: TokenType) *ParseRule {
return &rules[@enumToInt(ttype)];
}
fn expression(self: *Compiler) !void {
try self.parsePrecedence(.Assignment);
}
2019-06-01 23:48:26 +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);
}
fn declaration(self: *Compiler) !void {
try self.statement();
}
fn statement(self: *Compiler) !void {
if (try self.match(.PRINT)) {
try self.printStmt();
2019-06-02 21:04:36 +00:00
} else {
try self.exprStmt();
}
}
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();
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;
}
};