mutable lists and indexing

This commit is contained in:
TriMill 2022-09-10 17:19:23 -04:00
parent 910e758c26
commit cc216947ee
6 changed files with 268 additions and 28 deletions

68
examples/bf.cxpr Normal file
View 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("");
}

View File

@ -123,22 +123,28 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result<Value, RuntimeError> {
=> eval_comp(lhs, rhs, op, env), => eval_comp(lhs, rhs, op, env),
o => todo!("{:?}", o) // TODO other operations o => todo!("{:?}", o) // TODO other operations
}, },
Expr::Unary { arg, op } => eval_unary(arg, op, env),
Expr::List { items } => { Expr::List { items } => {
let mut list = Vec::with_capacity(items.len()); let mut list = Vec::with_capacity(items.len());
for item in items { for item in items {
list.push(eval_expr(item, env.clone())?); list.push(eval_expr(item, env.clone())?);
} }
Ok(Value::List(Rc::new(list))) Ok(Value::from(list))
}, },
Expr::FuncCall { func, args, pos } => { 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()); let mut arg_values = Vec::with_capacity(args.len());
for arg in args { for arg in args {
let result = eval_expr(arg, env.clone())?; let result = eval_expr(arg, env.clone())?;
arg_values.push(result); arg_values.push(result);
} }
func.call(arg_values, pos) 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 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"); .set(name.clone(), result.clone()).expect("unreachable");
Ok(result) 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 { } else {
unreachable!() 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)), .map(|o| Value::from(o as i8)),
_ => unreachable!() _ => 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() })
} }

View File

@ -27,7 +27,8 @@ pub enum Expr {
Ident { value: Token }, Ident { value: Token },
Literal { value: Token }, Literal { value: Token },
List { items: Vec<Expr> }, 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 { impl fmt::Debug for Expr {
@ -39,13 +40,14 @@ impl fmt::Debug for Expr {
Self::Literal { value } => write!(f, "(lit {:?})", value), Self::Literal { value } => write!(f, "(lit {:?})", value),
Self::List { items } => write!(f, "(list {:?})", items), Self::List { items } => write!(f, "(list {:?})", items),
Self::FuncCall { func, args, .. } => write!(f, "(call {:?} {:?})", func, args), Self::FuncCall { func, args, .. } => write!(f, "(call {:?} {:?})", func, args),
Self::Index { lhs, index, .. } => write!(f, "(index {:?} {:?})", lhs, index),
} }
} }
} }
impl Expr { impl Expr {
pub fn is_lvalue(&self) -> bool { pub fn is_lvalue(&self) -> bool {
matches!(self, Expr::Ident{..}) return matches!(self, Expr::Ident{..} | Expr::Index{..})
} }
pub fn is_assignment(&self) -> bool { pub fn is_assignment(&self) -> bool {

View File

@ -289,14 +289,31 @@ impl Parser {
} }
fn fncall(&mut self) -> Result<Expr, ParserError> { fn fncall(&mut self) -> Result<Expr, ParserError> {
let expr = self.expr_base()?; let mut expr = self.expr_base()?;
if !self.at_end() && self.peek().ty == TokenType::LParen { while !self.at_end() {
let lparen = self.next(); match self.peek().ty {
let args = self.commalist(TokenType::RParen, Self::assignment)?; TokenType::LParen => expr = self.fncall_inner(expr)?,
Ok(Expr::FuncCall { func: Box::new(expr), args, pos: lparen.pos.clone() }) TokenType::LBrack => expr = self.arrindex_inner(expr)?,
} else { _ => return Ok(expr)
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> { fn expr_base(&mut self) -> Result<Expr, ParserError> {

View File

@ -14,6 +14,14 @@ pub fn load(env: &mut Environment) {
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_println, arg_count: 1, name })); env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_println, arg_count: 1, name }));
name = Rc::from("input"); name = Rc::from("input");
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_input, arg_count: 0, name })); 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> { 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 mut buffer = String::new();
let stdin = std::io::stdin(); let stdin = std::io::stdin();
stdin.read_line(&mut buffer).map_err(|e| e.to_string())?; stdin.read_line(&mut buffer).map_err(|e| e.to_string())?;
if buffer.ends_with("\n") {
buffer.pop();
}
Ok(Value::from(buffer)) 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))
}
} }

