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};
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
76
src/eval.rs
76
src/eval.rs
|
@ -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!()
|
||||
|
@ -194,4 +213,33 @@ pub fn eval_binary(lhs: &Box<Expr>, rhs: &Box<Expr>, op: &Token, env: EnvRef) ->
|
|||
TokenType::Percent => &l % &r,
|
||||
_ => 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!()
|
||||
}
|
||||
}
|
15
src/expr.rs
15
src/expr.rs
|
@ -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,18 +27,18 @@ 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 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
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::Unary { arg, op } => write!(f, "({:?} {:?})", op, arg),
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
14
src/lexer.rs
14
src/lexer.rs
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
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,
|
||||
|
||||
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 {
|
||||
|
|
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>;
|
||||
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);
|
||||
|
|
|
@ -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