break and continue

This commit is contained in:
TriMill 2022-09-11 01:01:53 -04:00
parent cc216947ee
commit 652106135c
9 changed files with 143 additions and 28 deletions

View file

@ -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
View 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("");
}

View file

@ -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(())
} }

View file

@ -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 {

View file

@ -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;
} }
} }

View file

@ -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))
}; };

View file

@ -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

View file

@ -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))
}
} }

View file

@ -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 {