added comparisons, builtin functions, while loops

This commit is contained in:
TriMill 2022-09-09 16:08:40 -04:00
parent 3d915a5b94
commit 910e758c26
16 changed files with 382 additions and 103 deletions

3
examples/cat.cxpr Normal file
View file

@ -0,0 +1,3 @@
while true {
print(input());
}

10
examples/collatz.cxpr Normal file
View file

@ -0,0 +1,10 @@
let n = 29;
while n != 1 {
println(n);
if n % 2 == 0 {
n = n/2;
} else {
n = 3*n+1;
}
}
println(n);

9
examples/fib.cxpr Normal file
View file

@ -0,0 +1,9 @@
let x = 0;
let y = 0;
let z = 1;
while z < 1000000 {
x = y + z;
z = y;
y = x;
println(z);
}

2
examples/helloworld.cxpr Normal file
View file

@ -0,0 +1,2 @@
# Prints the string "Hello, world!" followed by a newline
println("Hello, world!");

View file

@ -1,6 +1,6 @@
use std::{rc::Rc, cell::RefCell, fs::{File, self}, io::{BufReader, Read}};
use std::{rc::Rc, cell::RefCell, fs};
use complexpr::{eval::Environment, interpreter::interpret, value::Value};
use complexpr::{eval::Environment, interpreter::interpret, value::Value, stdlib};
use rustyline::{self, error::ReadlineError};
const C_RESET: &'static str = "\x1b[0m";
@ -26,6 +26,7 @@ fn repl() -> Result<(), Box<dyn std::error::Error>> {
println!("Press {}Ctrl+D{} to exit.", C_BLUE, C_RESET);
let mut rl = rustyline::Editor::<()>::new()?;
let env = Rc::new(RefCell::new(Environment::new()));
stdlib::load(&mut env.borrow_mut());
loop {
let readline = rl.readline(PROMPT);
match readline {
@ -33,7 +34,7 @@ fn repl() -> Result<(), Box<dyn std::error::Error>> {
let result = interpret(&line, None, Some(env.clone()), true);
match result {
Ok(Value::Nil) => (),
Ok(value) => println!("{:?}", value),
Ok(value) => println!("{}", value.repr()),
Err(e) => println!("{}", e)
}
}

View file

@ -1,17 +1,14 @@
use std::{collections::HashMap, rc::Rc, cell::RefCell};
use num_complex::Complex64;
use crate::{value::Value, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}, RuntimeError};
use crate::{value::{Value, Complex}, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}, RuntimeError};
#[derive(Debug)]
pub struct Environment {
parent: Option<EnvRef>,
map: HashMap<Rc<str>, Value>
map: HashMap<Rc<str>, Value>,
}
type EnvRef = Rc<RefCell<Environment>>;
pub type EnvRef = Rc<RefCell<Environment>>;
impl Environment {
pub fn new() -> Self {
@ -27,24 +24,20 @@ impl Environment {
}
pub fn get(&self, name: &str) -> Result<Value,()> {
let x = match self.map.get(name) {
match self.map.get(name) {
Some(v) => Ok(v.clone()),
None => match self.parent {
Some(ref p) => p.borrow().get(name),
None => Err(())
}
};
println!("get {}: {:?}", name, x);
x
}
}
pub fn declare(&mut self, name: Rc<str>, value: Value) {
println!("declare {}: {:?}", name, value);
self.map.insert(name, value);
}
pub fn set(&mut self, name: Rc<str>, value: Value) -> Result<(),()> {
println!("set {}: {:?}", name, value);
match self.map.contains_key(&name) {
true => { self.map.insert(name, value); Ok(()) },
false => match self.parent {
@ -104,7 +97,15 @@ pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), RuntimeError> {
return Err(RuntimeError { message: "Cannot iterate this type".into(), pos: var.pos.clone() })
};
},
_ => unreachable!()
Stmt::While { expr, stmt } => {
loop {
let cond = eval_expr(expr, env.clone())?;
if !cond.truthy() {
break
}
eval_stmt(&stmt, env.clone())?;
}
},
}
Ok(())
}
@ -118,8 +119,26 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result<Value, RuntimeError> {
=> eval_assignment(lhs, rhs, op, env),
Some(OpType::Additive) | Some(OpType::Multiplicative)
=> eval_binary(lhs, rhs, op, env),
Some(OpType::Comparison)
=> eval_comp(lhs, rhs, op, env),
o => todo!("{:?}", o) // TODO other operations
},
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)))
},
Expr::FuncCall { func, args, pos } => {
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)
}
e => todo!("{:?}", e) // TODO other expression types
}
}
@ -131,7 +150,7 @@ pub fn eval_literal(token: &Token) -> Value {
TokenType::False => Value::Bool(false),
TokenType::Int(n) => Value::Int(*n),
TokenType::Float(f) => Value::Float(*f),
TokenType::ImFloat(f) => Value::Complex(Complex64::new(0.0, *f)),
TokenType::ImFloat(f) => Value::Complex(Complex::new(0.0, *f)),
TokenType::String(s) => Value::String(s.clone()),
TokenType::Char(c) => Value::Char(*c),
_ => todo!()
@ -195,3 +214,32 @@ pub fn eval_binary(lhs: &Box<Expr>, rhs: &Box<Expr>, op: &Token, env: EnvRef) ->
_ => todo!() // TODO other operations
}.map_err(|e| RuntimeError { message: e, pos: op.pos.clone() })
}
pub fn eval_comp(lhs: &Box<Expr>, rhs: &Box<Expr>, op: &Token, env: EnvRef) -> Result<Value, RuntimeError> {
let l = eval_expr(lhs, env.clone())?;
let r = eval_expr(rhs, env)?;
let mk_err = || RuntimeError {
message: format!("Cannot compare {:?} with {:?}", l, r),
pos: op.pos.clone()
};
match op.ty {
TokenType::DoubleEqual => Ok(Value::Bool(l == r)),
TokenType::BangEqual => Ok(Value::Bool(l != r)),
TokenType::Greater => l.partial_cmp(&r)
.ok_or_else(mk_err)
.map(|o| Value::from(o.is_gt())),
TokenType::Less => l.partial_cmp(&r)
.ok_or_else(mk_err)
.map(|o| Value::from(o.is_lt())),
TokenType::GreaterEqual => l.partial_cmp(&r)
.ok_or_else(mk_err)
.map(|o| Value::from(o.is_ge())),
TokenType::LessEqual => l.partial_cmp(&r)
.ok_or_else(mk_err)
.map(|o| Value::from(o.is_le())),
TokenType::Spaceship => l.partial_cmp(&r)
.ok_or_else(mk_err)
.map(|o| Value::from(o as i8)),
_ => unreachable!()
}
}

