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),
|
=> 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() })
|
||||||
}
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
}
|
}
|
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};
|
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, {});
|
Loading…
Reference in a new issue