compiler: finish parser (for math expressions)
This commit is contained in:
parent
230fef20b5
commit
0f8e19adf1
2 changed files with 152 additions and 6 deletions
150
src/compiler.zig
150
src/compiler.zig
|
@ -13,7 +13,8 @@ const TokenType = tokens.TokenType;
|
||||||
const Value = values.Value;
|
const Value = values.Value;
|
||||||
const OpCode = chunks.OpCode;
|
const OpCode = chunks.OpCode;
|
||||||
|
|
||||||
pub const Parser = struct {
|
/// Holds parser state for the compiler.
|
||||||
|
const Parser = struct {
|
||||||
previous: Token = undefined,
|
previous: Token = undefined,
|
||||||
current: Token = undefined,
|
current: Token = undefined,
|
||||||
|
|
||||||
|
@ -22,6 +23,93 @@ pub const Parser = struct {
|
||||||
panicMode: 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.
|
||||||
|
ParseRule{},
|
||||||
|
// this is specifically for the != operator
|
||||||
|
ParseRule{ .precedence = .Equality },
|
||||||
|
ParseRule{},
|
||||||
|
// this is specifically for the == operator
|
||||||
|
ParseRule{ .precedence = .Equality },
|
||||||
|
|
||||||
|
// all the comparison ones
|
||||||
|
ParseRule{ .precedence = .Comparison },
|
||||||
|
ParseRule{ .precedence = .Comparison },
|
||||||
|
ParseRule{ .precedence = .Comparison },
|
||||||
|
ParseRule{ .precedence = .Comparison },
|
||||||
|
|
||||||
|
ParseRule{},
|
||||||
|
ParseRule{},
|
||||||
|
ParseRule{ .prefix = Compiler.number },
|
||||||
|
ParseRule{ .precedence = .And },
|
||||||
|
ParseRule{},
|
||||||
|
ParseRule{},
|
||||||
|
ParseRule{},
|
||||||
|
ParseRule{},
|
||||||
|
ParseRule{},
|
||||||
|
ParseRule{},
|
||||||
|
ParseRule{},
|
||||||
|
ParseRule{ .precedence = .Or },
|
||||||
|
ParseRule{},
|
||||||
|
ParseRule{},
|
||||||
|
ParseRule{},
|
||||||
|
ParseRule{},
|
||||||
|
ParseRule{},
|
||||||
|
ParseRule{},
|
||||||
|
ParseRule{},
|
||||||
|
ParseRule{},
|
||||||
|
};
|
||||||
|
|
||||||
pub const Compiler = struct {
|
pub const Compiler = struct {
|
||||||
src: []const u8,
|
src: []const u8,
|
||||||
stdout: vm.StdOut,
|
stdout: vm.StdOut,
|
||||||
|
@ -29,12 +117,14 @@ pub const Compiler = struct {
|
||||||
parser: Parser,
|
parser: Parser,
|
||||||
scanr: Scanner = undefined,
|
scanr: Scanner = undefined,
|
||||||
chunk: *chunks.Chunk,
|
chunk: *chunks.Chunk,
|
||||||
|
debug_flag: bool = false,
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
allocator: *Allocator,
|
allocator: *Allocator,
|
||||||
chunk: *chunks.Chunk,
|
chunk: *chunks.Chunk,
|
||||||
stdout: vm.StdOut,
|
stdout: vm.StdOut,
|
||||||
source: []const u8,
|
source: []const u8,
|
||||||
|
debug_flag: bool,
|
||||||
) Compiler {
|
) Compiler {
|
||||||
return Compiler{
|
return Compiler{
|
||||||
.src = source,
|
.src = source,
|
||||||
|
@ -42,6 +132,7 @@ pub const Compiler = struct {
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.stdout = stdout,
|
.stdout = stdout,
|
||||||
.parser = Parser{},
|
.parser = Parser{},
|
||||||
|
.debug_flag = debug_flag,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +208,10 @@ pub const Compiler = struct {
|
||||||
|
|
||||||
fn end(self: *Compiler) !void {
|
fn end(self: *Compiler) !void {
|
||||||
try self.emitReturn();
|
try self.emitReturn();
|
||||||
|
|
||||||
|
if (self.debug_flag and !self.parser.hadError) {
|
||||||
|
try self.currentChunk().disassemble(self.stdout, "code");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn grouping(self: *Compiler) !void {
|
fn grouping(self: *Compiler) !void {
|
||||||
|
@ -126,21 +221,66 @@ pub const Compiler = struct {
|
||||||
|
|
||||||
/// Emits bytecode for a number being loaded into the code.
|
/// Emits bytecode for a number being loaded into the code.
|
||||||
fn number(self: *Compiler) !void {
|
fn number(self: *Compiler) !void {
|
||||||
var value: f64 = try std.fmt.parseFloat(f64, parser.previous.lexeme);
|
var value: f64 = try std.fmt.parseFloat(
|
||||||
|
f64,
|
||||||
|
self.parser.previous.lexeme,
|
||||||
|
);
|
||||||
try self.emitConstant(value);
|
try self.emitConstant(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emits bytecode for a given unary.
|
/// Emits bytecode for a given unary.
|
||||||
fn unary(self: *Compiler) !void {
|
fn unary(self: *Compiler) !void {
|
||||||
var ttype = self.parser.previous.ttype;
|
var ttype = self.parser.previous.ttype;
|
||||||
try self.expression();
|
try self.parsePrecedence(.Unary);
|
||||||
|
|
||||||
switch (ttype) {
|
switch (ttype) {
|
||||||
.MINUS => try self.emitByte(OpCode.Negate),
|
.MINUS => try self.emitByte(OpCode.Negate),
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expression(self: *Compiler) !void {}
|
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),
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/// Compile the source given when initializing the compiler
|
/// Compile the source given when initializing the compiler
|
||||||
/// into the given chunk.
|
/// into the given chunk.
|
||||||
|
@ -148,7 +288,7 @@ pub const Compiler = struct {
|
||||||
self.scanr = try scanner.Scanner.init(self.allocator, self.src);
|
self.scanr = try scanner.Scanner.init(self.allocator, self.src);
|
||||||
|
|
||||||
try self.advance();
|
try self.advance();
|
||||||
//try self.expression();
|
try self.expression();
|
||||||
try self.consume(.EOF, "Expect end of expression.");
|
try self.consume(.EOF, "Expect end of expression.");
|
||||||
try self.end();
|
try self.end();
|
||||||
|
|
||||||
|
|
|
@ -167,7 +167,13 @@ pub const VM = struct {
|
||||||
//return res;
|
//return res;
|
||||||
var chk = try Chunk.init(self.allocator);
|
var chk = try Chunk.init(self.allocator);
|
||||||
|
|
||||||
var cmpr = Compiler.init(self.allocator, &chk, self.stdout, self.src);
|
var cmpr = Compiler.init(
|
||||||
|
self.allocator,
|
||||||
|
&chk,
|
||||||
|
self.stdout,
|
||||||
|
self.src,
|
||||||
|
self.debug_flag,
|
||||||
|
);
|
||||||
if (!try cmpr.compile(&chk)) {
|
if (!try cmpr.compile(&chk)) {
|
||||||
return InterpretResult.CompileError;
|
return InterpretResult.CompileError;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue