use crate::perr::ParseErr;
use crate::r8vm::{ArgSpec, RuntimeError, Traceback, TraceFrame};
use crate::r8vm::r8c::Op as R8C;
use crate::nkgc::SymID;
use crate::ast::Value;
use crate::fmt::LispFmt;
use crate::sym_db::{SymDB, SYM_DB};
use std::result;
use std::error;
use std::fmt;
use std::num::TryFromIntError;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Source {
pub file: Option<String>,
pub line: u32,
pub col: u32,
}
impl Source {
pub fn new(line: u32, col: u32) -> Source {
Source { file: None, line, col }
}
pub fn none() -> Source {
Source { file: None, line: 0, col: 0 }
}
pub fn with_file(mut self, file: String) -> Source {
self.file = Some(file);
self
}
pub fn is_none(&self) -> bool {
self.line == 0
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SourceRef<'a> {
pub file: Option<&'a str>,
pub line: u32,
pub col: u32,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ErrorKind {
IllegalInstruction { inst: R8C },
TypeError { expect: SymID, got: SymID, op: SymID, argn: u32 },
TypeNError { expect: Vec<SymID>, got: SymID, op: SymID, argn: u32 },
ArgTypeError { expect: Vec<SymID>, got: Vec<SymID>, op: SymID },
EnumError { expect: Vec<SymID>, got: SymID, op: SymID, argn: u32 },
ArgError { expect: ArgSpec, got_num: u32, op: SymID },
OutsideContext { op: SymID, ctx: SymID },
SyntaxError { msg: String },
LinkError { dst: String, src: usize },
IDError { id: usize },
ConversionError { from: &'static str,
to: &'static str,
val: String },
NotEnough { expect: usize,
got: usize,
op: &'static str },
SomeError { msg: String },
UndefinedFunction { name: SymID },
UndefinedVariable { var: SymID },
UndefinedFunctionString { name: String },
UndefinedVariableString { name: String },
ModuleLoadError { lib: SymID },
Unsupported { op: &'static str },
IllegalVariableDeclaration { decl: Value },
Traceback { tb: Box<Traceback> },
IndexError { idx: usize },
Exit { status: SymID },
IOError { kind: std::io::ErrorKind },
MissingFeature { flag: &'static str },
}
impl From<std::io::Error> for Error {
fn from(v: std::io::Error) -> Self {
Error { src: None, ty: ErrorKind::IOError { kind: v.kind() } }
}
}
impl From<ErrorKind> for Error {
fn from(v: ErrorKind) -> Self {
Error { src: None, ty: v }
}
}
impl From<Traceback> for Error {
fn from(v: Traceback) -> Self {
Error { src: v.err.src.clone(),
ty: ErrorKind::Traceback { tb: Box::new(v) } }
}
}
impl From<String> for Error {
fn from(msg: String) -> Self {
Error { src: None, ty: ErrorKind::SomeError { msg } }
}
}
impl From<&str> for Error {
fn from(msg: &str) -> Self {
Error { src: None,
ty: ErrorKind::SomeError { msg: msg.to_string() } }
}
}
impl From<RuntimeError> for Error {
fn from(v: RuntimeError) -> Self {
Error { src: Some(Source { line: v.line, col: 0, file: None }),
ty: ErrorKind::SomeError { msg: v.msg } }
}
}
impl From<Error> for RuntimeError {
fn from(v: Error) -> Self {
let msg = format!("{}", &v);
RuntimeError { line: v.src.map(|src| src.line).unwrap_or(0),
msg }
}
}
impl From<ErrorKind> for RuntimeError {
fn from(v: ErrorKind) -> Self {
let v: Error = v.into();
v.into()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Error {
pub src: Option<Source>,
pub ty: ErrorKind,
}
fn fmt_error(err: &Error, f: &mut fmt::Formatter<'_>, db: &dyn SymDB) -> fmt::Result {
use ErrorKind::*;
let nameof = |sym| db.name(sym);
let tostring = |v: &Value| v.to_string(db);
match &err.ty {
IllegalInstruction { inst } =>
write!(f, "Illegal instruction: <{}>", inst)?,
TypeError { expect, got, op, argn } =>
write!(f, "Type Error: Expected {} for argument {} of ({} ...), but got {}",
nameof(*expect), argn, nameof(*op), nameof(*got))?,
TypeNError { expect, got, op, argn } =>
write!(f, "Type Error: Expected one of {} for argument {} of ({} ...), but got {}",
{
expect.iter()
.map(|v| nameof(*v))
.collect::<Vec<_>>()
.join(", ")
},
argn, nameof(*op), nameof(*got))?,
EnumError { expect, got, op, argn } =>
write!(f, "Argument Error: Expected one of {:?} for argument {} of ({} ...), but got {}",
expect.iter().copied().map(nameof).collect::<Vec<_>>(),
argn, nameof(*op), nameof(*got))?,
ArgError { expect, got_num, op } => {
write!(f, "Argument Error: ({} ...) ", nameof(*op))?;
match expect {
ArgSpec { nargs, nopt: 0, rest: false, .. } =>
write!(f, "expected {} arguments, but got {}",
nargs, got_num)?,
ArgSpec { nargs, nopt, rest: false, .. } =>
write!(f, "expected from {} to {} arguments, but got {}",
nargs, nargs+nopt, got_num)?,
ArgSpec { nargs, rest: true, .. } =>
write!(f, "expected at least {} arguments, but got {}",
nargs, got_num)?,
}
}
ArgTypeError { expect, got, op } =>
write!(f, "Argument Error: ({} ...) expected ({}) but got ({})",
nameof(*op),
expect.iter().copied().map(nameof).collect::<Vec<_>>().join(" "),
got.iter().copied().map(nameof).collect::<Vec<_>>().join(" "))?,
OutsideContext { op, ctx } =>
write!(f, "Syntax Error: Operator {} not allowed outside of {} context",
nameof(*op), nameof(*ctx))?,
SyntaxError { msg } =>
write!(f, "Syntax Error: {}", msg)?,
ConversionError { from, to, val } =>
write!(f, "Conversion Error: Could not convert the {} value `{}' into {}",
from, val, to)?,
NotEnough { expect, got, op } =>
write!(f, "Stack Error: Operation `{}' expected {} elements, but got {}",
op, expect, got)?,
SomeError { msg } => write!(f, "Error: {}", msg)?,
UndefinedFunction { name } =>
write!(f, "Undefined Function: Virtual call to undefined function '{}'",
nameof(*name))?,
UndefinedVariable { var } =>
write!(f, "Undefined Variable: {}", nameof(*var))?,
Unsupported { op } =>
write!(f, "Unsupported operation: {}", op)?,
ModuleLoadError { lib } =>
write!(f, "Module Error: Unable to load module {}", nameof(*lib))?,
IllegalVariableDeclaration { decl } =>
write!(f, "Syntax Error: Illegal variable declaration: {}",
tostring(decl))?,
ErrorKind::Traceback { tb } => {
writeln!(f, "Traceback:")?;
for TraceFrame { src, func, args } in tb.frames.iter() {
write!(f, " - ({}", nameof(*func))?;
let mut it = args.iter().peekable();
if it.peek().is_some() {
write!(f, " ")?;
}
while let Some(arg) = it.next() {
write!(f, "{}", arg.lisp_to_string(db))?;
if it.peek().is_some() {
write!(f, " ")?;
}
}
write!(f, ")")?;
writeln!(f, " {}", src)?;
}
fmt_error(&tb.err, f, db)?;
},
ErrorKind::IndexError { idx } =>
write!(f, "Index Error: No such index {}", idx)?,
ErrorKind::Exit { status } =>
write!(f, "Exit: {}", nameof(*status))?,
ErrorKind::IOError { kind } => {
let err: std::io::Error = (*kind).into();
write!(f, "IOError: {}", err)?;
}
ErrorKind::MissingFeature { flag } =>
write!(f, "Missing Feature: {}", flag)?,
x => unimplemented!("{:?}", x),
}
if let Some(src) = &err.src {
if !src.is_none() {
write!(f, " {}", src)?;
}
} else {
}
Ok(())
}
impl Error {
pub fn with_src(mut self, src: Source) -> Error {
self.src = Some(src);
self
}
pub fn op(mut self, new_op: SymID) -> Error {
match &mut self.ty {
ErrorKind::TypeError { ref mut op, .. } => *op = new_op,
ErrorKind::TypeNError { ref mut op, .. } => *op = new_op,
ErrorKind::ArgTypeError { ref mut op, .. } => *op = new_op,
ErrorKind::EnumError { ref mut op, .. } => *op = new_op,
ErrorKind::ArgError { ref mut op, .. } => *op = new_op,
ErrorKind::OutsideContext { ref mut op, .. } => *op = new_op,
_ => (),
}
self
}
pub fn argn(mut self, n: u32) -> Error {
match &mut self.ty {
ErrorKind::TypeError { ref mut argn, .. } => *argn = n,
ErrorKind::TypeNError { ref mut argn, .. } => *argn = n,
_ => ()
}
self
}
pub fn cause(&self) -> &Error {
match &self.ty {
ErrorKind::Traceback { tb } => {
&tb.err
},
_ => self
}
}
pub fn to_string(&self, db: &dyn SymDB) -> String {
format!("{}", {
struct PVFmtWrap<'a, 'b> {
val: &'b Error,
db: &'a dyn SymDB
}
impl fmt::Display for PVFmtWrap<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_error(self.val, f, self.db)
}
}
PVFmtWrap { val: self, db }
})
}
}
impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> result::Result<(), fmt::Error> {
fmt_error(&Error { ty: self.clone(), src: None }, f, &SYM_DB)
}
}
impl fmt::Display for Source {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> result::Result<(), fmt::Error> {
write!(f, "[@")?;
if self.line == 0 {
write!(f, "unknown")?;
} else {
if let Some(file) = &self.file {
write!(f, "{}-", file)?;
}
write!(f, "{}:{}", self.line, self.col)?;
}
write!(f, "]")
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> result::Result<(), fmt::Error> {
fmt_error(self, f, &SYM_DB)
}
}
impl error::Error for ErrorKind {}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(&self.ty)
}
}
impl From<TryFromIntError> for Error {
fn from(err: TryFromIntError) -> Self {
Error {
src: None,
ty: ErrorKind::SomeError { msg: err.to_string() }
}
}
}
impl From<std::convert::Infallible> for Error {
fn from(_: std::convert::Infallible) -> Self {
unreachable!();
}
}
impl From<ParseErr> for Error {
fn from(perr: ParseErr) -> Self {
Error {
src: Some(Source {
file: None,
line: perr.line as u32,
col: perr.col as u32,
}),
ty: ErrorKind::SyntaxError { msg: perr.msg }
}
}
}
macro_rules! err {
($kind:ident, $($init:tt)* ) => {
Err((crate::error::ErrorKind::$kind { $($init)* }).into())
};
}
macro_rules! error {
($kind:ident, $($init:tt)* ) => {
crate::error::Error {
src: None,
ty: crate::error::ErrorKind::$kind { $($init)* },
}
};
}
macro_rules! err_src {
($src:expr, $kind:ident, $($init:tt)* ) => {
Err(crate::error::Error { src: Some($src), ty: (crate::error::ErrorKind::$kind { $($init)* }) })
};
}
macro_rules! error_src {
($src:expr, $kind:ident, $($init:tt)* ) => {
crate::error::Error { src: Some($src), ty: (crate::error::ErrorKind::$kind { $($init)* }) }
};
}