diff --git a/src/compile.rs b/src/compile.rs index 6614da0..fe59cee 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -17,7 +17,7 @@ impl<'s> Compile<'s> { } } - pub fn compile(&mut self, stmt_list: Vec) -> Vec { + pub fn compile(&mut self, stmt_list: Vec) -> Vec { // Compile meta-statements let expr_list = self.compile_meta(stmt_list); self.compile_expr_list(&expr_list) @@ -42,7 +42,7 @@ impl<'s> Compile<'s> { todo!("includes") } - fn compile_expr_list(&mut self, expr_list: &Vec) -> Vec { + fn compile_expr_list(&mut self, expr_list: &Vec) -> Vec { // Scoping is done here. // Local scopes are implicit. If a variable is assigned to at // all in the current scope, it's considered to be a local. @@ -67,19 +67,20 @@ impl<'s> Compile<'s> { let quote = self.quote_table .insert(expr.span().clone(), locals, stmts.clone(), compiled); - Inst::PushValue(Value::Quote(quote)).into() + let inst = Inst::PushValue(Value::Quote(quote)); + SpInst::new(expr.span().clone(), inst).into() } } } fn compile_atom(&mut self, atom: &SpAtom) -> Thunk { - match atom.inner() { - Atom::Float(f) => Inst::PushValue(Value::Float(*f)).into(), - Atom::Int(i) => Inst::PushValue(Value::Int(*i)).into(), - Atom::Str(s) => Inst::PushValue(Value::Str(s.clone())).into(), + let inst = match atom.inner() { + Atom::Float(f) => Inst::PushValue(Value::Float(*f)), + Atom::Int(i) => Inst::PushValue(Value::Int(*i)), + Atom::Str(s) => Inst::PushValue(Value::Str(s.clone())), Atom::Assign(text) => { let word = self.scope_stack.insert_local(text); - Inst::Store(word).into() + Inst::Store(word) } Atom::Word(text) => { // Look for locally defined symbols first. One can't be found, @@ -90,10 +91,11 @@ impl<'s> Compile<'s> { } else { self.scope_stack.insert_local(text) }; - Inst::Load(word).into() + Inst::Load(word) } - Atom::Apply => Inst::Call.into(), - } + Atom::Apply => Inst::Call, + }; + SpInst::new(atom.span().clone(), inst).into() } /// Discovers and inserts local variables into the scope. @@ -111,18 +113,18 @@ impl<'s> Compile<'s> { #[derive(Debug, Clone)] enum Thunk { - Block(Vec), + Block(Vec), List(Vec), } -impl From for Thunk { - fn from(inst: Inst) -> Thunk { +impl From for Thunk { + fn from(inst: SpInst) -> Thunk { Thunk::Block(vec![inst]) } } impl Thunk { - fn flatten(self) -> Vec { + fn flatten(self) -> Vec { use Thunk::*; match self { Block(block) => block, diff --git a/src/main.rs b/src/main.rs index 1bae896..21aecfd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ use std::io::Read; use std::path::PathBuf; use structopt::StructOpt; use syn::parser::Parser; -use vm::{error::RuntimeSpanError, machine::MachineBuilder}; +use vm::{error::RuntimeError, machine::MachineBuilder}; #[derive(Debug, StructOpt)] struct Opt { @@ -48,6 +48,7 @@ fn main() -> Result { if let Err(e) = machine.eval(stmts) { // print error message + println!("Error traceback:"); print_error(&e); Ok(()) } else { @@ -55,12 +56,12 @@ fn main() -> Result { } } -fn print_error(error: &RuntimeSpanError) { +fn print_error(error: &RuntimeError) { match error { - RuntimeSpanError::Span(span, error) => { - println!("at {}", span); - print_error(error) + RuntimeError::Span(_, inner) => { + println!("{}", error); + print_error(inner) } - RuntimeSpanError::Error(error) => println!("{}", error), + _ => println!(" {}", error), } } diff --git a/src/object.rs b/src/object.rs index 6af6764..dea3b7b 100644 --- a/src/object.rs +++ b/src/object.rs @@ -5,7 +5,7 @@ use crate::syn::ast::SpStmt; use crate::vm::{error::Result, machine::Machine}; -use crate::{scope::Scope, syn::span::Span, vm::inst::Inst}; +use crate::{scope::Scope, syn::span::Span, vm::inst::SpInst}; use std::cell::RefCell; use std::collections::{BTreeMap, HashMap}; use std::fmt::{self, Debug, Display}; @@ -33,7 +33,7 @@ impl Quote { /// A table of compiled quotes, their expression trees, and their spans. #[derive(Debug, Clone, Default)] pub struct QuoteTable { - table: Vec<(Span, Scope, Vec, Rc>)>, + table: Vec<(Span, Scope, Vec, Rc>)>, } impl QuoteTable { @@ -46,14 +46,14 @@ impl QuoteTable { span: Span, scope: Scope, quote: Vec, - compiled: Rc>, + compiled: Rc>, ) -> Quote { let next = Quote(self.table.len()); self.table.push((span, scope, quote, compiled)); next } - pub fn get(&self, quote: Quote) -> &(Span, Scope, Vec, Rc>) { + pub fn get(&self, quote: Quote) -> &(Span, Scope, Vec, Rc>) { &self.table[quote.0] } } @@ -136,15 +136,21 @@ pub enum BuiltinExit { } #[derive(Clone)] -pub struct BuiltinFn(Rc); +pub struct BuiltinFn { + name: Rc, + fun: Rc, +} impl BuiltinFn { - pub fn new(fun: Rc) -> Self { - BuiltinFn(fun) + pub fn new(name: String, fun: Rc) -> Self { + BuiltinFn { + name: Rc::new(name), + fun, + } } pub fn call(&self, machine: &mut Machine, reentry: usize) -> Result { - (self.0)(machine, reentry) + (self.fun)(machine, reentry) } } @@ -156,7 +162,7 @@ impl Debug for BuiltinFn { write!( fmt, "builtin function at {:#x}", - (&self.0 as *const _ as usize) + (&self.fun as *const _ as usize) ) } } diff --git a/src/vm/error.rs b/src/vm/error.rs index 7f46dfa..48ce611 100644 --- a/src/vm/error.rs +++ b/src/vm/error.rs @@ -14,14 +14,12 @@ pub enum RuntimeError { #[error("cannot call non-quote value '{0}'")] CannotCall(String), + + #[error("at {0}")] + Span(Span, Box), } -pub type Result = std::result::Result; - -pub enum RuntimeSpanError { - Span(Span, Box), - Error(RuntimeError), -} +pub type Result = std::result::Result; /// Add a location to the implemented value. pub trait WithLocation { @@ -29,12 +27,12 @@ pub trait WithLocation { fn with_location(self, span: Span) -> Self::Out; } -impl WithLocation for Result { - type Out = Result; +impl WithLocation for Result { + type Out = Result; fn with_location(self, span: Span) -> Self::Out { if let Err(e) = self { - Err(RuntimeSpanError::Span(span, Box::new(e))) + Err(e.with_location(span)) } else { self } @@ -42,36 +40,9 @@ impl WithLocation for Result { } impl WithLocation for RuntimeError { - type Out = RuntimeSpanError; + type Out = RuntimeError; fn with_location(self, span: Span) -> Self::Out { - RuntimeSpanError::Span(span, Box::new(self.into())) + RuntimeError::Span(span, Box::new(self)) } } - -impl From for RuntimeSpanError { - fn from(other: RuntimeError) -> Self { - RuntimeSpanError::Error(other) - } -} - -// TODO -// Error building idea: -// pass RuntimeError values upward, but also keep a list of spans/locations -// added by functions adding errors upward. - -// e.g. - -/* - -fn do_thing() -> Result<()> { - ... - let result = do_fallible_thing(); - if let Err(e) = result { - e.add_location(context.file, context.span); - return Err(e); - } - ... -} - -*/ diff --git a/src/vm/inst.rs b/src/vm/inst.rs index 882625c..dd989a4 100644 --- a/src/vm/inst.rs +++ b/src/vm/inst.rs @@ -1,5 +1,6 @@ use crate::object::Value; use crate::scope::Word; +use crate::syn::span::Spanned; #[derive(Debug, Clone)] pub enum Inst { @@ -18,12 +19,4 @@ pub enum Inst { Call, } -// TODO - do we want separate function definition syntax? We would be able to -// know ahead of time which words are functions with this. -// : sq dup * ; -// vs. -// [ dup * ] =sq -// - do we want to have separate function call syntax? -// 5 sq call -// 5 sq ! -// and then store macros in the function variables. the ! means "apply" +pub type SpInst = Spanned; diff --git a/src/vm/machine.rs b/src/vm/machine.rs index 3381807..1826318 100644 --- a/src/vm/machine.rs +++ b/src/vm/machine.rs @@ -34,12 +34,12 @@ pub struct NativeFrame { #[derive(Debug, Clone)] pub struct QuoteFrame { locals: BTreeMap>, - code: Rc>, + code: Rc>, pc: usize, } impl QuoteFrame { - pub fn inst(&self) -> Option<&Inst> { + pub fn inst(&self) -> Option<&SpInst> { self.code.get(self.pc) } } @@ -176,6 +176,8 @@ impl Machine { // ///////////////////////////////////////////////////////////////////////// pub fn eval(&mut self, stmts: Vec) -> Result<()> { + // TODO - figure out the best way to figure out the call stack + // locations, and build an error out of that self.scope_stack.push_scope(); let mut compile = Compile::new(&mut self.scope_stack, &mut self.quote_table); let code = Rc::new(compile.compile(stmts)); @@ -200,7 +202,8 @@ impl Machine { let inst = frame.inst(); if let Some(inst) = inst { let inst = inst.clone(); - self.eval_inst(inst)?; + let span = inst.span().clone(); + self.eval_inst(inst).with_location(span)?; } else { self.call_stack.pop(); } @@ -242,11 +245,11 @@ impl Machine { native.call(self, reentry) } - fn eval_inst(&mut self, inst: Inst) -> Result<()> { + fn eval_inst(&mut self, inst: SpInst) -> Result<()> { let current_frame = self.call_stack.len() - 1; let next_pc = self.pc().unwrap() + 1; - match inst { - Inst::PushValue(value) => self.stack_push(value)?, + match inst.inner() { + Inst::PushValue(value) => self.stack_push(value.clone())?, Inst::Load(word) => { let value = self .lookup_word(&word) @@ -258,7 +261,7 @@ impl Machine { } Inst::Store(word) => { let value = self.stack_pop()?; - self.store_local(word, value); + self.store_local(word.clone(), value); } Inst::Call => match self.stack_pop()? { Value::Quote(quote) => { @@ -352,7 +355,10 @@ impl MachineBuilder { name: &str, fun: fn(&mut Machine, usize) -> Result, ) { - self.register_global(name, Value::BuiltinFn(BuiltinFn::new(Rc::new(fun)))); + self.register_global( + name, + Value::BuiltinFn(BuiltinFn::new(name.to_string(), Rc::new(fun))), + ); } fn register_global(&mut self, name: &str, value: Value) {