From 652106135c2381971b677c0fc5befbcccc735427 Mon Sep 17 00:00:00 2001 From: TriMill Date: Sun, 11 Sep 2022 01:01:53 -0400 Subject: [PATCH] break and continue --- examples/bf.cxpr | 9 ++++----- examples/mbrot.cxpr | 39 +++++++++++++++++++++++++++++++++++++++ src/eval.rs | 43 ++++++++++++++++++++++++++++++++++++++----- src/expr.rs | 2 ++ src/interpreter.rs | 2 +- src/lexer.rs | 2 ++ src/parser.rs | 45 +++++++++++++++++++++++++++++---------------- src/stdlib/mod.rs | 25 +++++++++++++++++++++++++ src/token.rs | 4 +++- 9 files changed, 143 insertions(+), 28 deletions(-) create mode 100644 examples/mbrot.cxpr diff --git a/examples/bf.cxpr b/examples/bf.cxpr index 36e058b..93732fa 100644 --- a/examples/bf.cxpr +++ b/examples/bf.cxpr @@ -27,12 +27,11 @@ while true { } elif op == '[' { if tape[ptr] == 0 { let depth = 0; - let running = true; - while running { + while true { i += 1; if program[i] == ']' { if depth == 0 { - running = false; + break; } depth -= 1; } elif program[i] == '[' { @@ -44,11 +43,11 @@ while true { if tape[ptr] != 0 { let depth = 0; let running = true; - while running { + while true { i -= 1; if program[i] == '[' { if depth == 0 { - running = false; + break; } depth -= 1; } elif program[i] == ']' { diff --git a/examples/mbrot.cxpr b/examples/mbrot.cxpr new file mode 100644 index 0000000..ec6a8c8 --- /dev/null +++ b/examples/mbrot.cxpr @@ -0,0 +1,39 @@ +let xscale = 100; +let yscale = xscale/2; + +for y: range(0,yscale) { + for x: range(0,xscale) { + + let a = (1.0*x/xscale)*3 - 2; + let b = (1.0*y/yscale)*3 - 1.5; + + let c = a + b*1i; + let z = c; + + let i = 0; + while re(z)*re(z) + im(z)*im(z) <= 4 { + z = z*z + c; + if i > 70 { + break; + } + i += 1; + } + + if i > 70 { + print(" "); + } elif i > 20 { + print("#"); + } elif i > 12 { + print("$"); + } elif i > 8 { + print("&"); + } elif i > 4 { + print("+"); + } elif i > 2 { + print(";"); + } else { + print("-"); + } + } + println(""); +} \ No newline at end of file diff --git a/src/eval.rs b/src/eval.rs index d021efd..9748344 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, rc::Rc, cell::RefCell}; -use crate::{value::{Value, Complex}, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}, RuntimeError}; +use crate::{value::{Value, Complex}, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}, RuntimeError, Position}; #[derive(Debug)] pub struct Environment { @@ -48,6 +48,27 @@ impl Environment { } } +pub enum Unwind { + Continue{pos: Position}, Break{pos: Position}, Return{pos: Position, value: Value}, Error(RuntimeError) +} + +impl Unwind { + pub fn as_error(self) -> RuntimeError { + match self { + Self::Error(e) => e, + Self::Continue { pos } => RuntimeError { message: "continue statement outside of loop".into(), pos }, + Self::Break { pos } => RuntimeError { message: "break statement outside of loop".into(), pos }, + Self::Return { pos, .. } => RuntimeError { message: "return statement outside of function".into(), pos }, + } + } +} + +impl From for Unwind { + fn from(e: RuntimeError) -> Self { + Self::Error(e) + } +} + fn unwrap_ident_token<'a>(tok: &'a Token) -> &'a Rc { if let Token { ty: TokenType::Ident(s),.. } = tok { s @@ -56,7 +77,7 @@ fn unwrap_ident_token<'a>(tok: &'a Token) -> &'a Rc { } } -pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), RuntimeError> { +pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), Unwind> { match stmt { Stmt::Expr{ expr } => drop(eval_expr(expr, env)), @@ -91,10 +112,15 @@ pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), RuntimeError> { for v in i { let env = env.clone(); env.borrow_mut().set(name.clone(), v).expect("unreachable"); - eval_stmt(&stmt, env)?; + match eval_stmt(&stmt, env) { + Ok(_) => (), + Err(Unwind::Break{..}) => break, + Err(Unwind::Continue{..}) => continue, + Err(e) => return Err(e) + } } } else { - return Err(RuntimeError { message: "Cannot iterate this type".into(), pos: var.pos.clone() }) + return Err(RuntimeError { message: "Cannot iterate this type".into(), pos: var.pos.clone() }.into()) }; }, Stmt::While { expr, stmt } => { @@ -103,9 +129,16 @@ pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), RuntimeError> { if !cond.truthy() { break } - eval_stmt(&stmt, env.clone())?; + match eval_stmt(&stmt, env.clone()) { + Ok(_) => (), + Err(Unwind::Break{..}) => break, + Err(Unwind::Continue{..}) => continue, + Err(e) => return Err(e) + } } }, + Stmt::Break { tok } => return Err(Unwind::Break { pos: tok.pos.clone() }), + Stmt::Continue { tok } => return Err(Unwind::Continue { pos: tok.pos.clone() }), } Ok(()) } diff --git a/src/expr.rs b/src/expr.rs index 8a973d4..baf5356 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -9,6 +9,8 @@ pub enum Stmt { If { if_clauses: Vec<(Expr, Stmt)>, else_clause: Option> }, For { var: Token, expr: Expr, stmt: Box }, While { expr: Expr, stmt: Box }, + Break { tok: Token }, + Continue { tok: Token }, } impl fmt::Debug for Stmt { diff --git a/src/interpreter.rs b/src/interpreter.rs index 025979d..bbfc969 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -20,7 +20,7 @@ pub fn interpret(src: &str, fname: Option, env: Option, repl: bo if let Stmt::Expr{expr} = stmt { result = eval_expr(&expr, environ.clone())?; } else { - eval_stmt(&stmt, environ.clone())?; + eval_stmt(&stmt, environ.clone()).map_err(|e| e.as_error())?; result = Value::Nil; } } diff --git a/src/lexer.rs b/src/lexer.rs index de088e8..ea91918 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -269,6 +269,8 @@ impl Lexer { "while" => TokenType::While, "for" => TokenType::For, "let" => TokenType::Let, + "break" => TokenType::Break, + "continue" => TokenType::Continue, "return" => TokenType::Return, s => TokenType::Ident(Rc::from(s)) }; diff --git a/src/parser.rs b/src/parser.rs index 7a00483..83317b1 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -51,9 +51,9 @@ impl Parser { } fn statement(&mut self) -> Result { - let next = self.peek(); + let next_ty = &self.peek().ty; - match next.ty { + match next_ty { TokenType::Let => { // let statement self.next(); @@ -79,27 +79,40 @@ impl Parser { self.next(); self.whilestmt() }, + TokenType::Break => { + let tok = self.next(); + self.terminate_stmt(Stmt::Break{ tok }) + } + TokenType::Continue => { + let tok = self.next(); + self.terminate_stmt(Stmt::Continue{ tok }) + } _ => { // fallback to an expression terminated with a semicolon let expr = self.assignment()?; - if self.at_end() { - if self.repl { - return Ok(Stmt::Expr{expr}) - } else { - self.err_on_eof()?; - } - } - - let next = self.next(); - - match next.ty { - TokenType::Semicolon => Ok(Stmt::Expr{expr}), - _ => Err(self.mk_error("Missing semicolon after statement")) - } + self.terminate_stmt(Stmt::Expr{ expr }) } } } + fn terminate_stmt(&mut self, stmt: Stmt) -> Result { + if self.at_end() { + if self.repl { + return Ok(stmt) + } else { + self.err_on_eof()?; + } + } + + let next = self.next(); + + match next.ty { + TokenType::Semicolon => Ok(stmt), + _ => Err(self.mk_error("Missing semicolon after statement")) + } + + } + fn letstmt(&mut self) -> Result { let expr = self.assignment()?; // must be followed by an assignment expression diff --git a/src/stdlib/mod.rs b/src/stdlib/mod.rs index 882d904..da37b04 100644 --- a/src/stdlib/mod.rs +++ b/src/stdlib/mod.rs @@ -1,5 +1,7 @@ use std::{rc::Rc, io::Write}; +use num_traits::ToPrimitive; + use crate::{value::{Value, BuiltinFn}, eval::Environment}; pub fn load(env: &mut Environment) { @@ -22,6 +24,10 @@ pub fn load(env: &mut Environment) { env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_range, arg_count: 2, name })); name = Rc::from("len"); env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_len, arg_count: 1, name })); + name = Rc::from("re"); + env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_re, arg_count: 1, name })); + name = Rc::from("im"); + env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_im, arg_count: 1, name })); } fn fn_str(args: Vec) -> Result { @@ -98,4 +104,23 @@ fn fn_len(args: Vec) -> Result { Value::Map(m) => Ok(Value::Int(m.len() as i64)), v => Err(format!("{:?} has no length", v)) } +} + +fn fn_re(args: Vec) -> Result { + match &args[0] { + Value::Int(x) => Ok(Value::Float(*x as f64)), + Value::Float(x) => Ok(Value::Float(*x)), + Value::Rational(x) => Ok(Value::Float(x.to_f64().unwrap())), + Value::Complex(x) => Ok(Value::Float(x.re)), + x => Err(format!("Cannot get real part of {:?}", x)) + } +} + +fn fn_im(args: Vec) -> Result { + match &args[0] { + Value::Int(_) | Value::Float(_) | Value::Rational(_) + => Ok(Value::Float(0.0)), + Value::Complex(x) => Ok(Value::Float(x.im)), + x => Err(format!("Cannot get real part of {:?}", x)) + } } \ No newline at end of file diff --git a/src/token.rs b/src/token.rs index 1d71204..06414e4 100644 --- a/src/token.rs +++ b/src/token.rs @@ -33,7 +33,9 @@ pub enum TokenType { LParen, RParen, LBrack, RBrack, LBrace, RBrace, - True, False, Nil, If, Elif, Else, For, While, Let, Return + True, False, Nil, + If, Elif, Else, For, While, + Let, Break, Continue, Return } impl TokenType {