From 75ae25daa9e7686bca60b518d9cf442bcfba3bf7 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 20 Dec 2020 23:55:40 +0100 Subject: 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 Tested-by: BuildkiteCI --- users/tazjin/rlox/src/errors.rs | 1 + users/tazjin/rlox/src/interpreter.rs | 42 +++++++++++++---------- users/tazjin/rlox/src/parser.rs | 65 ++++++++++++++++++++++++++---------- 3 files changed, 73 insertions(+), 35 deletions(-) diff --git a/users/tazjin/rlox/src/errors.rs b/users/tazjin/rlox/src/errors.rs index ae7e0515a025..9598893aa352 100644 --- a/users/tazjin/rlox/src/errors.rs +++ b/users/tazjin/rlox/src/errors.rs @@ -4,6 +4,7 @@ pub enum ErrorKind { UnterminatedString, UnmatchedParens, ExpectedExpression(String), + ExpectedSemicolon, TypeError(String), } diff --git a/users/tazjin/rlox/src/interpreter.rs b/users/tazjin/rlox/src/interpreter.rs index 6c411ded5e11..c68101cf68a6 100644 --- a/users/tazjin/rlox/src/interpreter.rs +++ b/users/tazjin/rlox/src/interpreter.rs @@ -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 = 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>) { - println!("Tokens:"); - for token in tokens { - println!("{:?}", token); - } -} - fn report_errors(errors: Vec) { for error in errors { report(&error); @@ -111,3 +103,19 @@ fn eval<'a>(expr: &Expr<'a>) -> Result { 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(()) +} diff --git a/users/tazjin/rlox/src/parser.rs b/users/tazjin/rlox/src/parser.rs index 611a99f01c22..d7155fff10a7 100644 --- a/users/tazjin/rlox/src/parser.rs +++ b/users/tazjin/rlox/src/parser.rs @@ -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>; + // 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, Error>; +type StmtResult<'a> = Result, 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>) -> Result, Vec> { +pub fn parse<'a>(tokens: Vec>) -> Result, Vec> { let mut parser = Parser { tokens, current: 0 }; + let mut program: Program<'a> = vec![]; let mut errors: Vec = 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) + } } -- cgit 1.4.1