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.
|
||||
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]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
@ -41,6 +71,7 @@ dependencies = [
|
|||
name = "complexpr"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"lazy_static",
|
||||
"num-complex",
|
||||
"num-rational",
|
||||
|
@ -128,6 +159,12 @@ dependencies = [
|
|||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "0.7.3"
|
||||
|
@ -167,6 +204,15 @@ version = "2.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "nibble_vec"
|
||||
version = "0.1.0"
|
||||
|
@ -238,6 +284,15 @@ dependencies = [
|
|||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.43"
|
||||
|
@ -286,6 +341,12 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.35.9"
|
||||
|
|
|
@ -9,3 +9,4 @@ num-complex = "0.4.1"
|
|||
num-rational = "0.4.0"
|
||||
num-traits = "0.2.15"
|
||||
rustyline = "10.0.0"
|
||||
backtrace = "0.3.66"
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
while true {
|
||||
print(input());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,4 +7,4 @@ while n != 1 {
|
|||
n = 3*n+1;
|
||||
}
|
||||
}
|
||||
println(n);
|
||||
println(n);
|
||||
|
|
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));
|
||||
}
|
|
@ -6,4 +6,4 @@ while z < 1000000 {
|
|||
z = y;
|
||||
y = x;
|
||||
println(z);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
# Prints the string "Hello, world!" followed by a newline
|
||||
println("Hello, world!");
|
||||
println("Hello, world!");
|
||||
|
|
|
@ -36,4 +36,4 @@ for y: range(0,yscale) {
|
|||
}
|
||||
}
|
||||
println("");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 rustyline::{self, error::ReadlineError};
|
||||
|
||||
|
@ -7,7 +8,24 @@ const C_RESET: &str = "\x1b[0m";
|
|||
const C_BLUE: &str = "\x1b[94m";
|
||||
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>> {
|
||||
panic::set_hook(Box::new(panic_hook));
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
if args.len() == 2 {
|
||||
let fname = &args[1];
|
||||
|
@ -35,7 +53,7 @@ fn repl() -> Result<(), Box<dyn std::error::Error>> {
|
|||
match result {
|
||||
Ok(Value::Nil) => (),
|
||||
Ok(value) => println!("{}", value.repr()),
|
||||
Err(e) => println!("{}", e)
|
||||
Err(e) => print!("{}", e)
|
||||
}
|
||||
}
|
||||
Err(ReadlineError::Eof) => break,
|
||||
|
|
75
src/eval.rs
75
src/eval.rs
|
@ -1,6 +1,6 @@
|
|||
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)]
|
||||
pub struct Environment {
|
||||
|
@ -54,17 +54,21 @@ impl Default for Environment {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
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 {
|
||||
pub fn as_error(self) -> RuntimeError {
|
||||
match self {
|
||||
Self::Error(e) => e,
|
||||
Self::Continue { pos } => RuntimeError { message: "continue statement outside of loop".into(), pos },
|
||||
Self::Break { pos } => RuntimeError { message: "break statement outside of loop".into(), pos },
|
||||
Self::Return { pos, .. } => RuntimeError { message: "return statement outside of function".into(), pos },
|
||||
Self::Continue { pos } => RuntimeError::new("continue statement outside of loop", pos),
|
||||
Self::Break { pos } => RuntimeError::new("break statement outside of loop", 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> {
|
||||
match stmt {
|
||||
Stmt::Expr{ expr }
|
||||
=> drop(eval_expr(expr, env)),
|
||||
=> drop(eval_expr(expr, env)?),
|
||||
Stmt::Let { lhs, rhs: None }
|
||||
=> env.borrow_mut().declare(unwrap_ident_token(lhs).clone(), Value::Nil),
|
||||
Stmt::Let { lhs, rhs: Some(rhs) } => {
|
||||
|
@ -95,8 +99,8 @@ pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), Unwind> {
|
|||
},
|
||||
Stmt::Block { stmts } => {
|
||||
let block_env = Environment::extend(env).wrap();
|
||||
for stmt in stmts {
|
||||
eval_stmt(stmt, block_env.clone())?
|
||||
for stmt in stmts {
|
||||
eval_stmt(stmt, block_env.clone())?;
|
||||
}
|
||||
},
|
||||
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);
|
||||
let iterator = iter.iter();
|
||||
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 {
|
||||
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::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(())
|
||||
}
|
||||
|
@ -186,7 +204,16 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result<Value, RuntimeError> {
|
|||
Expr::Index { lhs, index, pos } => {
|
||||
let l = eval_expr(lhs, env.clone())?;
|
||||
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 {
|
||||
env.borrow_mut()
|
||||
.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 {
|
||||
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())?;
|
||||
env.borrow_mut()
|
||||
.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)
|
||||
} else {
|
||||
// compound assignment
|
||||
let prev_value = env.borrow_mut()
|
||||
.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 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::PercentEqual => &prev_value % &r,
|
||||
_ => 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()
|
||||
.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())?;
|
||||
if op.ty == TokenType::Equal {
|
||||
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)
|
||||
} 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 result = match op.ty {
|
||||
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::PercentEqual => &prev_value % &r,
|
||||
_ => todo!() // TODO more operations
|
||||
}.map_err(|e| RuntimeError { message: e, pos: op.pos.clone() })?;
|
||||
l.assign_index(&idx, result.clone()).map_err(|e| RuntimeError { message: e, pos: pos.clone() })?;
|
||||
}.map_err(|e| RuntimeError::new(e, op.pos.clone()))?;
|
||||
l.assign_index(&idx, result.clone()).map_err(|e| RuntimeError::new(e, pos.clone()))?;
|
||||
Ok(result)
|
||||
}
|
||||
} else {
|
||||
|
@ -281,7 +308,7 @@ pub fn eval_binary(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result<Va
|
|||
TokenType::Slash => &l / &r,
|
||||
TokenType::Percent => &l % &r,
|
||||
_ => 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> {
|
||||
|
@ -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> {
|
||||
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()
|
||||
};
|
||||
let mk_err = || RuntimeError::new(
|
||||
format!("Cannot compare {:?} with {:?}", l, r),
|
||||
op.pos.clone()
|
||||
);
|
||||
match op.ty {
|
||||
TokenType::DoubleEqual => 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::Bang => Ok(Value::Bool(!a.truthy())),
|
||||
_ => 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};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Stmt {
|
||||
Expr { expr: Expr },
|
||||
Let { lhs: Token, rhs: Option<Expr> },
|
||||
|
@ -11,6 +12,8 @@ pub enum Stmt {
|
|||
While { expr: Expr, stmt: Box<Stmt> },
|
||||
Break { tok: Token },
|
||||
Continue { tok: Token },
|
||||
Return { tok: Token, expr: Expr },
|
||||
Fn { name: Token, args: Vec<Token>, body: Box<Stmt> },
|
||||
}
|
||||
|
||||
impl fmt::Debug for Stmt {
|
||||
|
@ -18,11 +21,13 @@ impl fmt::Debug for Stmt {
|
|||
match self {
|
||||
Self::Expr { expr } => write!(f, "{:?}", expr),
|
||||
Self::Let { lhs, rhs } => write!(f, "(let {:?} = {:?})", lhs, rhs),
|
||||
Self::Block { stmts } => write!(f, "(block {:?})", stmts),
|
||||
_ => todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Expr {
|
||||
Binary { lhs: Box<Expr>, rhs: Box<Expr>, op: Token },
|
||||
Unary { arg: Box<Expr>, op: Token },
|
||||
|
@ -31,6 +36,7 @@ pub enum Expr {
|
|||
List { items: Vec<Expr> },
|
||||
FuncCall { func: Box<Expr>, args: Vec<Expr>, pos: Position },
|
||||
Index { lhs: Box<Expr>, index: Box<Expr>, pos: Position },
|
||||
Fn { args: Vec<Token>, body: Box<Stmt> },
|
||||
}
|
||||
|
||||
impl fmt::Debug for Expr {
|
||||
|
@ -43,6 +49,7 @@ impl fmt::Debug for Expr {
|
|||
Self::List { items } => write!(f, "(list {:?})", items),
|
||||
Self::FuncCall { func, args, .. } => write!(f, "(call {:?} {:?})", func, args),
|
||||
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};
|
||||
|
||||
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()?;
|
||||
let mut parser = Parser::new(lexer.into_tokens(), repl);
|
||||
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;
|
||||
for stmt in ast {
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -268,6 +268,7 @@ impl Lexer {
|
|||
"else" => TokenType::Else,
|
||||
"while" => TokenType::While,
|
||||
"for" => TokenType::For,
|
||||
"fn" => TokenType::Fn,
|
||||
"let" => TokenType::Let,
|
||||
"break" => TokenType::Break,
|
||||
"continue" => TokenType::Continue,
|
||||
|
|
48
src/lib.rs
48
src/lib.rs
|
@ -24,10 +24,40 @@ pub struct ParserError {
|
|||
pub pos: Position
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Stackframe {
|
||||
pub pos: Position,
|
||||
pub fn_name: Option<Rc<str>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RuntimeError {
|
||||
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 {
|
||||
|
@ -43,12 +73,16 @@ impl fmt::Display for ParserError {
|
|||
|
||||
impl fmt::Display for RuntimeError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Error: {}\n In {} at {},{}",
|
||||
self.message,
|
||||
self.pos.file.as_ref().map(|o| o.as_ref()).unwrap_or("<unknown>"),
|
||||
self.pos.line,
|
||||
self.pos.col
|
||||
)
|
||||
writeln!(f, "Error: {}", self.message)?;
|
||||
for frame in &self.stacktrace {
|
||||
writeln!(f, " In {} at {}:{}:{}",
|
||||
frame.fn_name.as_ref().map(|o| o.as_ref()).unwrap_or("<anonymous fn>"),
|
||||
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 => {
|
||||
let tok = self.next();
|
||||
self.terminate_stmt(Stmt::Break{ tok })
|
||||
}
|
||||
},
|
||||
TokenType::Continue => {
|
||||
let tok = self.next();
|
||||
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
|
||||
let expr = self.assignment()?;
|
||||
|
@ -201,6 +210,36 @@ impl Parser {
|
|||
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> {
|
||||
let mut stmts = vec![];
|
||||
while !self.at_end() && self.peek().ty != TokenType::RBrace {
|
||||
|
@ -352,6 +391,19 @@ impl Parser {
|
|||
} else if next.ty == TokenType::LBrack {
|
||||
let items = self.commalist(TokenType::RBrack, Self::assignment)?;
|
||||
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 {
|
||||
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 crate::{value::{Value, BuiltinFn}, eval::Environment};
|
||||
use crate::{value::{Value, BuiltinFunc}, 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 }));
|
||||
env.declare(name.clone(), Value::BuiltinFunc(BuiltinFunc { 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 }));
|
||||
env.declare(name.clone(), Value::BuiltinFunc(BuiltinFunc { 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 }));
|
||||
env.declare(name.clone(), Value::BuiltinFunc(BuiltinFunc { 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 }));
|
||||
env.declare(name.clone(), Value::BuiltinFunc(BuiltinFunc { 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 }));
|
||||
env.declare(name.clone(), Value::BuiltinFunc(BuiltinFunc { func: fn_input, arg_count: 0, name }));
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
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> {
|
||||
|
|
|
@ -35,7 +35,7 @@ pub enum TokenType {
|
|||
|
||||
True, False, Nil,
|
||||
If, Elif, Else, For, While,
|
||||
Let, Break, Continue, Return
|
||||
Fn, Let, Break, Continue, Return
|
||||
}
|
||||
|
||||
impl TokenType {
|
||||
|
@ -63,6 +63,13 @@ impl TokenType {
|
|||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_ident(self) -> Option<Rc<str>> {
|
||||
match self {
|
||||
Self::Ident(s) => Some(s),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,Debug,PartialEq,Eq)]
|
||||
|
|
78
src/value.rs
78
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 crate::{RuntimeError, Position};
|
||||
use crate::{RuntimeError, Position, eval::{EnvRef, eval_stmt, Environment, Unwind}, expr::Stmt};
|
||||
|
||||
pub type Rational = num_rational::Ratio<i64>;
|
||||
pub type Complex = num_complex::Complex64;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BuiltinFn {
|
||||
pub struct BuiltinFunc {
|
||||
pub name: Rc<str>,
|
||||
pub func: fn(Vec<Value>) -> Result<Value, String>,
|
||||
pub arg_count: usize
|
||||
}
|
||||
|
||||
impl fmt::Debug for BuiltinFn {
|
||||
impl fmt::Debug for BuiltinFunc {
|
||||
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 Func {
|
||||
pub name: Option<Rc<str>>,
|
||||
pub args: Vec<Rc<str>>,
|
||||
pub env: EnvRef,
|
||||
pub func: Stmt
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Data {
|
||||
pub ty: usize,
|
||||
|
@ -42,7 +50,8 @@ pub enum Value {
|
|||
Char(char),
|
||||
String(Rc<str>),
|
||||
List(Rc<RefCell<Vec<Value>>>), Map(Rc<HashMap<Value,Value>>),
|
||||
BuiltinFn(BuiltinFn),
|
||||
BuiltinFunc(BuiltinFunc),
|
||||
Func(Func),
|
||||
Data(Data),
|
||||
}
|
||||
|
||||
|
@ -73,21 +82,44 @@ impl Value {
|
|||
|
||||
pub fn call(&self, args: Vec<Value>, pos: &Position) -> Result<Value, RuntimeError> {
|
||||
match self {
|
||||
Value::BuiltinFn(f) => {
|
||||
Value::BuiltinFunc(f) => {
|
||||
match args.len().cmp(&f.arg_count) {
|
||||
Ordering::Equal =>
|
||||
(f.func)(args).map_err(|e| RuntimeError { message: e, pos: pos.clone() }),
|
||||
Ordering::Less => Err(RuntimeError {
|
||||
message: format!("Not enough arguments for function: expected {}, got {}", f.arg_count, args.len()),
|
||||
pos: pos.clone()
|
||||
}),
|
||||
Ordering::Greater => Err(RuntimeError {
|
||||
message: format!("Too many arguments for function: expected {}, got {}", f.arg_count, args.len()),
|
||||
pos: pos.clone()
|
||||
})
|
||||
(f.func)(args).map_err(|e| RuntimeError::new(e, pos.clone())),
|
||||
Ordering::Less => Err(RuntimeError::new(
|
||||
format!("Not enough arguments for function: expected {}, got {}", f.arg_count, args.len()),
|
||||
pos.clone()
|
||||
)),
|
||||
Ordering::Greater => Err(RuntimeError::new(
|
||||
format!("Too many arguments for function: expected {}, got {}", f.arg_count, args.len()),
|
||||
pos.clone()
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(RuntimeError { message: "Cannot call".into(), pos: 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());
|
||||
}
|
||||
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::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::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!(),
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +158,11 @@ impl Value {
|
|||
Self::List(l) => Rc::from(format!("{:?}", l.borrow())), // 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::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!(),
|
||||
}
|
||||
}
|
||||
|
@ -195,7 +235,7 @@ impl PartialEq for Value {
|
|||
(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))
|
||||
(Self::BuiltinFunc(a), Self::BuiltinFunc(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
|
||||
|
|
Loading…
Reference in a new issue