mutable lists and indexing
This commit is contained in:
parent
910e758c26
commit
cc216947ee
6 changed files with 268 additions and 28 deletions
68
examples/bf.cxpr
Normal file
68
examples/bf.cxpr
Normal file
|
@ -0,0 +1,68 @@
|
|||
while true {
|
||||
print("bf> ");
|
||||
let program = input();
|
||||
let tape = [0]*256;
|
||||
let ptr = 0;
|
||||
let i = 0;
|
||||
while i < len(program) {
|
||||
let op = program[i];
|
||||
if op == '+' {
|
||||
tape[ptr] += 1;
|
||||
if tape[ptr] >= 256 {
|
||||
tape[ptr] -= 256;
|
||||
}
|
||||
} elif op == '-' {
|
||||
tape[ptr] -= 1;
|
||||
if tape[ptr] < 0 {
|
||||
tape[ptr] += 256;
|
||||
}
|
||||
} elif op == '>' {
|
||||
ptr += 1;
|
||||
} elif op == '<' {
|
||||
ptr -= 1;
|
||||
} elif op == '.' {
|
||||
print(chr(tape[ptr]));
|
||||
} elif op == ',' {
|
||||
tape[ptr] = ord(input()[0]);
|
||||
} elif op == '[' {
|
||||
if tape[ptr] == 0 {
|
||||
let depth = 0;
|
||||
let running = true;
|
||||
while running {
|
||||
i += 1;
|
||||
if program[i] == ']' {
|
||||
if depth == 0 {
|
||||
running = false;
|
||||
}
|
||||
depth -= 1;
|
||||
} elif program[i] == '[' {
|
||||
depth += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} elif op == ']' {
|
||||
if tape[ptr] != 0 {
|
||||
let depth = 0;
|
||||
let running = true;
|
||||
while running {
|
||||
i -= 1;
|
||||
if program[i] == '[' {
|
||||
if depth == 0 {
|
||||
running = false;
|
||||
}
|
||||
depth -= 1;
|
||||
} elif program[i] == ']' {
|
||||
depth += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ptr >= len(tape) {
|
||||
tape += [0]*256;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
println("");
|
||||
}
|
41
src/eval.rs
41
src/eval.rs
|
@ -123,22 +123,28 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result<Value, RuntimeError> {
|
|||
=> eval_comp(lhs, rhs, op, env),
|
||||
o => todo!("{:?}", o) // TODO other operations
|
||||
},
|
||||
Expr::Unary { arg, op } => eval_unary(arg, op, env),
|
||||
Expr::List { items } => {
|
||||
let mut list = Vec::with_capacity(items.len());
|
||||
for item in items {
|
||||
list.push(eval_expr(item, env.clone())?);
|
||||
}
|
||||
Ok(Value::List(Rc::new(list)))
|
||||
Ok(Value::from(list))
|
||||
},
|
||||
Expr::FuncCall { func, args, pos } => {
|
||||
let func = eval_expr(&func, env.clone())?;
|
||||
let func = eval_expr(func, env.clone())?;
|
||||
let mut arg_values = Vec::with_capacity(args.len());
|
||||
for arg in args {
|
||||
let result = eval_expr(arg, env.clone())?;
|
||||
arg_values.push(result);
|
||||
}
|
||||
func.call(arg_values, pos)
|
||||
}
|
||||
},
|
||||
Expr::Index { lhs, index, pos } => {
|
||||
let l = eval_expr(lhs, env.clone())?;
|
||||
let idx = eval_expr(index, env)?;
|
||||
l.index(&idx).map_err(|e| RuntimeError { message: e, pos: pos.clone() })
|
||||
},
|
||||
e => todo!("{:?}", e) // TODO other expression types
|
||||
}
|
||||
}
|
||||
|
@ -197,6 +203,27 @@ pub fn eval_assignment(lhs: &Box<Expr>, rhs: &Box<Expr>, op: &Token, env: EnvRef
|
|||
.set(name.clone(), result.clone()).expect("unreachable");
|
||||
Ok(result)
|
||||
}
|
||||
} else if let Expr::Index { lhs, index, pos } = &**lhs {
|
||||
let l = eval_expr(lhs, env.clone())?;
|
||||
let idx = eval_expr(index, env.clone())?;
|
||||
if op.ty == TokenType::Equal {
|
||||
let r = eval_expr(rhs, env)?;
|
||||
l.assign_index(&idx, r.clone()).map_err(|e| RuntimeError { message: e, pos: pos.clone() })?;
|
||||
Ok(r)
|
||||
} else {
|
||||
let prev_value = l.index(&idx).map_err(|e| RuntimeError { message: e, pos: pos.clone() })?;
|
||||
let r = eval_expr(rhs, env)?;
|
||||
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,
|
||||
TokenType::PercentEqual => &prev_value % &r,
|
||||
_ => todo!() // TODO more operations
|
||||
}.map_err(|e| RuntimeError { message: e, pos: op.pos.clone() })?;
|
||||
l.assign_index(&idx, result.clone()).map_err(|e| RuntimeError { message: e, pos: pos.clone() })?;
|
||||
Ok(result)
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
@ -242,4 +269,12 @@ pub fn eval_comp(lhs: &Box<Expr>, rhs: &Box<Expr>, op: &Token, env: EnvRef) -> R
|
|||
.map(|o| Value::from(o as i8)),
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval_unary(arg: &Box<Expr>, op: &Token, env: EnvRef) -> Result<Value, RuntimeError> {
|
||||
let a = eval_expr(arg, env)?;
|
||||
match op.ty {
|
||||
TokenType::Minus => -a,
|
||||
_ => todo!(),
|
||||
}.map_err(|e| RuntimeError { message: e, pos: op.pos.clone() })
|
||||
}
|
|
@ -27,7 +27,8 @@ pub enum Expr {
|
|||
Ident { value: Token },
|
||||
Literal { value: Token },
|
||||
List { items: Vec<Expr> },
|
||||
FuncCall { func: Box<Expr>, args: Vec<Expr>, pos: Position }
|
||||
FuncCall { func: Box<Expr>, args: Vec<Expr>, pos: Position },
|
||||
Index { lhs: Box<Expr>, index: Box<Expr>, pos: Position },
|
||||
}
|
||||
|
||||
impl fmt::Debug for Expr {
|
||||
|
@ -39,13 +40,14 @@ impl fmt::Debug for Expr {
|
|||
Self::Literal { value } => write!(f, "(lit {:?})", value),
|
||||
Self::List { items } => write!(f, "(list {:?})", items),
|
||||
Self::FuncCall { func, args, .. } => write!(f, "(call {:?} {:?})", func, args),
|
||||
Self::Index { lhs, index, .. } => write!(f, "(index {:?} {:?})", lhs, index),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
pub fn is_lvalue(&self) -> bool {
|
||||
matches!(self, Expr::Ident{..})
|
||||
return matches!(self, Expr::Ident{..} | Expr::Index{..})
|
||||
}
|
||||
|
||||
pub fn is_assignment(&self) -> bool {
|
||||
|
|
|
@ -289,14 +289,31 @@ impl Parser {
|
|||
}
|
||||
|
||||
fn fncall(&mut self) -> Result<Expr, ParserError> {
|
||||
let expr = self.expr_base()?;
|
||||
if !self.at_end() && self.peek().ty == TokenType::LParen {
|
||||
let lparen = self.next();
|
||||
let args = self.commalist(TokenType::RParen, Self::assignment)?;
|
||||
Ok(Expr::FuncCall { func: Box::new(expr), args, pos: lparen.pos.clone() })
|
||||
} else {
|
||||
Ok(expr)
|
||||
let mut expr = self.expr_base()?;
|
||||
while !self.at_end() {
|
||||
match self.peek().ty {
|
||||
TokenType::LParen => expr = self.fncall_inner(expr)?,
|
||||
TokenType::LBrack => expr = self.arrindex_inner(expr)?,
|
||||
_ => return Ok(expr)
|
||||
}
|
||||
}
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
fn fncall_inner(&mut self, expr: Expr) -> Result<Expr, ParserError> {
|
||||
let lparen = self.next();
|
||||
let args = self.commalist(TokenType::RParen, Self::assignment)?;
|
||||
Ok(Expr::FuncCall { func: Box::new(expr), args, pos: lparen.pos.clone() })
|
||||
}
|
||||
|
||||
fn arrindex_inner(&mut self, expr: Expr) -> Result<Expr, ParserError> {
|
||||
let lbrack = self.next();
|
||||
let index = self.assignment()?;
|
||||
self.err_on_eof()?;
|
||||
if self.next().ty != TokenType::RBrack {
|
||||
return Err(ParserError { message: "Expected RBrack after collection index".into(), pos: lbrack.pos.clone() });
|
||||
}
|
||||
Ok(Expr::Index { lhs: Box::new(expr), index: Box::new(index), pos: lbrack.pos.clone() })
|
||||
}
|
||||
|
||||
fn expr_base(&mut self) -> Result<Expr, ParserError> {
|
||||
|
|
|
@ -14,6 +14,14 @@ pub fn load(env: &mut Environment) {
|
|||
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_println, arg_count: 1, name }));
|
||||
name = Rc::from("input");
|
||||
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_input, arg_count: 0, name }));
|
||||
name = Rc::from("ord");
|
||||
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_ord, arg_count: 1, name }));
|
||||
name = Rc::from("chr");
|
||||
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_chr, arg_count: 1, name }));
|
||||
name = Rc::from("range");
|
||||
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 }));
|
||||
}
|
||||
|
||||
fn fn_str(args: Vec<Value>) -> Result<Value, String> {
|
||||
|
@ -39,5 +47,55 @@ fn fn_input(_: Vec<Value>) -> Result<Value, String> {
|
|||
let mut buffer = String::new();
|
||||
let stdin = std::io::stdin();
|
||||
stdin.read_line(&mut buffer).map_err(|e| e.to_string())?;
|
||||
if buffer.ends_with("\n") {
|
||||
buffer.pop();
|
||||
}
|
||||
Ok(Value::from(buffer))
|
||||
}
|
||||
|
||||
fn fn_ord(args: Vec<Value>) -> Result<Value, String> {
|
||||
if let Value::Char(c) = args[0] {
|
||||
Ok(Value::from(c as u32))
|
||||
} else {
|
||||
Err("Argument to ord must be a char".into())
|
||||
}
|
||||
}
|
||||
|
||||
fn fn_chr(args: Vec<Value>) -> Result<Value, String> {
|
||||
if let Value::Int(i) = args[0] {
|
||||
if i >= 0 && i < (u32::MAX as i64) {
|
||||
if let Some(c) = char::from_u32(i as u32) {
|
||||
return Ok(Value::from(c))
|
||||
}
|
||||
}
|
||||
Err("Out of range".into())
|
||||
} else {
|
||||
Err("Argument to chr must be an integer".into())
|
||||
}
|
||||
}
|
||||
|
||||
fn fn_range(args: Vec<Value>) -> Result<Value, String> {
|
||||
let min = &args[0];
|
||||
let max = &args[1];
|
||||
match (min, max) {
|
||||
(Value::Int(a), Value::Int(b)) => {
|
||||
if a == b {
|
||||
Ok(Value::from(vec![]))
|
||||
} else if a < b {
|
||||
Ok(Value::from((*a..*b).map(|x| Value::Int(x)).collect::<Vec<Value>>()))
|
||||
} else {
|
||||
Ok(Value::from(((*b+1)..(*a+1)).rev().map(|x| Value::Int(x)).collect::<Vec<Value>>()))
|
||||
}
|
||||
},
|
||||
_ => Err("Both arguments to range must be integers".into())
|
||||
}
|
||||
}
|
||||
|
||||
fn fn_len(args: Vec<Value>) -> Result<Value, String> {
|
||||
match &args[0] {
|
||||
Value::String(s) => Ok(Value::Int(s.len() as i64)),
|
||||
Value::List(l) => Ok(Value::Int(l.borrow().len() as i64)),
|
||||
Value::Map(m) => Ok(Value::Int(m.len() as i64)),
|
||||
v => Err(format!("{:?} has no length", v))
|
||||
}
|
||||
}
|
92
src/value.rs
92
src/value.rs
|
@ -1,4 +1,4 @@
|
|||
use std::{rc::Rc, collections::HashMap, ops::*, fmt, cmp::Ordering};
|
||||
use std::{rc::Rc, collections::HashMap, ops::*, fmt, cmp::Ordering, cell::RefCell};
|
||||
|
||||
use num_traits::{Zero, ToPrimitive};
|
||||
|
||||
|
@ -41,7 +41,7 @@ pub enum Value {
|
|||
Bool(bool),
|
||||
Char(char),
|
||||
String(Rc<str>),
|
||||
List(Rc<Vec<Value>>), Map(Rc<HashMap<Value,Value>>),
|
||||
List(Rc<RefCell<Vec<Value>>>), Map(Rc<HashMap<Value,Value>>),
|
||||
BuiltinFn(BuiltinFn),
|
||||
Data(Data),
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ impl Value {
|
|||
Complex(z) => !z.is_zero(),
|
||||
Rational(r) => !r.is_zero(),
|
||||
String(s) => !s.len() == 0,
|
||||
List(l) => !l.len() == 0,
|
||||
List(l) => !l.borrow().len() == 0,
|
||||
Map(m) => !m.len() == 0,
|
||||
_ => true
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ impl Value {
|
|||
Value::String(s)
|
||||
=> Ok(Box::new(s.chars()
|
||||
.map(|c| Value::Char(c)))),
|
||||
Value::List(l) => Ok(Box::new(l.iter().cloned())),
|
||||
Value::List(l) => Ok(Box::new(l.borrow().clone().into_iter())),
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
|
@ -120,13 +120,51 @@ impl Value {
|
|||
Self::Complex(z) => Rc::from(z.to_string()),
|
||||
Self::Char(c) => Rc::from(format!("'{}'", c)), // TODO escaping
|
||||
Self::String(s) => Rc::from(format!("\"{}\"", s)), // TODO escaping
|
||||
Self::List(l) => Rc::from(format!("{:?}", l)), // TODO fix
|
||||
Self::List(l) => Rc::from(format!("{:?}", l.borrow())), // TODO fix
|
||||
Self::Map(m) => Rc::from(format!("{:?}", m)), // TODO fix
|
||||
Self::Type(_) => todo!(),
|
||||
Self::BuiltinFn(bf) => Rc::from(format!("<builtin fn {} at {:?}>", bf.name, bf.func as *const ())),
|
||||
Self::Data(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index(&self, idx: &Value) -> Result<Value, String> {
|
||||
match self {
|
||||
Self::String(s) => match idx {
|
||||
Value::Int(i) if *i >= 0 => s.chars().nth(*i as usize)
|
||||
.ok_or_else(|| format!("String index {} out of bounds for length {}", i, s.chars().count()))
|
||||
.map(|c| Value::Char(c)),
|
||||
Value::Int(i) => Err(format!("String index {} cannot be negative", i)),
|
||||
_ => Err(format!("Cannot index {:?} with {:?}", self, idx))
|
||||
},
|
||||
Self::List(l) => match idx {
|
||||
Value::Int(i) if *i >= 0 => l.borrow().get(*i as usize)
|
||||
.ok_or_else(|| format!("List index {} out of bounds for length {}", i, l.borrow().len()))
|
||||
.map(|v| v.clone()),
|
||||
Value::Int(i) => Err(format!("List index {} cannot be negative", i)),
|
||||
_ => Err(format!("Cannot index {:?} with {:?}", self, idx))
|
||||
}
|
||||
Self::Map(_) => todo!(),
|
||||
v => Err(format!("Cannot index into {:?}", v))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assign_index(&self, idx: &Value, value: Value) -> Result<(), String> {
|
||||
match self {
|
||||
Self::String(s) => todo!("Can't mutate strings yet"),
|
||||
Self::List(l) => match idx {
|
||||
Value::Int(i) if *i >= 0 && (*i as usize) < l.borrow().len() => {
|
||||
l.borrow_mut()[*i as usize] = value;
|
||||
Ok(())
|
||||
}
|
||||
Value::Int(i) if *i >= 0 => Err(format!("List index {} out of bounds for length {}", i, l.borrow().len())),
|
||||
Value::Int(i) => Err(format!("List index {} cannot be negative", i)),
|
||||
_ => Err(format!("Cannot index {:?} with {:?}", self, idx))
|
||||
}
|
||||
Self::Map(_) => todo!(),
|
||||
v => Err(format!("Cannot index into {:?}", v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Value {
|
||||
|
@ -202,6 +240,23 @@ macro_rules! value_from {
|
|||
};
|
||||
}
|
||||
|
||||
impl From<Vec<Value>> for Value {
|
||||
fn from(x: Vec<Value>) -> Self {
|
||||
Self::List(RefCell::new(x).into())
|
||||
}
|
||||
}
|
||||
|
||||
value_from!(Int, u8 u16 u32 i8 i16 i32 i64);
|
||||
value_from!(Float, f32 f64);
|
||||
value_from!(Complex, Complex);
|
||||
value_from!(Rational, Rational);
|
||||
value_from!(Bool, bool);
|
||||
value_from!(String, String Rc<str>);
|
||||
value_from!(List, RefCell<Vec<Value>>);
|
||||
value_from!(Char, char);
|
||||
value_from!(Map, HashMap<Value,Value>);
|
||||
|
||||
|
||||
macro_rules! impl_numeric_op {
|
||||
($optrait:ty, $fnname:ident, { $($bonus:tt)* }) => {
|
||||
impl $optrait for &Value {
|
||||
|
@ -235,15 +290,15 @@ macro_rules! impl_numeric_op {
|
|||
}
|
||||
}
|
||||
|
||||
value_from!(Int, u8 u16 u32 i8 i16 i32 i64);
|
||||
value_from!(Float, f32 f64);
|
||||
value_from!(Complex, Complex);
|
||||
value_from!(Rational, Rational);
|
||||
value_from!(Bool, bool);
|
||||
value_from!(String, String Rc<str>);
|
||||
value_from!(Char, char);
|
||||
value_from!(List, Vec<Value>);
|
||||
value_from!(Map, HashMap<Value,Value>);
|
||||
impl Neg for Value {
|
||||
type Output = Result<Value, String>;
|
||||
fn neg(self) -> Self::Output {
|
||||
match self {
|
||||
Value::Int(a) => Ok(Value::Int(-a)),
|
||||
_ => Err(format!("Unsupported operation 'neg' on {:?}", self))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_numeric_op!(Add, add, {
|
||||
(String(a), String(b)) => Ok(((**a).to_owned() + b).into()),
|
||||
|
@ -260,11 +315,16 @@ impl_numeric_op!(Add, add, {
|
|||
}
|
||||
(List(a), List(b)) => {
|
||||
let mut a = (**a).clone();
|
||||
a.append(&mut (**b).clone());
|
||||
a.borrow_mut().append(&mut (**b).borrow().clone());
|
||||
Ok(a.into())
|
||||
},
|
||||
});
|
||||
impl_numeric_op!(Sub, sub, {});
|
||||
impl_numeric_op!(Mul, mul, {});
|
||||
impl_numeric_op!(Mul, mul, {
|
||||
(String(a), Int(b)) | (Int(b), String(a))
|
||||
=> Ok(Value::from(a.chars().cycle().take(a.chars().count()*(*b as usize)).collect::<std::string::String>())),
|
||||
(List(a), Int(b)) | (Int(b), List(a))
|
||||
=> Ok(Value::from(a.borrow().iter().cycle().take(a.borrow().len()*(*b as usize)).cloned().collect::<Vec<Value>>())),
|
||||
});
|
||||
impl_numeric_op!(Div, div, {});
|
||||
impl_numeric_op!(Rem, rem, {});
|
Loading…
Reference in a new issue