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:
		
							parent
							
								
									c3bbb861b8
								
							
						
					
					
						commit
						75ae25daa9
					
				
					 3 changed files with 73 additions and 35 deletions
				
			
		| 
						 | 
					@ -4,6 +4,7 @@ pub enum ErrorKind {
 | 
				
			||||||
    UnterminatedString,
 | 
					    UnterminatedString,
 | 
				
			||||||
    UnmatchedParens,
 | 
					    UnmatchedParens,
 | 
				
			||||||
    ExpectedExpression(String),
 | 
					    ExpectedExpression(String),
 | 
				
			||||||
 | 
					    ExpectedSemicolon,
 | 
				
			||||||
    TypeError(String),
 | 
					    TypeError(String),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,30 +1,22 @@
 | 
				
			||||||
use crate::errors::{report, Error, ErrorKind};
 | 
					use crate::errors::{report, Error, ErrorKind};
 | 
				
			||||||
use crate::parser::{self, Expr, Literal};
 | 
					use crate::parser::{self, Expr, Literal, Program, Statement};
 | 
				
			||||||
use crate::scanner::{self, Token, TokenKind};
 | 
					use crate::scanner::{self, 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) {
 | 
				
			||||||
    let chars: Vec<char> = code.chars().collect();
 | 
					    let chars: Vec<char> = code.chars().collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    match scanner::scan(&chars) {
 | 
					    match scanner::scan(&chars) {
 | 
				
			||||||
        Ok(tokens) => {
 | 
					        Ok(tokens) => match parser::parse(tokens) {
 | 
				
			||||||
            print_tokens(&tokens);
 | 
					            Ok(program) => {
 | 
				
			||||||
            match parser::parse(tokens) {
 | 
					                println!("Program:\n{:?}", program);
 | 
				
			||||||
                Ok(expr) => {
 | 
					                if let Err(err) = run_program(&program) {
 | 
				
			||||||
                    println!("Expression:\n{:?}", expr);
 | 
					                    println!("Error in program: {:?}", err);
 | 
				
			||||||
                    println!("Result: {:?}", eval(&expr));
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                Err(errors) => report_errors(errors),
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            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);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -111,3 +103,19 @@ fn eval<'a>(expr: &Expr<'a>) -> Result<Literal, Error> {
 | 
				
			||||||
        Expr::Binary(binary) => eval_binary(binary),
 | 
					        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(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,9 +42,25 @@ pub enum Expr<'a> {
 | 
				
			||||||
    Unary(Unary<'a>),
 | 
					    Unary(Unary<'a>),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub enum Statement<'a> {
 | 
				
			||||||
 | 
					    Expr(Expr<'a>),
 | 
				
			||||||
 | 
					    Print(Expr<'a>),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub type Program<'a> = Vec<Statement<'a>>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Parser
 | 
					// Parser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 | 
					program        → statement* EOF ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					statement      → exprStmt
 | 
				
			||||||
 | 
					               | printStmt ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exprStmt       → expression ";" ;
 | 
				
			||||||
 | 
					printStmt      → "print" expression ";" ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
expression     → equality ;
 | 
					expression     → equality ;
 | 
				
			||||||
equality       → comparison ( ( "!=" | "==" ) comparison )* ;
 | 
					equality       → comparison ( ( "!=" | "==" ) comparison )* ;
 | 
				
			||||||
comparison     → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
 | 
					comparison     → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
 | 
				
			||||||
| 
						 | 
					@ -62,10 +78,31 @@ struct Parser<'a> {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ExprResult<'a> = Result<Expr<'a>, Error>;
 | 
					type ExprResult<'a> = Result<Expr<'a>, Error>;
 | 
				
			||||||
 | 
					type StmtResult<'a> = Result<Statement<'a>, Error>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'a> Parser<'a> {
 | 
					impl<'a> Parser<'a> {
 | 
				
			||||||
    // recursive-descent parser functions
 | 
					    // 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> {
 | 
					    fn expression(&mut self) -> ExprResult<'a> {
 | 
				
			||||||
        self.equality()
 | 
					        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 parser = Parser { tokens, current: 0 };
 | 
				
			||||||
 | 
					    let mut program: Program<'a> = vec![];
 | 
				
			||||||
    let mut errors: Vec<Error> = vec![];
 | 
					    let mut errors: Vec<Error> = vec![];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    while !parser.is_at_end() {
 | 
					    while !parser.is_at_end() {
 | 
				
			||||||
        match parser.expression() {
 | 
					        match parser.statement() {
 | 
				
			||||||
            Err(err) => {
 | 
					            Err(err) => {
 | 
				
			||||||
                errors.push(err);
 | 
					                errors.push(err);
 | 
				
			||||||
                parser.synchronise();
 | 
					                parser.synchronise();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Ok(expr) => {
 | 
					            Ok(stmt) => {
 | 
				
			||||||
                if !parser.is_at_end() {
 | 
					                program.push(stmt);
 | 
				
			||||||
                    // 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);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Err(errors);
 | 
					    if errors.is_empty() {
 | 
				
			||||||
 | 
					        Ok(program)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        Err(errors)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue