mirror of
https://github.com/TheM1Stery/izanami.git
synced 2025-04-20 08:51:12 +00:00
Compare commits
3 Commits
3cfde406d3
...
129d03b74d
Author | SHA1 | Date | |
---|---|---|---|
129d03b74d | |||
31e348643f | |||
9262620e07 |
23
src/ast.rs
23
src/ast.rs
@ -22,4 +22,27 @@ pub enum Expr {
|
|||||||
op: Token,
|
op: Token,
|
||||||
right: Box<Expr>,
|
right: Box<Expr>,
|
||||||
},
|
},
|
||||||
|
Variable {
|
||||||
|
name: Token,
|
||||||
|
},
|
||||||
|
Assign {
|
||||||
|
name: Token,
|
||||||
|
value: Box<Expr>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Stmt {
|
||||||
|
Block {
|
||||||
|
statements: Vec<Stmt>,
|
||||||
|
},
|
||||||
|
Expression {
|
||||||
|
expression: Expr,
|
||||||
|
},
|
||||||
|
Print {
|
||||||
|
expression: Expr,
|
||||||
|
},
|
||||||
|
Var {
|
||||||
|
name: Token,
|
||||||
|
initializer: Option<Expr>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
65
src/environment.rs
Normal file
65
src/environment.rs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
|
use crate::token::{LiteralType, Token};
|
||||||
|
|
||||||
|
pub struct Environment {
|
||||||
|
values: HashMap<String, LiteralType>,
|
||||||
|
enclosing: Option<Rc<RefCell<Environment>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum EnvironmentError {
|
||||||
|
AssignError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Environment {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
values: HashMap::new(),
|
||||||
|
enclosing: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_enclosing(enclosing: &Rc<RefCell<Environment>>) -> Self {
|
||||||
|
Self {
|
||||||
|
values: HashMap::new(),
|
||||||
|
enclosing: Some(Rc::clone(enclosing)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn define(&mut self, name: &str, val: LiteralType) {
|
||||||
|
// do not like this at all. String is allocated each time a variable is defined. Might be
|
||||||
|
// bad or might be good. I don't know :D
|
||||||
|
self.values.insert(name.to_string(), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assign(&mut self, name: &Token, val: LiteralType) -> Result<(), EnvironmentError> {
|
||||||
|
let cloned = val.clone();
|
||||||
|
let assigned = self
|
||||||
|
.values
|
||||||
|
.get_mut(&name.lexeme)
|
||||||
|
.map(|l| *l = val)
|
||||||
|
.ok_or(EnvironmentError::AssignError);
|
||||||
|
|
||||||
|
if assigned.is_err() {
|
||||||
|
if let Some(enclosing) = &mut self.enclosing {
|
||||||
|
return enclosing.borrow_mut().assign(name, cloned);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assigned
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, name: &Token) -> Option<LiteralType> {
|
||||||
|
//self.values.get(&name.lexeme).cloned()
|
||||||
|
|
||||||
|
let value = self.values.get(&name.lexeme);
|
||||||
|
|
||||||
|
if value.is_none() {
|
||||||
|
if let Some(enclosing) = &self.enclosing {
|
||||||
|
return enclosing.borrow().get(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value.cloned()
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,8 @@
|
|||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::Expr,
|
ast::{Expr, Stmt},
|
||||||
|
environment::Environment,
|
||||||
token::{LiteralType, Token, TokenType},
|
token::{LiteralType, Token, TokenType},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -18,27 +21,99 @@ impl RuntimeError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interpret(expr: &Expr) -> Result<LiteralType, RuntimeError> {
|
pub fn interpret(
|
||||||
|
statements: &Vec<Stmt>,
|
||||||
|
environment: &Rc<RefCell<Environment>>,
|
||||||
|
) -> Result<(), RuntimeError> {
|
||||||
|
for statement in statements {
|
||||||
|
execute(statement, &environment)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute(statement: &Stmt, environment: &Rc<RefCell<Environment>>) -> Result<(), RuntimeError> {
|
||||||
|
match statement {
|
||||||
|
Stmt::Expression { expression } => {
|
||||||
|
evaluate(expression, &mut environment.borrow_mut())?;
|
||||||
|
}
|
||||||
|
Stmt::Print { expression } => {
|
||||||
|
let expr = evaluate(expression, &mut environment.borrow_mut())?;
|
||||||
|
println!("{expr}");
|
||||||
|
}
|
||||||
|
Stmt::Var { name, initializer } => {
|
||||||
|
let value = if let Some(initializer) = initializer {
|
||||||
|
evaluate(initializer, &mut environment.borrow_mut())?
|
||||||
|
} else {
|
||||||
|
LiteralType::Nil
|
||||||
|
};
|
||||||
|
environment.borrow_mut().define(&name.lexeme, value);
|
||||||
|
}
|
||||||
|
Stmt::Block { statements } => {
|
||||||
|
execute_block(statements, environment)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_block(
|
||||||
|
statements: &Vec<Stmt>,
|
||||||
|
environment: &Rc<RefCell<Environment>>,
|
||||||
|
) -> Result<(), RuntimeError> {
|
||||||
|
let block_enviroment = Rc::new(RefCell::new(Environment::with_enclosing(environment)));
|
||||||
|
for stmt in statements {
|
||||||
|
execute(stmt, &block_enviroment)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evaluate(expr: &Expr, environment: &mut Environment) -> Result<LiteralType, RuntimeError> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Ternary {
|
Expr::Ternary {
|
||||||
first,
|
first,
|
||||||
second,
|
second,
|
||||||
third,
|
third,
|
||||||
..
|
..
|
||||||
} => ternary(first, second, third),
|
} => ternary(first, second, third, environment),
|
||||||
Expr::Binary { left, op, right } => binary(&interpret(left)?, &interpret(right)?, op),
|
Expr::Binary { left, op, right } => binary(
|
||||||
Expr::Grouping { expression } => interpret(expression),
|
&evaluate(left, environment)?,
|
||||||
|
&evaluate(right, environment)?,
|
||||||
|
op,
|
||||||
|
),
|
||||||
|
Expr::Grouping { expression } => evaluate(expression, environment),
|
||||||
Expr::Literal { value } => Ok(value.clone()),
|
Expr::Literal { value } => Ok(value.clone()),
|
||||||
Expr::Unary { op, right } => Ok(unary(&interpret(right)?, op)),
|
Expr::Unary { op, right } => Ok(unary(&evaluate(right, environment)?, op)),
|
||||||
|
Expr::Variable { name } => environment.get(name).ok_or_else(|| RuntimeError {
|
||||||
|
token: name.clone(),
|
||||||
|
message: format!("Undefined variable {}.", name.lexeme),
|
||||||
|
}),
|
||||||
|
Expr::Assign { name, value } => {
|
||||||
|
let value = evaluate(value, environment)?;
|
||||||
|
environment
|
||||||
|
.assign(name, value.clone())
|
||||||
|
.map_err(|_| RuntimeError {
|
||||||
|
token: name.clone(),
|
||||||
|
message: format!("Undefined variable {}.", name.lexeme),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ternary(first: &Expr, second: &Expr, third: &Expr) -> Result<LiteralType, RuntimeError> {
|
fn ternary(
|
||||||
let first = interpret(first)?;
|
first: &Expr,
|
||||||
|
second: &Expr,
|
||||||
|
third: &Expr,
|
||||||
|
environment: &mut Environment,
|
||||||
|
) -> Result<LiteralType, RuntimeError> {
|
||||||
|
let first = evaluate(first, environment)?;
|
||||||
if is_truthy(&first) {
|
if is_truthy(&first) {
|
||||||
return interpret(second);
|
return evaluate(second, environment);
|
||||||
}
|
}
|
||||||
interpret(third)
|
evaluate(third, environment)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn binary(
|
fn binary(
|
||||||
@ -71,7 +146,7 @@ fn binary(
|
|||||||
(Greater | GreaterEqual | Less | LessEqual | Minus | Slash | Star, _, _) => Err(RuntimeError::new(op, "Operands must be numbers")),
|
(Greater | GreaterEqual | Less | LessEqual | Minus | Slash | Star, _, _) => Err(RuntimeError::new(op, "Operands must be numbers")),
|
||||||
(Plus, _, _) => Err(RuntimeError::new(op, "Operands must be two numbers or two strings")),
|
(Plus, _, _) => Err(RuntimeError::new(op, "Operands must be two numbers or two strings")),
|
||||||
|
|
||||||
_ => unreachable!("Shouldn't happen. Expr::Binary for interpret. Some case is a binary operation that wasn't matched")
|
_ => unreachable!("Shouldn't happen. Expr::Binary for evaluate. Some case is a binary operation that wasn't matched")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +154,7 @@ fn unary(right: &LiteralType, op: &Token) -> LiteralType {
|
|||||||
match (op.t_type, &right) {
|
match (op.t_type, &right) {
|
||||||
(TokenType::Minus, LiteralType::Number(num)) => LiteralType::Number(-num),
|
(TokenType::Minus, LiteralType::Number(num)) => LiteralType::Number(-num),
|
||||||
(TokenType::Bang, _) => LiteralType::Bool(!is_truthy(right)),
|
(TokenType::Bang, _) => LiteralType::Bool(!is_truthy(right)),
|
||||||
_ => unreachable!("Shouldn't happen. Expr::Unary for interpret"),
|
_ => unreachable!("Shouldn't happen. Expr::Unary for evaluate"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
43
src/lib.rs
43
src/lib.rs
@ -1,15 +1,19 @@
|
|||||||
use std::{
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
error::Error,
|
error::Error,
|
||||||
fs,
|
fs,
|
||||||
io::{self, Write},
|
io::{self, Write},
|
||||||
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use environment::Environment;
|
||||||
use interpreter::RuntimeError;
|
use interpreter::RuntimeError;
|
||||||
use parser::Parser;
|
use parser::{ParseError, Parser};
|
||||||
use scanner::Scanner;
|
use scanner::Scanner;
|
||||||
use token::{Token, TokenType};
|
use token::{Token, TokenType};
|
||||||
|
|
||||||
mod ast;
|
mod ast;
|
||||||
|
mod environment;
|
||||||
mod interpreter;
|
mod interpreter;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod printer;
|
mod printer;
|
||||||
@ -22,6 +26,7 @@ pub enum RunError {
|
|||||||
FileReadError(io::Error),
|
FileReadError(io::Error),
|
||||||
OtherError(Box<dyn Error>), // to be added,
|
OtherError(Box<dyn Error>), // to be added,
|
||||||
RuntimeError(RuntimeError),
|
RuntimeError(RuntimeError),
|
||||||
|
ParseError,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Error + 'static> From<E> for RunError {
|
impl<E: Error + 'static> From<E> for RunError {
|
||||||
@ -32,37 +37,53 @@ impl<E: Error + 'static> From<E> for RunError {
|
|||||||
|
|
||||||
pub fn run_file(path: &str) -> Result<(), RunError> {
|
pub fn run_file(path: &str) -> Result<(), RunError> {
|
||||||
let file = fs::read_to_string(path).map_err(RunError::FileReadError)?;
|
let file = fs::read_to_string(path).map_err(RunError::FileReadError)?;
|
||||||
|
let environment = Rc::new(RefCell::new(Environment::new()));
|
||||||
|
|
||||||
run(&file)?;
|
run(&file, &environment)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(src: &str) -> Result<(), RunError> {
|
pub fn run(src: &str, environment: &Rc<RefCell<Environment>>) -> Result<(), RunError> {
|
||||||
let mut scanner = Scanner::new(src.to_string());
|
let mut scanner = Scanner::new(src.to_string());
|
||||||
let tokens = scanner.scan_tokens()?;
|
let tokens = scanner.scan_tokens()?;
|
||||||
|
|
||||||
let mut parser = Parser::new(tokens);
|
let mut parser = Parser::new(tokens);
|
||||||
|
|
||||||
let expression = parser.parse().inspect_err(|e| error(&e.token, &e.msg))?;
|
let statements = parser.parse();
|
||||||
|
|
||||||
let interpreted_value = interpreter::interpret(&expression)
|
// i don't want to collect the errors and allocate a vec
|
||||||
|
let mut p_error = false;
|
||||||
|
|
||||||
|
for err in statements.iter().filter_map(|x| x.as_ref().err()) {
|
||||||
|
if !p_error {
|
||||||
|
p_error = true;
|
||||||
|
}
|
||||||
|
error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if p_error {
|
||||||
|
return Err(RunError::ParseError);
|
||||||
|
}
|
||||||
|
|
||||||
|
let statements = statements.into_iter().flatten().collect();
|
||||||
|
|
||||||
|
interpreter::interpret(&statements, environment)
|
||||||
.inspect_err(runtime_error)
|
.inspect_err(runtime_error)
|
||||||
.map_err(RunError::RuntimeError)?;
|
.map_err(RunError::RuntimeError)?;
|
||||||
|
|
||||||
println!("{interpreted_value}");
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_prompt() -> Result<(), Box<dyn Error>> {
|
pub fn run_prompt() -> Result<(), Box<dyn Error>> {
|
||||||
let stdin = io::stdin();
|
let stdin = io::stdin();
|
||||||
let input = &mut String::new();
|
let input = &mut String::new();
|
||||||
|
let environment = Rc::new(RefCell::new(Environment::new()));
|
||||||
loop {
|
loop {
|
||||||
input.clear();
|
input.clear();
|
||||||
print!("> ");
|
print!("> ");
|
||||||
io::stdout().flush()?;
|
io::stdout().flush()?;
|
||||||
stdin.read_line(input)?;
|
stdin.read_line(input)?;
|
||||||
let _ = run(input);
|
let _ = run(input, &environment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,10 +97,10 @@ pub fn report(line: usize, location: &str, message: &str) {
|
|||||||
eprintln!("[line {line}] Error {location}: {message}");
|
eprintln!("[line {line}] Error {location}: {message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error(token: &Token, message: &str) {
|
fn error(ParseError { token, msg }: &ParseError) {
|
||||||
match token.t_type {
|
match token.t_type {
|
||||||
TokenType::EOF => report(token.line, " at end", message),
|
TokenType::EOF => report(token.line, " at end", msg),
|
||||||
_ => report(token.line, &format!(" at '{}'", token.lexeme), message),
|
_ => report(token.line, &format!("at '{}'", token.lexeme), msg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,10 @@ fn main() -> ExitCode {
|
|||||||
if let Err(RunError::RuntimeError(r)) = result {
|
if let Err(RunError::RuntimeError(r)) = result {
|
||||||
return ExitCode::from(70);
|
return ExitCode::from(70);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Err(RunError::ParseError) = result {
|
||||||
|
return ExitCode::from(75);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let result = run_prompt();
|
let result = run_prompt();
|
||||||
|
|
||||||
|
117
src/parser.rs
117
src/parser.rs
@ -1,7 +1,7 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::Expr,
|
ast::{Expr, Stmt},
|
||||||
token::{LiteralType, Token, TokenType},
|
token::{LiteralType, Token, TokenType},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -29,8 +29,85 @@ impl Parser<'_> {
|
|||||||
Parser { tokens, current: 0 }
|
Parser { tokens, current: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(&mut self) -> Result<Expr, ParseError> {
|
//pub fn parse(&mut self) -> Result<Expr, ParseError> {
|
||||||
self.expression()
|
// self.expression()
|
||||||
|
//}
|
||||||
|
|
||||||
|
// maps to program rule in the grammar
|
||||||
|
pub fn parse(&mut self) -> Vec<Result<Stmt, ParseError>> {
|
||||||
|
let mut statements = Vec::new();
|
||||||
|
|
||||||
|
while !self.is_at_end() {
|
||||||
|
statements.push(self.declaration());
|
||||||
|
}
|
||||||
|
|
||||||
|
statements
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declaration(&mut self) -> Result<Stmt, ParseError> {
|
||||||
|
let stmt = if self.match_token(&[TokenType::Var]) {
|
||||||
|
self.var_declaration()
|
||||||
|
} else {
|
||||||
|
self.statement()
|
||||||
|
};
|
||||||
|
|
||||||
|
stmt.inspect_err(|_| self.synchronize())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn var_declaration(&mut self) -> Result<Stmt, ParseError> {
|
||||||
|
let name = self.consume(TokenType::Identifier, "Expect variable name")?;
|
||||||
|
let initializer = if self.match_token(&[TokenType::Equal]) {
|
||||||
|
Some(self.expression()?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
self.consume(
|
||||||
|
TokenType::Semicolon,
|
||||||
|
"Expect ';' after variable declaration.",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Stmt::Var { name, initializer })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn statement(&mut self) -> Result<Stmt, ParseError> {
|
||||||
|
if self.match_token(&[TokenType::Print]) {
|
||||||
|
return self.print_statement();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.match_token(&[TokenType::LeftBrace]) {
|
||||||
|
return Ok(Stmt::Block {
|
||||||
|
statements: self.block()?,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.expression_statement()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block(&mut self) -> Result<Vec<Stmt>, ParseError> {
|
||||||
|
let mut statements = Vec::new();
|
||||||
|
|
||||||
|
while !self.check(TokenType::RightBrace) && !self.is_at_end() {
|
||||||
|
statements.push(self.declaration()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.consume(TokenType::RightBrace, "Expect '}' after block.")?;
|
||||||
|
|
||||||
|
Ok(statements)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_statement(&mut self) -> Result<Stmt, ParseError> {
|
||||||
|
let expression = self.expression()?;
|
||||||
|
self.consume(TokenType::Semicolon, "Expect ';' after value.")?;
|
||||||
|
|
||||||
|
Ok(Stmt::Print { expression })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expression_statement(&mut self) -> Result<Stmt, ParseError> {
|
||||||
|
let expression = self.expression()?;
|
||||||
|
self.consume(TokenType::Semicolon, "Expect ';' after expression.")?;
|
||||||
|
|
||||||
|
Ok(Stmt::Expression { expression })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expression(&mut self) -> Result<Expr, ParseError> {
|
fn expression(&mut self) -> Result<Expr, ParseError> {
|
||||||
@ -41,7 +118,29 @@ impl Parser<'_> {
|
|||||||
// comma -> equality ("," equality)* ; // expression grammar
|
// comma -> equality ("," equality)* ; // expression grammar
|
||||||
fn comma(&mut self) -> Result<Expr, ParseError> {
|
fn comma(&mut self) -> Result<Expr, ParseError> {
|
||||||
use TokenType::*;
|
use TokenType::*;
|
||||||
self.left_association_binary(&[Comma], Self::ternary)
|
self.left_association_binary(&[Comma], Self::assignment)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assignment(&mut self) -> Result<Expr, ParseError> {
|
||||||
|
let expr = self.ternary()?;
|
||||||
|
|
||||||
|
if self.match_token(&[TokenType::Equal]) {
|
||||||
|
let value = self.assignment()?;
|
||||||
|
let equals = self.previous();
|
||||||
|
|
||||||
|
if let Expr::Variable { name } = expr {
|
||||||
|
return Ok(Expr::Assign {
|
||||||
|
name,
|
||||||
|
value: Box::new(value),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Err(ParseError {
|
||||||
|
token: equals.clone(),
|
||||||
|
msg: "Invalid assignment target.".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ternary -> equality ("?" expression : ternary)? // expression grammar
|
// ternary -> equality ("?" expression : ternary)? // expression grammar
|
||||||
@ -140,6 +239,12 @@ impl Parser<'_> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.match_token(&[Identifier]) {
|
||||||
|
return Ok(Expr::Variable {
|
||||||
|
name: self.previous().clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if self.match_token(&[Equal, BangEqual]) {
|
if self.match_token(&[Equal, BangEqual]) {
|
||||||
let _ = self.equality();
|
let _ = self.equality();
|
||||||
return Err(ParseError {
|
return Err(ParseError {
|
||||||
@ -189,7 +294,6 @@ impl Parser<'_> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// will not be used for the time being (per the book)
|
|
||||||
// used for error recovery
|
// used for error recovery
|
||||||
fn synchronize(&mut self) {
|
fn synchronize(&mut self) {
|
||||||
use TokenType::*;
|
use TokenType::*;
|
||||||
@ -203,10 +307,9 @@ impl Parser<'_> {
|
|||||||
if let Class | Fun | Var | For | If | While | Print | Return = self.peek().t_type {
|
if let Class | Fun | Var | For | If | While | Print | Return = self.peek().t_type {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
self.advance();
|
self.advance();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn left_association_binary(
|
fn left_association_binary(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -16,6 +16,8 @@ pub fn pretty_print(expr: &Expr) -> String {
|
|||||||
second,
|
second,
|
||||||
third,
|
third,
|
||||||
} => parenthesize("?:", &[first, second, third]),
|
} => parenthesize("?:", &[first, second, third]),
|
||||||
|
Expr::Variable { name } => name.lexeme.clone(),
|
||||||
|
Expr::Assign { name, value } => parenthesize(&name.lexeme, &[value]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user