added functions (they are not good yet)

This commit is contained in:
TriMill 2022-09-12 16:53:04 -04:00
parent 509c9d3c04
commit fe7a71eed0
18 changed files with 335 additions and 75 deletions

61
Cargo.lock generated
View file

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

View file

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

View file

@ -1,3 +1,3 @@
while true { while true {
print(input()); print(input());
} }

View file

@ -7,4 +7,4 @@ while n != 1 {
n = 3*n+1; n = 3*n+1;
} }
} }
println(n); println(n);

11
examples/factorial.cxpr Normal file
View 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));
}

View file

@ -6,4 +6,4 @@ while z < 1000000 {
z = y; z = y;
y = x; y = x;
println(z); println(z);
} }

View file

@ -1,2 +1,2 @@
# Prints the string "Hello, world!" followed by a newline # Prints the string "Hello, world!" followed by a newline
println("Hello, world!"); println("Hello, world!");

View file

@ -36,4 +36,4 @@ for y: range(0,yscale) {
} }
} }
println(""); println("");
} }

View file

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

View file

@ -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) } => {
@ -95,8 +99,8 @@ 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()))
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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)]

View file

@ -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()
}) ))
} }
} },
_ => 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::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