trace
All checks were successful
docs / test (push) Successful in 9s

This commit is contained in:
trimill 2025-01-01 16:27:14 -05:00
parent b1fccce8e3
commit c75dafac8e
15 changed files with 203 additions and 56 deletions

View file

@ -101,11 +101,12 @@ fn read_init_file(args: &Args) -> Result<Option<String>, std::io::Error> {
fn exec_line(
vm: &mut Vm,
line: &str,
line_no: u32,
args: &Args,
c: &ReplColors,
globals: &mut Vec<Symbol>,
) {
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;
}
}

View file

@ -240,6 +240,7 @@ pub struct TryTable {
#[derive(Clone, Debug, Default)]
pub struct Chunk {
pub lines: Vec<(u32, u32)>,
pub consts: Vec<Value>,
pub instrs: Vec<Instruction>,
pub try_tables: Vec<TryTable>,
@ -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<u32> {
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,

View file

@ -81,13 +81,18 @@ struct Compiler<'a> {
pub fn compile(expr: &Expr, name: Option<Symbol>) -> Result<Function> {
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<Symbol>) -> Result<Function> {
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);

View file

@ -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<T> = std::result::Result<T, Exception>;
#[derive(Clone, Debug)]
pub struct Trace {
line: Option<u32>,
scope: Option<Symbol>,
}
impl Trace {
pub fn from_value(value: &Value) -> Option<Self> {
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<LStr>,
pub trace: Vec<Trace>,
}
impl Exception {
pub fn new(ty: Symbol, msg: Rc<LStr>) -> Self {
Self { ty, msg }
Self {
ty,
msg,
trace: Vec::new(),
}
}
pub fn from_value(value: &Value) -> Option<Self> {
@ -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::<Option<_>>()?;
Some(Self {
ty: *ty,
msg: msg.clone(),
trace,
})
}
pub fn to_value(self) -> Value {
let trace: Vec<Value> = 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<u32>, scope: Option<Symbol>) {
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 <anonymous>")?;
}
if let Some(line) = trace.line {
write!(f, " at {line}")?;
}
}
Ok(())
}
}
@ -73,4 +138,5 @@ macro_rules! throw {
};
}
use num::ToPrimitive;
pub use throw;

View file

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

View file

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

View file

@ -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<Token<'s>> {
self.lexer.next().unwrap()
}
@ -739,3 +745,7 @@ impl<'s> Parser<'s> {
pub fn parse(src: &str) -> Result<Expr> {
Parser::new(src).parse()
}
pub fn parse_from(src: &str, line: u32) -> Result<Expr> {
Parser::new_at(src, line).parse()
}

View file

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

View file

@ -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!("<index...>")).into())
} else {
throw!(*SYM_TYPE_ERROR, "cannot index {col:#} with {idx:#}")
}

View file

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

View file

@ -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!("<range iter>")).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!("<string iter>")).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!("<list iter>")).into())
}
Self::Table(table) => {
let keys: Vec<HashValue> = 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!("<table iter>")).into())
}
_ => throw!(*SYM_TYPE_ERROR, "cannot iterate {self:#}"),
}

View file

@ -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<Value>) -> Result<CallOutcome> {
Ordering::Less => {
let remaining = attrs.arity - argc;
let f = f.clone();
let name = match attrs.name {
None => Symbol::get("<partial>"),
Some(s) => Symbol::get(&format!("<partial {}>", s.name())),
};
let nf = move |vm: &mut Vm, inner_args: Vec<Value>| {
let mut ia = inner_args.into_iter();
ia.next();
let args: Vec<Value> = 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 {

View file

@ -223,7 +223,7 @@ pub fn sort_key(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
),
}
};
let nf = NativeFunc::new(Box::new(f), 2, symbol!("inner[sort_key]")).into();
let nf = NativeFunc::new(Box::new(f), 2, symbol!("<sort_key inner>")).into();
sort_inner(&mut list.borrow_mut(), Some(&nf), vm)?;
Ok(list.into())
}

View file

@ -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!("<tcp_listen inner>")).into()
}
#[native_func(1)]

View file

@ -95,7 +95,7 @@ pub fn pairs(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(Value::iter_pack(None))
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[pairs]")).into())
Ok(NativeFunc::new(Box::new(f), 0, symbol!("<pairs iterator>")).into())
}
#[native_func(1)]
@ -104,7 +104,7 @@ pub fn once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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!("<once iterator>")).into())
}
#[native_func(1)]
@ -112,7 +112,7 @@ pub fn forever(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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!("<forever iterator>")).into())
}
//
@ -128,7 +128,7 @@ pub fn map(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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!("<map iterator>")).into())
}
#[native_func(2)]
@ -143,7 +143,7 @@ pub fn tee(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}
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!("<tee iterator>")).into())
}
#[native_func(3)]
@ -161,7 +161,7 @@ pub fn scan(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*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!("<scan iterator>")).into())
}
#[native_func(2)]
@ -178,7 +178,7 @@ pub fn filter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
return Ok(next)
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[filter]")).into())
Ok(NativeFunc::new(Box::new(f), 0, symbol!("<filter iterator>")).into())
}
#[native_func(2)]
@ -207,7 +207,7 @@ pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*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!("<take iterator>")).into())
}
#[native_func(2)]
@ -235,7 +235,7 @@ pub fn skip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}
vmcall!(vm; iter.clone())
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[skip]")).into())
Ok(NativeFunc::new(Box::new(f), 0, symbol!("<skip iterator>")).into())
}
#[native_func(1)]
@ -254,7 +254,7 @@ pub fn enumerate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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!("<enumerate iterator>")).into())
}
#[native_func(1)]
@ -288,7 +288,7 @@ pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[cycle]")).into())
Ok(NativeFunc::new(Box::new(f), 0, symbol!("<cycle iterator>")).into())
}
#[derive(Clone, Copy, Default)]
@ -336,7 +336,7 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}
Step::End => Ok(Value::Nil),
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[step]")).into())
Ok(NativeFunc::new(Box::new(f), 0, symbol!("<step iterator>")).into())
}
#[native_func(1)]
@ -355,7 +355,7 @@ pub fn rev(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}
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!("<rev iterator>")).into())
}
//
@ -381,7 +381,7 @@ pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(Value::from(res))
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[zip]")).into())
Ok(NativeFunc::new(Box::new(f), 0, symbol!("<zip iterator>")).into())
}
#[native_func(1)]
@ -407,7 +407,7 @@ pub fn zipn(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(Value::from(res))
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[zipn]")).into())
Ok(NativeFunc::new(Box::new(f), 0, symbol!("<zipn iterator>")).into())
}
#[native_func(2)]
@ -434,7 +434,7 @@ pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[alternate]")).into())
Ok(NativeFunc::new(Box::new(f), 0, symbol!("<alternate iterator>")).into())
}
#[native_func(1)]
@ -466,7 +466,7 @@ pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[alternaten]")).into())
Ok(NativeFunc::new(Box::new(f), 0, symbol!("<alternaten iterator>")).into())
}
#[derive(Default)]
@ -510,7 +510,7 @@ pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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!("<intersperse iterator>")).into())
}
#[native_func(2)]
@ -532,7 +532,7 @@ pub fn chain(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[chain]")).into())
Ok(NativeFunc::new(Box::new(f), 0, symbol!("<chain iterator>")).into())
}
#[derive(Default, Debug)]
@ -597,7 +597,7 @@ pub fn cartprod(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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!("<cart_prod iterator>")).into())
}
//