View file

@ -1,13 +1,14 @@
use std::fmt;
use crate::{token::{Token, OpType}};
use crate::{token::{Token, OpType}, Position};
pub enum Stmt {
Expr { expr: Expr },
Let { lhs: Token, rhs: Option<Expr> },
Block { stmts: Vec<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> },
}
impl fmt::Debug for Stmt {
@ -26,7 +27,7 @@ pub enum Expr {
Ident { value: Token },
Literal { value: Token },
List { items: Vec<Expr> },
FuncCall { func: Box<Expr>, args: Vec<Expr> }
FuncCall { func: Box<Expr>, args: Vec<Expr>, pos: Position }
}
impl fmt::Debug for Expr {
@ -34,10 +35,10 @@ impl fmt::Debug for Expr {
match self {
Self::Binary { lhs: left, rhs: right, op} => write!(f, "({:?} {:?} {:?})", op, left, right),
Self::Unary { arg, op } => write!(f, "({:?} {:?})", op, arg),
Self::Ident { value, .. } => write!(f, "(ident {:?})", value),
Self::Literal { value, .. } => write!(f, "(lit {:?})", value),
Self::Ident { value } => write!(f, "(ident {:?})", value),
Self::Literal { value } => write!(f, "(lit {:?})", value),
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),
}
}
}

View file

