statements in rust
This commit is contained in:
parent
15b331f447
commit
a629ddca05
66
rust/rox/src/environment.rs
Normal file
66
rust/rox/src/environment.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::{token::Token, value::Value};
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum EnvironmentError {
|
||||||
|
#[error("line {0}: undefined variable: {1}")]
|
||||||
|
UndefinedVariable(usize, String),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Environment mapping variable names to their respective values. Can have an optional enclosing
|
||||||
|
/// environment. The outermost (global) environment has no enclosing one.
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
|
pub struct Environment {
|
||||||
|
values: HashMap<String, Value>,
|
||||||
|
enclosing: Option<Box<Environment>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Environment {
|
||||||
|
pub fn with_enclosing(enclosing: Environment) -> Self {
|
||||||
|
Self {
|
||||||
|
values: HashMap::default(),
|
||||||
|
enclosing: Some(Box::new(enclosing)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Define a new variable binding with a value.
|
||||||
|
///
|
||||||
|
/// Overwrite any binding of the same name.
|
||||||
|
pub fn define(&mut self, name: String, value: Value) {
|
||||||
|
self.values.insert(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assign a new value to an existing (defined) variable. Error if there is no such variable in
|
||||||
|
/// this environment or any of the enclosing ones.
|
||||||
|
pub fn assign(&mut self, token: &Token, value: Value) -> Result<(), EnvironmentError> {
|
||||||
|
if self.values.contains_key(token.lexeme.as_str()) {
|
||||||
|
self.values.insert(token.lexeme.clone(), value);
|
||||||
|
Ok(())
|
||||||
|
} else if let Some(enclosing) = &mut self.enclosing {
|
||||||
|
enclosing.assign(token, value)
|
||||||
|
} else {
|
||||||
|
Err(EnvironmentError::UndefinedVariable(
|
||||||
|
token.line,
|
||||||
|
token.lexeme.clone(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the value of an existing (defined) variable. Error if there is no such variable in
|
||||||
|
/// this environment or any of the enclosing ones.
|
||||||
|
pub fn get(&self, token: &Token) -> Result<Value, EnvironmentError> {
|
||||||
|
if let Some(v) = self.values.get(token.lexeme.as_str()) {
|
||||||
|
Ok(v.clone())
|
||||||
|
} else if let Some(enclosing) = &self.enclosing {
|
||||||
|
enclosing.get(token)
|
||||||
|
} else {
|
||||||
|
Err(EnvironmentError::UndefinedVariable(
|
||||||
|
token.line,
|
||||||
|
token.lexeme.clone(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,12 @@
|
|||||||
use crate::token::{self, Token};
|
use crate::token::{self, Token};
|
||||||
|
|
||||||
|
/// Enumeration of all types of expressions.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
|
Assign {
|
||||||
|
name: Token,
|
||||||
|
value: Box<Expression>,
|
||||||
|
},
|
||||||
Binary {
|
Binary {
|
||||||
left: Box<Expression>,
|
left: Box<Expression>,
|
||||||
operator: Token,
|
operator: Token,
|
||||||
@ -17,4 +22,7 @@ pub enum Expression {
|
|||||||
operator: Token,
|
operator: Token,
|
||||||
right: Box<Expression>,
|
right: Box<Expression>,
|
||||||
},
|
},
|
||||||
|
Variable {
|
||||||
|
name: Token,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,51 +1,154 @@
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
environment::{Environment, EnvironmentError},
|
||||||
expression::Expression,
|
expression::Expression,
|
||||||
|
statement::Statement,
|
||||||
token::{Literal, Token, TokenType},
|
token::{Literal, Token, TokenType},
|
||||||
value::Value,
|
value::Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum InterpreterError {
|
pub enum InterpreterError {
|
||||||
#[error("line {0}: MINUS unary expression expects a number on the right")]
|
#[error("[line {0}] MINUS unary expression expects a number on the right")]
|
||||||
UnaryExpressionNotANumber(usize),
|
UnaryExpressionNotANumber(usize),
|
||||||
#[error("line {0}: unknown unary operator: {1}")]
|
#[error("[line {0}] unknown unary operator: {1}")]
|
||||||
UnaryOperatorUnknown(usize, String),
|
UnaryOperatorUnknown(usize, String),
|
||||||
#[error("line {0}: unknown binary operator: {1}")]
|
#[error("[line {0}] unknown binary operator: {1}")]
|
||||||
BinaryOperatorUnknown(usize, String),
|
BinaryOperatorUnknown(usize, String),
|
||||||
|
#[error("[line {0}] left or right is not a number.")]
|
||||||
|
BinaryExpressionNeedsNumber(usize),
|
||||||
|
#[error("[line {0}] left or right is neither a number nor string.")]
|
||||||
|
BinaryExpressionNeedsNumberOrString(usize),
|
||||||
|
#[error("{0}")]
|
||||||
|
UndefinedVariable(EnvironmentError),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to evaluate an expression and return its result.
|
/// Interpreter for the Lox language.
|
||||||
pub fn evaluate(expression: Expression) -> Result<Value, InterpreterError> {
|
#[derive(Default, Debug)]
|
||||||
|
pub struct Interpreter {
|
||||||
|
environment: Environment,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpreter {
|
||||||
|
/// Try to evaluate an expression and return its result.
|
||||||
|
pub fn run(&mut self, statements: Vec<Statement>) -> Result<(), InterpreterError> {
|
||||||
|
for stmt in statements {
|
||||||
|
match self.execute(stmt) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => error!("{e}"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
///Execute a statement.
|
||||||
|
fn execute(&mut self, statement: Statement) -> Result<(), InterpreterError> {
|
||||||
|
match statement {
|
||||||
|
Statement::Block(statements) => {
|
||||||
|
let sub_env = Environment::with_enclosing(self.environment.clone());
|
||||||
|
self.block(statements, sub_env)?
|
||||||
|
}
|
||||||
|
Statement::Print(expression) => self.print_statement(expression)?,
|
||||||
|
Statement::Expression(expression) => {
|
||||||
|
self.evaluate(expression)?;
|
||||||
|
}
|
||||||
|
Statement::Var { name, initializer } => self.var_statement(name, *initializer)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute all statements within a block, using a new environment (with the old one as the
|
||||||
|
/// enclosing one).
|
||||||
|
fn block(
|
||||||
|
&mut self,
|
||||||
|
statements: Vec<Statement>,
|
||||||
|
environment: Environment,
|
||||||
|
) -> Result<(), InterpreterError> {
|
||||||
|
let prev_env = &self.environment.clone();
|
||||||
|
self.environment = environment;
|
||||||
|
|
||||||
|
for stmt in statements {
|
||||||
|
if let Err(e) = self.execute(stmt) {
|
||||||
|
error!("{e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.environment = prev_env.clone();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate an expression and return its value.
|
||||||
|
fn evaluate(&mut self, expression: Expression) -> Result<Value, InterpreterError> {
|
||||||
match expression {
|
match expression {
|
||||||
Expression::Literal { value } => literal(value),
|
Expression::Literal { value } => self.literal(value),
|
||||||
Expression::Grouping { expression } => grouping(*expression),
|
Expression::Grouping { expression } => self.grouping(*expression),
|
||||||
Expression::Unary {
|
Expression::Unary {
|
||||||
operator: op,
|
operator: op,
|
||||||
right,
|
right,
|
||||||
} => unary(op, *right),
|
} => self.unary(op, *right),
|
||||||
Expression::Binary {
|
Expression::Binary {
|
||||||
left,
|
left,
|
||||||
operator,
|
operator,
|
||||||
right,
|
right,
|
||||||
} => binary(*left, operator, *right),
|
} => self.binary(*left, operator, *right),
|
||||||
|
Expression::Variable { name } => self.var_expression(&name),
|
||||||
|
Expression::Assign { name, value } => self.assign(&name, *value),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert the literal value into a Value.
|
/// Evaluate an expression and print its value to stdout.
|
||||||
fn literal(literal: Literal) -> Result<Value, InterpreterError> {
|
fn print_statement(&mut self, expression: Expression) -> Result<(), InterpreterError> {
|
||||||
|
let value = self.evaluate(expression)?;
|
||||||
|
println!("{value}");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize a variable with an initializer expression or nil.
|
||||||
|
fn var_statement(
|
||||||
|
&mut self,
|
||||||
|
name: Token,
|
||||||
|
initializer: Option<Expression>,
|
||||||
|
) -> Result<(), InterpreterError> {
|
||||||
|
let value = if let Some(initializer) = initializer {
|
||||||
|
self.evaluate(initializer)
|
||||||
|
} else {
|
||||||
|
Ok(Value::Nil)
|
||||||
|
}?;
|
||||||
|
|
||||||
|
self.environment.define(name.lexeme, value);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assign the value of an expression to a variable.
|
||||||
|
fn assign(&mut self, name: &Token, value: Expression) -> Result<Value, InterpreterError> {
|
||||||
|
let value = self.evaluate(value)?;
|
||||||
|
|
||||||
|
self.environment
|
||||||
|
.assign(name, value.clone())
|
||||||
|
.map_err(InterpreterError::UndefinedVariable)?;
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the literal value into a Value.
|
||||||
|
fn literal(&self, literal: Literal) -> Result<Value, InterpreterError> {
|
||||||
Ok(literal.into())
|
Ok(literal.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate the inner expression.
|
/// Evaluate the inner expression.
|
||||||
fn grouping(inner: Expression) -> Result<Value, InterpreterError> {
|
fn grouping(&mut self, inner: Expression) -> Result<Value, InterpreterError> {
|
||||||
evaluate(inner)
|
self.evaluate(inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate the expression on the right and use its result when evaluating the unary operator.
|
/// Evaluate the expression on the right and use its result when evaluating the unary operator.
|
||||||
fn unary(op: Token, right: Expression) -> Result<Value, InterpreterError> {
|
fn unary(&mut self, op: Token, right: Expression) -> Result<Value, InterpreterError> {
|
||||||
let right = evaluate(right)?;
|
let right = self.evaluate(right)?;
|
||||||
|
|
||||||
match op.token_type {
|
match op.token_type {
|
||||||
TokenType::Minus => {
|
TokenType::Minus => {
|
||||||
@ -58,10 +161,17 @@ fn unary(op: Token, right: Expression) -> Result<Value, InterpreterError> {
|
|||||||
TokenType::Bang => Ok(Value::Boolean(!right.is_truthy())),
|
TokenType::Bang => Ok(Value::Boolean(!right.is_truthy())),
|
||||||
_ => Err(InterpreterError::UnaryOperatorUnknown(op.line, op.lexeme)),
|
_ => Err(InterpreterError::UnaryOperatorUnknown(op.line, op.lexeme)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate number operations.
|
/// Get the value of a variable.
|
||||||
fn number_op(left: f64, op: TokenType, right: f64) -> f64 {
|
fn var_expression(&mut self, name: &Token) -> Result<Value, InterpreterError> {
|
||||||
|
self.environment
|
||||||
|
.get(name)
|
||||||
|
.map_err(InterpreterError::UndefinedVariable)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate number operations.
|
||||||
|
fn number_op(&self, left: f64, op: TokenType, right: f64) -> f64 {
|
||||||
match op {
|
match op {
|
||||||
TokenType::Minus => left - right,
|
TokenType::Minus => left - right,
|
||||||
TokenType::Plus => left + right,
|
TokenType::Plus => left + right,
|
||||||
@ -69,10 +179,10 @@ fn number_op(left: f64, op: TokenType, right: f64) -> f64 {
|
|||||||
TokenType::Star => left * right,
|
TokenType::Star => left * right,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate boolean operations.
|
/// Calculate boolean operations.
|
||||||
fn boolean_op(left: f64, op: TokenType, right: f64) -> bool {
|
fn boolean_op(&self, left: f64, op: TokenType, right: f64) -> bool {
|
||||||
match op {
|
match op {
|
||||||
TokenType::Greater => left > right,
|
TokenType::Greater => left > right,
|
||||||
TokenType::GreaterEqual => left >= right,
|
TokenType::GreaterEqual => left >= right,
|
||||||
@ -80,31 +190,44 @@ fn boolean_op(left: f64, op: TokenType, right: f64) -> bool {
|
|||||||
TokenType::LessEqual => left <= right,
|
TokenType::LessEqual => left <= right,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate the left and right expressions (in that order) and then combine them with the
|
/// Evaluate the left and right expressions (in that order) and then combine them with the
|
||||||
/// specified operator.
|
/// specified operator.
|
||||||
fn binary(left: Expression, op: Token, right: Expression) -> Result<Value, InterpreterError> {
|
fn binary(
|
||||||
let left = evaluate(left)?;
|
&mut self,
|
||||||
let right = evaluate(right)?;
|
left: Expression,
|
||||||
|
op: Token,
|
||||||
|
right: Expression,
|
||||||
|
) -> Result<Value, InterpreterError> {
|
||||||
|
let left = self.evaluate(left)?;
|
||||||
|
let right = self.evaluate(right)?;
|
||||||
|
|
||||||
match op.token_type {
|
match op.token_type {
|
||||||
TokenType::Minus | TokenType::Slash | TokenType::Star | TokenType::Plus => {
|
TokenType::Minus | TokenType::Slash | TokenType::Star | TokenType::Plus => {
|
||||||
if let (Value::Number(left), Value::Number(right)) = (left, right) {
|
if let (Value::Number(left), Value::Number(right)) = (left.clone(), right.clone()) {
|
||||||
Ok(Value::Number(number_op(left, op.token_type, right)))
|
Ok(Value::Number(self.number_op(left, op.token_type, right)))
|
||||||
|
} else if let (Value::String(left), Value::String(right)) = (left, right) {
|
||||||
|
Ok(Value::String(format!("{}{}", left.clone(), right.clone())))
|
||||||
} else {
|
} else {
|
||||||
todo!()
|
Err(InterpreterError::BinaryExpressionNeedsNumberOrString(
|
||||||
|
op.line,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TokenType::Greater | TokenType::GreaterEqual | TokenType::Less | TokenType::LessEqual => {
|
TokenType::Greater
|
||||||
|
| TokenType::GreaterEqual
|
||||||
|
| TokenType::Less
|
||||||
|
| TokenType::LessEqual => {
|
||||||
if let (Value::Number(left), Value::Number(right)) = (left, right) {
|
if let (Value::Number(left), Value::Number(right)) = (left, right) {
|
||||||
Ok(Value::Boolean(boolean_op(left, op.token_type, right)))
|
Ok(Value::Boolean(self.boolean_op(left, op.token_type, right)))
|
||||||
} else {
|
} else {
|
||||||
todo!()
|
Err(InterpreterError::BinaryExpressionNeedsNumber(op.line))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TokenType::BangEqual => Ok(Value::Boolean(left != right)),
|
TokenType::BangEqual => Ok(Value::Boolean(left != right)),
|
||||||
TokenType::EqualEqual => Ok(Value::Boolean(left == right)),
|
TokenType::EqualEqual => Ok(Value::Boolean(left == right)),
|
||||||
_ => Err(InterpreterError::BinaryOperatorUnknown(op.line, op.lexeme)),
|
_ => Err(InterpreterError::BinaryOperatorUnknown(op.line, op.lexeme)),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,14 +7,17 @@ use std::{
|
|||||||
path::Path,
|
path::Path,
|
||||||
};
|
};
|
||||||
|
|
||||||
use tracing::{error, info};
|
use interpreter::Interpreter;
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
|
pub mod environment;
|
||||||
pub mod expression;
|
pub mod expression;
|
||||||
pub mod interpreter;
|
pub mod interpreter;
|
||||||
pub mod keywords;
|
pub mod keywords;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod scanner;
|
pub mod scanner;
|
||||||
|
pub mod statement;
|
||||||
pub mod token;
|
pub mod token;
|
||||||
pub mod tokenizer {
|
pub mod tokenizer {
|
||||||
pub mod comment;
|
pub mod comment;
|
||||||
@ -32,13 +35,16 @@ pub mod value;
|
|||||||
/// Read the source code in a file and scan it to tokens.
|
/// Read the source code in a file and scan it to tokens.
|
||||||
pub fn compile(source: &Path) -> Result<(), io::Error> {
|
pub fn compile(source: &Path) -> Result<(), io::Error> {
|
||||||
let input = fs::read_to_string(source)?;
|
let input = fs::read_to_string(source)?;
|
||||||
run(&input);
|
let mut interpreter = Interpreter::default();
|
||||||
|
|
||||||
|
run(&input, &mut interpreter);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run a Lox REPL until SIGINT.
|
/// Run a Lox REPL until SIGINT.
|
||||||
pub fn repl() {
|
pub fn repl() {
|
||||||
|
let mut interpreter = Interpreter::default();
|
||||||
loop {
|
loop {
|
||||||
print!("> ");
|
print!("> ");
|
||||||
let _ = io::stdout().flush();
|
let _ = io::stdout().flush();
|
||||||
@ -49,16 +55,16 @@ pub fn repl() {
|
|||||||
Err(e) => error!("{}", e),
|
Err(e) => error!("{}", e),
|
||||||
}
|
}
|
||||||
let input = input.trim().to_string();
|
let input = input.trim().to_string();
|
||||||
run(&input);
|
run(&input, &mut interpreter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a Lox input string and print errors or output.
|
/// Evaluate a Lox input string and print errors or output.
|
||||||
fn run(input: &str) {
|
fn run(input: &str, interpreter: &mut Interpreter) {
|
||||||
let tokens = scanner::tokenize(input);
|
let tokens = scanner::tokenize(input);
|
||||||
match parser::generate_ast(tokens) {
|
match parser::ast(tokens) {
|
||||||
Ok(ast) => match interpreter::evaluate(ast) {
|
Ok(ast) => match interpreter.run(ast) {
|
||||||
Ok(value) => println!("{value}"),
|
Ok(_) => {}
|
||||||
Err(e) => error!("{e}"),
|
Err(e) => error!("{e}"),
|
||||||
},
|
},
|
||||||
Err(e) => error!("{e}"),
|
Err(e) => error!("{e}"),
|
||||||
|
@ -3,6 +3,7 @@ use tracing::error;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
expression::Expression,
|
expression::Expression,
|
||||||
|
statement::Statement,
|
||||||
token::{
|
token::{
|
||||||
self, Token,
|
self, Token,
|
||||||
TokenType::{self, *},
|
TokenType::{self, *},
|
||||||
@ -13,14 +14,24 @@ use crate::{
|
|||||||
pub enum ParserError {
|
pub enum ParserError {
|
||||||
#[error("empty token stream")]
|
#[error("empty token stream")]
|
||||||
NoTokens,
|
NoTokens,
|
||||||
#[error("line {0}: expected expression")]
|
#[error("[line {0}] expected expression")]
|
||||||
ExpressionExpected(usize),
|
ExpressionExpected(usize),
|
||||||
#[error("line {0}: expected ')' after expression.")]
|
#[error("[line {0}] expected ')' after expression.")]
|
||||||
ParenAfterExpression(usize),
|
ParenAfterExpression(usize),
|
||||||
#[error("Out of bounds access at index {0}.")]
|
#[error("[Out of bounds access at index {0}.")]
|
||||||
OutOfBoundsAccess(usize),
|
OutOfBoundsAccess(usize),
|
||||||
#[error("line {0}: literal expected.")]
|
#[error("[line {0}] literal expected.")]
|
||||||
LiteralExpected(usize),
|
LiteralExpected(usize),
|
||||||
|
#[error("[line {0}] expected ';' after value.")]
|
||||||
|
SemicolonAfterValueExpected(usize),
|
||||||
|
#[error("[line {0}] expected ';' after expression.")]
|
||||||
|
SemicolonAfterExpressionExpected(usize),
|
||||||
|
#[error("[line {0}] expected variable name.")]
|
||||||
|
VariableNameExpected(usize),
|
||||||
|
#[error("[line {0}] invalid assignment target.")]
|
||||||
|
InvalidAssignmentTarget(usize),
|
||||||
|
#[error("[line {0}] expected '}}' after block.")]
|
||||||
|
RightBraceAfterBlockExpected(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the Lox language.
|
/// Parse the Lox language.
|
||||||
@ -43,6 +54,23 @@ impl Parser {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse all tokens to a list of statements for execution.
|
||||||
|
fn run(&mut self) -> Result<Vec<Statement>, ParserError> {
|
||||||
|
let mut statements = Vec::new();
|
||||||
|
|
||||||
|
while !self.is_at_end() {
|
||||||
|
match self.declaration() {
|
||||||
|
Ok(x) => statements.push(x),
|
||||||
|
Err(e) => {
|
||||||
|
error!("{e}");
|
||||||
|
self.synchronize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(statements)
|
||||||
|
}
|
||||||
|
|
||||||
/// Check if any of the provided types match the type of the current token.
|
/// Check if any of the provided types match the type of the current token.
|
||||||
///
|
///
|
||||||
/// If so, advance the current token.
|
/// If so, advance the current token.
|
||||||
@ -117,7 +145,99 @@ impl Parser {
|
|||||||
|
|
||||||
/// expression -> equality ;
|
/// expression -> equality ;
|
||||||
fn expression(&mut self) -> Result<Expression, ParserError> {
|
fn expression(&mut self) -> Result<Expression, ParserError> {
|
||||||
self.equality()
|
self.assignment()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declaration(&mut self) -> Result<Statement, ParserError> {
|
||||||
|
if self.matches(&[Var]) {
|
||||||
|
self.var_declaration()
|
||||||
|
} else {
|
||||||
|
self.statement()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn statement(&mut self) -> Result<Statement, ParserError> {
|
||||||
|
if self.matches(&[Print]) {
|
||||||
|
self.print_statement()
|
||||||
|
} else if self.matches(&[LeftBrace]) {
|
||||||
|
Ok(Statement::Block(self.block()?))
|
||||||
|
} else {
|
||||||
|
self.expression_statement()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_statement(&mut self) -> Result<Statement, ParserError> {
|
||||||
|
let value = self.expression()?;
|
||||||
|
let line = self.current_token.line;
|
||||||
|
self.consume(&Semicolon)
|
||||||
|
.ok_or(ParserError::SemicolonAfterValueExpected(line))?;
|
||||||
|
|
||||||
|
Ok(Statement::Print(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn var_declaration(&mut self) -> Result<Statement, ParserError> {
|
||||||
|
let line = self.current_token.line;
|
||||||
|
let name = self
|
||||||
|
.consume(&Identifier)
|
||||||
|
.ok_or(ParserError::VariableNameExpected(line))?
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
let initializer = if self.matches(&[Equal]) {
|
||||||
|
Some(self.expression()?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
self.consume(&Semicolon)
|
||||||
|
.ok_or(ParserError::SemicolonAfterExpressionExpected(line))?;
|
||||||
|
|
||||||
|
Ok(Statement::Var {
|
||||||
|
name,
|
||||||
|
initializer: Box::new(initializer),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expression_statement(&mut self) -> Result<Statement, ParserError> {
|
||||||
|
let expr = self.expression()?;
|
||||||
|
let line = self.current_token.line;
|
||||||
|
self.consume(&Semicolon)
|
||||||
|
.ok_or(ParserError::SemicolonAfterExpressionExpected(line))?;
|
||||||
|
|
||||||
|
Ok(Statement::Expression(expr))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block(&mut self) -> Result<Vec<Statement>, ParserError> {
|
||||||
|
let mut statements = Vec::new();
|
||||||
|
|
||||||
|
while !self.check(&RightBrace) && !self.is_at_end() {
|
||||||
|
statements.push(self.declaration()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
let line = self.previous()?.line;
|
||||||
|
self.consume(&RightBrace)
|
||||||
|
.ok_or(ParserError::RightBraceAfterBlockExpected(line))?;
|
||||||
|
|
||||||
|
Ok(statements)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assignment(&mut self) -> Result<Expression, ParserError> {
|
||||||
|
let expr = self.equality()?;
|
||||||
|
|
||||||
|
if self.matches(&[Equal]) {
|
||||||
|
let equals = self.previous()?.clone();
|
||||||
|
let value = self.assignment()?;
|
||||||
|
|
||||||
|
if let Expression::Variable { name } = expr {
|
||||||
|
Ok(Expression::Assign {
|
||||||
|
name,
|
||||||
|
value: Box::new(value),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(ParserError::InvalidAssignmentTarget(equals.line))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// equality -> comparison ( ( "!=" | "==" ) comparison )* ;
|
/// equality -> comparison ( ( "!=" | "==" ) comparison )* ;
|
||||||
@ -169,6 +289,9 @@ impl Parser {
|
|||||||
Ok(Expression::Literal {
|
Ok(Expression::Literal {
|
||||||
value: token::Literal::Nil,
|
value: token::Literal::Nil,
|
||||||
})
|
})
|
||||||
|
} else if self.matches(&[Identifier]) {
|
||||||
|
let prev = self.previous()?.clone();
|
||||||
|
Ok(Expression::Variable { name: prev })
|
||||||
} else if self.matches(&[Number, String]) {
|
} else if self.matches(&[Number, String]) {
|
||||||
let prev = self.previous()?;
|
let prev = self.previous()?;
|
||||||
let value = prev
|
let value = prev
|
||||||
@ -205,106 +328,7 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Try to parse the provided tokens into an AST.
|
/// Try to parse the provided tokens into an AST.
|
||||||
pub fn generate_ast(tokens: Vec<Token>) -> Result<Expression, ParserError> {
|
pub fn ast(tokens: Vec<Token>) -> Result<Vec<Statement>, ParserError> {
|
||||||
let mut parser = Parser::new(tokens)?;
|
let mut parser = Parser::new(tokens)?;
|
||||||
parser.expression()
|
parser.run()
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::{
|
|
||||||
expression::Expression,
|
|
||||||
token::{Literal, Token, TokenType},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::generate_ast;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn simple_expression() {
|
|
||||||
let ast = generate_ast(vec![
|
|
||||||
Token {
|
|
||||||
token_type: TokenType::Number,
|
|
||||||
lexeme: "3".into(),
|
|
||||||
literal: Some(Literal::Number(3.0)),
|
|
||||||
line: 1,
|
|
||||||
},
|
|
||||||
Token {
|
|
||||||
token_type: TokenType::Star,
|
|
||||||
lexeme: "*".into(),
|
|
||||||
literal: None,
|
|
||||||
line: 1,
|
|
||||||
},
|
|
||||||
Token {
|
|
||||||
token_type: TokenType::Number,
|
|
||||||
lexeme: "4".into(),
|
|
||||||
literal: Some(Literal::Number(4.0)),
|
|
||||||
line: 1,
|
|
||||||
},
|
|
||||||
Token {
|
|
||||||
token_type: TokenType::Plus,
|
|
||||||
lexeme: "+".into(),
|
|
||||||
literal: None,
|
|
||||||
line: 1,
|
|
||||||
},
|
|
||||||
Token {
|
|
||||||
token_type: TokenType::Number,
|
|
||||||
lexeme: "2".into(),
|
|
||||||
literal: Some(Literal::Number(2.0)),
|
|
||||||
line: 1,
|
|
||||||
},
|
|
||||||
Token {
|
|
||||||
token_type: TokenType::Star,
|
|
||||||
lexeme: "*".into(),
|
|
||||||
literal: None,
|
|
||||||
line: 1,
|
|
||||||
},
|
|
||||||
Token {
|
|
||||||
token_type: TokenType::Number,
|
|
||||||
lexeme: "6".into(),
|
|
||||||
literal: Some(Literal::Number(6.0)),
|
|
||||||
line: 1,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
ast,
|
|
||||||
Expression::Binary {
|
|
||||||
left: Box::new(Expression::Binary {
|
|
||||||
left: Box::new(Expression::Literal {
|
|
||||||
value: Literal::Number(3.0)
|
|
||||||
}),
|
|
||||||
operator: Token {
|
|
||||||
token_type: TokenType::Star,
|
|
||||||
lexeme: "*".into(),
|
|
||||||
literal: None,
|
|
||||||
line: 1
|
|
||||||
},
|
|
||||||
right: Box::new(Expression::Literal {
|
|
||||||
value: Literal::Number(4.0)
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
operator: Token {
|
|
||||||
token_type: TokenType::Plus,
|
|
||||||
lexeme: "+".into(),
|
|
||||||
literal: None,
|
|
||||||
line: 1
|
|
||||||
},
|
|
||||||
right: Box::new(Expression::Binary {
|
|
||||||
left: Box::new(Expression::Literal {
|
|
||||||
value: Literal::Number(2.0)
|
|
||||||
}),
|
|
||||||
operator: Token {
|
|
||||||
token_type: TokenType::Star,
|
|
||||||
lexeme: "*".into(),
|
|
||||||
literal: None,
|
|
||||||
line: 1
|
|
||||||
},
|
|
||||||
right: Box::new(Expression::Literal {
|
|
||||||
value: Literal::Number(6.0)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
13
rust/rox/src/statement.rs
Normal file
13
rust/rox/src/statement.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use crate::{expression::Expression, token::Token};
|
||||||
|
|
||||||
|
/// Enumeration of all types of statements.
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Statement {
|
||||||
|
Block(Vec<Statement>),
|
||||||
|
Print(Expression),
|
||||||
|
Expression(Expression),
|
||||||
|
Var {
|
||||||
|
name: Token,
|
||||||
|
initializer: Box<Option<Expression>>,
|
||||||
|
},
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user