added functions (they are not good yet)
This commit is contained in:
parent
509c9d3c04
commit
fe7a71eed0
18 changed files with 335 additions and 75 deletions
61
Cargo.lock
generated
61
Cargo.lock
generated
|
@ -2,12 +2,42 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "addr2line"
|
||||||
|
version = "0.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
|
||||||
|
dependencies = [
|
||||||
|
"gimli",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "backtrace"
|
||||||
|
version = "0.3.66"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
|
||||||
|
dependencies = [
|
||||||
|
"addr2line",
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"miniz_oxide",
|
||||||
|
"object",
|
||||||
|
"rustc-demangle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
@ -41,6 +71,7 @@ dependencies = [
|
||||||
name = "complexpr"
|
name = "complexpr"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"backtrace",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"num-complex",
|
"num-complex",
|
||||||
"num-rational",
|
"num-rational",
|
||||||
|
@ -128,6 +159,12 @@ dependencies = [
|
||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gimli"
|
||||||
|
version = "0.26.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-lifetimes"
|
name = "io-lifetimes"
|
||||||
version = "0.7.3"
|
version = "0.7.3"
|
||||||
|
@ -167,6 +204,15 @@ version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
|
||||||
|
dependencies = [
|
||||||
|
"adler",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nibble_vec"
|
name = "nibble_vec"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -238,6 +284,15 @@ dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "object"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.43"
|
version = "1.0.43"
|
||||||
|
@ -286,6 +341,12 @@ dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-demangle"
|
||||||
|
version = "0.1.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.35.9"
|
version = "0.35.9"
|
||||||
|
|
|
@ -9,3 +9,4 @@ num-complex = "0.4.1"
|
||||||
num-rational = "0.4.0"
|
num-rational = "0.4.0"
|
||||||
num-traits = "0.2.15"
|
num-traits = "0.2.15"
|
||||||
rustyline = "10.0.0"
|
rustyline = "10.0.0"
|
||||||
|
backtrace = "0.3.66"
|
||||||
|
|
11
examples/factorial.cxpr
Normal file
11
examples/factorial.cxpr
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
fn factorial(n) {
|
||||||
|
if n <= 1 {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return n * factorial(n-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for n : range(0, 10) {
|
||||||
|
println(factorial(n));
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
use std::{rc::Rc, cell::RefCell, fs};
|
use std::{rc::Rc, cell::RefCell, fs, panic::{self, PanicInfo}, thread::Thread};
|
||||||
|
|
||||||
|
use backtrace::Backtrace;
|
||||||
use complexpr::{eval::Environment, interpreter::interpret, value::Value, stdlib};
|
use complexpr::{eval::Environment, interpreter::interpret, value::Value, stdlib};
|
||||||
use rustyline::{self, error::ReadlineError};
|
use rustyline::{self, error::ReadlineError};
|
||||||
|
|
||||||
|
@ -7,7 +8,24 @@ const C_RESET: &str = "\x1b[0m";
|
||||||
const C_BLUE: &str = "\x1b[94m";
|
const C_BLUE: &str = "\x1b[94m";
|
||||||
const PROMPT: &str = "\x1b[94m>> \x1b[0m";
|
const PROMPT: &str = "\x1b[94m>> \x1b[0m";
|
||||||
|
|
||||||
|
fn panic_hook(info: &PanicInfo) {
|
||||||
|
eprintln!("{:?}", Backtrace::new());
|
||||||
|
eprintln!("!!! Internal interpreter error occured !!!");
|
||||||
|
if let Some(s) = std::thread::current().name() {
|
||||||
|
eprintln!("Thread: {}", s);
|
||||||
|
}
|
||||||
|
if let Some(loc) = info.location() {
|
||||||
|
eprintln!("Location: {}:{}:{}", loc.file(), loc.line(), loc.column())
|
||||||
|
}
|
||||||
|
if let Some(s) = info.payload().downcast_ref::<&str>() {
|
||||||
|
eprintln!("Message: {}", s);
|
||||||
|
} else if let Some(s) = info.payload().downcast_ref::<String>() {
|
||||||
|
eprintln!("Message: {}", s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
panic::set_hook(Box::new(panic_hook));
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
if args.len() == 2 {
|
if args.len() == 2 {
|
||||||
let fname = &args[1];
|
let fname = &args[1];
|
||||||
|
@ -35,7 +53,7 @@ fn repl() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
match result {
|
match result {
|
||||||
Ok(Value::Nil) => (),
|
Ok(Value::Nil) => (),
|
||||||
Ok(value) => println!("{}", value.repr()),
|
Ok(value) => println!("{}", value.repr()),
|
||||||
Err(e) => println!("{}", e)
|
Err(e) => print!("{}", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(ReadlineError::Eof) => break,
|
Err(ReadlineError::Eof) => break,
|
||||||
|
|
73
src/eval.rs
73
src/eval.rs
|
@ -1,6 +1,6 @@
|
||||||
use std::{collections::HashMap, rc::Rc, cell::RefCell};
|
use std::{collections::HashMap, rc::Rc, cell::RefCell};
|
||||||
|
|
||||||
use crate::{value::{Value, Complex}, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}, RuntimeError, Position};
|
use crate::{value::{Value, Complex, Func}, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}, RuntimeError, Position};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Environment {
|
pub struct Environment {
|
||||||
|
@ -54,17 +54,21 @@ impl Default for Environment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Unwind {
|
pub enum Unwind {
|
||||||
Continue{pos: Position}, Break{pos: Position}, Return{pos: Position, value: Value}, Error(RuntimeError)
|
Continue{pos: Position},
|
||||||
|
Break{pos: Position},
|
||||||
|
Return{pos: Position, value: Value},
|
||||||
|
Error(RuntimeError)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Unwind {
|
impl Unwind {
|
||||||
pub fn as_error(self) -> RuntimeError {
|
pub fn as_error(self) -> RuntimeError {
|
||||||
match self {
|
match self {
|
||||||
Self::Error(e) => e,
|
Self::Error(e) => e,
|
||||||
Self::Continue { pos } => RuntimeError { message: "continue statement outside of loop".into(), pos },
|
Self::Continue { pos } => RuntimeError::new("continue statement outside of loop", pos),
|
||||||
Self::Break { pos } => RuntimeError { message: "break statement outside of loop".into(), pos },
|
Self::Break { pos } => RuntimeError::new("break statement outside of loop", pos),
|
||||||
Self::Return { pos, .. } => RuntimeError { message: "return statement outside of function".into(), pos },
|
Self::Return { pos, .. } => RuntimeError::new("return statement outside of function", pos),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +90,7 @@ fn unwrap_ident_token(tok: &Token) -> &Rc<str> {
|
||||||
pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), Unwind> {
|
pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), Unwind> {
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Expr{ expr }
|
Stmt::Expr{ expr }
|
||||||
=> drop(eval_expr(expr, env)),
|
=> drop(eval_expr(expr, env)?),
|
||||||
Stmt::Let { lhs, rhs: None }
|
Stmt::Let { lhs, rhs: None }
|
||||||
=> env.borrow_mut().declare(unwrap_ident_token(lhs).clone(), Value::Nil),
|
=> env.borrow_mut().declare(unwrap_ident_token(lhs).clone(), Value::Nil),
|
||||||
Stmt::Let { lhs, rhs: Some(rhs) } => {
|
Stmt::Let { lhs, rhs: Some(rhs) } => {
|
||||||
|
@ -96,7 +100,7 @@ pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), Unwind> {
|
||||||
Stmt::Block { stmts } => {
|
Stmt::Block { stmts } => {
|
||||||
let block_env = Environment::extend(env).wrap();
|
let block_env = Environment::extend(env).wrap();
|
||||||
for stmt in stmts {
|
for stmt in stmts {
|
||||||
eval_stmt(stmt, block_env.clone())?
|
eval_stmt(stmt, block_env.clone())?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Stmt::If { if_clauses, else_clause } => {
|
Stmt::If { if_clauses, else_clause } => {
|
||||||
|
@ -116,7 +120,7 @@ pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), Unwind> {
|
||||||
env.borrow_mut().declare(name.clone(), Value::Nil);
|
env.borrow_mut().declare(name.clone(), Value::Nil);
|
||||||
let iterator = iter.iter();
|
let iterator = iter.iter();
|
||||||
if let Err(e) = iterator {
|
if let Err(e) = iterator {
|
||||||
return Err(RuntimeError { message: e, pos: var.pos.clone() }.into())
|
return Err(RuntimeError::new(e, var.pos.clone()).into())
|
||||||
}
|
}
|
||||||
if let Ok(i) = iterator {
|
if let Ok(i) = iterator {
|
||||||
for v in i {
|
for v in i {
|
||||||
|
@ -145,8 +149,22 @@ pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), Unwind> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Stmt::Fn { name, args, body } => {
|
||||||
|
let name = name.ty.clone().as_ident().unwrap();
|
||||||
|
let func = Func {
|
||||||
|
name: Some(name.clone()),
|
||||||
|
args: args.into_iter().map(|a| a.ty.clone().as_ident().unwrap()).collect(),
|
||||||
|
env: env.clone(),
|
||||||
|
func: body.as_ref().clone()
|
||||||
|
};
|
||||||
|
env.borrow_mut().declare(name, Value::Func(func));
|
||||||
|
},
|
||||||
Stmt::Break { tok } => return Err(Unwind::Break { pos: tok.pos.clone() }),
|
Stmt::Break { tok } => return Err(Unwind::Break { pos: tok.pos.clone() }),
|
||||||
Stmt::Continue { tok } => return Err(Unwind::Continue { pos: tok.pos.clone() }),
|
Stmt::Continue { tok } => return Err(Unwind::Continue { pos: tok.pos.clone() }),
|
||||||
|
Stmt::Return { tok, expr } => {
|
||||||
|
let value = eval_expr(expr, env)?;
|
||||||
|
return Err(Unwind::Return { pos: tok.pos.clone(), value })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -186,7 +204,16 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result<Value, RuntimeError> {
|
||||||
Expr::Index { lhs, index, pos } => {
|
Expr::Index { lhs, index, pos } => {
|
||||||
let l = eval_expr(lhs, env.clone())?;
|
let l = eval_expr(lhs, env.clone())?;
|
||||||
let idx = eval_expr(index, env)?;
|
let idx = eval_expr(index, env)?;
|
||||||
l.index(&idx).map_err(|e| RuntimeError { message: e, pos: pos.clone() })
|
l.index(&idx).map_err(|e| RuntimeError::new(e, pos.clone()))
|
||||||
|
},
|
||||||
|
Expr::Fn { args, body } => {
|
||||||
|
let func = Func {
|
||||||
|
name: None,
|
||||||
|
args: args.into_iter().map(|a| a.ty.clone().as_ident().unwrap()).collect(),
|
||||||
|
env: env.clone(),
|
||||||
|
func: body.as_ref().clone()
|
||||||
|
};
|
||||||
|
Ok(Value::Func(func))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,7 +236,7 @@ pub fn eval_ident(token: &Token, env: EnvRef) -> Result<Value, RuntimeError> {
|
||||||
if let Token { ty: TokenType::Ident(name), ..} = token {
|
if let Token { ty: TokenType::Ident(name), ..} = token {
|
||||||
env.borrow_mut()
|
env.borrow_mut()
|
||||||
.get(name)
|
.get(name)
|
||||||
.ok_or_else(|| RuntimeError { message: "Variable not defined in scope".into(), pos: token.pos.clone() })
|
.ok_or_else(|| RuntimeError::new("Variable not defined in scope", token.pos.clone()))
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
@ -223,13 +250,13 @@ pub fn eval_assignment(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Resul
|
||||||
let r = eval_expr(rhs, env.clone())?;
|
let r = eval_expr(rhs, env.clone())?;
|
||||||
env.borrow_mut()
|
env.borrow_mut()
|
||||||
.set(name.clone(), r)
|
.set(name.clone(), r)
|
||||||
.map_err(|_| RuntimeError { message: "Variable not declared before assignment".into(), pos: op.pos.clone() })?;
|
.map_err(|_| RuntimeError::new("Variable not declared before assignment", op.pos.clone()))?;
|
||||||
Ok(Value::Nil)
|
Ok(Value::Nil)
|
||||||
} else {
|
} else {
|
||||||
// compound assignment
|
// compound assignment
|
||||||
let prev_value = env.borrow_mut()
|
let prev_value = env.borrow_mut()
|
||||||
.get(name)
|
.get(name)
|
||||||
.ok_or_else(|| RuntimeError { message: "Variable not defined in scope".into(), pos: op.pos.clone()})?;
|
.ok_or_else(|| RuntimeError::new("Variable not defined in scope", op.pos.clone()))?;
|
||||||
let r = eval_expr(rhs, env.clone())?;
|
let r = eval_expr(rhs, env.clone())?;
|
||||||
|
|
||||||
let result = match op.ty {
|
let result = match op.ty {
|
||||||
|
@ -239,7 +266,7 @@ pub fn eval_assignment(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Resul
|
||||||
TokenType::SlashEqual => &prev_value / &r,
|
TokenType::SlashEqual => &prev_value / &r,
|
||||||
TokenType::PercentEqual => &prev_value % &r,
|
TokenType::PercentEqual => &prev_value % &r,
|
||||||
_ => todo!() // TODO more operations
|
_ => todo!() // TODO more operations
|
||||||
}.map_err(|e| RuntimeError { message: e, pos: op.pos.clone() })?;
|
}.map_err(|e| RuntimeError::new(e, op.pos.clone()))?;
|
||||||
|
|
||||||
env.borrow_mut()
|
env.borrow_mut()
|
||||||
.set(name.clone(), result.clone()).expect("unreachable");
|
.set(name.clone(), result.clone()).expect("unreachable");
|
||||||
|
@ -250,10 +277,10 @@ pub fn eval_assignment(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Resul
|
||||||
let idx = eval_expr(index, env.clone())?;
|
let idx = eval_expr(index, env.clone())?;
|
||||||
if op.ty == TokenType::Equal {
|
if op.ty == TokenType::Equal {
|
||||||
let r = eval_expr(rhs, env)?;
|
let r = eval_expr(rhs, env)?;
|
||||||
l.assign_index(&idx, r.clone()).map_err(|e| RuntimeError { message: e, pos: pos.clone() })?;
|
l.assign_index(&idx, r.clone()).map_err(|e| RuntimeError::new(e, pos.clone()))?;
|
||||||
Ok(r)
|
Ok(r)
|
||||||
} else {
|
} else {
|
||||||
let prev_value = l.index(&idx).map_err(|e| RuntimeError { message: e, pos: pos.clone() })?;
|
let prev_value = l.index(&idx).map_err(|e| RuntimeError::new(e, pos.clone()))?;
|
||||||
let r = eval_expr(rhs, env)?;
|
let r = eval_expr(rhs, env)?;
|
||||||
let result = match op.ty {
|
let result = match op.ty {
|
||||||
TokenType::PlusEqual => &prev_value + &r,
|
TokenType::PlusEqual => &prev_value + &r,
|
||||||
|
@ -262,8 +289,8 @@ pub fn eval_assignment(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Resul
|
||||||
TokenType::SlashEqual => &prev_value / &r,
|
TokenType::SlashEqual => &prev_value / &r,
|
||||||
TokenType::PercentEqual => &prev_value % &r,
|
TokenType::PercentEqual => &prev_value % &r,
|
||||||
_ => todo!() // TODO more operations
|
_ => todo!() // TODO more operations
|
||||||
}.map_err(|e| RuntimeError { message: e, pos: op.pos.clone() })?;
|
}.map_err(|e| RuntimeError::new(e, op.pos.clone()))?;
|
||||||
l.assign_index(&idx, result.clone()).map_err(|e| RuntimeError { message: e, pos: pos.clone() })?;
|
l.assign_index(&idx, result.clone()).map_err(|e| RuntimeError::new(e, pos.clone()))?;
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -281,7 +308,7 @@ pub fn eval_binary(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result<Va
|
||||||
TokenType::Slash => &l / &r,
|
TokenType::Slash => &l / &r,
|
||||||
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::new(e, op.pos.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_boolean(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result<Value, RuntimeError> {
|
pub fn eval_boolean(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result<Value, RuntimeError> {
|
||||||
|
@ -304,10 +331,10 @@ pub fn eval_boolean(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result<V
|
||||||
pub fn eval_comp(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result<Value, RuntimeError> {
|
pub fn eval_comp(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result<Value, RuntimeError> {
|
||||||
let l = eval_expr(lhs, env.clone())?;
|
let l = eval_expr(lhs, env.clone())?;
|
||||||
let r = eval_expr(rhs, env)?;
|
let r = eval_expr(rhs, env)?;
|
||||||
let mk_err = || RuntimeError {
|
let mk_err = || RuntimeError::new(
|
||||||
message: format!("Cannot compare {:?} with {:?}", l, r),
|
format!("Cannot compare {:?} with {:?}", l, r),
|
||||||
pos: op.pos.clone()
|
op.pos.clone()
|
||||||
};
|
);
|
||||||
match op.ty {
|
match op.ty {
|
||||||
TokenType::DoubleEqual => Ok(Value::Bool(l == r)),
|
TokenType::DoubleEqual => Ok(Value::Bool(l == r)),
|
||||||
TokenType::BangEqual => Ok(Value::Bool(l != r)),
|
TokenType::BangEqual => Ok(Value::Bool(l != r)),
|
||||||
|
@ -336,5 +363,5 @@ pub fn eval_unary(arg: &Expr, op: &Token, env: EnvRef) -> Result<Value, RuntimeE
|
||||||
TokenType::Minus => -a,
|
TokenType::Minus => -a,
|
||||||
TokenType::Bang => Ok(Value::Bool(!a.truthy())),
|
TokenType::Bang => Ok(Value::Bool(!a.truthy())),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}.map_err(|e| RuntimeError { message: e, pos: op.pos.clone() })
|
}.map_err(|e| RuntimeError::new(e, op.pos.clone()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ use std::fmt;
|
||||||
|
|
||||||
use crate::{token::{Token, OpType}, Position};
|
use crate::{token::{Token, OpType}, Position};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum Stmt {
|
pub enum Stmt {
|
||||||
Expr { expr: Expr },
|
Expr { expr: Expr },
|
||||||
Let { lhs: Token, rhs: Option<Expr> },
|
Let { lhs: Token, rhs: Option<Expr> },
|
||||||
|
@ -11,6 +12,8 @@ pub enum Stmt {
|
||||||
While { expr: Expr, stmt: Box<Stmt> },
|
While { expr: Expr, stmt: Box<Stmt> },
|
||||||
Break { tok: Token },
|
Break { tok: Token },
|
||||||
Continue { tok: Token },
|
Continue { tok: Token },
|
||||||
|
Return { tok: Token, expr: Expr },
|
||||||
|
Fn { name: Token, args: Vec<Token>, body: Box<Stmt> },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Stmt {
|
impl fmt::Debug for Stmt {
|
||||||
|
@ -18,11 +21,13 @@ impl fmt::Debug for Stmt {
|
||||||
match self {
|
match self {
|
||||||
Self::Expr { expr } => write!(f, "{:?}", expr),
|
Self::Expr { expr } => write!(f, "{:?}", expr),
|
||||||
Self::Let { lhs, rhs } => write!(f, "(let {:?} = {:?})", lhs, rhs),
|
Self::Let { lhs, rhs } => write!(f, "(let {:?} = {:?})", lhs, rhs),
|
||||||
|
Self::Block { stmts } => write!(f, "(block {:?})", stmts),
|
||||||
_ => todo!()
|
_ => todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
Binary { lhs: Box<Expr>, rhs: Box<Expr>, op: Token },
|
Binary { lhs: Box<Expr>, rhs: Box<Expr>, op: Token },
|
||||||
Unary { arg: Box<Expr>, op: Token },
|
Unary { arg: Box<Expr>, op: Token },
|
||||||
|
@ -31,6 +36,7 @@ pub enum Expr {
|
||||||
List { items: Vec<Expr> },
|
List { items: Vec<Expr> },
|
||||||
FuncCall { func: Box<Expr>, args: Vec<Expr>, pos: Position },
|
FuncCall { func: Box<Expr>, args: Vec<Expr>, pos: Position },
|
||||||
Index { lhs: Box<Expr>, index: Box<Expr>, pos: Position },
|
Index { lhs: Box<Expr>, index: Box<Expr>, pos: Position },
|
||||||
|
Fn { args: Vec<Token>, body: Box<Stmt> },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Expr {
|
impl fmt::Debug for Expr {
|
||||||
|
@ -43,6 +49,7 @@ impl fmt::Debug for Expr {
|
||||||
Self::List { items } => write!(f, "(list {:?})", items),
|
Self::List { items } => write!(f, "(list {:?})", items),
|
||||||
Self::FuncCall { func, args, .. } => write!(f, "(call {:?} {:?})", func, args),
|
Self::FuncCall { func, args, .. } => write!(f, "(call {:?} {:?})", func, args),
|
||||||
Self::Index { lhs, index, .. } => write!(f, "(index {:?} {:?})", lhs, index),
|
Self::Index { lhs, index, .. } => write!(f, "(index {:?} {:?})", lhs, index),
|
||||||
|
Self::Fn { args, body } => write!(f, "(fn {:?} {:?})", args, body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,8 @@ use std::{cell::RefCell, rc::Rc};
|
||||||
use crate::{value::Value, lexer::Lexer, parser::Parser, eval::{Environment, eval_stmt, eval_expr, EnvRef}, expr::Stmt, stdlib};
|
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<EnvRef>, 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 ctx_name = if repl { "<interactive input>" } else { fname.as_ref().map(|s| s.as_ref()).unwrap_or("<unknown>") };
|
||||||
|
let mut lexer = Lexer::new(src, fname.clone());
|
||||||
lexer.lex()?;
|
lexer.lex()?;
|
||||||
let mut parser = Parser::new(lexer.into_tokens(), repl);
|
let mut parser = Parser::new(lexer.into_tokens(), repl);
|
||||||
let ast = parser.parse()?;
|
let ast = parser.parse()?;
|
||||||
|
@ -18,9 +19,9 @@ pub fn interpret(src: &str, fname: Option<String>, env: Option<EnvRef>, repl: bo
|
||||||
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 {
|
||||||
result = eval_expr(&expr, environ.clone())?;
|
result = eval_expr(&expr, environ.clone()).map_err(|e| e.finish(Some(Rc::from(ctx_name))))?;
|
||||||
} else {
|
} else {
|
||||||
eval_stmt(&stmt, environ.clone()).map_err(|e| e.as_error())?;
|
eval_stmt(&stmt, environ.clone()).map_err(|e| e.as_error().finish(Some(Rc::from(ctx_name))))?;
|
||||||
result = Value::Nil;
|
result = Value::Nil;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -268,6 +268,7 @@ impl Lexer {
|
||||||
"else" => TokenType::Else,
|
"else" => TokenType::Else,
|
||||||
"while" => TokenType::While,
|
"while" => TokenType::While,
|
||||||
"for" => TokenType::For,
|
"for" => TokenType::For,
|
||||||
|
"fn" => TokenType::Fn,
|
||||||
"let" => TokenType::Let,
|
"let" => TokenType::Let,
|
||||||
"break" => TokenType::Break,
|
"break" => TokenType::Break,
|
||||||
"continue" => TokenType::Continue,
|
"continue" => TokenType::Continue,
|
||||||
|
|
48
src/lib.rs
48
src/lib.rs
|
@ -24,10 +24,40 @@ pub struct ParserError {
|
||||||
pub pos: Position
|
pub pos: Position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Stackframe {
|
||||||
|
pub pos: Position,
|
||||||
|
pub fn_name: Option<Rc<str>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RuntimeError {
|
pub struct RuntimeError {
|
||||||
pub message: String,
|
pub message: String,
|
||||||
pub pos: Position
|
pub stacktrace: Vec<Stackframe>,
|
||||||
|
pub last_pos: Option<Position>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RuntimeError {
|
||||||
|
pub fn new<S>(message: S, pos: Position) -> Self
|
||||||
|
where S: Into<String> {
|
||||||
|
Self {
|
||||||
|
message: message.into(),
|
||||||
|
stacktrace: vec![],
|
||||||
|
last_pos: Some(pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit_fn(mut self, fn_name: Option<Rc<str>>, pos: Position) -> Self {
|
||||||
|
self.stacktrace.push(Stackframe { pos: self.last_pos.unwrap(), fn_name });
|
||||||
|
self.last_pos = Some(pos);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish(mut self, ctx_name: Option<Rc<str>>) -> Self {
|
||||||
|
self.stacktrace.push(Stackframe { pos: self.last_pos.unwrap(), fn_name: ctx_name });
|
||||||
|
self.last_pos = None;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ParserError {
|
impl fmt::Display for ParserError {
|
||||||
|
@ -43,12 +73,16 @@ impl fmt::Display for ParserError {
|
||||||
|
|
||||||
impl fmt::Display for RuntimeError {
|
impl fmt::Display for RuntimeError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "Error: {}\n In {} at {},{}",
|
writeln!(f, "Error: {}", self.message)?;
|
||||||
self.message,
|
for frame in &self.stacktrace {
|
||||||
self.pos.file.as_ref().map(|o| o.as_ref()).unwrap_or("<unknown>"),
|
writeln!(f, " In {} at {}:{}:{}",
|
||||||
self.pos.line,
|
frame.fn_name.as_ref().map(|o| o.as_ref()).unwrap_or("<anonymous fn>"),
|
||||||
self.pos.col
|
frame.pos.file.as_ref().map(|o| o.as_ref()).unwrap_or("<unknown>"),
|
||||||
)
|
frame.pos.line,
|
||||||
|
frame.pos.col
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,11 +82,20 @@ impl Parser {
|
||||||
TokenType::Break => {
|
TokenType::Break => {
|
||||||
let tok = self.next();
|
let tok = self.next();
|
||||||
self.terminate_stmt(Stmt::Break{ tok })
|
self.terminate_stmt(Stmt::Break{ tok })
|
||||||
}
|
},
|
||||||
TokenType::Continue => {
|
TokenType::Continue => {
|
||||||
let tok = self.next();
|
let tok = self.next();
|
||||||
self.terminate_stmt(Stmt::Continue{ tok })
|
self.terminate_stmt(Stmt::Continue{ tok })
|
||||||
}
|
},
|
||||||
|
TokenType::Return => {
|
||||||
|
let tok = self.next();
|
||||||
|
let expr = self.assignment()?;
|
||||||
|
self.terminate_stmt(Stmt::Return{ tok, expr })
|
||||||
|
},
|
||||||
|
TokenType::Fn => {
|
||||||
|
let tok = self.next();
|
||||||
|
self.fndef()
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
// fallback to an expression terminated with a semicolon
|
// fallback to an expression terminated with a semicolon
|
||||||
let expr = self.assignment()?;
|
let expr = self.assignment()?;
|
||||||
|
@ -201,6 +210,36 @@ impl Parser {
|
||||||
Ok(Stmt::While{ expr, stmt: Box::new(stmt) })
|
Ok(Stmt::While{ expr, stmt: Box::new(stmt) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fndef(&mut self) -> Result<Stmt, ParserError> {
|
||||||
|
self.err_on_eof()?;
|
||||||
|
let name = self.next();
|
||||||
|
let name = if let TokenType::Ident(_) = name.ty {
|
||||||
|
name
|
||||||
|
} else {
|
||||||
|
return Err(ParserError { message: "Expected identifer in function declaration".into(), pos: name.pos })
|
||||||
|
};
|
||||||
|
self.err_on_eof()?;
|
||||||
|
let next = self.next();
|
||||||
|
if let TokenType::LParen = next.ty {} else {
|
||||||
|
return Err(ParserError { message: "Expected left parenthesis to start arguments list".into(), pos: next.pos })
|
||||||
|
}
|
||||||
|
let args = self.commalist(TokenType::RParen, Self::ident)?.into_iter().map(|e| {
|
||||||
|
if let Expr::Ident { value: i } = e { i }
|
||||||
|
else { panic!() }
|
||||||
|
}).collect();
|
||||||
|
self.err_on_eof()?;
|
||||||
|
let body = self.statement()?;
|
||||||
|
Ok(Stmt::Fn { name, args, body: Box::new(body) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ident(&mut self) -> Result<Expr, ParserError> {
|
||||||
|
let next = self.next();
|
||||||
|
match next.ty {
|
||||||
|
TokenType::Ident(_) => Ok(Expr::Ident{ value: next }),
|
||||||
|
_ => Err(ParserError { message: "Expected identifier".into(), pos: next.pos })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -352,6 +391,19 @@ impl Parser {
|
||||||
} else if next.ty == TokenType::LBrack {
|
} else if next.ty == TokenType::LBrack {
|
||||||
let items = self.commalist(TokenType::RBrack, Self::assignment)?;
|
let items = self.commalist(TokenType::RBrack, Self::assignment)?;
|
||||||
Ok(Expr::List { items })
|
Ok(Expr::List { items })
|
||||||
|
} else if next.ty == TokenType::Fn {
|
||||||
|
self.err_on_eof()?;
|
||||||
|
let next = self.next();
|
||||||
|
if let TokenType::LParen = next.ty {} else {
|
||||||
|
return Err(ParserError { message: "Expected left parenthesis to start arguments list".into(), pos: next.pos })
|
||||||
|
}
|
||||||
|
let args = self.commalist(TokenType::RParen, Self::ident)?.into_iter().map(|e| {
|
||||||
|
if let Expr::Ident { value: i } = e { i }
|
||||||
|
else { panic!() }
|
||||||
|
}).collect();
|
||||||
|
self.err_on_eof()?;
|
||||||
|
let body = self.statement()?;
|
||||||
|
Ok(Expr::Fn { args, body: Box::new(body) })
|
||||||
} else {
|
} else {
|
||||||
Err(self.mk_error(format!("Unexpected token: {:?}", next.ty)))
|
Err(self.mk_error(format!("Unexpected token: {:?}", next.ty)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,32 +2,32 @@ use std::{rc::Rc, io::Write, cmp::Ordering};
|
||||||
|
|
||||||
use num_traits::ToPrimitive;
|
use num_traits::ToPrimitive;
|
||||||
|
|
||||||
use crate::{value::{Value, BuiltinFn}, eval::Environment};
|
use crate::{value::{Value, BuiltinFunc}, eval::Environment};
|
||||||
|
|
||||||
pub fn load(env: &mut Environment) {
|
pub fn load(env: &mut Environment) {
|
||||||
let mut name: Rc<str>;
|
let mut name: Rc<str>;
|
||||||
name = Rc::from("str");
|
name = Rc::from("str");
|
||||||
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_str, arg_count: 1, name }));
|
env.declare(name.clone(), Value::BuiltinFunc(BuiltinFunc { func: fn_str, arg_count: 1, name }));
|
||||||
name = Rc::from("repr");
|
name = Rc::from("repr");
|
||||||
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_repr, arg_count: 1, name }));
|
env.declare(name.clone(), Value::BuiltinFunc(BuiltinFunc { func: fn_repr, arg_count: 1, name }));
|
||||||
name = Rc::from("print");
|
name = Rc::from("print");
|
||||||
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_print, arg_count: 1, name }));
|
env.declare(name.clone(), Value::BuiltinFunc(BuiltinFunc { func: fn_print, arg_count: 1, name }));
|
||||||
name = Rc::from("println");
|
name = Rc::from("println");
|
||||||
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_println, arg_count: 1, name }));
|
env.declare(name.clone(), Value::BuiltinFunc(BuiltinFunc { func: fn_println, arg_count: 1, name }));
|
||||||
name = Rc::from("input");
|
name = Rc::from("input");
|
||||||
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_input, arg_count: 0, name }));
|
env.declare(name.clone(), Value::BuiltinFunc(BuiltinFunc { func: fn_input, arg_count: 0, name }));
|
||||||
name = Rc::from("ord");
|
name = Rc::from("ord");
|
||||||
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_ord, arg_count: 1, name }));
|
env.declare(name.clone(), Value::BuiltinFunc(BuiltinFunc { func: fn_ord, arg_count: 1, name }));
|
||||||
name = Rc::from("chr");
|
name = Rc::from("chr");
|
||||||
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_chr, arg_count: 1, name }));
|
env.declare(name.clone(), Value::BuiltinFunc(BuiltinFunc { func: fn_chr, arg_count: 1, name }));
|
||||||
name = Rc::from("range");
|
name = Rc::from("range");
|
||||||
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_range, arg_count: 2, name }));
|
env.declare(name.clone(), Value::BuiltinFunc(BuiltinFunc { func: fn_range, arg_count: 2, name }));
|
||||||
name = Rc::from("len");
|
name = Rc::from("len");
|
||||||
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_len, arg_count: 1, name }));
|
env.declare(name.clone(), Value::BuiltinFunc(BuiltinFunc { func: fn_len, arg_count: 1, name }));
|
||||||
name = Rc::from("re");
|
name = Rc::from("re");
|
||||||
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_re, arg_count: 1, name }));
|
env.declare(name.clone(), Value::BuiltinFunc(BuiltinFunc { func: fn_re, arg_count: 1, name }));
|
||||||
name = Rc::from("im");
|
name = Rc::from("im");
|
||||||
env.declare(name.clone(), Value::BuiltinFn(BuiltinFn { func: fn_im, arg_count: 1, name }));
|
env.declare(name.clone(), Value::BuiltinFunc(BuiltinFunc { func: fn_im, arg_count: 1, name }));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fn_str(args: Vec<Value>) -> Result<Value, String> {
|
fn fn_str(args: Vec<Value>) -> Result<Value, String> {
|
||||||
|
|
|
@ -35,7 +35,7 @@ pub enum TokenType {
|
||||||
|
|
||||||
True, False, Nil,
|
True, False, Nil,
|
||||||
If, Elif, Else, For, While,
|
If, Elif, Else, For, While,
|
||||||
Let, Break, Continue, Return
|
Fn, Let, Break, Continue, Return
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenType {
|
impl TokenType {
|
||||||
|
@ -63,6 +63,13 @@ impl TokenType {
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_ident(self) -> Option<Rc<str>> {
|
||||||
|
match self {
|
||||||
|
Self::Ident(s) => Some(s),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,Copy,Debug,PartialEq,Eq)]
|
#[derive(Clone,Copy,Debug,PartialEq,Eq)]
|
||||||
|
|
76
src/value.rs
76
src/value.rs
|
@ -2,24 +2,32 @@ use std::{rc::Rc, collections::HashMap, ops::*, fmt, cmp::Ordering, cell::RefCel
|
||||||
|
|
||||||
use num_traits::{Zero, ToPrimitive};
|
use num_traits::{Zero, ToPrimitive};
|
||||||
|
|
||||||
use crate::{RuntimeError, Position};
|
use crate::{RuntimeError, Position, eval::{EnvRef, eval_stmt, Environment, Unwind}, expr::Stmt};
|
||||||
|
|
||||||
pub type Rational = num_rational::Ratio<i64>;
|
pub type Rational = num_rational::Ratio<i64>;
|
||||||
pub type Complex = num_complex::Complex64;
|
pub type Complex = num_complex::Complex64;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BuiltinFn {
|
pub struct BuiltinFunc {
|
||||||
pub name: Rc<str>,
|
pub name: Rc<str>,
|
||||||
pub func: fn(Vec<Value>) -> Result<Value, String>,
|
pub func: fn(Vec<Value>) -> Result<Value, String>,
|
||||||
pub arg_count: usize
|
pub arg_count: usize
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for BuiltinFn {
|
impl fmt::Debug for BuiltinFunc {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("BuiltinFn").field("name", &self.name).field("arg_count", &self.arg_count).finish()
|
f.debug_struct("BuiltinFn").field("name", &self.name).field("arg_count", &self.arg_count).finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Func {
|
||||||
|
pub name: Option<Rc<str>>,
|
||||||
|
pub args: Vec<Rc<str>>,
|
||||||
|
pub env: EnvRef,
|
||||||
|
pub func: Stmt
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
pub ty: usize,
|
pub ty: usize,
|
||||||
|
@ -42,7 +50,8 @@ pub enum Value {
|
||||||
Char(char),
|
Char(char),
|
||||||
String(Rc<str>),
|
String(Rc<str>),
|
||||||
List(Rc<RefCell<Vec<Value>>>), Map(Rc<HashMap<Value,Value>>),
|
List(Rc<RefCell<Vec<Value>>>), Map(Rc<HashMap<Value,Value>>),
|
||||||
BuiltinFn(BuiltinFn),
|
BuiltinFunc(BuiltinFunc),
|
||||||
|
Func(Func),
|
||||||
Data(Data),
|
Data(Data),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,21 +82,44 @@ impl Value {
|
||||||
|
|
||||||
pub fn call(&self, args: Vec<Value>, pos: &Position) -> Result<Value, RuntimeError> {
|
pub fn call(&self, args: Vec<Value>, pos: &Position) -> Result<Value, RuntimeError> {
|
||||||
match self {
|
match self {
|
||||||
Value::BuiltinFn(f) => {
|
Value::BuiltinFunc(f) => {
|
||||||
match args.len().cmp(&f.arg_count) {
|
match args.len().cmp(&f.arg_count) {
|
||||||
Ordering::Equal =>
|
Ordering::Equal =>
|
||||||
(f.func)(args).map_err(|e| RuntimeError { message: e, pos: pos.clone() }),
|
(f.func)(args).map_err(|e| RuntimeError::new(e, pos.clone())),
|
||||||
Ordering::Less => Err(RuntimeError {
|
Ordering::Less => Err(RuntimeError::new(
|
||||||
message: format!("Not enough arguments for function: expected {}, got {}", f.arg_count, args.len()),
|
format!("Not enough arguments for function: expected {}, got {}", f.arg_count, args.len()),
|
||||||
pos: pos.clone()
|
pos.clone()
|
||||||
}),
|
)),
|
||||||
Ordering::Greater => Err(RuntimeError {
|
Ordering::Greater => Err(RuntimeError::new(
|
||||||
message: format!("Too many arguments for function: expected {}, got {}", f.arg_count, args.len()),
|
format!("Too many arguments for function: expected {}, got {}", f.arg_count, args.len()),
|
||||||
pos: pos.clone()
|
pos.clone()
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
Value::Func(f) => {
|
||||||
|
match args.len().cmp(&f.args.len()) {
|
||||||
|
Ordering::Equal => {
|
||||||
|
let mut env = Environment::extend(f.env.clone());
|
||||||
|
for (k, v) in f.args.iter().zip(args.iter()) {
|
||||||
|
env.declare(k.clone(), v.clone());
|
||||||
}
|
}
|
||||||
_ => Err(RuntimeError { message: "Cannot call".into(), pos: pos.clone() })
|
match eval_stmt(&f.func, env.wrap()) {
|
||||||
|
Ok(()) => Ok(Value::Nil),
|
||||||
|
Err(Unwind::Return{ value, .. }) => Ok(value),
|
||||||
|
Err(e) => Err(e.as_error().exit_fn(f.name.clone(), pos.clone()))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Ordering::Less => Err(RuntimeError::new(
|
||||||
|
format!("Not enough arguments for function: expected {}, got {}", f.args.len(), args.len()),
|
||||||
|
pos.clone()
|
||||||
|
)),
|
||||||
|
Ordering::Greater => Err(RuntimeError::new(
|
||||||
|
format!("Too many arguments for function: expected {}, got {}", f.args.len(), args.len()),
|
||||||
|
pos.clone()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => Err(RuntimeError::new("Cannot call", pos.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +136,11 @@ impl Value {
|
||||||
Self::List(l) => Rc::from(format!("{:?}", l)), // TODO fix
|
Self::List(l) => Rc::from(format!("{:?}", l)), // TODO fix
|
||||||
Self::Map(m) => Rc::from(format!("{:?}", m)), // TODO fix
|
Self::Map(m) => Rc::from(format!("{:?}", m)), // TODO fix
|
||||||
Self::Type(_) => todo!(),
|
Self::Type(_) => todo!(),
|
||||||
Self::BuiltinFn(bf) => Rc::from(format!("<builtin fn {} at {:?}>", bf.name, bf.func as *const ())),
|
Self::BuiltinFunc(bf) => Rc::from(format!("<builtin fn {} at {:?}>", bf.name, bf.func as *const ())),
|
||||||
|
Self::Func(f) => match &f.name {
|
||||||
|
Some(name) => Rc::from(format!("<fn {}>", name)),
|
||||||
|
None => Rc::from("<anonymous fn>"),
|
||||||
|
},
|
||||||
Self::Data(_) => todo!(),
|
Self::Data(_) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,7 +158,11 @@ impl Value {
|
||||||
Self::List(l) => Rc::from(format!("{:?}", l.borrow())), // TODO fix
|
Self::List(l) => Rc::from(format!("{:?}", l.borrow())), // TODO fix
|
||||||
Self::Map(m) => Rc::from(format!("{:?}", m)), // TODO fix
|
Self::Map(m) => Rc::from(format!("{:?}", m)), // TODO fix
|
||||||
Self::Type(_) => todo!(),
|
Self::Type(_) => todo!(),
|
||||||
Self::BuiltinFn(bf) => Rc::from(format!("<builtin fn {} at {:?}>", bf.name, bf.func as *const ())),
|
Self::BuiltinFunc(bf) => Rc::from(format!("<builtin fn {} at {:?}>", bf.name, bf.func as *const ())),
|
||||||
|
Self::Func(f) => match &f.name {
|
||||||
|
Some(name) => Rc::from(format!("<fn {}>", name)),
|
||||||
|
None => Rc::from("<anonymous fn>"),
|
||||||
|
},
|
||||||
Self::Data(_) => todo!(),
|
Self::Data(_) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,7 +235,7 @@ impl PartialEq for Value {
|
||||||
(Self::String(a), Self::String(b)) => a == b,
|
(Self::String(a), Self::String(b)) => a == b,
|
||||||
(Self::List(a), Self::List(b)) => a == b,
|
(Self::List(a), Self::List(b)) => a == b,
|
||||||
(Self::Map(_), Self::Map(_)) => todo!("Can't test maps for equality yet"),
|
(Self::Map(_), Self::Map(_)) => todo!("Can't test maps for equality yet"),
|
||||||
(Self::BuiltinFn(a), Self::BuiltinFn(b))
|
(Self::BuiltinFunc(a), Self::BuiltinFunc(b))
|
||||||
=> (a.func as *const ()) == (b.func as *const ()) && a.arg_count == b.arg_count,
|
=> (a.func as *const ()) == (b.func as *const ()) && a.arg_count == b.arg_count,
|
||||||
(Self::Data(_), Self::Data(_)) => todo!("Can't compare data yet"),
|
(Self::Data(_), Self::Data(_)) => todo!("Can't compare data yet"),
|
||||||
_ => false
|
_ => false
|
||||||
|
|
Loading…
Reference in a new issue