@ -1,8 +1,8 @@
use std::{cell::RefCell, rc::Rc};
use crate::{value::Value, lexer::Lexer, parser::Parser, eval::{Environment, eval_stmt, eval_expr}, expr::Stmt};
use crate::{value::Value, lexer::Lexer, parser::Parser, eval::{Environment, eval_stmt, eval_expr, EnvRef}, expr::Stmt, stdlib};
pub fn interpret(src: &str, fname: Option<String>, env: Option<Rc<RefCell<Environment>>>, repl: bool) -> Result<Value, Box<dyn std::error::Error>> {
pub fn interpret(src: &str, fname: Option<String>, env: Option<EnvRef>, repl: bool) -> Result<Value, Box<dyn std::error::Error>> {
let mut lexer = Lexer::new(src, fname);
lexer.lex()?;
let mut parser = Parser::new(lexer.into_tokens(), repl);
@ -11,8 +11,10 @@ pub fn interpret(src: &str, fname: Option<String>, env: Option<Rc<RefCell<Enviro
if let Some(env) = env {
environ = env;
} else {
environ = Rc::new(RefCell::new(Environment::new()))
environ = Rc::new(RefCell::new(Environment::new()));
stdlib::load(&mut environ.borrow_mut());
}
let mut result = Value::Nil;
for stmt in ast {
if let Stmt::Expr{expr} = stmt {

View file

@ -29,6 +29,7 @@ impl Lexer {
}
fn expect(&mut self, chars: &[char]) -> Option<char> {
if self.at_end() { return None }
for c in chars {
if self.code[self.current] == *c {
self.advance(*c == '\n');
@ -126,7 +127,10 @@ impl Lexer {
_ => self.add_token(TokenType::Greater, ">")
},
'<' => match self.expect(&['=']) {
Some('=') => self.add_token(TokenType::LessEqual, "<="),
Some('=') => match self.expect(&['>']) {
Some('>') => self.add_token(TokenType::Spaceship, "<=>"),
_ => self.add_token(TokenType::LessEqual, "<="),
}
_ => self.add_token(TokenType::Less, "<")
},
'&' => match self.expect(&['&']) {
@ -176,6 +180,7 @@ impl Lexer {
if self.at_end() { return Err(self.mk_error("Unexpected EOF in character literal")) }
// TODO Escapes
}
if self.at_end() { return Err(self.mk_error("Unexpected EOF in character literal")) }
self.expect(&['\'']).ok_or(self.mk_error("Expected ' to terminate character literal"))?;
self.add_token(TokenType::Char(c), self.collect_literal());
Ok(())
@ -186,6 +191,9 @@ impl Lexer {
while !self.at_end() && self.peek() != '"' {
if self.peek() == '\\' {
self.advance(false);
if self.at_end() {
return Err(self.mk_error("Unexpected EOF in string literal"))
}
// TODO more escape codes! \xHH, \u{HH..} or maybe \uhhhh \Uhhhhhhhh
match self.peek() {
'0' => s.push('\0'),
@ -205,7 +213,7 @@ impl Lexer {
}
}
if self.at_end() {
return Err(self.mk_error("Unexpected EOF while parsing string"))
return Err(self.mk_error("Unexpected EOF in string literal"))
}
self.advance(false);
self.add_token(TokenType::String(Rc::from(s)), self.collect_literal());
@ -217,7 +225,7 @@ impl Lexer {
while !self.at_end() && (self.peek().is_numeric() || self.peek() == '.') {
if self.peek() == '.' {
if has_dot {
return Err(self.mk_error("Numeric literals cannot contain two decimal points"))
break;
} else {
has_dot = true;
}

View file

@ -7,6 +7,7 @@ pub mod parser;
pub mod value;
pub mod eval;
pub mod interpreter;
pub mod stdlib;
#[derive(Clone, Debug)]
pub struct Position {

View file

@ -70,10 +70,15 @@ impl Parser {
self.ifstmt()
},
TokenType::For => {
// for statement
// for loop
self.next();
self.forstmt()
},
TokenType::While => {
// while loop
self.next();
self.whilestmt()
},
_ => {
// fallback to an expression terminated with a semicolon
let expr = self.assignment()?;
@ -177,6 +182,14 @@ impl Parser {
}
}
fn whilestmt(&mut self) -> Result<Stmt, ParserError> {
self.err_on_eof()?;
let expr = self.assignment()?;
self.err_on_eof()?;
let stmt = self.statement()?;
Ok(Stmt::While{ expr, stmt: Box::new(stmt) })
}
fn block(&mut self) -> Result<Stmt, ParserError> {
let mut stmts = vec![];
while !self.at_end() && self.peek().ty != TokenType::RBrace {
@ -200,9 +213,10 @@ impl Parser {
fn commalist(&mut self, terminator: TokenType, parse_item: fn(&mut Parser) -> Result<Expr, ParserError>) -> Result<Vec<Expr>, ParserError> {
let mut items = vec![];
while self.peek().ty != terminator {
while !self.at_end() && self.peek().ty != terminator {
let expr = parse_item(self)?;
items.push(expr);
self.err_on_eof()?;
if self.peek().ty == TokenType::Comma {
self.next();
} else if self.peek().ty == terminator {
@ -211,6 +225,7 @@ impl Parser {
return Err(self.mk_error(format!("Expected Comma or {:?} after list", terminator)))
}
}
self.err_on_eof()?;
self.next();
Ok(items)
}
@ -233,7 +248,11 @@ impl Parser {
}
fn pipeline(&mut self) -> Result<Expr, ParserError> {
self.expr(OpType::Pipeline, Self::additive)
self.expr(OpType::Pipeline, Self::comparison)
}
fn comparison(&mut self) -> Result<Expr, ParserError> {
self.expr(OpType::Comparison, Self::additive)
}
fn additive(&mut self) -> Result<Expr, ParserError> {
@ -272,9 +291,9 @@ impl Parser {
fn fncall(&mut self) -> Result<Expr, ParserError> {
let expr = self.expr_base()?;
if !self.at_end() && self.peek().ty == TokenType::LParen {
self.next();
let lparen = self.next();
let args = self.commalist(TokenType::RParen, Self::assignment)?;
Ok(Expr::FuncCall { func: Box::new(expr), args })
Ok(Expr::FuncCall { func: Box::new(expr), args, pos: lparen.pos.clone() })
} else {
Ok(expr)
}
@ -298,6 +317,9 @@ impl Parser {
} else {
Ok(expr)
}
} else if next.ty == TokenType::LBrack {
let items = self.commalist(TokenType::RBrack, Self::assignment)?;
Ok(Expr::List { items })
} else {
Err(self.mk_error(format!("Unexpected token: {:?}", next.ty)))
}

43
src/stdlib/mod.rs Normal file
View file

@ -0,0 +1,43 @@
use std::{rc::Rc, io::Write};
use crate::{value::{Value, BuiltinFn}, eval::Environment};
pub fn load(env: &mut Environment) {
let mut name: Rc<str>;
name = Rc::from("str");
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_str, arg_count: 1, name }));
name = Rc::from("repr");
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_repr, arg_count: 1, name }));
name = Rc::from("print");
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_print, arg_count: 1, name }));
name = Rc::from("println");
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 }));
}
fn fn_str(args: Vec<Value>) -> Result<Value, String> {
Ok(Value::String(args[0].to_string()))
}
fn fn_repr(args: Vec<Value>) -> Result<Value, String> {
Ok(Value::String(args[0].repr()))
}
fn fn_print(args: Vec<Value>) -> Result<Value, String> {
print!("{}", args[0].to_string());
std::io::stdout().flush().map_err(|e| e.to_string())?;
Ok(Value::Nil)
}
fn fn_println(args: Vec<Value>) -> Result<Value, String> {
println!("{}", args[0].to_string());
Ok(Value::Nil)
}
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())?;
Ok(Value::from(buffer))
}

View file

@ -25,7 +25,7 @@ pub enum TokenType {
Bang, Amper, Pipe, DoubleAmper, DoublePipe,
Equal, PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual, DoubleSlashEqual, CaretEqual,
DoubleEqual, BangEqual, Greater, GreaterEqual, Less, LessEqual,
DoubleEqual, BangEqual, Greater, GreaterEqual, Less, LessEqual, Spaceship,
Arrow, PipeColon, PipePoint, PipeQuestion, PipeAmper,
@ -49,6 +49,9 @@ impl TokenType {
Self::PipeColon | Self::PipeAmper | Self::PipePoint
| Self::PipeQuestion => Some(OpType::Pipeline),
Self::Greater | Self::GreaterEqual | Self::Less | Self::LessEqual
| Self::DoubleEqual | Self::BangEqual | Self::Spaceship => Some(OpType::Comparison),
Self::Equal | Self::PlusEqual | Self::MinusEqual
| Self::StarEqual | Self::SlashEqual | Self::DoubleSlashEqual
| Self::CaretEqual | Self::PercentEqual => Some(OpType::Assignment),
@ -60,7 +63,7 @@ impl TokenType {
#[derive(Clone,Copy,Debug,PartialEq)]
pub enum OpType {
Assignment, Pipeline, Additive, Multiplicative, Exponential
Assignment, Comparison, Pipeline, Additive, Multiplicative, Exponential
}
impl OpType {

View file

@ -1,62 +1,49 @@
use std::{rc::Rc, collections::HashMap, ops::*, ascii::AsciiExt};
use std::{rc::Rc, collections::HashMap, ops::*, fmt, cmp::Ordering};
use num_traits::Zero;
use num_traits::{Zero, ToPrimitive};
type Rational = num_rational::Ratio<i64>;
type Complex = num_complex::Complex64;
use crate::{RuntimeError, Position};
macro_rules! value_from {
($variant:ident, $($kind:ty)*) => {
$(
impl From<$kind> for Value {
fn from(x: $kind) -> Self {
Self::$variant(x.into())
}
}
)*
};
pub type Rational = num_rational::Ratio<i64>;
pub type Complex = num_complex::Complex64;
#[derive(Clone)]
pub struct BuiltinFn {
pub name: Rc<str>,
pub func: fn(Vec<Value>) -> Result<Value, String>,
pub arg_count: usize
}
macro_rules! impl_numeric_op {
($optrait:ty, $fnname:ident, { $($bonus:tt)* }) => {
impl $optrait for &Value {
type Output = Result<Value, String>;
fn $fnname(self, other: Self) -> Self::Output {
use Value::*;
use num_traits::ToPrimitive;
const RATIO_CAST_FAIL: &'static str = "Failed to cast Rational to Float";
match (self, other) {
(Int(a), Int(b)) => Ok(a.$fnname(b).into()),
(Rational(a), Int(b)) => Ok(a.$fnname(b).into()),
(Int(a), Rational(b)) => Ok(self::Rational::from(*a).$fnname(b).into()),
(Float(a), Int(b)) => Ok(a.$fnname(*b as f64).into()),
(Int(a), Float(b)) => Ok((*a as f64).$fnname(b).into()),
(Float(a), Rational(b)) => Ok(a.$fnname(b.to_f64().ok_or(RATIO_CAST_FAIL)?).into()),
(Rational(a), Float(b)) => Ok(a.to_f64().ok_or(RATIO_CAST_FAIL)?.$fnname(b).into()),
(Float(a), Float(b)) => Ok(a.$fnname(b).into()),
(Int(a), Complex(b)) => Ok(self::Complex::from(*a as f64).$fnname(b).into()),
(Complex(a), Int(b)) => Ok(a.$fnname(self::Complex::from(*b as f64)).into()),
(Float(a), Complex(b)) => Ok(self::Complex::from(a).$fnname(b).into()),
(Complex(a), Float(b)) => Ok(a.$fnname(self::Complex::from(b)).into()),
(Rational(a), Complex(b)) => Ok(self::Complex::from(a.to_f64().ok_or(RATIO_CAST_FAIL)?).$fnname(b).into()),
(Complex(a), Rational(b)) => Ok(a.$fnname(self::Complex::from(b.to_f64().ok_or(RATIO_CAST_FAIL)?)).into()),
(Complex(a), Complex(b)) => Ok(a.$fnname(b).into()),
$($bonus)*
(lhs, rhs) => Err(format!("Unsupported operation '{}' between {:?} and {:?}", stringify!($fnname), lhs, rhs))
}
}
}
impl fmt::Debug for BuiltinFn {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BuiltinFn").field("name", &self.name).field("arg_count", &self.arg_count).finish()
}
}
#[derive(Clone, Debug)]
pub struct Data {
pub ty: usize,
// TODO user-defined data types
}
#[derive(Clone, Debug)]
pub struct Type {
pub name: Rc<str>,
pub id: usize
}
#[derive(Clone, Debug)]
#[repr(u8)]
pub enum Value {
Nil,
Type(usize),
Int(i64), Float(f64), Complex(Complex), Rational(Rational),
Bool(bool),
Char(char),
String(Rc<str>),
List(Rc<Vec<Value>>), Map(Rc<HashMap<Value,Value>>),
BuiltinFn(BuiltinFn),
Data(Data),
}
impl Value {
@ -83,8 +70,170 @@ impl Value {
_ => Err(())
}
}
pub fn call(&self, args: Vec<Value>, pos: &Position) -> Result<Value, RuntimeError> {
match self {
Value::BuiltinFn(f) => {
if args.len() == f.arg_count {
(f.func)(args).map_err(|e| RuntimeError { message: e, pos: pos.clone() })
} else if args.len() < f.arg_count {
Err(RuntimeError {
message: format!("Not enough arguments for function: expected {}, got {}", f.arg_count, args.len()),
pos: pos.clone()
})
} else {
Err(RuntimeError {
message: format!("Too many arguments for function: expected {}, got {}", f.arg_count, args.len()),
pos: pos.clone()
})
}
}
_ => Err(RuntimeError { message: "Cannot call".into(), pos: pos.clone() })
}
}
pub fn to_string(&self) -> Rc<str> {
match self {
Self::Nil => Rc::from("nil"),
Self::Bool(b) => Rc::from(b.to_string()),
Self::Int(n) => Rc::from(n.to_string()),
Self::Float(f) => Rc::from(f.to_string()),
Self::Rational(r) => Rc::from(r.to_string()),
Self::Complex(z) => Rc::from(z.to_string()),
Self::Char(c) => Rc::from(c.to_string()),
Self::String(s) => s.clone(),
Self::List(l) => Rc::from(format!("{:?}", l)), // 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 repr(&self) -> Rc<str> {
match self {
Self::Nil => Rc::from("nil"),
Self::Bool(b) => Rc::from(b.to_string()),
Self::Int(n) => Rc::from(n.to_string()),
Self::Float(f) => Rc::from(f.to_string()),
Self::Rational(r) => Rc::from(r.to_string()),
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::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!(),
}
}
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Nil, Self::Nil) => true,
(Self::Type(a), Self::Type(b)) => a == b,
(Self::Int(a), Self::Int(b)) => a == b,
(Self::Rational(a), Self::Int(b)) => *a == Rational::from(*b),
(Self::Int(a), Self::Rational(b)) => Rational::from(*a) == *b,
(Self::Rational(a), Self::Rational(b)) => a == b,
(Self::Float(a), Self::Int(b)) => *a == *b as f64,
(Self::Int(a), Self::Float(b)) => *a as f64 == *b,
(Self::Float(a), Self::Rational(b)) => *a == b.to_f64().unwrap(),
(Self::Rational(a), Self::Float(b)) => a.to_f64().unwrap() == *b,
(Self::Float(a), Self::Float(b)) => a == b,
(Self::Complex(a), Self::Int(b)) => *a == Complex::from(*b as f64),
(Self::Int(a), Self::Complex(b)) => Complex::from(*a as f64) == *b,
(Self::Complex(a), Self::Rational(b)) => *a == Complex::from(b.to_f64().unwrap()),
(Self::Rational(a), Self::Complex(b)) => Complex::from(a.to_f64().unwrap()) == *b,
(Self::Complex(a), Self::Float(b)) => *a == Complex::from(*b),
(Self::Float(a), Self::Complex(b)) => Complex::from(*a) == *b,
(Self::Complex(a), Self::Complex(b)) => a == b,
(Self::Bool(a), Self::Bool(b)) => a == b,
(Self::Char(a), Self::Char(b)) => a == b,
(Self::String(a), Self::String(b)) => a == b,
(Self::List(a), Self::List(b)) => a == b,
(Self::Map(_), Self::Map(_)) => todo!("Can't test maps for equality yet"),
(Self::BuiltinFn(a), Self::BuiltinFn(b))
=> (a.func as *const ()) == (b.func as *const ()) && a.arg_count == b.arg_count,
(Self::Data(_), Self::Data(_)) => todo!("Can't compare data yet"),
_ => false
}
}
}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self == other {
return Some(Ordering::Equal)
}
match (self, other) {
(Self::Int(a), Self::Int(b)) => a.partial_cmp(b),
(Self::Int(a), Self::Float(b)) => (*a as f64).partial_cmp(b),
(Self::Float(a), Self::Int(b)) => a.partial_cmp(&(*b as f64)),
(Self::Float(a), Self::Float(b)) => a.partial_cmp(b),
(Self::Int(a), Self::Rational(b)) => Rational::from(*a).partial_cmp(b),
(Self::Rational(a), Self::Int(b)) => a.partial_cmp(&Rational::from(*b)),
(Self::Float(a), Self::Rational(b)) => a.partial_cmp(&b.to_f64().unwrap()),
(Self::Rational(a), Self::Float(b)) => a.to_f64().unwrap().partial_cmp(b),
(Self::Rational(a), Self::Rational(b)) => a.partial_cmp(b),
(Self::Char(a), Self::Char(b)) => a.partial_cmp(b),
(Self::String(a), Self::String(b)) => a.partial_cmp(b),
(Self::List(a), Self::List(b)) => a.partial_cmp(b),
_ => None
}
}
}
macro_rules! value_from {
($variant:ident, $($kind:ty)*) => {
$(
impl From<$kind> for Value {
fn from(x: $kind) -> Self {
Self::$variant(x.into())
}
}
)*
};
}
macro_rules! impl_numeric_op {
($optrait:ty, $fnname:ident, { $($bonus:tt)* }) => {
impl $optrait for &Value {
type Output = Result<Value, String>;
fn $fnname(self, other: Self) -> Self::Output {
use Value::*;
use num_traits::ToPrimitive;
const RATIO_CAST_FAIL: &'static str = "Failed to cast Rational to Float";
match (self, other) {
(Int(a), Int(b)) => Ok(a.$fnname(b).into()),
(Rational(a), Int(b)) => Ok(a.$fnname(b).into()),
(Int(a), Rational(b)) => Ok(self::Rational::from(*a).$fnname(b).into()),
(Rational(a), Rational(b)) => Ok(a.$fnname(b).into()),
(Float(a), Int(b)) => Ok(a.$fnname(*b as f64).into()),
(Int(a), Float(b)) => Ok((*a as f64).$fnname(b).into()),
(Float(a), Rational(b)) => Ok(a.$fnname(b.to_f64().ok_or(RATIO_CAST_FAIL)?).into()),
(Rational(a), Float(b)) => Ok(a.to_f64().ok_or(RATIO_CAST_FAIL)?.$fnname(b).into()),
(Float(a), Float(b)) => Ok(a.$fnname(b).into()),
(Int(a), Complex(b)) => Ok(self::Complex::from(*a as f64).$fnname(b).into()),
(Complex(a), Int(b)) => Ok(a.$fnname(self::Complex::from(*b as f64)).into()),
(Float(a), Complex(b)) => Ok(self::Complex::from(a).$fnname(b).into()),
(Complex(a), Float(b)) => Ok(a.$fnname(self::Complex::from(b)).into()),
(Rational(a), Complex(b)) => Ok(self::Complex::from(a.to_f64().ok_or(RATIO_CAST_FAIL)?).$fnname(b).into()),
(Complex(a), Rational(b)) => Ok(a.$fnname(self::Complex::from(b.to_f64().ok_or(RATIO_CAST_FAIL)?)).into()),
(Complex(a), Complex(b)) => Ok(a.$fnname(b).into()),
$($bonus)*
(lhs, rhs) => Err(format!("Unsupported operation '{}' between {:?} and {:?}", stringify!($fnname), lhs, rhs))
}
}
}
}
}
value_from!(Int, u8 u16 u32 i8 i16 i32 i64);
value_from!(Float, f32 f64);

View file

@ -1,4 +0,0 @@
for c: "asdfghjkl" {
let b = 'q' + c;
}

View file

@ -1,19 +0,0 @@
#![cfg(test)]
use std::{cell::RefCell, rc::Rc};
use complexpr::{lexer::Lexer, parser::Parser, eval::{Environment, eval_stmt}};
#[test]
pub fn test() {
let mut lexer = Lexer::new("let a = 1 + 1; let b = a + 1;", None);
lexer.lex().unwrap();
let mut parser = Parser::new(lexer.into_tokens(), false);
let ast = parser.parse().unwrap();
let env = Rc::new(RefCell::new(Environment::new()));
for stmt in ast {
eval_stmt(&stmt, env.clone()).unwrap();
}
println!("{:?}", env);
todo!("end of tests")
}