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 == '[' {
|
} elif op == '[' {
|
||||||
if tape[ptr] == 0 {
|
if tape[ptr] == 0 {
|
||||||
let depth = 0;
|
let depth = 0;
|
||||||
let running = true;
|
while true {
|
||||||
while running {
|
|
||||||
i += 1;
|
i += 1;
|
||||||
if program[i] == ']' {
|
if program[i] == ']' {
|
||||||
if depth == 0 {
|
if depth == 0 {
|
||||||
running = false;
|
break;
|
||||||
}
|
}
|
||||||
depth -= 1;
|
depth -= 1;
|
||||||
} elif program[i] == '[' {
|
} elif program[i] == '[' {
|
||||||
|
@ -44,11 +43,11 @@ while true {
|
||||||
if tape[ptr] != 0 {
|
if tape[ptr] != 0 {
|
||||||
let depth = 0;
|
let depth = 0;
|
||||||
let running = true;
|
let running = true;
|
||||||
while running {
|
while true {
|
||||||
i -= 1;
|
i -= 1;
|
||||||
if program[i] == '[' {
|
if program[i] == '[' {
|
||||||
if depth == 0 {
|
if depth == 0 {
|
||||||
running = false;
|
break;
|
||||||
}
|
}
|
||||||
depth -= 1;
|
depth -= 1;
|
||||||
} elif program[i] == ']' {
|
} 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 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)]
|
#[derive(Debug)]
|
||||||
pub struct Environment {
|
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> {
|
fn unwrap_ident_token<'a>(tok: &'a Token) -> &'a Rc<str> {
|
||||||
if let Token { ty: TokenType::Ident(s),.. } = tok {
|
if let Token { ty: TokenType::Ident(s),.. } = tok {
|
||||||
s
|
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 {
|
match stmt {
|
||||||
Stmt::Expr{ expr }
|
Stmt::Expr{ expr }
|
||||||
=> drop(eval_expr(expr, env)),
|
=> drop(eval_expr(expr, env)),
|
||||||
|
@ -91,10 +112,15 @@ pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), RuntimeError> {
|
||||||
for v in i {
|
for v in i {
|
||||||
let env = env.clone();
|
let env = env.clone();
|
||||||
env.borrow_mut().set(name.clone(), v).expect("unreachable");
|
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 {
|
} 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 } => {
|
Stmt::While { expr, stmt } => {
|
||||||
|
@ -103,9 +129,16 @@ pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), RuntimeError> {
|
||||||
if !cond.truthy() {
|
if !cond.truthy() {
|
||||||
break
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ pub enum Stmt {
|
||||||
If { if_clauses: Vec<(Expr, Stmt)>, else_clause: Option<Box<Stmt>> },
|
If { if_clauses: Vec<(Expr, Stmt)>, else_clause: Option<Box<Stmt>> },
|
||||||
For { var: Token, expr: Expr, stmt: Box<Stmt> },
|
For { var: Token, expr: Expr, stmt: Box<Stmt> },
|
||||||
While { expr: Expr, stmt: Box<Stmt> },
|
While { expr: Expr, stmt: Box<Stmt> },
|
||||||
|
Break { tok: Token },
|
||||||
|
Continue { tok: Token },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Stmt {
|
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 {
|
if let Stmt::Expr{expr} = stmt {
|
||||||
result = eval_expr(&expr, environ.clone())?;
|
result = eval_expr(&expr, environ.clone())?;
|
||||||
} else {
|
} else {
|
||||||
eval_stmt(&stmt, environ.clone())?;
|
eval_stmt(&stmt, environ.clone()).map_err(|e| e.as_error())?;
|
||||||
result = Value::Nil;
|
result = Value::Nil;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,8 @@ impl Lexer {
|
||||||
"while" => TokenType::While,
|
"while" => TokenType::While,
|
||||||
"for" => TokenType::For,
|
"for" => TokenType::For,
|
||||||
"let" => TokenType::Let,
|
"let" => TokenType::Let,
|
||||||
|
"break" => TokenType::Break,
|
||||||
|
"continue" => TokenType::Continue,
|
||||||
"return" => TokenType::Return,
|
"return" => TokenType::Return,
|
||||||
s => TokenType::Ident(Rc::from(s))
|
s => TokenType::Ident(Rc::from(s))
|
||||||
};
|
};
|
||||||
|
|
|
@ -51,9 +51,9 @@ impl Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn statement(&mut self) -> Result<Stmt, ParserError> {
|
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 => {
|
TokenType::Let => {
|
||||||
// let statement
|
// let statement
|
||||||
self.next();
|
self.next();
|
||||||
|
@ -79,27 +79,40 @@ impl Parser {
|
||||||
self.next();
|
self.next();
|
||||||
self.whilestmt()
|
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
|
// fallback to an expression terminated with a semicolon
|
||||||
let expr = self.assignment()?;
|
let expr = self.assignment()?;
|
||||||
if self.at_end() {
|
self.terminate_stmt(Stmt::Expr{ expr })
|
||||||
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"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn terminate_stmt(&mut self, stmt: Stmt) -> Result<Stmt, ParserError> {
|
||||||
|
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<Stmt, ParserError> {
|
fn letstmt(&mut self) -> Result<Stmt, ParserError> {
|
||||||
let expr = self.assignment()?;
|
let expr = self.assignment()?;
|
||||||
// must be followed by an assignment expression
|
// must be followed by an assignment expression
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use std::{rc::Rc, io::Write};
|
use std::{rc::Rc, io::Write};
|
||||||
|
|
||||||
|
use num_traits::ToPrimitive;
|
||||||
|
|
||||||
use crate::{value::{Value, BuiltinFn}, eval::Environment};
|
use crate::{value::{Value, BuiltinFn}, eval::Environment};
|
||||||
|
|
||||||
pub fn load(env: &mut 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 }));
|
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_range, arg_count: 2, name }));
|
||||||
name = Rc::from("len");
|
name = Rc::from("len");
|
||||||
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_len, arg_count: 1, name }));
|
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> {
|
fn fn_str(args: Vec<Value>) -> Result<Value, String> {
|
||||||
|
@ -98,4 +104,23 @@ fn fn_len(args: Vec<Value>) -> Result<Value, String> {
|
||||||
Value::Map(m) => Ok(Value::Int(m.len() as i64)),
|
Value::Map(m) => Ok(Value::Int(m.len() as i64)),
|
||||||
v => Err(format!("{:?} has no length", v))
|
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,
|
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 {
|
impl TokenType {
|
||||||
|
|
Loading…
Reference in a new issue