break and continue
This commit is contained in:
parent
cc216947ee
commit
652106135c
9 changed files with 143 additions and 28 deletions
|
@ -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] == ']' {
|
||||
|
|
39
examples/mbrot.cxpr
Normal file
39
examples/mbrot.cxpr
Normal file
|
@ -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("");
|
||||
}
|
43
src/eval.rs
43
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<RuntimeError> for Unwind {
|
||||
fn from(e: RuntimeError) -> Self {
|
||||
Self::Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_ident_token<'a>(tok: &'a Token) -> &'a Rc<str> {
|
||||
if let Token { ty: TokenType::Ident(s),.. } = tok {
|
||||
s
|
||||
|
@ -56,7 +77,7 @@ fn unwrap_ident_token<'a>(tok: &'a Token) -> &'a Rc<str> {
|
|||
}
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ pub enum Stmt {
|
|||
If { if_clauses: Vec<(Expr, Stmt)>, else_clause: Option<Box<Stmt>> },
|
||||
For { var: Token, expr: Expr, stmt: Box<Stmt> },
|
||||
While { expr: Expr, stmt: Box<Stmt> },
|
||||
Break { tok: Token },
|
||||
Continue { tok: Token },
|
||||
}
|
||||
|
||||
impl fmt::Debug for Stmt {
|
||||
|
|
|
@ -20,7 +20,7 @@ pub fn interpret(src: &str, fname: Option<String>, env: Option<EnvRef>, 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
};
|
||||
|
|
|
@ -51,9 +51,9 @@ impl Parser {
|
|||
}
|
||||
|
||||
fn statement(&mut self) -> Result<Stmt, ParserError> {
|
||||
let next = self.peek();
|
||||
let next_ty = &self.peek().ty;
|
||||
|
||||
match next.ty {
|
||||
match next_ty {
|
||||
TokenType::Let => {
|
||||
// let statement
|
||||
self.next();
|
||||
|
@ -79,12 +79,26 @@ 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()?;
|
||||
self.terminate_stmt(Stmt::Expr{ expr })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn terminate_stmt(&mut self, stmt: Stmt) -> Result<Stmt, ParserError> {
|
||||
if self.at_end() {
|
||||
if self.repl {
|
||||
return Ok(Stmt::Expr{expr})
|
||||
return Ok(stmt)
|
||||
} else {
|
||||
self.err_on_eof()?;
|
||||
}
|
||||
|
@ -93,11 +107,10 @@ impl Parser {
|
|||
let next = self.next();
|
||||
|
||||
match next.ty {
|
||||
TokenType::Semicolon => Ok(Stmt::Expr{expr}),
|
||||
TokenType::Semicolon => Ok(stmt),
|
||||
_ => Err(self.mk_error("Missing semicolon after statement"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn letstmt(&mut self) -> Result<Stmt, ParserError> {
|
||||
|
|
|
@ -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<Value>) -> Result<Value, String> {
|
||||
|
@ -99,3 +105,22 @@ fn fn_len(args: Vec<Value>) -> Result<Value, String> {
|
|||
v => Err(format!("{:?} has no length", v))
|
||||
}
|
||||
}
|
||||
|
||||
fn fn_re(args: Vec<Value>) -> Result<Value, String> {
|
||||
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<Value>) -> Result<Value, String> {
|
||||
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))
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue