From 9f1c29f7129557390352b9f378835156be392560 Mon Sep 17 00:00:00 2001 From: TriMill Date: Wed, 7 Sep 2022 16:11:51 -0400 Subject: [PATCH] cleanup, added if statements --- Cargo.toml | 2 - idea.cxpr | 2 +- src/bin/main.rs | 16 +++-- src/eval.rs | 156 ++++++++++++++++++++++++++++++++------------- src/expr.rs | 3 +- src/interpreter.rs | 6 +- src/parser.rs | 121 +++++++++++++++++++++++++---------- src/value.rs | 18 ++++++ tests/code.cxpr | 10 ++- tests/test.rs | 2 +- 10 files changed, 240 insertions(+), 96 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 362e9b7..5cd29bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,8 +3,6 @@ name = "complexpr" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] lazy_static = "1.4.0" num-complex = "0.4.1" diff --git a/idea.cxpr b/idea.cxpr index e9db2fa..f68f23f 100644 --- a/idea.cxpr +++ b/idea.cxpr @@ -1,5 +1,5 @@ let square = x -> x^2; -let also_square(x) = x^2 +let also_square(x) = x^2; let primes = range(100) |: filter(is_prime); let prime_squares = range(100) |: map(square); diff --git a/src/bin/main.rs b/src/bin/main.rs index a6a8034..db593f9 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -3,12 +3,19 @@ use std::{rc::Rc, cell::RefCell, fs::{File, self}, io::{BufReader, Read}}; use complexpr::{eval::Environment, interpreter::interpret, value::Value}; use rustyline::{self, error::ReadlineError}; +const C_RESET: &'static str = "\x1b[0m"; +const C_BLUE: &'static str = "\x1b[94m"; +const PROMPT: &'static str = "\x1b[94m>> \x1b[0m"; + fn main() -> Result<(), Box> { let args: Vec = std::env::args().collect(); if args.len() == 2 { let fname = &args[1]; let src = fs::read_to_string(fname)?; - interpret(&src, None)?; + let res = interpret(&src, Some(fname.into()), None, false); + if let Err(e) = res { + println!("{}", e); + } } else { repl()?; } @@ -16,17 +23,18 @@ fn main() -> Result<(), Box> { } fn repl() -> Result<(), Box> { + println!("Press {}Ctrl+D{} to exit.", C_BLUE, C_RESET); let mut rl = rustyline::Editor::<()>::new()?; let env = Rc::new(RefCell::new(Environment::new())); loop { - let readline = rl.readline(">> "); + let readline = rl.readline(PROMPT); match readline { Ok(line) => { - let result = interpret(&line, Some(env.clone())); + let result = interpret(&line, None, Some(env.clone()), true); match result { Ok(Value::Nil) => (), Ok(value) => println!("{:?}", value), - Err(e) => println!("Error: {}", e) + Err(e) => println!("{}", e) } } Err(ReadlineError::Eof) => break, diff --git a/src/eval.rs b/src/eval.rs index a94be78..51fdbba 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -2,38 +2,49 @@ use std::{collections::HashMap, rc::Rc, cell::RefCell}; use num_complex::Complex64; -use crate::{value::Value, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}}; +use crate::{value::Value, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}, RuntimeError}; + #[derive(Debug)] pub struct Environment { - parent: Option>>, + parent: Option, map: HashMap, Value> } +type EnvRef = Rc>; + impl Environment { pub fn new() -> Self { Self { parent: None, map: HashMap::new() } } - pub fn extend(parent: Rc>) -> Self { + pub fn wrap(self) -> EnvRef { + Rc::new(RefCell::new(self)) + } + + pub fn extend(parent: EnvRef) -> Self { Self { parent: Some(parent), map: HashMap::new() } } pub fn get(&self, name: &str) -> Result { - match self.map.get(name) { + let x = match self.map.get(name) { Some(v) => Ok(v.clone()), None => match self.parent { Some(ref p) => p.borrow().get(name), None => Err(()) } - } + }; + println!("get {}: {:?}", name, x); + x } pub fn declare(&mut self, name: Rc, value: Value) { + println!("declare {}: {:?}", name, value); self.map.insert(name, value); } pub fn set(&mut self, name: Rc, value: Value) -> Result<(),()> { + println!("set {}: {:?}", name, value); match self.map.contains_key(&name) { true => { self.map.insert(name, value); Ok(()) }, false => match self.parent { @@ -44,7 +55,7 @@ impl Environment { } } -pub fn eval_stmt(stmt: &Stmt, env: Rc>) -> Result<(), String> { +pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), RuntimeError> { match stmt { Stmt::Expr{ expr } => drop(eval_expr(expr, env)), @@ -54,55 +65,108 @@ pub fn eval_stmt(stmt: &Stmt, env: Rc>) -> Result<(), Strin let r = eval_expr(rhs, env.clone())?; env.borrow_mut().declare(s.clone(), r) }, - Stmt::If { conditions, bodies, else_clause } - => todo!(), // TODO if statements + Stmt::Block { stmts } => { + let block_env = Environment::extend(env).wrap(); + for stmt in stmts { + eval_stmt(stmt, block_env.clone())? + } + } + Stmt::If { if_clauses, else_clause } => { + for ic in if_clauses { + let cond = eval_expr(&ic.0, env.clone())?; + if cond.truthy() { + return eval_stmt(&ic.1, env.clone()) + } + } + if let Some(ec) = else_clause { + return eval_stmt(&ec, env) + } + } _ => unreachable!() } Ok(()) } -pub fn eval_expr(expr: &Expr, env: Rc>) -> Result { +pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result { match expr { - Expr::Literal { value } => match &value.ty { - TokenType::Nil => Ok(Value::Nil), - TokenType::True => Ok(Value::Bool(true)), - TokenType::False => Ok(Value::Bool(false)), - TokenType::Int(n) => Ok(Value::Int(*n)), - TokenType::Float(f) => Ok(Value::Float(*f)), - TokenType::ImFloat(f) => Ok(Value::Complex(Complex64::new(0.0, *f))), - TokenType::String(s) => Ok(Value::String(s.clone())), - _ => todo!() - }, - Expr::Ident { value } => if let Token { ty: TokenType::Ident(name), ..} = value { - env.borrow_mut().get(name).map_err(|_| "Variable not defined in scope".into()) - } else { unreachable!() }, + Expr::Literal { value } => Ok(eval_literal(value)), + Expr::Ident { value } => eval_ident(value, env), Expr::Binary { lhs, rhs, op } => match op.ty.get_op_type() { - Some(OpType::Assignment) => { - let r = eval_expr(rhs, env.clone())?; - if let Expr::Ident{value: Token{ty: TokenType::Ident(name),..}} = &**lhs { - if op.ty == TokenType::Equal { - env.borrow_mut().set(name.clone(), r).map_err(|_| "Variable not declared before assignment")?; - Ok(Value::Nil) - } else { - todo!() // TODO +=, -=, etc - } - } else { - unreachable!() - } - }, - Some(OpType::Additive) | Some(OpType::Multiplicative) => { - let l = eval_expr(lhs, env.clone())?; - let r = eval_expr(rhs, env)?; - match op.ty { - TokenType::Plus => &l + &r, - TokenType::Minus => &l - &r, - TokenType::Star => &l * &r, - TokenType::Slash => &l / &r, - _ => todo!() // TODO other operations - } - }, + Some(OpType::Assignment) + => eval_assignment(lhs, rhs, op, env), + Some(OpType::Additive) | Some(OpType::Multiplicative) + => eval_binary(lhs, rhs, op, env), o => todo!("{:?}", o) // TODO other operations }, e => todo!("{:?}", e) // TODO other expression types } +} + +pub fn eval_literal(token: &Token) -> Value { + match &token.ty { + TokenType::Nil => Value::Nil, + TokenType::True => Value::Bool(true), + TokenType::False => Value::Bool(false), + TokenType::Int(n) => Value::Int(*n), + TokenType::Float(f) => Value::Float(*f), + TokenType::ImFloat(f) => Value::Complex(Complex64::new(0.0, *f)), + TokenType::String(s) => Value::String(s.clone()), + _ => todo!() + } +} + +pub fn eval_ident(token: &Token, env: EnvRef) -> Result { + if let Token { ty: TokenType::Ident(name), ..} = token { + env.borrow_mut() + .get(name) + .map_err(|_| RuntimeError { message: "Variable not defined in scope".into(), pos: token.pos.clone() }) + } else { + unreachable!() + } +} + +pub fn eval_assignment(lhs: &Box, rhs: &Box, op: &Token, env: EnvRef) -> Result { + // lhs must be an identifier (checked in parser) + if let Expr::Ident{value: Token{ty: TokenType::Ident(name),..}} = &**lhs { + if op.ty == TokenType::Equal { + // plain assignment + let r = eval_expr(rhs, env.clone())?; + env.borrow_mut() + .set(name.clone(), r) + .map_err(|_| RuntimeError { message: "Variable not declared before assignment".into(), pos: op.pos.clone() })?; + Ok(Value::Nil) + } else { + // compound assignment + let prev_value = env.borrow_mut() + .get(name) + .map_err(|_| RuntimeError { message: "Variable not defined in scope".into(), pos: op.pos.clone()})?; + let r = eval_expr(rhs, env.clone())?; + + let result = match op.ty { + TokenType::PlusEqual => &prev_value + &r, + TokenType::MinusEqual => &prev_value - &r, + TokenType::StarEqual => &prev_value * &r, + TokenType::SlashEqual => &prev_value / &r, + _ => todo!() // TODO more operations + }.map_err(|e| RuntimeError { message: e, pos: op.pos.clone() })?; + + env.borrow_mut() + .set(name.clone(), result.clone()).expect("unreachable"); + Ok(result) + } + } else { + unreachable!() + } +} + +pub fn eval_binary(lhs: &Box, rhs: &Box, op: &Token, env: EnvRef) -> Result { + let l = eval_expr(lhs, env.clone())?; + let r = eval_expr(rhs, env)?; + match op.ty { + TokenType::Plus => &l + &r, + TokenType::Minus => &l - &r, + TokenType::Star => &l * &r, + TokenType::Slash => &l / &r, + _ => todo!() // TODO other operations + }.map_err(|e| RuntimeError { message: e, pos: op.pos.clone() }) } \ No newline at end of file diff --git a/src/expr.rs b/src/expr.rs index d118102..d24f052 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -5,7 +5,8 @@ use crate::{token::{Token, OpType}}; pub enum Stmt { Expr { expr: Expr }, Let { lhs: Token, rhs: Option }, - If { conditions: Vec, bodies: Vec>, else_clause: Option> } + Block { stmts: Vec }, + If { if_clauses: Vec<(Expr, Stmt)>, else_clause: Option> } } impl fmt::Debug for Stmt { diff --git a/src/interpreter.rs b/src/interpreter.rs index 5ce8bbb..172601f 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -2,10 +2,10 @@ use std::{cell::RefCell, rc::Rc}; use crate::{value::Value, lexer::Lexer, parser::Parser, eval::{Environment, eval_stmt, eval_expr}, expr::Stmt}; -pub fn interpret(src: &str, env: Option>>) -> Result> { - let mut lexer = Lexer::new(src, None); +pub fn interpret(src: &str, fname: Option, env: Option>>, repl: bool) -> Result> { + let mut lexer = Lexer::new(src, fname); lexer.lex()?; - let mut parser = Parser::new(lexer.into_tokens()); + let mut parser = Parser::new(lexer.into_tokens(), repl); let ast = parser.parse()?; let environ; if let Some(env) = env { diff --git a/src/parser.rs b/src/parser.rs index db9f33f..38c5c8e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2,12 +2,13 @@ use crate::{token::{Token, TokenType, OpType}, ParserError, expr::{Stmt, Expr}}; pub struct Parser { tokens: Vec, + repl: bool, idx: usize } impl Parser { - pub fn new(tokens: Vec) -> Self { - Self { tokens, idx: 0 } + pub fn new(tokens: Vec, repl: bool) -> Self { + Self { tokens, repl, idx: 0 } } fn at_end(&self) -> bool { @@ -56,34 +57,11 @@ impl Parser { // let statement TokenType::Let => { self.next(); - let expr = self.assignment()?; - - // must be followed by an assignment expression - if let Expr::Binary{lhs, rhs, op: Token{ty: TokenType::Equal,..}} = expr { - if let Expr::Ident{value: tok} = *lhs { - if self.at_end() { - return Ok(Stmt::Let{lhs: tok, rhs: Some(*rhs)}) - } - let next = self.next(); - return match next.ty { - TokenType::Semicolon => Ok(Stmt::Let{lhs: tok, rhs: Some(*rhs)}), - _ => Err(self.mk_error("Missing semicolon after 'let' statement".to_owned())) - }; - } else { - Err(self.mk_error("Invalid expression after 'let'".to_owned())) - } - } else if let Expr::Ident{value: tok} = expr { - if self.at_end() { - return Ok(Stmt::Let{lhs: tok, rhs: None}) - } - let next = self.next(); - return match next.ty { - TokenType::Semicolon => Ok(Stmt::Let{lhs: tok, rhs: None}), - _ => Err(self.mk_error("Missing semicolon after 'let' statement".to_owned())) - }; - } else { - Err(self.mk_error("Invalid expression after 'let'".to_owned())) - } + return self.letstmt() + } + TokenType::LBrace => { + self.next(); + return self.block() } // if statement @@ -96,7 +74,11 @@ impl Parser { _ => { let expr = self.assignment()?; if self.at_end() { - return Ok(Stmt::Expr{expr}) + if self.repl { + return Ok(Stmt::Expr{expr}) + } else { + self.err_on_eof()?; + } } let next = self.next(); @@ -109,6 +91,79 @@ impl Parser { } } + fn letstmt(&mut self) -> Result { + let expr = self.assignment()?; + // must be followed by an assignment expression + if let Expr::Binary{lhs, rhs, op: Token{ty: TokenType::Equal,..}} = expr { + if let Expr::Ident{value: tok} = *lhs { + if self.at_end() { + if self.repl { + return Ok(Stmt::Let{lhs: tok, rhs: Some(*rhs)}) + } else { + self.err_on_eof()?; + } + } + let next = self.next(); + return match next.ty { + TokenType::Semicolon => Ok(Stmt::Let{lhs: tok, rhs: Some(*rhs)}), + _ => Err(self.mk_error("Missing semicolon after 'let' statement".to_owned())) + }; + } else { + Err(self.mk_error("Invalid expression after 'let'".to_owned())) + } + } else if let Expr::Ident{value: tok} = expr { + if self.at_end() { + if self.repl { + return Ok(Stmt::Let{lhs: tok, rhs: None}) + } else { + self.err_on_eof()?; + } + } + let next = self.next(); + return match next.ty { + TokenType::Semicolon => Ok(Stmt::Let{lhs: tok, rhs: None}), + _ => Err(self.mk_error("Missing semicolon after 'let' statement".to_owned())) + }; + } else { + Err(self.mk_error("Invalid expression after 'let'".to_owned())) + } + } + + fn ifstmt(&mut self) -> Result { + let mut if_clauses = vec![]; + let mut ec = false; + loop { + let condition = self.assignment()?; + let body = self.statement()?; + if_clauses.push((condition, body)); + match self.peek().ty { + TokenType::Elif => { self.next(); continue }, + TokenType::Else => { self.next(); ec = true; break }, + _ => break + } + } + let else_clause; + if ec { + else_clause = Some(Box::new(self.statement()?)); + } else { + else_clause = None; + } + return Ok(Stmt::If{ + if_clauses: if_clauses, + else_clause: else_clause + }) + } + + fn block(&mut self) -> Result { + let mut stmts = vec![]; + while !self.at_end() && self.peek().ty != TokenType::RBrace { + stmts.push(self.statement()?) + } + self.err_on_eof()?; + self.next(); + return Ok(Stmt::Block{ stmts }) + } + // Generic method for left-associative operators fn expr(&mut self, op_type: OpType, next_level: fn(&mut Parser) -> Result) -> Result { let mut expr = next_level(self)?; @@ -224,8 +279,4 @@ impl Parser { Err(self.mk_error(format!("Unexpected token: {:?}", next.ty))) } } - - fn ifstmt(&mut self) -> Result { - todo!() - } } \ No newline at end of file diff --git a/src/value.rs b/src/value.rs index af00ab3..fb2981d 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,5 +1,7 @@ use std::{rc::Rc, collections::HashMap, ops::*}; +use num_traits::Zero; + type Rational = num_rational::Ratio; type Complex = num_complex::Complex64; @@ -56,6 +58,22 @@ pub enum Value { List(Rc>), Map(Rc>), } +impl Value { + pub fn truthy(&self) -> bool { + use Value::*; + match self { + Bool(false) | Nil | Int(0) => false, + Float(f) => *f != 0.0, + Complex(z) => !z.is_zero(), + Rational(r) => !r.is_zero(), + String(s) => !s.len() == 0, + List(l) => !l.len() == 0, + Map(m) => !m.len() == 0, + _ => true + } + } +} + value_from!(Int, u8 u16 u32 i8 i16 i32 i64); value_from!(Float, f32 f64); diff --git a/tests/code.cxpr b/tests/code.cxpr index 7f306a8..a4be086 100644 --- a/tests/code.cxpr +++ b/tests/code.cxpr @@ -1,3 +1,7 @@ -let a = 1; -let b = 2; -a = a + b; \ No newline at end of file +if false { + +} elif true { + +} else { + +} \ No newline at end of file diff --git a/tests/test.rs b/tests/test.rs index 00c415d..b06f238 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -8,7 +8,7 @@ use complexpr::{lexer::Lexer, parser::Parser, eval::{Environment, eval_stmt}}; pub fn test() { let mut lexer = Lexer::new("let a = 1 + 1; let b = a + 1;", None); lexer.lex().unwrap(); - let mut parser = Parser::new(lexer.into_tokens()); + let mut parser = Parser::new(lexer.into_tokens(), false); let ast = parser.parse().unwrap(); let env = Rc::new(RefCell::new(Environment::new())); for stmt in ast {