diff --git a/talc-bin/src/repl.rs b/talc-bin/src/repl.rs index 6050cbb..0a448e7 100644 --- a/talc-bin/src/repl.rs +++ b/talc-bin/src/repl.rs @@ -101,11 +101,12 @@ fn read_init_file(args: &Args) -> Result, std::io::Error> { fn exec_line( vm: &mut Vm, line: &str, + line_no: u32, args: &Args, c: &ReplColors, globals: &mut Vec, ) { - let mut ex = match parser::parse(line) { + let mut ex = match parser::parse_from(line, line_no) { Ok(ex) => ex, Err(e) => { eprintln!("{}Error:{} {e}", c.error, c.reset); @@ -192,11 +193,13 @@ pub fn repl(args: &Args) -> ExitCode { }; let vm = Rc::new(RefCell::new(vm)); + let mut line_no = 1; rl.set_helper(Some(TalcHelper::new(vm.clone()))); if let Some(src) = init_src { - exec_line(&mut vm.borrow_mut(), &src, args, &c, &mut globals); + exec_line(&mut vm.borrow_mut(), &src, line_no, args, &c, &mut globals); + line_no += 1; } loop { @@ -214,6 +217,7 @@ pub fn repl(args: &Args) -> ExitCode { } }; - exec_line(&mut vm.borrow_mut(), &line, args, &c, &mut globals); + exec_line(&mut vm.borrow_mut(), &line, line_no, args, &c, &mut globals); + line_no += 1; } } diff --git a/talc-lang/src/chunk.rs b/talc-lang/src/chunk.rs index 107e115..e75131b 100644 --- a/talc-lang/src/chunk.rs +++ b/talc-lang/src/chunk.rs @@ -240,6 +240,7 @@ pub struct TryTable { #[derive(Clone, Debug, Default)] pub struct Chunk { + pub lines: Vec<(u32, u32)>, pub consts: Vec, pub instrs: Vec, pub try_tables: Vec, @@ -250,6 +251,26 @@ impl Chunk { Self::default() } + pub fn set_line(&mut self, line: u32) { + let instr = self.instrs.len() as u32; + if let Some((i, l)) = self.lines.last_mut() { + if *i == instr { + *l = line; + return + } + } + self.lines.push((instr, line)); + } + + pub fn get_line(&self, instr: u32) -> Option { + let res = self.lines.binary_search_by(|(i, _)| i.cmp(&instr)); + let n = match res { + Ok(n) => n, + Err(n) => n.saturating_sub(1), + }; + self.lines.get(n).map(|(_, l)| *l) + } + pub fn add_const(&mut self, v: Value) -> usize { assert!( self.consts.len() < 0xff_ffff, diff --git a/talc-lang/src/compiler.rs b/talc-lang/src/compiler.rs index 1e83959..964117c 100644 --- a/talc-lang/src/compiler.rs +++ b/talc-lang/src/compiler.rs @@ -81,13 +81,18 @@ struct Compiler<'a> { pub fn compile(expr: &Expr, name: Option) -> Result { let mut comp = Compiler::new_module(name, None); + comp.chunk.set_line(0); comp.expr(expr)?; Ok(comp.finish()) } pub fn compile_repl(expr: &Expr, globals: &mut Vec) -> Result { let mut comp = Compiler::new_repl(globals); + comp.chunk.set_line(0); match &expr.kind { + ExprKind::Block(xs) if xs.is_empty() => { + comp.emit(I::Nil); + } ExprKind::Block(xs) => { for x in &xs[0..xs.len() - 1] { comp.expr(x)?; @@ -439,6 +444,7 @@ impl<'a> Compiler<'a> { fn expr(&mut self, e: &Expr) -> Result<()> { let Expr { kind, span } = e; + self.chunk.set_line(span.start.line); match kind { ExprKind::Block(xs) if xs.is_empty() => { self.emit(I::Nil); @@ -845,6 +851,7 @@ impl<'a> Compiler<'a> { } fn lvalue(&mut self, lv: &LValue) -> Result<()> { + self.chunk.set_line(lv.span.start.line); match &lv.kind { LValueKind::Ident(i) => { self.emit(I::Dup); diff --git a/talc-lang/src/exception.rs b/talc-lang/src/exception.rs index 86e9c3d..0d00612 100644 --- a/talc-lang/src/exception.rs +++ b/talc-lang/src/exception.rs @@ -1,21 +1,59 @@ use crate::{ lstring::LStr, - symbol::{Symbol, SYM_MSG, SYM_TYPE}, + symbol::{Symbol, SYM_MSG, SYM_TRACE, SYM_TYPE}, value::Value, }; use std::{fmt::Display, rc::Rc}; pub type Result = std::result::Result; +#[derive(Clone, Debug)] +pub struct Trace { + line: Option, + scope: Option, +} + +impl Trace { + pub fn from_value(value: &Value) -> Option { + let Value::List(lst) = value else { return None }; + let lst = lst.borrow(); + if lst.len() < 2 { + return None; + } + let line = match &lst[0] { + Value::Int(s) => Some(s.to_u32()?), + Value::Nil => None, + _ => return None, + }; + let scope = match &lst[1] { + Value::Symbol(s) => Some(*s), + Value::Nil => None, + _ => return None, + }; + Some(Self { line, scope }) + } + + pub fn to_value(&self) -> Value { + let line = self.line.map_or(Value::Nil, Value::from); + let scope = self.scope.map_or(Value::Nil, Value::from); + vec![line, scope].into() + } +} + #[derive(Clone, Debug)] pub struct Exception { pub ty: Symbol, pub msg: Rc, + pub trace: Vec, } impl Exception { pub fn new(ty: Symbol, msg: Rc) -> Self { - Self { ty, msg } + Self { + ty, + msg, + trace: Vec::new(), + } } pub fn from_value(value: &Value) -> Option { @@ -25,27 +63,54 @@ impl Exception { let table = table.borrow(); let ty = table.get(&(*SYM_TYPE).into())?; let msg = table.get(&(*SYM_MSG).into())?; - if let (Value::Symbol(ty), Value::String(msg)) = (ty, msg) { - Some(Self { - ty: *ty, - msg: msg.clone(), - }) - } else { - None - } + let (Value::Symbol(ty), Value::String(msg)) = (ty, msg) else { + return None + }; + let trace_lst = table.get(&(*SYM_TRACE).into())?; + let Value::List(trace_lst) = trace_lst else { + return None + }; + let trace = trace_lst + .borrow() + .iter() + .map(Trace::from_value) + .collect::>()?; + Some(Self { + ty: *ty, + msg: msg.clone(), + trace, + }) } pub fn to_value(self) -> Value { + let trace: Vec = self.trace.iter().map(Trace::to_value).collect(); Value::new_table(|table| { table.insert((*SYM_TYPE).into(), self.ty.into()); table.insert((*SYM_MSG).into(), Value::String(self.msg)); + table.insert((*SYM_TRACE).into(), trace.into()); }) } + + pub fn add_trace(&mut self, line: Option, scope: Option) { + self.trace.push(Trace { line, scope }); + } } impl Display for Exception { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}: {}", self.ty.name(), self.msg) + write!(f, "{}: {}", self.ty.name(), self.msg)?; + for trace in &self.trace { + write!(f, "\n ")?; + if let Some(scope) = trace.scope { + write!(f, "in {}", scope.name())?; + } else { + write!(f, "in ")?; + } + if let Some(line) = trace.line { + write!(f, " at {line}")?; + } + } + Ok(()) } } @@ -73,4 +138,5 @@ macro_rules! throw { }; } +use num::ToPrimitive; pub use throw; diff --git a/talc-lang/src/lstring.rs b/talc-lang/src/lstring.rs index 77e77fa..5223995 100644 --- a/talc-lang/src/lstring.rs +++ b/talc-lang/src/lstring.rs @@ -393,6 +393,13 @@ impl<'a> From<&'a str> for &'a LStr { } } +impl<'a> From<&'a String> for &'a LStr { + #[inline] + fn from(value: &'a String) -> Self { + Self::from(value.as_str()) + } +} + impl<'a> From<&'a OsStr> for &'a LStr { #[inline] fn from(value: &'a OsStr) -> Self { diff --git a/talc-lang/src/parser/lexer.rs b/talc-lang/src/parser/lexer.rs index 092c483..571586c 100644 --- a/talc-lang/src/parser/lexer.rs +++ b/talc-lang/src/parser/lexer.rs @@ -212,11 +212,20 @@ pub struct Lexer<'s> { impl<'s> Lexer<'s> { pub fn new(src: &'s str) -> Self { + Self::new_at(src, 1) + } + + pub fn new_at(src: &'s str, line: u32) -> Self { + let pos = Pos { + line, + col: 0, + idx: 0, + }; Self { src, chars: src.chars().peekable(), - start_pos: Pos::new(), - pos: Pos::new(), + start_pos: pos, + pos, } } diff --git a/talc-lang/src/parser/parser.rs b/talc-lang/src/parser/parser.rs index fe2d501..14e9c96 100644 --- a/talc-lang/src/parser/parser.rs +++ b/talc-lang/src/parser/parser.rs @@ -229,6 +229,12 @@ impl<'s> Parser<'s> { } } + fn new_at(src: &'s str, line: u32) -> Self { + Self { + lexer: Lexer::new_at(src, line).peekable(), + } + } + fn next(&mut self) -> Result> { self.lexer.next().unwrap() } @@ -739,3 +745,7 @@ impl<'s> Parser<'s> { pub fn parse(src: &str) -> Result { Parser::new(src).parse() } + +pub fn parse_from(src: &str, line: u32) -> Result { + Parser::new_at(src, line).parse() +} diff --git a/talc-lang/src/symbol.rs b/talc-lang/src/symbol.rs index abb266b..bbffc87 100644 --- a/talc-lang/src/symbol.rs +++ b/talc-lang/src/symbol.rs @@ -32,6 +32,7 @@ lazy_static! { pub static ref SYM_END_ITERATION: Symbol = symbol!(end_iteration); pub static ref SYM_TYPE: Symbol = symbol!(type); pub static ref SYM_MSG: Symbol = symbol!(msg); + pub static ref SYM_TRACE: Symbol = symbol!(trace); pub static ref SYM_TYPE_ERROR: Symbol = symbol!(type_error); pub static ref SYM_VALUE_ERROR: Symbol = symbol!(value_error); pub static ref SYM_NAME_ERROR: Symbol = symbol!(name_error); diff --git a/talc-lang/src/value/index.rs b/talc-lang/src/value/index.rs index 3e605d1..233d36c 100644 --- a/talc-lang/src/value/index.rs +++ b/talc-lang/src/value/index.rs @@ -75,7 +75,7 @@ impl Value { Some(i) => col.index(i), None => Ok(Value::from(*SYM_END_ITERATION)), }; - Ok(NativeFunc::new(Box::new(func), 0, symbol!("index...")).into()) + Ok(NativeFunc::new(Box::new(func), 0, symbol!("")).into()) } else { throw!(*SYM_TYPE_ERROR, "cannot index {col:#} with {idx:#}") } diff --git a/talc-lang/src/value/mod.rs b/talc-lang/src/value/mod.rs index 774cd82..3849040 100644 --- a/talc-lang/src/value/mod.rs +++ b/talc-lang/src/value/mod.rs @@ -448,9 +448,21 @@ macro_rules! impl_from { impl_from!(bool, Bool, hash); impl_from!(Symbol, Symbol, hash); impl_from!(Range, Range, rc); + impl_from!(Int, Int, hash); -impl_from!(i64, Int, into); impl_from!(BigInt, Int, into); +impl_from!(i8, Int, into); +impl_from!(i16, Int, into); +impl_from!(i32, Int, into); +impl_from!(i64, Int, into); +impl_from!(isize, Int, into); +impl_from!(u8, Int, into); +impl_from!(u16, Int, into); +impl_from!(u32, Int, into); +impl_from!(u64, Int, into); +impl_from!(usize, Int, into); + +impl_from!(f32, Float, into); impl_from!(f64, Float); impl_from!(Ratio, Ratio, rchash); impl_from!(Complex64, Complex); diff --git a/talc-lang/src/value/ops.rs b/talc-lang/src/value/ops.rs index b900986..16db777 100644 --- a/talc-lang/src/value/ops.rs +++ b/talc-lang/src/value/ops.rs @@ -546,7 +546,7 @@ impl Value { range_iter.borrow_mut().next().map(Value::from), )) }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[range]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } Self::String(s) => { let byte_pos = RefCell::new(0); @@ -559,7 +559,7 @@ impl Value { Ok(Value::from(*SYM_END_ITERATION)) } }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[string]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } Self::List(list) => { let idx = RefCell::new(0); @@ -572,7 +572,7 @@ impl Value { Ok(Value::from(*SYM_END_ITERATION)) } }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[list]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } Self::Table(table) => { let keys: Vec = table.borrow().keys().cloned().collect(); @@ -582,7 +582,7 @@ impl Value { keys.borrow_mut().next().map(HashValue::into_inner), )) }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[table]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } _ => throw!(*SYM_TYPE_ERROR, "cannot iterate {self:#}"), } diff --git a/talc-lang/src/vm.rs b/talc-lang/src/vm.rs index 2439b06..3580aed 100644 --- a/talc-lang/src/vm.rs +++ b/talc-lang/src/vm.rs @@ -16,7 +16,7 @@ use crate::{ SYM_VALUE_ERROR, }, value::{ - function::{FuncAttrs, Function, NativeFunc}, + function::{Function, NativeFunc}, HashValue, Value, }, }; @@ -112,19 +112,20 @@ fn get_call_outcome(args: Vec) -> Result { Ordering::Less => { let remaining = attrs.arity - argc; let f = f.clone(); + + let name = match attrs.name { + None => Symbol::get(""), + Some(s) => Symbol::get(&format!("", s.name())), + }; + let nf = move |vm: &mut Vm, inner_args: Vec| { let mut ia = inner_args.into_iter(); ia.next(); let args: Vec = args.clone().into_iter().chain(ia).collect(); vm.call_value(f.clone(), args) }; - let nf = NativeFunc { - attrs: FuncAttrs { - arity: remaining, - name: None, - }, - func: Box::new(nf), - }; + + let nf = NativeFunc::new(Box::new(nf), remaining, name); Ok(CallOutcome::Partial(nf.into())) } } @@ -175,7 +176,10 @@ impl Vm { CallOutcome::Partial(v) => Ok(v), CallOutcome::Call(args) => match value { Value::Function(f) => self.run_function(f, args), - Value::NativeFunc(f) => (f.func)(self, args), + Value::NativeFunc(f) => (f.func)(self, args).map_err(|mut e| { + e.add_trace(None, f.attrs.name); + e + }), _ => unreachable!("already verified by calling get_call_type"), }, } @@ -198,8 +202,8 @@ impl Vm { self.stack.truncate(init_stack_len); return Ok(v) } - Err(e) => { - if let Err(e) = self.handle_exception(&mut frame, e) { + Err(exc) => { + if let Err(e) = self.handle_exception(&mut frame, exc) { self.stack.truncate(init_stack_len); return Err(e) } @@ -208,8 +212,14 @@ impl Vm { } } - fn handle_exception(&mut self, frame: &mut CallFrame, exc: Exception) -> Result<()> { + fn handle_exception( + &mut self, + frame: &mut CallFrame, + mut exc: Exception, + ) -> Result<()> { loop { + let line = frame.func.chunk.get_line(frame.ip as u32); + exc.add_trace(line, frame.func.attrs.name); while let Some(try_frame) = frame.try_frames.pop() { let table = &frame.func.chunk.try_tables[try_frame.idx]; for catch in &table.catches { diff --git a/talc-std/src/collection.rs b/talc-std/src/collection.rs index 82f957d..64c1fd8 100644 --- a/talc-std/src/collection.rs +++ b/talc-std/src/collection.rs @@ -223,7 +223,7 @@ pub fn sort_key(vm: &mut Vm, args: Vec) -> Result { ), } }; - let nf = NativeFunc::new(Box::new(f), 2, symbol!("inner[sort_key]")).into(); + let nf = NativeFunc::new(Box::new(f), 2, symbol!("")).into(); sort_inner(&mut list.borrow_mut(), Some(&nf), vm)?; Ok(list.into()) } diff --git a/talc-std/src/file.rs b/talc-std/src/file.rs index 27f87ab..a3342ef 100644 --- a/talc-std/src/file.rs +++ b/talc-std/src/file.rs @@ -509,7 +509,7 @@ fn tcp_listen_inner(listener: TcpListener) -> Value { }, Err(e) => throw!(*SYM_IO_ERROR, "{e}"), }; - NativeFunc::new(Box::new(func), 0, symbol!("inner[tcp_listen]")).into() + NativeFunc::new(Box::new(func), 0, symbol!("")).into() } #[native_func(1)] diff --git a/talc-std/src/iter.rs b/talc-std/src/iter.rs index bc4dc09..59617f2 100644 --- a/talc-std/src/iter.rs +++ b/talc-std/src/iter.rs @@ -95,7 +95,7 @@ pub fn pairs(_: &mut Vm, args: Vec) -> Result { Ok(Value::iter_pack(None)) } }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[pairs]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } #[native_func(1)] @@ -104,7 +104,7 @@ pub fn once(_: &mut Vm, args: Vec) -> Result { let v = RefCell::new(Some(val)); let f = move |_: &mut Vm, _| Ok(Value::iter_pack(v.borrow_mut().take())); - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[once]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } #[native_func(1)] @@ -112,7 +112,7 @@ pub fn forever(_: &mut Vm, args: Vec) -> Result { let [_, val] = unpack_args!(args); let f = move |_: &mut Vm, _| Ok(val.clone()); - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[forever]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } // @@ -128,7 +128,7 @@ pub fn map(_: &mut Vm, args: Vec) -> Result { Some(val) => Ok(vmcall!(vm; map.clone(), val)?), None => Ok(Value::iter_pack(None)), }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[map]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } #[native_func(2)] @@ -143,7 +143,7 @@ pub fn tee(_: &mut Vm, args: Vec) -> Result { } None => Ok(Value::iter_pack(None)), }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[tee]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } #[native_func(3)] @@ -161,7 +161,7 @@ pub fn scan(_: &mut Vm, args: Vec) -> Result { *result.borrow_mut() = r.clone(); Ok(r) }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[scan]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } #[native_func(2)] @@ -178,7 +178,7 @@ pub fn filter(_: &mut Vm, args: Vec) -> Result { return Ok(next) } }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[filter]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } #[native_func(2)] @@ -207,7 +207,7 @@ pub fn take(_: &mut Vm, args: Vec) -> Result { *taken.borrow_mut() += 1; Ok(next) }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[take]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } #[native_func(2)] @@ -235,7 +235,7 @@ pub fn skip(_: &mut Vm, args: Vec) -> Result { } vmcall!(vm; iter.clone()) }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[skip]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } #[native_func(1)] @@ -254,7 +254,7 @@ pub fn enumerate(_: &mut Vm, args: Vec) -> Result { Ok(Value::from(vec![n.into(), next])) }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[enumerate]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } #[native_func(1)] @@ -288,7 +288,7 @@ pub fn cycle(_: &mut Vm, args: Vec) -> Result { } }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[cycle]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } #[derive(Clone, Copy, Default)] @@ -336,7 +336,7 @@ pub fn step(_: &mut Vm, args: Vec) -> Result { } Step::End => Ok(Value::Nil), }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[step]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } #[native_func(1)] @@ -355,7 +355,7 @@ pub fn rev(_: &mut Vm, args: Vec) -> Result { } Ok(Value::iter_pack(lst.borrow_mut().as_mut().unwrap().pop())) }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[rev]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } // @@ -381,7 +381,7 @@ pub fn zip(_: &mut Vm, args: Vec) -> Result { Ok(Value::from(res)) }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[zip]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } #[native_func(1)] @@ -407,7 +407,7 @@ pub fn zipn(_: &mut Vm, args: Vec) -> Result { Ok(Value::from(res)) }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[zipn]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } #[native_func(2)] @@ -434,7 +434,7 @@ pub fn alternate(_: &mut Vm, args: Vec) -> Result { } }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[alternate]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } #[native_func(1)] @@ -466,7 +466,7 @@ pub fn alternaten(_: &mut Vm, args: Vec) -> Result { } }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[alternaten]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } #[derive(Default)] @@ -510,7 +510,7 @@ pub fn intersperse(_: &mut Vm, args: Vec) -> Result { Intersperse::End => Ok(Value::iter_pack(None)), }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[intersperse]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } #[native_func(2)] @@ -532,7 +532,7 @@ pub fn chain(_: &mut Vm, args: Vec) -> Result { } }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[chain]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } #[derive(Default, Debug)] @@ -597,7 +597,7 @@ pub fn cartprod(_: &mut Vm, args: Vec) -> Result { Ok(Value::from(vec![a_res, b_res])) }; - Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[cart_prod]")).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("")).into()) } //