feat(tazjin/rlox): Set up precedence parsing scaffolding

Defines a new precedence levels enum which can be used to restrict the
parser precedence in any given location. As an example, unary
expressions and grouping are implemented, as these have a different
precedence from e.g. expression()

Change-Id: I91f299fc77530f76c3aba717f638985428104ee5
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2558
Reviewed-by: tazjin <mail@tazj.in>
Tested-by: BuildkiteCI
This commit is contained in:
Vincent Ambo 2021-02-27 16:38:46 +02:00 committed by tazjin
parent b13a6736dd
commit 1d3d9d32e3

View file

@ -1,25 +1,44 @@
use super::chunk::Chunk;
use super::errors::{Error, ErrorKind, LoxResult};
use super::opcode::OpCode;
use crate::scanner;
use super::value::Value;
use crate::scanner::{self, Token, TokenKind};
struct Compiler<T: Iterator<Item = scanner::Token>> {
#[cfg(test)]
mod tests;
struct Compiler<T: Iterator<Item = Token>> {
tokens: T,
chunk: Chunk,
panic: bool,
errors: Vec<Error>,
// TODO(tazjin): Restructure so that these don't need to be Option?
current: Option<scanner::Token>,
previous: Option<scanner::Token>,
current: Option<Token>,
previous: Option<Token>,
}
impl<T: Iterator<Item = scanner::Token>> Compiler<T> {
#[derive(Debug, PartialEq, PartialOrd)]
enum Precedence {
None,
Assignment, // =
Or, // or
And, // and
Equality, // == !=
Comparison, // < > <= >=
Term, // + -
Factor, // * /
Unary, // ! -
Call, // . ()
Primary,
}
impl<T: Iterator<Item = Token>> Compiler<T> {
fn compile(&mut self) -> LoxResult<()> {
self.advance();
self.expression();
self.expression()?;
self.consume(
&scanner::TokenKind::Eof,
&TokenKind::Eof,
ErrorKind::ExpectedToken("Expected end of expression"),
)?;
@ -31,13 +50,44 @@ impl<T: Iterator<Item = scanner::Token>> Compiler<T> {
self.current = self.tokens.next();
}
fn expression(&mut self) {
unimplemented!()
fn expression(&mut self) -> LoxResult<()> {
self.parse_precedence(Precedence::Assignment)
}
// TODO(tazjin): Assumption is that we have access to the previous
// token wherever this ends up invoked. True?
fn number(&mut self, num: f64) {
self.emit_constant(num);
}
fn grouping(&mut self, num: f64) -> LoxResult<()> {
self.expression()?;
self.consume(
&TokenKind::RightParen,
ErrorKind::ExpectedToken("Expected ')' after expression"),
)
}
fn unary(&mut self, kind: &TokenKind) -> LoxResult<()> {
// Compile the operand
self.parse_precedence(Precedence::Unary)?;
// Emit operator instruction
match kind {
TokenKind::Minus => self.emit_op(OpCode::OpNegate),
_ => unreachable!("only called for unary operator tokens"),
}
Ok(())
}
fn parse_precedence(&mut self, precedence: Precedence) -> LoxResult<()> {
unimplemented!("what goes here?")
}
fn consume(
&mut self,
expected: &scanner::TokenKind,
expected: &TokenKind,
err: ErrorKind,
) -> LoxResult<()> {
unimplemented!()
@ -57,13 +107,18 @@ impl<T: Iterator<Item = scanner::Token>> Compiler<T> {
self.current_chunk().add_op(op, line);
}
fn previous(&self) -> &scanner::Token {
fn emit_constant(&mut self, val: Value) {
let idx = self.chunk.add_constant(val);
self.emit_op(OpCode::OpConstant(idx));
}
fn previous(&self) -> &Token {
self.previous
.as_ref()
.expect("invalid internal compiler state: missing previous token")
}
fn error_at(&mut self, token: &scanner::Token, kind: ErrorKind) {
fn error_at(&mut self, token: &Token, kind: ErrorKind) {
if self.panic {
return;
}