feat(tazjin/rlox): Implement tree-walk interpreter of exprs
This is only a subset of the Lox spec so far. It implements the language up to the runtime error chapter on https://craftinginterpreters.com/evaluating-expressions.html Change-Id: I295dbf4b6544420d6fe80b6aaba661fb21acdea6 Reviewed-on: https://cl.tvl.fyi/c/depot/+/2281 Reviewed-by: tazjin <mail@tazj.in> Tested-by: BuildkiteCI
This commit is contained in:
parent
e115e58f9c
commit
bc6775c318
2 changed files with 74 additions and 10 deletions
|
@ -1,6 +1,6 @@
|
||||||
use crate::errors::{report, Error};
|
use crate::errors::{report, Error};
|
||||||
use crate::parser;
|
use crate::parser::{self, Expr, Literal};
|
||||||
use crate::scanner::{self, Token};
|
use crate::scanner::{self, Token, TokenKind};
|
||||||
|
|
||||||
// Run some Lox code and print it to stdout
|
// Run some Lox code and print it to stdout
|
||||||
pub fn run(code: &str) {
|
pub fn run(code: &str) {
|
||||||
|
@ -10,7 +10,10 @@ pub fn run(code: &str) {
|
||||||
Ok(tokens) => {
|
Ok(tokens) => {
|
||||||
print_tokens(&tokens);
|
print_tokens(&tokens);
|
||||||
match parser::parse(tokens) {
|
match parser::parse(tokens) {
|
||||||
Ok(expr) => println!("Expression:\n{:?}", expr),
|
Ok(expr) => {
|
||||||
|
println!("Expression:\n{:?}", expr);
|
||||||
|
println!("Result: {:?}", eval(&expr));
|
||||||
|
}
|
||||||
Err(errors) => report_errors(errors),
|
Err(errors) => report_errors(errors),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,3 +33,64 @@ fn report_errors(errors: Vec<Error>) {
|
||||||
report(&error);
|
report(&error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tree-walk interpreter
|
||||||
|
|
||||||
|
fn eval_truthy(lit: &Literal) -> bool {
|
||||||
|
match lit {
|
||||||
|
Literal::Nil => false,
|
||||||
|
Literal::Boolean(b) => *b,
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_unary<'a>(expr: &parser::Unary<'a>) -> Literal {
|
||||||
|
let right = eval(&*expr.right);
|
||||||
|
|
||||||
|
match (&expr.operator.kind, right) {
|
||||||
|
(TokenKind::Minus, Literal::Number(num)) => Literal::Number(-num),
|
||||||
|
(TokenKind::Bang, right) => Literal::Boolean(!eval_truthy(&right)),
|
||||||
|
_ => unimplemented!("no handling of type errors"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_binary<'a>(expr: &parser::Binary<'a>) -> Literal {
|
||||||
|
let left = eval(&*expr.left);
|
||||||
|
let right = eval(&*expr.right);
|
||||||
|
|
||||||
|
match (&expr.operator.kind, left, right) {
|
||||||
|
// Numeric
|
||||||
|
(TokenKind::Minus, Literal::Number(l), Literal::Number(r)) => Literal::Number(l - r),
|
||||||
|
(TokenKind::Slash, Literal::Number(l), Literal::Number(r)) => Literal::Number(l / r),
|
||||||
|
(TokenKind::Star, Literal::Number(l), Literal::Number(r)) => Literal::Number(l * r),
|
||||||
|
(TokenKind::Plus, Literal::Number(l), Literal::Number(r)) => Literal::Number(l + r),
|
||||||
|
|
||||||
|
// Strings
|
||||||
|
(TokenKind::Plus, Literal::String(l), Literal::String(r)) => {
|
||||||
|
Literal::String(format!("{}{}", l, r))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparators (on numbers only?)
|
||||||
|
(TokenKind::Greater, Literal::Number(l), Literal::Number(r)) => Literal::Boolean(l > r),
|
||||||
|
(TokenKind::GreaterEqual, Literal::Number(l), Literal::Number(r)) => {
|
||||||
|
Literal::Boolean(l >= r)
|
||||||
|
}
|
||||||
|
(TokenKind::Less, Literal::Number(l), Literal::Number(r)) => Literal::Boolean(l < r),
|
||||||
|
(TokenKind::LessEqual, Literal::Number(l), Literal::Number(r)) => Literal::Boolean(l <= r),
|
||||||
|
|
||||||
|
// Equality
|
||||||
|
(TokenKind::Equal, l, r) => Literal::Boolean(l == r),
|
||||||
|
(TokenKind::BangEqual, l, r) => Literal::Boolean(l != r),
|
||||||
|
|
||||||
|
_ => unimplemented!("type errors unhandled"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval<'a>(expr: &Expr<'a>) -> Literal {
|
||||||
|
match expr {
|
||||||
|
Expr::Literal(lit) => lit.clone(),
|
||||||
|
Expr::Grouping(grouping) => eval(&*grouping.0),
|
||||||
|
Expr::Unary(unary) => eval_unary(unary),
|
||||||
|
Expr::Binary(binary) => eval_binary(binary),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,15 +12,15 @@ use crate::scanner::{Token, TokenKind};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Binary<'a> {
|
pub struct Binary<'a> {
|
||||||
left: Box<Expr<'a>>,
|
pub left: Box<Expr<'a>>,
|
||||||
operator: Token<'a>,
|
pub operator: Token<'a>,
|
||||||
right: Box<Expr<'a>>,
|
pub right: Box<Expr<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Grouping<'a>(Box<Expr<'a>>);
|
pub struct Grouping<'a>(pub Box<Expr<'a>>);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Literal {
|
pub enum Literal {
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Number(f64),
|
Number(f64),
|
||||||
|
@ -30,8 +30,8 @@ pub enum Literal {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Unary<'a> {
|
pub struct Unary<'a> {
|
||||||
operator: Token<'a>,
|
pub operator: Token<'a>,
|
||||||
right: Box<Expr<'a>>,
|
pub right: Box<Expr<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
Loading…
Reference in a new issue