Remove RuntimeSpanError and just incorporate it into RuntimeError; add Span information to compiled instructions

* RuntimeSpanError was an extraneous solution to adding spans to the
  errors; so instead this behavior has been delegated to the
  RuntimeError itself.
* Usage of Inst has mostly been replaced with SpInst so if an error is
  encountered, we can spit out where it happened in the source code
  (hopefully with a stack trace).

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2022-01-16 16:49:54 -08:00
parent 67054b2c84
commit 1eb7eb73cb
6 changed files with 64 additions and 85 deletions

View File

@@ -17,7 +17,7 @@ impl<'s> Compile<'s> {
} }
} }
pub fn compile(&mut self, stmt_list: Vec<SpStmt>) -> Vec<Inst> { pub fn compile(&mut self, stmt_list: Vec<SpStmt>) -> Vec<SpInst> {
// Compile meta-statements // Compile meta-statements
let expr_list = self.compile_meta(stmt_list); let expr_list = self.compile_meta(stmt_list);
self.compile_expr_list(&expr_list) self.compile_expr_list(&expr_list)
@@ -42,7 +42,7 @@ impl<'s> Compile<'s> {
todo!("includes") todo!("includes")
} }
fn compile_expr_list(&mut self, expr_list: &Vec<SpExpr>) -> Vec<Inst> { fn compile_expr_list(&mut self, expr_list: &Vec<SpExpr>) -> Vec<SpInst> {
// Scoping is done here. // Scoping is done here.
// Local scopes are implicit. If a variable is assigned to at // Local scopes are implicit. If a variable is assigned to at
// all in the current scope, it's considered to be a local. // all in the current scope, it's considered to be a local.
@@ -67,19 +67,20 @@ impl<'s> Compile<'s> {
let quote = let quote =
self.quote_table self.quote_table
.insert(expr.span().clone(), locals, stmts.clone(), compiled); .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 { fn compile_atom(&mut self, atom: &SpAtom) -> Thunk {
match atom.inner() { let inst = match atom.inner() {
Atom::Float(f) => Inst::PushValue(Value::Float(*f)).into(), Atom::Float(f) => Inst::PushValue(Value::Float(*f)),
Atom::Int(i) => Inst::PushValue(Value::Int(*i)).into(), Atom::Int(i) => Inst::PushValue(Value::Int(*i)),
Atom::Str(s) => Inst::PushValue(Value::Str(s.clone())).into(), Atom::Str(s) => Inst::PushValue(Value::Str(s.clone())),
Atom::Assign(text) => { Atom::Assign(text) => {
let word = self.scope_stack.insert_local(text); let word = self.scope_stack.insert_local(text);
Inst::Store(word).into() Inst::Store(word)
} }
Atom::Word(text) => { Atom::Word(text) => {
// Look for locally defined symbols first. One can't be found, // Look for locally defined symbols first. One can't be found,
@@ -90,10 +91,11 @@ impl<'s> Compile<'s> {
} else { } else {
self.scope_stack.insert_local(text) 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. /// Discovers and inserts local variables into the scope.
@@ -111,18 +113,18 @@ impl<'s> Compile<'s> {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum Thunk { enum Thunk {
Block(Vec<Inst>), Block(Vec<SpInst>),
List(Vec<Thunk>), List(Vec<Thunk>),
} }
impl From<Inst> for Thunk { impl From<SpInst> for Thunk {
fn from(inst: Inst) -> Thunk { fn from(inst: SpInst) -> Thunk {
Thunk::Block(vec![inst]) Thunk::Block(vec![inst])
} }
} }
impl Thunk { impl Thunk {
fn flatten(self) -> Vec<Inst> { fn flatten(self) -> Vec<SpInst> {
use Thunk::*; use Thunk::*;
match self { match self {
Block(block) => block, Block(block) => block,

View File

@@ -8,7 +8,7 @@ use std::io::Read;
use std::path::PathBuf; use std::path::PathBuf;
use structopt::StructOpt; use structopt::StructOpt;
use syn::parser::Parser; use syn::parser::Parser;
use vm::{error::RuntimeSpanError, machine::MachineBuilder}; use vm::{error::RuntimeError, machine::MachineBuilder};
#[derive(Debug, StructOpt)] #[derive(Debug, StructOpt)]
struct Opt { struct Opt {
@@ -48,6 +48,7 @@ fn main() -> Result {
if let Err(e) = machine.eval(stmts) { if let Err(e) = machine.eval(stmts) {
// print error message // print error message
println!("Error traceback:");
print_error(&e); print_error(&e);
Ok(()) Ok(())
} else { } else {
@@ -55,12 +56,12 @@ fn main() -> Result {
} }
} }
fn print_error(error: &RuntimeSpanError) { fn print_error(error: &RuntimeError) {
match error { match error {
RuntimeSpanError::Span(span, error) => { RuntimeError::Span(_, inner) => {
println!("at {}", span); println!("{}", error);
print_error(error) print_error(inner)
} }
RuntimeSpanError::Error(error) => println!("{}", error), _ => println!(" {}", error),
} }
} }

View File

@@ -5,7 +5,7 @@
use crate::syn::ast::SpStmt; use crate::syn::ast::SpStmt;
use crate::vm::{error::Result, machine::Machine}; 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::cell::RefCell;
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use std::fmt::{self, Debug, Display}; use std::fmt::{self, Debug, Display};
@@ -33,7 +33,7 @@ impl Quote {
/// A table of compiled quotes, their expression trees, and their spans. /// A table of compiled quotes, their expression trees, and their spans.
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct QuoteTable { pub struct QuoteTable {
table: Vec<(Span, Scope, Vec<SpStmt>, Rc<Vec<Inst>>)>, table: Vec<(Span, Scope, Vec<SpStmt>, Rc<Vec<SpInst>>)>,
} }
impl QuoteTable { impl QuoteTable {
@@ -46,14 +46,14 @@ impl QuoteTable {
span: Span, span: Span,
scope: Scope, scope: Scope,
quote: Vec<SpStmt>, quote: Vec<SpStmt>,
compiled: Rc<Vec<Inst>>, compiled: Rc<Vec<SpInst>>,
) -> Quote { ) -> Quote {
let next = Quote(self.table.len()); let next = Quote(self.table.len());
self.table.push((span, scope, quote, compiled)); self.table.push((span, scope, quote, compiled));
next next
} }
pub fn get(&self, quote: Quote) -> &(Span, Scope, Vec<SpStmt>, Rc<Vec<Inst>>) { pub fn get(&self, quote: Quote) -> &(Span, Scope, Vec<SpStmt>, Rc<Vec<SpInst>>) {
&self.table[quote.0] &self.table[quote.0]
} }
} }
@@ -136,15 +136,21 @@ pub enum BuiltinExit {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct BuiltinFn(Rc<BuiltinFnPtr>); pub struct BuiltinFn {
name: Rc<String>,
fun: Rc<BuiltinFnPtr>,
}
impl BuiltinFn { impl BuiltinFn {
pub fn new(fun: Rc<BuiltinFnPtr>) -> Self { pub fn new(name: String, fun: Rc<BuiltinFnPtr>) -> Self {
BuiltinFn(fun) BuiltinFn {
name: Rc::new(name),
fun,
}
} }
pub fn call(&self, machine: &mut Machine, reentry: usize) -> Result<BuiltinExit> { pub fn call(&self, machine: &mut Machine, reentry: usize) -> Result<BuiltinExit> {
(self.0)(machine, reentry) (self.fun)(machine, reentry)
} }
} }
@@ -156,7 +162,7 @@ impl Debug for BuiltinFn {
write!( write!(
fmt, fmt,
"builtin function at {:#x}", "builtin function at {:#x}",
(&self.0 as *const _ as usize) (&self.fun as *const _ as usize)
) )
} }
} }

View File

@@ -14,14 +14,12 @@ pub enum RuntimeError {
#[error("cannot call non-quote value '{0}'")] #[error("cannot call non-quote value '{0}'")]
CannotCall(String), CannotCall(String),
#[error("at {0}")]
Span(Span, Box<RuntimeError>),
} }
pub type Result<T, E = RuntimeSpanError> = std::result::Result<T, E>; pub type Result<T, E = RuntimeError> = std::result::Result<T, E>;
pub enum RuntimeSpanError {
Span(Span, Box<RuntimeSpanError>),
Error(RuntimeError),
}
/// Add a location to the implemented value. /// Add a location to the implemented value.
pub trait WithLocation { pub trait WithLocation {
@@ -29,12 +27,12 @@ pub trait WithLocation {
fn with_location(self, span: Span) -> Self::Out; fn with_location(self, span: Span) -> Self::Out;
} }
impl<T> WithLocation for Result<T, RuntimeSpanError> { impl<T> WithLocation for Result<T, RuntimeError> {
type Out = Result<T, RuntimeSpanError>; type Out = Result<T, RuntimeError>;
fn with_location(self, span: Span) -> Self::Out { fn with_location(self, span: Span) -> Self::Out {
if let Err(e) = self { if let Err(e) = self {
Err(RuntimeSpanError::Span(span, Box::new(e))) Err(e.with_location(span))
} else { } else {
self self
} }
@@ -42,36 +40,9 @@ impl<T> WithLocation for Result<T, RuntimeSpanError> {
} }
impl WithLocation for RuntimeError { impl WithLocation for RuntimeError {
type Out = RuntimeSpanError; type Out = RuntimeError;
fn with_location(self, span: Span) -> Self::Out { fn with_location(self, span: Span) -> Self::Out {
RuntimeSpanError::Span(span, Box::new(self.into())) RuntimeError::Span(span, Box::new(self))
} }
} }
impl From<RuntimeError> 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);
}
...
}
*/

View File

@@ -1,5 +1,6 @@
use crate::object::Value; use crate::object::Value;
use crate::scope::Word; use crate::scope::Word;
use crate::syn::span::Spanned;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Inst { pub enum Inst {
@@ -18,12 +19,4 @@ pub enum Inst {
Call, Call,
} }
// TODO - do we want separate function definition syntax? We would be able to pub type SpInst = Spanned<Inst>;
// 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"

View File

@@ -34,12 +34,12 @@ pub struct NativeFrame {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct QuoteFrame { pub struct QuoteFrame {
locals: BTreeMap<Word, Option<Value>>, locals: BTreeMap<Word, Option<Value>>,
code: Rc<Vec<Inst>>, code: Rc<Vec<SpInst>>,
pc: usize, pc: usize,
} }
impl QuoteFrame { impl QuoteFrame {
pub fn inst(&self) -> Option<&Inst> { pub fn inst(&self) -> Option<&SpInst> {
self.code.get(self.pc) self.code.get(self.pc)
} }
} }
@@ -176,6 +176,8 @@ impl Machine {
// ///////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////
pub fn eval(&mut self, stmts: Vec<SpStmt>) -> Result<()> { pub fn eval(&mut self, stmts: Vec<SpStmt>) -> 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(); self.scope_stack.push_scope();
let mut compile = Compile::new(&mut self.scope_stack, &mut self.quote_table); let mut compile = Compile::new(&mut self.scope_stack, &mut self.quote_table);
let code = Rc::new(compile.compile(stmts)); let code = Rc::new(compile.compile(stmts));
@@ -200,7 +202,8 @@ impl Machine {
let inst = frame.inst(); let inst = frame.inst();
if let Some(inst) = inst { if let Some(inst) = inst {
let inst = inst.clone(); let inst = inst.clone();
self.eval_inst(inst)?; let span = inst.span().clone();
self.eval_inst(inst).with_location(span)?;
} else { } else {
self.call_stack.pop(); self.call_stack.pop();
} }
@@ -242,11 +245,11 @@ impl Machine {
native.call(self, reentry) 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 current_frame = self.call_stack.len() - 1;
let next_pc = self.pc().unwrap() + 1; let next_pc = self.pc().unwrap() + 1;
match inst { match inst.inner() {
Inst::PushValue(value) => self.stack_push(value)?, Inst::PushValue(value) => self.stack_push(value.clone())?,
Inst::Load(word) => { Inst::Load(word) => {
let value = self let value = self
.lookup_word(&word) .lookup_word(&word)
@@ -258,7 +261,7 @@ impl Machine {
} }
Inst::Store(word) => { Inst::Store(word) => {
let value = self.stack_pop()?; let value = self.stack_pop()?;
self.store_local(word, value); self.store_local(word.clone(), value);
} }
Inst::Call => match self.stack_pop()? { Inst::Call => match self.stack_pop()? {
Value::Quote(quote) => { Value::Quote(quote) => {
@@ -352,7 +355,10 @@ impl MachineBuilder {
name: &str, name: &str,
fun: fn(&mut Machine, usize) -> Result<BuiltinExit>, fun: fn(&mut Machine, usize) -> Result<BuiltinExit>,
) { ) {
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) { fn register_global(&mut self, name: &str, value: Value) {