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:
@@ -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,
|
||||||
|
|||||||
13
src/main.rs
13
src/main.rs
@@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|||||||
@@ -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"
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user