Engine::register_type_name for nice error messages for arbitrary types

This commit is contained in:
steve donovan 2018-07-22 11:43:21 +02:00
parent 2bf3f5e957
commit 3fb2f543bb

View File

@ -22,7 +22,7 @@ pub enum EvalAltResult {
ErrorVariableNotFound(String), ErrorVariableNotFound(String),
ErrorFunctionArityNotSupported, ErrorFunctionArityNotSupported,
ErrorAssignmentToUnknownLHS, ErrorAssignmentToUnknownLHS,
ErrorMismatchOutputType, ErrorMismatchOutputType(String),
ErrorCantOpenScriptFile, ErrorCantOpenScriptFile,
InternalErrorMalformedDotExpression, InternalErrorMalformedDotExpression,
LoopBreak, LoopBreak,
@ -34,6 +34,7 @@ impl EvalAltResult {
match *self { match *self {
EvalAltResult::ErrorVariableNotFound(ref s) => Some(s.as_str()), EvalAltResult::ErrorVariableNotFound(ref s) => Some(s.as_str()),
EvalAltResult::ErrorFunctionNotFound(ref s) => Some(s.as_str()), EvalAltResult::ErrorFunctionNotFound(ref s) => Some(s.as_str()),
EvalAltResult::ErrorMismatchOutputType(ref s) => Some(s.as_str()),
_ => None _ => None
} }
} }
@ -52,7 +53,7 @@ impl PartialEq for EvalAltResult {
(&ErrorVariableNotFound(ref a), &ErrorVariableNotFound(ref b)) => a == b, (&ErrorVariableNotFound(ref a), &ErrorVariableNotFound(ref b)) => a == b,
(&ErrorFunctionArityNotSupported, &ErrorFunctionArityNotSupported) => true, (&ErrorFunctionArityNotSupported, &ErrorFunctionArityNotSupported) => true,
(&ErrorAssignmentToUnknownLHS, &ErrorAssignmentToUnknownLHS) => true, (&ErrorAssignmentToUnknownLHS, &ErrorAssignmentToUnknownLHS) => true,
(&ErrorMismatchOutputType, &ErrorMismatchOutputType) => true, (&ErrorMismatchOutputType(ref a), &ErrorMismatchOutputType(ref b)) => a == b,
(&ErrorCantOpenScriptFile, &ErrorCantOpenScriptFile) => true, (&ErrorCantOpenScriptFile, &ErrorCantOpenScriptFile) => true,
(&InternalErrorMalformedDotExpression, &InternalErrorMalformedDotExpression) => true, (&InternalErrorMalformedDotExpression, &InternalErrorMalformedDotExpression) => true,
(&LoopBreak, &LoopBreak) => true, (&LoopBreak, &LoopBreak) => true,
@ -78,7 +79,7 @@ impl Error for EvalAltResult {
EvalAltResult::ErrorAssignmentToUnknownLHS => { EvalAltResult::ErrorAssignmentToUnknownLHS => {
"Assignment to an unsupported left-hand side" "Assignment to an unsupported left-hand side"
} }
EvalAltResult::ErrorMismatchOutputType => "Cast of output failed", EvalAltResult::ErrorMismatchOutputType(_) => "Cast of output failed",
EvalAltResult::ErrorCantOpenScriptFile => "Cannot open script file", EvalAltResult::ErrorCantOpenScriptFile => "Cannot open script file",
EvalAltResult::InternalErrorMalformedDotExpression => { EvalAltResult::InternalErrorMalformedDotExpression => {
"[Internal error] Unexpected expression in dot expression" "[Internal error] Unexpected expression in dot expression"
@ -123,10 +124,11 @@ pub struct FnSpec {
/// } /// }
/// } /// }
/// ``` /// ```
#[derive(Clone)] // #[derive(Clone)]
pub struct Engine { pub struct Engine {
/// A hashmap containing all functions known to the engine /// A hashmap containing all functions known to the engine
pub fns: HashMap<FnSpec, Arc<FnIntExt>>, pub fns: HashMap<FnSpec, Arc<FnIntExt>>,
pub type_names: HashMap<TypeId,String>,
} }
pub enum FnIntExt { pub enum FnIntExt {
@ -163,7 +165,7 @@ impl Engine {
.and_then(|b| { .and_then(|b| {
b.downcast() b.downcast()
.map(|b| *b) .map(|b| *b)
.map_err(|_| EvalAltResult::ErrorMismatchOutputType) .map_err(|a| EvalAltResult::ErrorMismatchOutputType(self.nice_type_name(a)))
}) })
} }
@ -228,6 +230,13 @@ impl Engine {
// currently a no-op, exists for future extensibility // currently a no-op, exists for future extensibility
} }
/// Register a type, providing a name for nice error messages.
pub fn register_type_name<T: Any>(&mut self, name: &str) {
self.register_type::<T>();
debug_println!("register type {}: {:?}", name, TypeId::of::<T>());
self.type_names.insert(TypeId::of::<T>(), name.into());
}
/// Register a get function for a member of a registered type /// Register a get function for a member of a registered type
pub fn register_get<T: Clone + Any, U: Clone + Any, F>(&mut self, name: &str, get_fn: F) pub fn register_get<T: Clone + Any, U: Clone + Any, F>(&mut self, name: &str, get_fn: F)
where where
@ -613,17 +622,9 @@ impl Engine {
} }
fn nice_type_name(&self, b: Box<Any>) -> String { fn nice_type_name(&self, b: Box<Any>) -> String {
if b.is::<String>() { let tid = (&*b).type_id();
"string".into() if let Some(name) = self.type_names.get(&tid) {
} else name.to_string()
if b.is::<i64>() {
"integer".into()
} else
if b.is::<f64>() {
"float".into()
} else
if b.is::<Vec<Box<Any>>>() {
"array".into()
} else { } else {
format!("<unknown> {:?}", b.type_id()) format!("<unknown> {:?}", b.type_id())
} }
@ -692,7 +693,7 @@ impl Engine {
match x.downcast::<T>() { match x.downcast::<T>() {
Ok(out) => Ok(*out), Ok(out) => Ok(*out),
Err(_) => Err(EvalAltResult::ErrorMismatchOutputType), Err(a) => Err(EvalAltResult::ErrorMismatchOutputType(self.nice_type_name(a))),
} }
} }
Err(_) => Err(EvalAltResult::ErrorFunctionArgMismatch), Err(_) => Err(EvalAltResult::ErrorFunctionArgMismatch),
@ -775,15 +776,17 @@ impl Engine {
/// Register the default library. That means, numberic types, char, bool /// Register the default library. That means, numberic types, char, bool
/// String, arithmetics and string concatenations. /// String, arithmetics and string concatenations.
pub fn register_default_lib(engine: &mut Engine) { pub fn register_default_lib(engine: &mut Engine) {
engine.register_type::<i32>(); engine.register_type_name::<i32>("i32");
engine.register_type::<u32>(); engine.register_type_name::<u32>("u32");
engine.register_type::<i64>(); engine.register_type_name::<i64>("integer");
engine.register_type::<u64>(); engine.register_type_name::<u64>("u64");
engine.register_type::<f32>(); engine.register_type_name::<u64>("usize");
engine.register_type::<f64>(); engine.register_type_name::<f32>("f64");
engine.register_type::<String>(); engine.register_type_name::<f64>("float");
engine.register_type::<char>(); engine.register_type_name::<String>("string");
engine.register_type::<bool>(); engine.register_type_name::<char>("char");
engine.register_type_name::<bool>("boolean");
engine.register_type_name::<Vec<Box<Any>>>("array");
macro_rules! reg_op { macro_rules! reg_op {
($engine:expr, $x:expr, $op:expr, $( $y:ty ),*) => ( ($engine:expr, $x:expr, $op:expr, $( $y:ty ),*) => (
@ -871,12 +874,14 @@ impl Engine {
// FIXME? Registering array lookups are a special case because we want to return boxes // FIXME? Registering array lookups are a special case because we want to return boxes
// directly let ent = engine.fns.entry("[]".to_string()).or_insert_with(Vec::new); // directly let ent = engine.fns.entry("[]".to_string()).or_insert_with(Vec::new);
// (*ent).push(FnType::ExternalFn2(Box::new(idx))); // (*ent).push(FnType::ExternalFn2(Box::new(idx)));
} }
/// Make a new engine /// Make a new engine
pub fn new() -> Engine { pub fn new() -> Engine {
let mut engine = Engine { let mut engine = Engine {
fns: HashMap::new(), fns: HashMap::new(),
type_names: HashMap::new(),
}; };
Engine::register_default_lib(&mut engine); Engine::register_default_lib(&mut engine);