View File

@ -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}; use num_traits::{Zero, ToPrimitive};
@ -41,7 +41,7 @@ pub enum Value {
Bool(bool), Bool(bool),
Char(char), Char(char),
String(Rc<str>), String(Rc<str>),
List(Rc<Vec<Value>>), Map(Rc<HashMap<Value,Value>>), List(Rc<RefCell<Vec<Value>>>), Map(Rc<HashMap<Value,Value>>),
BuiltinFn(BuiltinFn), BuiltinFn(BuiltinFn),
Data(Data), Data(Data),
} }
@ -55,7 +55,7 @@ impl Value {
Complex(z) => !z.is_zero(), Complex(z) => !z.is_zero(),
Rational(r) => !r.is_zero(), Rational(r) => !r.is_zero(),
String(s) => !s.len() == 0, String(s) => !s.len() == 0,
List(l) => !l.len() == 0, List(l) => !l.borrow().len() == 0,
Map(m) => !m.len() == 0, Map(m) => !m.len() == 0,
_ => true _ => true
} }
@ -66,7 +66,7 @@ impl Value {
Value::String(s) Value::String(s)
=> Ok(Box::new(s.chars() => Ok(Box::new(s.chars()
.map(|c| Value::Char(c)))), .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(()) _ => Err(())
} }
} }
@ -120,13 +120,51 @@ impl Value {
Self::Complex(z) => Rc::from(z.to_string()), Self::Complex(z) => Rc::from(z.to_string()),
Self::Char(c) => Rc::from(format!("'{}'", c)), // TODO escaping Self::Char(c) => Rc::from(format!("'{}'", c)), // TODO escaping
Self::String(s) => Rc::from(format!("\"{}\"", s)), // 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::Map(m) => Rc::from(format!("{:?}", m)), // TODO fix
Self::Type(_) => todo!(), Self::Type(_) => todo!(),
Self::BuiltinFn(bf) => Rc::from(format!("<builtin fn {} at {:?}>", bf.name, bf.func as *const ())), Self::BuiltinFn(bf) => Rc::from(format!("<builtin fn {} at {:?}>", bf.name, bf.func as *const ())),
Self::Data(_) => todo!(), 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 { 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 { macro_rules! impl_numeric_op {
($optrait:ty, $fnname:ident, { $($bonus:tt)* }) => { ($optrait:ty, $fnname:ident, { $($bonus:tt)* }) => {
impl $optrait for &Value { impl $optrait for &Value {
@ -235,15 +290,15 @@ macro_rules! impl_numeric_op {
} }
} }
value_from!(Int, u8 u16 u32 i8 i16 i32 i64); impl Neg for Value {
value_from!(Float, f32 f64); type Output = Result<Value, String>;
value_from!(Complex, Complex); fn neg(self) -> Self::Output {
value_from!(Rational, Rational); match self {
value_from!(Bool, bool); Value::Int(a) => Ok(Value::Int(-a)),
value_from!(String, String Rc<str>); _ => Err(format!("Unsupported operation 'neg' on {:?}", self))
value_from!(Char, char); }
value_from!(List, Vec<Value>); }
value_from!(Map, HashMap<Value,Value>); }
impl_numeric_op!(Add, add, { impl_numeric_op!(Add, add, {
(String(a), String(b)) => Ok(((**a).to_owned() + b).into()), (String(a), String(b)) => Ok(((**a).to_owned() + b).into()),
@ -260,11 +315,16 @@ impl_numeric_op!(Add, add, {
} }
(List(a), List(b)) => { (List(a), List(b)) => {
let mut a = (**a).clone(); let mut a = (**a).clone();
a.append(&mut (**b).clone()); a.borrow_mut().append(&mut (**b).borrow().clone());
Ok(a.into()) Ok(a.into())
}, },
}); });
impl_numeric_op!(Sub, sub, {}); 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!(Div, div, {});
impl_numeric_op!(Rem, rem, {}); impl_numeric_op!(Rem, rem, {});