feat(tazjin/rlox): Add support for statements

First part of
https://craftinginterpreters.com/statements-and-state.html

Supports print statements, as well as evaluation for the sake of
it (i.e. future side-effects).

Change-Id: Ic6653b568f98d6cfe3f297615b7113c0ba1d9a70
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2287
Reviewed-by: tazjin <mail@tazj.in>
Tested-by: BuildkiteCI
This commit is contained in:
Vincent Ambo 2020-12-20 23:55:40 +01:00 committed by tazjin
parent c3bbb861b8
commit 75ae25daa9
3 changed files with 73 additions and 35 deletions

View file

@ -4,6 +4,7 @@ pub enum ErrorKind {
UnterminatedString,
UnmatchedParens,
ExpectedExpression(String),
ExpectedSemicolon,
TypeError(String),
}

View file

@ -1,33 +1,25 @@
use crate::errors::{report, Error, ErrorKind};
use crate::parser::{self, Expr, Literal};
use crate::scanner::{self, Token, TokenKind};
use crate::parser::{self, Expr, Literal, Program, Statement};
use crate::scanner::{self, TokenKind};
// Run some Lox code and print it to stdout
pub fn run(code: &str) {
let chars: Vec<char> = code.chars().collect();
match scanner::scan(&chars) {
Ok(tokens) => {
print_tokens(&tokens);
match parser::parse(tokens) {
Ok(expr) => {
println!("Expression:\n{:?}", expr);
println!("Result: {:?}", eval(&expr));
Ok(tokens) => match parser::parse(tokens) {
Ok(program) => {
println!("Program:\n{:?}", program);
if let Err(err) = run_program(&program) {
println!("Error in program: {:?}", err);
}
Err(errors) => report_errors(errors),
}
}
Err(errors) => report_errors(errors),
},
Err(errors) => report_errors(errors),
}
}
fn print_tokens<'a>(tokens: &Vec<Token<'a>>) {
println!("Tokens:");
for token in tokens {
println!("{:?}", token);
}
}
fn report_errors(errors: Vec<Error>) {
for error in errors {
report(&error);
@ -111,3 +103,19 @@ fn eval<'a>(expr: &Expr<'a>) -> Result<Literal, Error> {
Expr::Binary(binary) => eval_binary(binary),
}
}
fn run_program<'a>(program: &Program<'a>) -> Result<(), Error> {
for stmt in program {
match stmt {
Statement::Expr(expr) => {
eval(expr)?;
}
Statement::Print(expr) => {
let result = eval(expr)?;
println!("{:?}", result)
}
}
}
Ok(())
}

View file

@ -42,9 +42,25 @@ pub enum Expr<'a> {
Unary(Unary<'a>),
}
#[derive(Debug)]
pub enum Statement<'a> {
Expr(Expr<'a>),
Print(Expr<'a>),
}
pub type Program<'a> = Vec<Statement<'a>>;
// Parser
/*
program statement* EOF ;
statement exprStmt
| printStmt ;
exprStmt expression ";" ;
printStmt "print" expression ";" ;
expression equality ;
equality comparison ( ( "!=" | "==" ) comparison )* ;
comparison term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
@ -62,10 +78,31 @@ struct Parser<'a> {
}
type ExprResult<'a> = Result<Expr<'a>, Error>;
type StmtResult<'a> = Result<Statement<'a>, Error>;
impl<'a> Parser<'a> {
// recursive-descent parser functions
fn statement(&mut self) -> StmtResult<'a> {
if self.match_token(&[TokenKind::Print]) {
self.print_statement()
} else {
self.expr_statement()
}
}
fn print_statement(&mut self) -> StmtResult<'a> {
let expr = self.expression()?;
self.consume(&TokenKind::Semicolon, ErrorKind::ExpectedSemicolon)?;
Ok(Statement::Print(expr))
}
fn expr_statement(&mut self) -> StmtResult<'a> {
let expr = self.expression()?;
self.consume(&TokenKind::Semicolon, ErrorKind::ExpectedSemicolon)?;
Ok(Statement::Expr(expr))
}
fn expression(&mut self) -> ExprResult<'a> {
self.equality()
}
@ -231,34 +268,26 @@ impl<'a> Parser<'a> {
}
}
pub fn parse<'a>(tokens: Vec<Token<'a>>) -> Result<Expr<'a>, Vec<Error>> {
pub fn parse<'a>(tokens: Vec<Token<'a>>) -> Result<Program<'a>, Vec<Error>> {
let mut parser = Parser { tokens, current: 0 };
let mut program: Program<'a> = vec![];
let mut errors: Vec<Error> = vec![];
while !parser.is_at_end() {
match parser.expression() {
match parser.statement() {
Err(err) => {
errors.push(err);
parser.synchronise();
}
Ok(expr) => {
if !parser.is_at_end() {
// TODO(tazjin): This isn't a functional language
// - multiple statements should be allowed, at
// some point.
let current = &parser.tokens[parser.current];
errors.push(Error {
line: current.line,
kind: ErrorKind::UnexpectedChar(current.lexeme[0]),
});
}
if errors.is_empty() {
return Ok(expr);
}
Ok(stmt) => {
program.push(stmt);
}
}
}
return Err(errors);
if errors.is_empty() {
Ok(program)
} else {
Err(errors)
}
}