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

View File

@ -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!()
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

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

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