added comparisons, builtin functions, while loops
This commit is contained in:
parent
3d915a5b94
commit
910e758c26
16 changed files with 382 additions and 103 deletions
3
examples/cat.cxpr
Normal file
3
examples/cat.cxpr
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
while true {
|
||||||
|
print(input());
|
||||||
|
}
|
10
examples/collatz.cxpr
Normal file
10
examples/collatz.cxpr
Normal 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
9
examples/fib.cxpr
Normal 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
2
examples/helloworld.cxpr
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Prints the string "Hello, world!" followed by a newline
|
||||||
|
println("Hello, world!");
|
|
@ -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};
|
use rustyline::{self, error::ReadlineError};
|
||||||
|
|
||||||
const C_RESET: &'static str = "\x1b[0m";
|
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);
|
println!("Press {}Ctrl+D{} to exit.", C_BLUE, C_RESET);
|
||||||
let mut rl = rustyline::Editor::<()>::new()?;
|
let mut rl = rustyline::Editor::<()>::new()?;
|
||||||
let env = Rc::new(RefCell::new(Environment::new()));
|
let env = Rc::new(RefCell::new(Environment::new()));
|
||||||
|
stdlib::load(&mut env.borrow_mut());
|
||||||
loop {
|
loop {
|
||||||
let readline = rl.readline(PROMPT);
|
let readline = rl.readline(PROMPT);
|
||||||
match readline {
|
match readline {
|
||||||
|
@ -33,7 +34,7 @@ fn repl() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let result = interpret(&line, None, Some(env.clone()), true);
|
let result = interpret(&line, None, Some(env.clone()), true);
|
||||||
match result {
|
match result {
|
||||||
Ok(Value::Nil) => (),
|
Ok(Value::Nil) => (),
|
||||||
Ok(value) => println!("{:?}", value),
|
Ok(value) => println!("{}", value.repr()),
|
||||||
Err(e) => println!("{}", e)
|
Err(e) => println!("{}", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
76
src/eval.rs
76
src/eval.rs
|
@ -1,17 +1,14 @@
|
||||||
use std::{collections::HashMap, rc::Rc, cell::RefCell};
|
use std::{collections::HashMap, rc::Rc, cell::RefCell};
|
||||||
|
|
||||||
use num_complex::Complex64;
|
use crate::{value::{Value, Complex}, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}, RuntimeError};
|
||||||
|
|
||||||
use crate::{value::Value, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}, RuntimeError};
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Environment {
|
pub struct Environment {
|
||||||
parent: Option<EnvRef>,
|
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 {
|
impl Environment {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
@ -27,24 +24,20 @@ impl Environment {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, name: &str) -> Result<Value,()> {
|
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()),
|
Some(v) => Ok(v.clone()),
|
||||||
None => match self.parent {
|
None => match self.parent {
|
||||||
Some(ref p) => p.borrow().get(name),
|
Some(ref p) => p.borrow().get(name),
|
||||||
None => Err(())
|
None => Err(())
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
println!("get {}: {:?}", name, x);
|
|
||||||
x
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn declare(&mut self, name: Rc<str>, value: Value) {
|
pub fn declare(&mut self, name: Rc<str>, value: Value) {
|
||||||
println!("declare {}: {:?}", name, value);
|
|
||||||
self.map.insert(name, value);
|
self.map.insert(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, name: Rc<str>, value: Value) -> Result<(),()> {
|
pub fn set(&mut self, name: Rc<str>, value: Value) -> Result<(),()> {
|
||||||
println!("set {}: {:?}", name, value);
|
|
||||||
match self.map.contains_key(&name) {
|
match self.map.contains_key(&name) {
|
||||||
true => { self.map.insert(name, value); Ok(()) },
|
true => { self.map.insert(name, value); Ok(()) },
|
||||||
false => match self.parent {
|
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() })
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -118,8 +119,26 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result<Value, RuntimeError> {
|
||||||
=> eval_assignment(lhs, rhs, op, env),
|
=> eval_assignment(lhs, rhs, op, env),
|
||||||
Some(OpType::Additive) | Some(OpType::Multiplicative)
|
Some(OpType::Additive) | Some(OpType::Multiplicative)
|
||||||
=> eval_binary(lhs, rhs, op, env),
|
=> eval_binary(lhs, rhs, op, env),
|
||||||
|
Some(OpType::Comparison)
|
||||||
|
=> eval_comp(lhs, rhs, op, env),
|
||||||
o => todo!("{:?}", o) // TODO other operations
|
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
|
e => todo!("{:?}", e) // TODO other expression types
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,7 +150,7 @@ pub fn eval_literal(token: &Token) -> Value {
|
||||||
TokenType::False => Value::Bool(false),
|
TokenType::False => Value::Bool(false),
|
||||||
TokenType::Int(n) => Value::Int(*n),
|
TokenType::Int(n) => Value::Int(*n),
|
||||||
TokenType::Float(f) => Value::Float(*f),
|
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::String(s) => Value::String(s.clone()),
|
||||||
TokenType::Char(c) => Value::Char(*c),
|
TokenType::Char(c) => Value::Char(*c),
|
||||||
_ => todo!()
|
_ => todo!()
|
||||||
|
@ -194,4 +213,33 @@ pub fn eval_binary(lhs: &Box<Expr>, rhs: &Box<Expr>, op: &Token, env: EnvRef) ->
|
||||||
TokenType::Percent => &l % &r,
|
TokenType::Percent => &l % &r,
|
||||||
_ => todo!() // TODO other operations
|
_ => todo!() // TODO other operations
|
||||||
}.map_err(|e| RuntimeError { message: e, pos: op.pos.clone() })
|
}.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!()
|
||||||
|
}
|
||||||
}
|
}
|
15
src/expr.rs
15
src/expr.rs
|
@ -1,13 +1,14 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::{token::{Token, OpType}};
|
use crate::{token::{Token, OpType}, Position};
|
||||||
|
|
||||||
pub enum Stmt {
|
pub enum Stmt {
|
||||||
Expr { expr: Expr },
|
Expr { expr: Expr },
|
||||||
Let { lhs: Token, rhs: Option<Expr> },
|
Let { lhs: Token, rhs: Option<Expr> },
|
||||||
Block { stmts: Vec<Stmt> },
|
Block { stmts: Vec<Stmt> },
|
||||||
If { if_clauses: Vec<(Expr, Stmt)>, else_clause: Option<Box<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 {
|
impl fmt::Debug for Stmt {
|
||||||
|
@ -26,18 +27,18 @@ 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> }
|
FuncCall { func: Box<Expr>, args: Vec<Expr>, pos: Position }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Expr {
|
impl fmt::Debug for Expr {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Binary { lhs: left, rhs: right, op} => write!(f, "({:?} {:?} {:?})", op, left, right),
|
Self::Binary { lhs: left, rhs: right, op} => write!(f, "({:?} {:?} {:?})", op, left, right),
|
||||||
Self::Unary { arg, op} => write!(f, "({:?} {:?})", op, arg),
|
Self::Unary { arg, op } => write!(f, "({:?} {:?})", op, arg),
|
||||||
Self::Ident { value, .. } => write!(f, "(ident {:?})", value),
|
Self::Ident { value } => write!(f, "(ident {:?})", value),
|
||||||
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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use std::{cell::RefCell, rc::Rc};
|
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);
|
let mut lexer = Lexer::new(src, fname);
|
||||||
lexer.lex()?;
|
lexer.lex()?;
|
||||||
let mut parser = Parser::new(lexer.into_tokens(), repl);
|
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 {
|
if let Some(env) = env {
|
||||||
environ = env;
|
environ = env;
|
||||||
} else {
|
} 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;
|
let mut result = Value::Nil;
|
||||||
for stmt in ast {
|
for stmt in ast {
|
||||||
if let Stmt::Expr{expr} = stmt {
|
if let Stmt::Expr{expr} = stmt {
|
||||||
|
|
14
src/lexer.rs
14
src/lexer.rs
|
@ -29,6 +29,7 @@ impl Lexer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expect(&mut self, chars: &[char]) -> Option<char> {
|
fn expect(&mut self, chars: &[char]) -> Option<char> {
|
||||||
|
if self.at_end() { return None }
|
||||||
for c in chars {
|
for c in chars {
|
||||||
if self.code[self.current] == *c {
|
if self.code[self.current] == *c {
|
||||||
self.advance(*c == '\n');
|
self.advance(*c == '\n');
|
||||||
|
@ -126,7 +127,10 @@ impl Lexer {
|
||||||
_ => self.add_token(TokenType::Greater, ">")
|
_ => self.add_token(TokenType::Greater, ">")
|
||||||
},
|
},
|
||||||
'<' => match self.expect(&['=']) {
|
'<' => 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, "<")
|
_ => self.add_token(TokenType::Less, "<")
|
||||||
},
|
},
|
||||||
'&' => match self.expect(&['&']) {
|
'&' => match self.expect(&['&']) {
|
||||||
|
@ -176,6 +180,7 @@ impl Lexer {
|
||||||
if self.at_end() { return Err(self.mk_error("Unexpected EOF in character literal")) }
|
if self.at_end() { return Err(self.mk_error("Unexpected EOF in character literal")) }
|
||||||
// TODO Escapes
|
// 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.expect(&['\'']).ok_or(self.mk_error("Expected ' to terminate character literal"))?;
|
||||||
self.add_token(TokenType::Char(c), self.collect_literal());
|
self.add_token(TokenType::Char(c), self.collect_literal());
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -186,6 +191,9 @@ impl Lexer {
|
||||||
while !self.at_end() && self.peek() != '"' {
|
while !self.at_end() && self.peek() != '"' {
|
||||||
if self.peek() == '\\' {
|
if self.peek() == '\\' {
|
||||||
self.advance(false);
|
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
|
// TODO more escape codes! \xHH, \u{HH..} or maybe \uhhhh \Uhhhhhhhh
|
||||||
match self.peek() {
|
match self.peek() {
|
||||||
'0' => s.push('\0'),
|
'0' => s.push('\0'),
|
||||||
|
@ -205,7 +213,7 @@ impl Lexer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.at_end() {
|
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.advance(false);
|
||||||
self.add_token(TokenType::String(Rc::from(s)), self.collect_literal());
|
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() == '.') {
|
while !self.at_end() && (self.peek().is_numeric() || self.peek() == '.') {
|
||||||
if self.peek() == '.' {
|
if self.peek() == '.' {
|
||||||
if has_dot {
|
if has_dot {
|
||||||
return Err(self.mk_error("Numeric literals cannot contain two decimal points"))
|
break;
|
||||||
} else {
|
} else {
|
||||||
has_dot = true;
|
has_dot = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ pub mod parser;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
pub mod eval;
|
pub mod eval;
|
||||||
pub mod interpreter;
|
pub mod interpreter;
|
||||||
|
pub mod stdlib;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Position {
|
pub struct Position {
|
||||||
|
|
|
@ -70,10 +70,15 @@ impl Parser {
|
||||||
self.ifstmt()
|
self.ifstmt()
|
||||||
},
|
},
|
||||||
TokenType::For => {
|
TokenType::For => {
|
||||||
// for statement
|
// for loop
|
||||||
self.next();
|
self.next();
|
||||||
self.forstmt()
|
self.forstmt()
|
||||||
},
|
},
|
||||||
|
TokenType::While => {
|
||||||
|
// while loop
|
||||||
|
self.next();
|
||||||
|
self.whilestmt()
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
// fallback to an expression terminated with a semicolon
|
// fallback to an expression terminated with a semicolon
|
||||||
let expr = self.assignment()?;
|
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> {
|
fn block(&mut self) -> Result<Stmt, ParserError> {
|
||||||
let mut stmts = vec![];
|
let mut stmts = vec![];
|
||||||
while !self.at_end() && self.peek().ty != TokenType::RBrace {
|
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> {
|
fn commalist(&mut self, terminator: TokenType, parse_item: fn(&mut Parser) -> Result<Expr, ParserError>) -> Result<Vec<Expr>, ParserError> {
|
||||||
let mut items = vec![];
|
let mut items = vec![];
|
||||||
while self.peek().ty != terminator {
|
while !self.at_end() && self.peek().ty != terminator {
|
||||||
let expr = parse_item(self)?;
|
let expr = parse_item(self)?;
|
||||||
items.push(expr);
|
items.push(expr);
|
||||||
|
self.err_on_eof()?;
|
||||||
if self.peek().ty == TokenType::Comma {
|
if self.peek().ty == TokenType::Comma {
|
||||||
self.next();
|
self.next();
|
||||||
} else if self.peek().ty == terminator {
|
} else if self.peek().ty == terminator {
|
||||||
|
@ -211,6 +225,7 @@ impl Parser {
|
||||||
return Err(self.mk_error(format!("Expected Comma or {:?} after list", terminator)))
|
return Err(self.mk_error(format!("Expected Comma or {:?} after list", terminator)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.err_on_eof()?;
|
||||||
self.next();
|
self.next();
|
||||||
Ok(items)
|
Ok(items)
|
||||||
}
|
}
|
||||||
|
@ -233,7 +248,11 @@ impl Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pipeline(&mut self) -> Result<Expr, ParserError> {
|
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> {
|
fn additive(&mut self) -> Result<Expr, ParserError> {
|
||||||
|
@ -272,9 +291,9 @@ impl Parser {
|
||||||
fn fncall(&mut self) -> Result<Expr, ParserError> {
|
fn fncall(&mut self) -> Result<Expr, ParserError> {
|
||||||
let expr = self.expr_base()?;
|
let expr = self.expr_base()?;
|
||||||
if !self.at_end() && self.peek().ty == TokenType::LParen {
|
if !self.at_end() && self.peek().ty == TokenType::LParen {
|
||||||
self.next();
|
let lparen = self.next();
|
||||||
let args = self.commalist(TokenType::RParen, Self::assignment)?;
|
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 {
|
} else {
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
@ -298,6 +317,9 @@ impl Parser {
|
||||||
} else {
|
} else {
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
} else if next.ty == TokenType::LBrack {
|
||||||
|
let items = self.commalist(TokenType::RBrack, Self::assignment)?;
|
||||||
|
Ok(Expr::List { items })
|
||||||
} else {
|
} else {
|
||||||
Err(self.mk_error(format!("Unexpected token: {:?}", next.ty)))
|
Err(self.mk_error(format!("Unexpected token: {:?}", next.ty)))
|
||||||
}
|
}
|
||||||
|
|
43
src/stdlib/mod.rs
Normal file
43
src/stdlib/mod.rs
Normal 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))
|
||||||
|
}
|
|
@ -25,7 +25,7 @@ pub enum TokenType {
|
||||||
Bang, Amper, Pipe, DoubleAmper, DoublePipe,
|
Bang, Amper, Pipe, DoubleAmper, DoublePipe,
|
||||||
|
|
||||||
Equal, PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual, DoubleSlashEqual, CaretEqual,
|
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,
|
Arrow, PipeColon, PipePoint, PipeQuestion, PipeAmper,
|
||||||
|
|
||||||
|
@ -49,6 +49,9 @@ impl TokenType {
|
||||||
Self::PipeColon | Self::PipeAmper | Self::PipePoint
|
Self::PipeColon | Self::PipeAmper | Self::PipePoint
|
||||||
| Self::PipeQuestion => Some(OpType::Pipeline),
|
| 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::Equal | Self::PlusEqual | Self::MinusEqual
|
||||||
| Self::StarEqual | Self::SlashEqual | Self::DoubleSlashEqual
|
| Self::StarEqual | Self::SlashEqual | Self::DoubleSlashEqual
|
||||||
| Self::CaretEqual | Self::PercentEqual => Some(OpType::Assignment),
|
| Self::CaretEqual | Self::PercentEqual => Some(OpType::Assignment),
|
||||||
|
@ -60,7 +63,7 @@ impl TokenType {
|
||||||
|
|
||||||
#[derive(Clone,Copy,Debug,PartialEq)]
|
#[derive(Clone,Copy,Debug,PartialEq)]
|
||||||
pub enum OpType {
|
pub enum OpType {
|
||||||
Assignment, Pipeline, Additive, Multiplicative, Exponential
|
Assignment, Comparison, Pipeline, Additive, Multiplicative, Exponential
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OpType {
|
impl OpType {
|
||||||
|
|
235
src/value.rs
235
src/value.rs
|
@ -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>;
|
use crate::{RuntimeError, Position};
|
||||||
type Complex = num_complex::Complex64;
|
|
||||||
|
|
||||||
macro_rules! value_from {
|
pub type Rational = num_rational::Ratio<i64>;
|
||||||
($variant:ident, $($kind:ty)*) => {
|
pub type Complex = num_complex::Complex64;
|
||||||
$(
|
|
||||||
impl From<$kind> for Value {
|
#[derive(Clone)]
|
||||||
fn from(x: $kind) -> Self {
|
pub struct BuiltinFn {
|
||||||
Self::$variant(x.into())
|
pub name: Rc<str>,
|
||||||
}
|
pub func: fn(Vec<Value>) -> Result<Value, String>,
|
||||||
}
|
pub arg_count: usize
|
||||||
)*
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_numeric_op {
|
impl fmt::Debug for BuiltinFn {
|
||||||
($optrait:ty, $fnname:ident, { $($bonus:tt)* }) => {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
impl $optrait for &Value {
|
f.debug_struct("BuiltinFn").field("name", &self.name).field("arg_count", &self.arg_count).finish()
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[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 {
|
pub enum Value {
|
||||||
Nil,
|
Nil,
|
||||||
|
Type(usize),
|
||||||
Int(i64), Float(f64), Complex(Complex), Rational(Rational),
|
Int(i64), Float(f64), Complex(Complex), Rational(Rational),
|
||||||
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<Vec<Value>>), Map(Rc<HashMap<Value,Value>>),
|
||||||
|
BuiltinFn(BuiltinFn),
|
||||||
|
Data(Data),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
|
@ -83,8 +70,170 @@ impl Value {
|
||||||
_ => Err(())
|
_ => 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!(Int, u8 u16 u32 i8 i16 i32 i64);
|
||||||
value_from!(Float, f32 f64);
|
value_from!(Float, f32 f64);
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
|
|
||||||
for c: "asdfghjkl" {
|
|
||||||
let b = 'q' + c;
|
|
||||||
}
|
|
|
@ -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")
|
|
||||||
}
|
|
Loading…
Reference in a new issue