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

View file

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

View file

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

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

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