diff --git a/src/api.rs b/src/api.rs index 4b66d55d..84cefab4 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,7 +1,6 @@ -use crate::any::{Any, AnyExt}; -use crate::engine::{FnIntExt, FnSpec}; -use crate::parser::{lex, parse}; -use crate::{Dynamic, Engine, EvalAltResult, ParseError, Scope, AST}; +use crate::any::{Any, AnyExt, Dynamic}; +use crate::engine::{Engine, EvalAltResult, FnIntExt, FnSpec, Scope}; +use crate::parser::{lex, parse, ParseError, Position, AST}; use std::sync::Arc; impl Engine { @@ -88,9 +87,14 @@ impl Engine { self.script_fns.clear(); // Clean up engine match result { - Err(EvalAltResult::Return(out)) | Ok(out) => Ok(*out + Err(EvalAltResult::Return(out, pos)) => Ok(*out .downcast::() - .map_err(|err| EvalAltResult::ErrorMismatchOutputType((*err).type_name()))?), + .map_err(|a| EvalAltResult::ErrorMismatchOutputType((*a).type_name(), pos))?), + + Ok(out) => Ok(*out.downcast::().map_err(|a| { + EvalAltResult::ErrorMismatchOutputType((*a).type_name(), Position::eof()) + })?), + Err(err) => Err(err), } } diff --git a/src/engine.rs b/src/engine.rs index 156fa342..7818d96a 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use crate::any::{Any, AnyExt, Dynamic, Variant}; use crate::call::FunArgs; use crate::fn_register::RegisterFn; -use crate::parser::{Expr, FnDef, ParseError, Stmt}; +use crate::parser::{Expr, FnDef, ParseError, Position, Stmt}; pub type Array = Vec; pub type FnCallArgs<'a> = Vec<&'a mut Variant>; @@ -15,101 +15,58 @@ pub type FnCallArgs<'a> = Vec<&'a mut Variant>; #[derive(Debug)] pub enum EvalAltResult { ErrorParsing(ParseError), - ErrorFunctionNotFound(String), - ErrorFunctionArgsMismatch(String, usize), - ErrorBooleanArgMismatch(String), - ErrorArrayBounds(usize, i64), - ErrorStringBounds(usize, i64), - ErrorIndexing, - ErrorIndexExpr, - ErrorIfGuard, - ErrorFor, - ErrorVariableNotFound(String), - ErrorAssignmentToUnknownLHS, - ErrorMismatchOutputType(String), + ErrorFunctionNotFound(String, Position), + ErrorFunctionArgsMismatch(String, usize, Position), + ErrorBooleanArgMismatch(String, Position), + ErrorArrayBounds(usize, i64, Position), + ErrorStringBounds(usize, i64, Position), + ErrorIndexing(Position), + ErrorIndexExpr(Position), + ErrorIfGuard(Position), + ErrorFor(Position), + ErrorVariableNotFound(String, Position), + ErrorAssignmentToUnknownLHS(Position), + ErrorMismatchOutputType(String, Position), ErrorCantOpenScriptFile(String, std::io::Error), - ErrorDotExpr, - ErrorArithmetic(String), + ErrorDotExpr(Position), + ErrorArithmetic(String, Position), LoopBreak, - Return(Dynamic), -} - -impl EvalAltResult { - fn as_str(&self) -> Option<&str> { - Some(match self { - Self::ErrorVariableNotFound(s) - | Self::ErrorFunctionNotFound(s) - | Self::ErrorMismatchOutputType(s) - | Self::ErrorArithmetic(s) => s, - _ => return None, - }) - } -} - -impl PartialEq for EvalAltResult { - fn eq(&self, other: &Self) -> bool { - use EvalAltResult::*; - - match (self, other) { - (ErrorParsing(a), ErrorParsing(b)) => a == b, - (ErrorFunctionNotFound(a), ErrorFunctionNotFound(b)) => a == b, - (ErrorFunctionArgsMismatch(f1, n1), ErrorFunctionArgsMismatch(f2, n2)) => { - f1 == f2 && *n1 == *n2 - } - (ErrorBooleanArgMismatch(a), ErrorBooleanArgMismatch(b)) => a == b, - (ErrorIndexExpr, ErrorIndexExpr) => true, - (ErrorIndexing, ErrorIndexing) => true, - (ErrorArrayBounds(max1, index1), ErrorArrayBounds(max2, index2)) => { - max1 == max2 && index1 == index2 - } - (ErrorStringBounds(max1, index1), ErrorStringBounds(max2, index2)) => { - max1 == max2 && index1 == index2 - } - (ErrorIfGuard, ErrorIfGuard) => true, - (ErrorFor, ErrorFor) => true, - (ErrorVariableNotFound(a), ErrorVariableNotFound(b)) => a == b, - (ErrorAssignmentToUnknownLHS, ErrorAssignmentToUnknownLHS) => true, - (ErrorMismatchOutputType(a), ErrorMismatchOutputType(b)) => a == b, - (ErrorCantOpenScriptFile(a, _), ErrorCantOpenScriptFile(b, _)) => a == b, - (ErrorDotExpr, ErrorDotExpr) => true, - (ErrorArithmetic(a), ErrorArithmetic(b)) => a == b, - (LoopBreak, LoopBreak) => true, - _ => false, - } - } + Return(Dynamic, Position), } impl Error for EvalAltResult { fn description(&self) -> &str { match self { Self::ErrorParsing(p) => p.description(), - Self::ErrorFunctionNotFound(_) => "Function not found", - Self::ErrorFunctionArgsMismatch(_, _) => "Function call with wrong number of arguments", - Self::ErrorBooleanArgMismatch(_) => "Boolean operator expects boolean operands", - Self::ErrorIndexExpr => "Indexing into an array or string expects an integer index", - Self::ErrorIndexing => "Indexing can only be performed on an array or a string", - Self::ErrorArrayBounds(_, index) if *index < 0 => { + Self::ErrorFunctionNotFound(_, _) => "Function not found", + Self::ErrorFunctionArgsMismatch(_, _, _) => { + "Function call with wrong number of arguments" + } + Self::ErrorBooleanArgMismatch(_, _) => "Boolean operator expects boolean operands", + Self::ErrorIndexExpr(_) => "Indexing into an array or string expects an integer index", + Self::ErrorIndexing(_) => "Indexing can only be performed on an array or a string", + Self::ErrorArrayBounds(_, index, _) if *index < 0 => { "Array access expects non-negative index" } - Self::ErrorArrayBounds(max, _) if *max == 0 => "Access of empty array", - Self::ErrorArrayBounds(_, _) => "Array index out of bounds", - Self::ErrorStringBounds(_, index) if *index < 0 => { + Self::ErrorArrayBounds(max, _, _) if *max == 0 => "Access of empty array", + Self::ErrorArrayBounds(_, _, _) => "Array index out of bounds", + Self::ErrorStringBounds(_, index, _) if *index < 0 => { "Indexing a string expects a non-negative index" } - Self::ErrorStringBounds(max, _) if *max == 0 => "Indexing of empty string", - Self::ErrorStringBounds(_, _) => "String index out of bounds", - Self::ErrorIfGuard => "If guards expect boolean expression", - Self::ErrorFor => "For loops expect array", - Self::ErrorVariableNotFound(_) => "Variable not found", - Self::ErrorAssignmentToUnknownLHS => { + Self::ErrorStringBounds(max, _, _) if *max == 0 => "Indexing of empty string", + Self::ErrorStringBounds(_, _, _) => "String index out of bounds", + Self::ErrorIfGuard(_) => "If guard expects boolean expression", + Self::ErrorFor(_) => "For loop expects array or range", + Self::ErrorVariableNotFound(_, _) => "Variable not found", + Self::ErrorAssignmentToUnknownLHS(_) => { "Assignment to an unsupported left-hand side expression" } - Self::ErrorMismatchOutputType(_) => "Output type is incorrect", + Self::ErrorMismatchOutputType(_, _) => "Output type is incorrect", Self::ErrorCantOpenScriptFile(_, _) => "Cannot open script file", - Self::ErrorDotExpr => "Malformed dot expression", - Self::ErrorArithmetic(_) => "Arithmetic error", + Self::ErrorDotExpr(_) => "Malformed dot expression", + Self::ErrorArithmetic(_, _) => "Arithmetic error", Self::LoopBreak => "[Not Error] Breaks out of loop", - Self::Return(_) => "[Not Error] Function returns value", + Self::Return(_, _) => "[Not Error] Function returns value", } } @@ -120,35 +77,44 @@ impl Error for EvalAltResult { impl std::fmt::Display for EvalAltResult { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if let Some(s) = self.as_str() { - write!(f, "{}: {}", self.description(), s) - } else { - match self { - Self::ErrorCantOpenScriptFile(filename, err) => { - write!(f, "Cannot open script file '{}': {}", filename, err) - } - Self::ErrorParsing(p) => write!(f, "Syntax error: {}", p), - Self::ErrorFunctionArgsMismatch(fun, n) => { - write!(f, "Function '{}' expects {} argument(s)", fun, n) - } - Self::ErrorBooleanArgMismatch(op) => { - write!(f, "Boolean {} operator expects boolean operands", op) - } - Self::ErrorArrayBounds(_, index) if *index < 0 => { - write!(f, "{}: {} < 0", self.description(), index) - } - Self::ErrorArrayBounds(max, _) if *max == 0 => write!(f, "{}", self.description()), - Self::ErrorArrayBounds(max, index) => { - write!(f, "{} (max {}): {}", self.description(), max - 1, index) - } - Self::ErrorStringBounds(_, index) if *index < 0 => { - write!(f, "{}: {} < 0", self.description(), index) - } - Self::ErrorStringBounds(max, _) if *max == 0 => write!(f, "{}", self.description()), - Self::ErrorStringBounds(max, index) => { - write!(f, "{} (max {}): {}", self.description(), max - 1, index) - } - err => write!(f, "{}", err.description()), + let desc = self.description(); + + match self { + Self::ErrorFunctionNotFound(s, pos) => write!(f, "{}: '{}' ({})", desc, s, pos), + Self::ErrorVariableNotFound(s, pos) => write!(f, "{}: '{}' ({})", desc, s, pos), + Self::ErrorIndexing(pos) => write!(f, "{} ({})", desc, pos), + Self::ErrorIndexExpr(pos) => write!(f, "{} ({})", desc, pos), + Self::ErrorIfGuard(pos) => write!(f, "{} ({})", desc, pos), + Self::ErrorFor(pos) => write!(f, "{} ({})", desc, pos), + Self::ErrorAssignmentToUnknownLHS(pos) => write!(f, "{} ({})", desc, pos), + Self::ErrorMismatchOutputType(s, pos) => write!(f, "{}: {} ({})", desc, s, pos), + Self::ErrorDotExpr(pos) => write!(f, "{} ({})", desc, pos), + Self::ErrorArithmetic(s, pos) => write!(f, "{}: {} ({})", desc, s, pos), + Self::LoopBreak => write!(f, "{}", desc), + Self::Return(_, pos) => write!(f, "{} ({})", desc, pos), + Self::ErrorCantOpenScriptFile(filename, err) => { + write!(f, "{} '{}': {}", desc, filename, err) + } + Self::ErrorParsing(p) => write!(f, "Syntax error: {}", p), + Self::ErrorFunctionArgsMismatch(fun, n, pos) => { + write!(f, "Function '{}' expects {} argument(s) ({})", fun, n, pos) + } + Self::ErrorBooleanArgMismatch(op, pos) => { + write!(f, "{} operator expects boolean operands ({})", op, pos) + } + Self::ErrorArrayBounds(_, index, pos) if *index < 0 => { + write!(f, "{}: {} < 0 ({})", desc, index, pos) + } + Self::ErrorArrayBounds(max, _, pos) if *max == 0 => write!(f, "{} ({})", desc, pos), + Self::ErrorArrayBounds(max, index, pos) => { + write!(f, "{} (max {}): {} ({})", desc, max - 1, index, pos) + } + Self::ErrorStringBounds(_, index, pos) if *index < 0 => { + write!(f, "{}: {} < 0 ({})", desc, index, pos) + } + Self::ErrorStringBounds(max, _, pos) if *max == 0 => write!(f, "{} ({})", desc, pos), + Self::ErrorStringBounds(max, index, pos) => { + write!(f, "{} (max {}): {} ({})", desc, max - 1, index, pos) } } } @@ -193,7 +159,7 @@ pub enum FnIntExt { Int(FnDef), } -pub type FnAny = dyn Fn(FnCallArgs) -> Result; +pub type FnAny = dyn Fn(FnCallArgs, Position) -> Result; /// A type containing information about current scope. /// Useful for keeping state between `Engine` runs @@ -218,11 +184,13 @@ impl Engine { A: FunArgs<'a>, T: Any + Clone, { - self.call_fn_raw(ident.into(), args.into_vec(), None) + let pos = Position { line: 0, pos: 0 }; + + self.call_fn_raw(ident.into(), args.into_vec(), None, pos) .and_then(|b| { b.downcast() .map(|b| *b) - .map_err(|a| EvalAltResult::ErrorMismatchOutputType((*a).type_name())) + .map_err(|a| EvalAltResult::ErrorMismatchOutputType((*a).type_name(), pos)) }) } @@ -233,6 +201,7 @@ impl Engine { ident: String, args: FnCallArgs, def_value: Option<&Dynamic>, + pos: Position, ) -> Result { debug_println!( "Trying to call function {:?} with args {:?}", @@ -257,7 +226,7 @@ impl Engine { if let Some(f) = fn_def { match **f { FnIntExt::Ext(ref f) => { - let r = f(args); + let r = f(args, pos); if r.is_err() { return r; @@ -288,7 +257,7 @@ impl Engine { ); match self.eval_stmt(&mut scope, &*f.body) { - Err(EvalAltResult::Return(x)) => Ok(x), + Err(EvalAltResult::Return(x, _)) => Ok(x), other => other, } } @@ -302,11 +271,10 @@ impl Engine { .map(|x| (*(&**x).into_dynamic()).type_name()) .collect::>(); - Err(EvalAltResult::ErrorFunctionNotFound(format!( - "{} ({})", - ident, - type_names.join(", ") - ))) + Err(EvalAltResult::ErrorFunctionNotFound( + format!("{} ({})", ident, type_names.join(", ")), + pos, + )) } } @@ -377,7 +345,7 @@ impl Engine { use std::iter::once; match dot_rhs { - Expr::FunctionCall(fn_name, args, def_value) => { + Expr::FunctionCall(fn_name, args, def_value, pos) => { let mut args: Array = args .iter() .map(|arg| self.eval_expr(scope, arg)) @@ -387,53 +355,59 @@ impl Engine { .chain(args.iter_mut().map(|b| b.as_mut())) .collect(); - self.call_fn_raw(fn_name.into(), args, def_value.as_ref()) + self.call_fn_raw(fn_name.into(), args, def_value.as_ref(), *pos) } - Expr::Identifier(id) => { + Expr::Identifier(id, pos) => { let get_fn_name = "get$".to_string() + id; - self.call_fn_raw(get_fn_name, vec![this_ptr], None) + self.call_fn_raw(get_fn_name, vec![this_ptr], None, *pos) } - Expr::Index(id, idx_raw) => { + Expr::Index(id, idx_raw, pos) => { let idx = self .eval_expr(scope, idx_raw)? .downcast_ref::() .map(|i| *i) - .ok_or(EvalAltResult::ErrorIndexExpr)?; + .ok_or(EvalAltResult::ErrorIndexExpr(idx_raw.position()))?; let get_fn_name = "get$".to_string() + id; - let mut val = self.call_fn_raw(get_fn_name, vec![this_ptr], None)?; + let mut val = self.call_fn_raw(get_fn_name, vec![this_ptr], None, *pos)?; if let Some(arr) = (*val).downcast_mut() as Option<&mut Array> { if idx >= 0 { arr.get(idx as usize) .cloned() - .ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr.len(), idx)) + .ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr.len(), idx, *pos)) } else { - Err(EvalAltResult::ErrorArrayBounds(arr.len(), idx)) + Err(EvalAltResult::ErrorArrayBounds(arr.len(), idx, *pos)) } } else if let Some(s) = (*val).downcast_mut() as Option<&mut String> { if idx >= 0 { s.chars() .nth(idx as usize) .map(|ch| Box::new(ch) as Dynamic) - .ok_or_else(|| EvalAltResult::ErrorStringBounds(s.chars().count(), idx)) + .ok_or_else(|| { + EvalAltResult::ErrorStringBounds(s.chars().count(), idx, *pos) + }) } else { - Err(EvalAltResult::ErrorStringBounds(s.chars().count(), idx)) + Err(EvalAltResult::ErrorStringBounds( + s.chars().count(), + idx, + *pos, + )) } } else { - Err(EvalAltResult::ErrorIndexing) + Err(EvalAltResult::ErrorIndexing(*pos)) } } Expr::Dot(inner_lhs, inner_rhs) => match **inner_lhs { - Expr::Identifier(ref id) => { + Expr::Identifier(ref id, pos) => { let get_fn_name = "get$".to_string() + id; let value = self - .call_fn_raw(get_fn_name, vec![this_ptr], None) + .call_fn_raw(get_fn_name, vec![this_ptr], None, pos) .and_then(|mut v| self.get_dot_val_helper(scope, v.as_mut(), inner_rhs))?; // TODO - Should propagate changes back in this scenario: @@ -446,17 +420,18 @@ impl Engine { Ok(value) } - Expr::Index(_, _) => { + Expr::Index(_, _, pos) => { // TODO - Handle Expr::Index for these scenarios: // // let x = obj.prop[2].x; // obj.prop[3] = 42; // - Err(EvalAltResult::ErrorDotExpr) + Err(EvalAltResult::ErrorDotExpr(pos)) } - _ => Err(EvalAltResult::ErrorDotExpr), + _ => Err(EvalAltResult::ErrorDotExpr(inner_lhs.position())), }, - _ => Err(EvalAltResult::ErrorDotExpr), + + _ => Err(EvalAltResult::ErrorDotExpr(dot_rhs.position())), } } @@ -464,13 +439,14 @@ impl Engine { scope: &'a mut Scope, id: &str, map: impl FnOnce(&'a mut Variant) -> Result, + begin: Position, ) -> Result<(usize, T), EvalAltResult> { scope .iter_mut() .enumerate() .rev() .find(|&(_, &mut (ref name, _))| id == name) - .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(id.into())) + .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(id.into(), begin)) .and_then(move |(idx, &mut (_, ref mut val))| map(val.as_mut()).map(|val| (idx, val))) } @@ -479,40 +455,52 @@ impl Engine { scope: &mut Scope, id: &str, idx: &Expr, + begin: Position, ) -> Result<(bool, usize, usize, Dynamic), EvalAltResult> { let idx = *self .eval_expr(scope, idx)? .downcast::() - .map_err(|_| EvalAltResult::ErrorIndexExpr)?; + .map_err(|_| EvalAltResult::ErrorIndexExpr(idx.position()))?; let mut is_array = false; - Self::search_scope(scope, id, |val| { - if let Some(arr) = (*val).downcast_mut() as Option<&mut Array> { - is_array = true; + Self::search_scope( + scope, + id, + |val| { + if let Some(arr) = (*val).downcast_mut() as Option<&mut Array> { + is_array = true; - if idx >= 0 { - arr.get(idx as usize) - .cloned() - .ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr.len(), idx)) - } else { - Err(EvalAltResult::ErrorArrayBounds(arr.len(), idx)) - } - } else if let Some(s) = (*val).downcast_mut() as Option<&mut String> { - is_array = false; + if idx >= 0 { + arr.get(idx as usize) + .cloned() + .ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr.len(), idx, begin)) + } else { + Err(EvalAltResult::ErrorArrayBounds(arr.len(), idx, begin)) + } + } else if let Some(s) = (*val).downcast_mut() as Option<&mut String> { + is_array = false; - if idx >= 0 { - s.chars() - .nth(idx as usize) - .map(|ch| Box::new(ch) as Dynamic) - .ok_or_else(|| EvalAltResult::ErrorStringBounds(s.chars().count(), idx)) + if idx >= 0 { + s.chars() + .nth(idx as usize) + .map(|ch| Box::new(ch) as Dynamic) + .ok_or_else(|| { + EvalAltResult::ErrorStringBounds(s.chars().count(), idx, begin) + }) + } else { + Err(EvalAltResult::ErrorStringBounds( + s.chars().count(), + idx, + begin, + )) + } } else { - Err(EvalAltResult::ErrorStringBounds(s.chars().count(), idx)) + Err(EvalAltResult::ErrorIndexing(begin)) } - } else { - Err(EvalAltResult::ErrorIndexing) - } - }) + }, + begin, + ) .map(|(idx_sc, val)| (is_array, idx_sc, idx as usize, val)) } @@ -539,8 +527,9 @@ impl Engine { dot_rhs: &Expr, ) -> Result { match dot_lhs { - Expr::Identifier(id) => { - let (sc_idx, mut target) = Self::search_scope(scope, id, |x| Ok(x.into_dynamic()))?; + Expr::Identifier(id, pos) => { + let (sc_idx, mut target) = + Self::search_scope(scope, id, |x| Ok(x.into_dynamic()), *pos)?; let value = self.get_dot_val_helper(scope, target.as_mut(), dot_rhs); // In case the expression mutated `target`, we need to reassign it because @@ -550,8 +539,9 @@ impl Engine { value } - Expr::Index(id, idx_raw) => { - let (is_array, sc_idx, idx, mut target) = self.indexed_value(scope, id, idx_raw)?; + Expr::Index(id, idx_raw, pos) => { + let (is_array, sc_idx, idx, mut target) = + self.indexed_value(scope, id, idx_raw, *pos)?; let value = self.get_dot_val_helper(scope, target.as_mut(), dot_rhs); // In case the expression mutated `target`, we need to reassign it because @@ -569,7 +559,8 @@ impl Engine { value } - _ => Err(EvalAltResult::ErrorDotExpr), + + _ => Err(EvalAltResult::ErrorDotExpr(dot_lhs.position())), } } @@ -580,17 +571,17 @@ impl Engine { mut source_val: Dynamic, ) -> Result { match dot_rhs { - Expr::Identifier(id) => { + Expr::Identifier(id, pos) => { let set_fn_name = "set$".to_string() + id; - self.call_fn_raw(set_fn_name, vec![this_ptr, source_val.as_mut()], None) + self.call_fn_raw(set_fn_name, vec![this_ptr, source_val.as_mut()], None, *pos) } Expr::Dot(inner_lhs, inner_rhs) => match **inner_lhs { - Expr::Identifier(ref id) => { + Expr::Identifier(ref id, pos) => { let get_fn_name = "get$".to_string() + id; - self.call_fn_raw(get_fn_name, vec![this_ptr], None) + self.call_fn_raw(get_fn_name, vec![this_ptr], None, pos) .and_then(|mut v| { self.set_dot_val_helper(v.as_mut(), inner_rhs, source_val) .map(|_| v) // Discard Ok return value @@ -598,12 +589,13 @@ impl Engine { .and_then(|mut v| { let set_fn_name = "set$".to_string() + id; - self.call_fn_raw(set_fn_name, vec![this_ptr, v.as_mut()], None) + self.call_fn_raw(set_fn_name, vec![this_ptr, v.as_mut()], None, pos) }) } - _ => Err(EvalAltResult::ErrorDotExpr), + _ => Err(EvalAltResult::ErrorDotExpr(inner_lhs.position())), }, - _ => Err(EvalAltResult::ErrorDotExpr), + + _ => Err(EvalAltResult::ErrorDotExpr(dot_rhs.position())), } } @@ -615,8 +607,9 @@ impl Engine { source_val: Dynamic, ) -> Result { match dot_lhs { - Expr::Identifier(id) => { - let (sc_idx, mut target) = Self::search_scope(scope, id, |x| Ok(x.into_dynamic()))?; + Expr::Identifier(id, pos) => { + let (sc_idx, mut target) = + Self::search_scope(scope, id, |x| Ok(x.into_dynamic()), *pos)?; let value = self.set_dot_val_helper(target.as_mut(), dot_rhs, source_val); // In case the expression mutated `target`, we need to reassign it because @@ -626,8 +619,9 @@ impl Engine { value } - Expr::Index(id, idx_raw) => { - let (is_array, sc_idx, idx, mut target) = self.indexed_value(scope, id, idx_raw)?; + Expr::Index(id, idx_raw, pos) => { + let (is_array, sc_idx, idx, mut target) = + self.indexed_value(scope, id, idx_raw, *pos)?; let value = self.set_dot_val_helper(target.as_mut(), dot_rhs, source_val); // In case the expression mutated `target`, we need to reassign it because @@ -644,34 +638,35 @@ impl Engine { value } - _ => Err(EvalAltResult::ErrorDotExpr), + + _ => Err(EvalAltResult::ErrorDotExpr(dot_lhs.position())), } } fn eval_expr(&self, scope: &mut Scope, expr: &Expr) -> Result { match expr { - Expr::IntegerConstant(i) => Ok(Box::new(*i)), - Expr::FloatConstant(i) => Ok(Box::new(*i)), - Expr::StringConstant(s) => Ok(Box::new(s.clone())), - Expr::CharConstant(c) => Ok(Box::new(*c)), + Expr::IntegerConstant(i, _) => Ok(Box::new(*i)), + Expr::FloatConstant(i, _) => Ok(Box::new(*i)), + Expr::StringConstant(s, _) => Ok(Box::new(s.clone())), + Expr::CharConstant(c, _) => Ok(Box::new(*c)), - Expr::Identifier(id) => scope + Expr::Identifier(id, pos) => scope .iter() .rev() .filter(|(name, _)| id == name) .next() .map(|(_, val)| val.clone()) - .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(id.clone())), + .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(id.clone(), *pos)), - Expr::Index(id, idx_raw) => { - self.indexed_value(scope, id, idx_raw).map(|(_, _, _, x)| x) - } + Expr::Index(id, idx_raw, pos) => self + .indexed_value(scope, id, idx_raw, *pos) + .map(|(_, _, _, x)| x), Expr::Assignment(ref id, rhs) => { let rhs_val = self.eval_expr(scope, rhs)?; match **id { - Expr::Identifier(ref n) => scope + Expr::Identifier(ref n, pos) => scope .iter_mut() .rev() .filter(|(name, _)| n == name) @@ -680,12 +675,14 @@ impl Engine { *val = rhs_val; Box::new(()) as Dynamic }) - .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(n.clone())), + .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(n.clone(), pos)), + + Expr::Index(ref id, ref idx_raw, pos) => { + let idx_pos = idx_raw.position(); - Expr::Index(ref id, ref idx_raw) => { let idx = *match self.eval_expr(scope, &idx_raw)?.downcast_ref::() { Some(x) => x, - _ => return Err(EvalAltResult::ErrorIndexExpr), + _ => return Err(EvalAltResult::ErrorIndexExpr(idx_pos)), }; let variable = &mut scope @@ -697,14 +694,14 @@ impl Engine { let val = match variable { Some(v) => v, - _ => return Err(EvalAltResult::ErrorVariableNotFound(id.clone())), + _ => return Err(EvalAltResult::ErrorVariableNotFound(id.clone(), pos)), }; if let Some(arr) = val.downcast_mut() as Option<&mut Array> { if idx < 0 { - Err(EvalAltResult::ErrorArrayBounds(arr.len(), idx)) + Err(EvalAltResult::ErrorArrayBounds(arr.len(), idx, idx_pos)) } else if idx as usize >= arr.len() { - Err(EvalAltResult::ErrorArrayBounds(arr.len(), idx)) + Err(EvalAltResult::ErrorArrayBounds(arr.len(), idx, idx_pos)) } else { arr[idx as usize] = rhs_val; Ok(Box::new(())) @@ -713,9 +710,9 @@ impl Engine { let s_len = s.chars().count(); if idx < 0 { - Err(EvalAltResult::ErrorStringBounds(s_len, idx)) + Err(EvalAltResult::ErrorStringBounds(s_len, idx, idx_pos)) } else if idx as usize >= s_len { - Err(EvalAltResult::ErrorStringBounds(s_len, idx)) + Err(EvalAltResult::ErrorStringBounds(s_len, idx, idx_pos)) } else { Self::str_replace_char( s, @@ -725,7 +722,7 @@ impl Engine { Ok(Box::new(())) } } else { - Err(EvalAltResult::ErrorIndexExpr) + Err(EvalAltResult::ErrorIndexExpr(idx_pos)) } } @@ -733,13 +730,13 @@ impl Engine { self.set_dot_val(scope, dot_lhs, dot_rhs, rhs_val) } - _ => Err(EvalAltResult::ErrorAssignmentToUnknownLHS), + _ => Err(EvalAltResult::ErrorAssignmentToUnknownLHS(id.position())), } } Expr::Dot(lhs, rhs) => self.get_dot_val(scope, lhs, rhs), - Expr::Array(contents) => { + Expr::Array(contents, _) => { let mut arr = Vec::new(); contents.iter().try_for_each(|item| { @@ -751,7 +748,7 @@ impl Engine { Ok(Box::new(arr)) } - Expr::FunctionCall(fn_name, args, def_value) => self.call_fn_raw( + Expr::FunctionCall(fn_name, args, def_value, pos) => self.call_fn_raw( fn_name.into(), args.iter() .map(|expr| self.eval_expr(scope, expr)) @@ -760,33 +757,42 @@ impl Engine { .map(|b| b.as_mut()) .collect(), def_value.as_ref(), + *pos, ), Expr::And(lhs, rhs) => Ok(Box::new( *self .eval_expr(scope, &*lhs)? .downcast::() - .map_err(|_| EvalAltResult::ErrorBooleanArgMismatch("AND".into()))? + .map_err(|_| { + EvalAltResult::ErrorBooleanArgMismatch("AND".into(), lhs.position()) + })? && *self .eval_expr(scope, &*rhs)? .downcast::() - .map_err(|_| EvalAltResult::ErrorBooleanArgMismatch("AND".into()))?, + .map_err(|_| { + EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position()) + })?, )), Expr::Or(lhs, rhs) => Ok(Box::new( *self .eval_expr(scope, &*lhs)? .downcast::() - .map_err(|_| EvalAltResult::ErrorBooleanArgMismatch("OR".into()))? + .map_err(|_| { + EvalAltResult::ErrorBooleanArgMismatch("OR".into(), lhs.position()) + })? || *self .eval_expr(scope, &*rhs)? .downcast::() - .map_err(|_| EvalAltResult::ErrorBooleanArgMismatch("OR".into()))?, + .map_err(|_| { + EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position()) + })?, )), - Expr::True => Ok(Box::new(true)), - Expr::False => Ok(Box::new(false)), - Expr::Unit => Ok(Box::new(())), + Expr::True(_) => Ok(Box::new(true)), + Expr::False(_) => Ok(Box::new(false)), + Expr::Unit(_) => Ok(Box::new(())), } } @@ -821,7 +827,7 @@ impl Engine { Stmt::IfElse(guard, body, else_body) => self .eval_expr(scope, guard)? .downcast::() - .map_err(|_| EvalAltResult::ErrorIfGuard) + .map_err(|_| EvalAltResult::ErrorIfGuard(guard.position())) .and_then(|guard_val| { if *guard_val { self.eval_stmt(scope, body) @@ -845,7 +851,7 @@ impl Engine { return Ok(Box::new(())); } } - Err(_) => return Err(EvalAltResult::ErrorIfGuard), + Err(_) => return Err(EvalAltResult::ErrorIfGuard(guard.position())), } }, @@ -877,20 +883,20 @@ impl Engine { scope.remove(idx); Ok(Box::new(())) } else { - return Err(EvalAltResult::ErrorFor); + return Err(EvalAltResult::ErrorFor(expr.position())); } } - Stmt::Break => Err(EvalAltResult::LoopBreak), + Stmt::Break(_) => Err(EvalAltResult::LoopBreak), - Stmt::Return => Err(EvalAltResult::Return(Box::new(()))), + Stmt::Return(pos) => Err(EvalAltResult::Return(Box::new(()), *pos)), - Stmt::ReturnWithVal(a) => { + Stmt::ReturnWithVal(a, pos) => { let result = self.eval_expr(scope, a)?; - Err(EvalAltResult::Return(result)) + Err(EvalAltResult::Return(result, *pos)) } - Stmt::Let(name, init) => { + Stmt::Let(name, init, _) => { if let Some(v) = init { let i = self.eval_expr(scope, v)?; scope.push((name.clone(), i)); diff --git a/src/fn_register.rs b/src/fn_register.rs index 8a65952f..f340f5bc 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -2,6 +2,7 @@ use std::any::TypeId; use crate::any::{Any, Dynamic}; use crate::engine::{Engine, EvalAltResult, FnCallArgs}; +use crate::parser::Position; pub trait RegisterFn { fn register_fn(&mut self, name: &str, f: FN); @@ -32,13 +33,13 @@ macro_rules! def_register { fn register_fn(&mut self, name: &str, f: FN) { let fn_name = name.to_string(); - let fun = move |mut args: FnCallArgs| { + let fun = move |mut args: FnCallArgs, pos: Position| { // Check for length at the beginning to avoid // per-element bound checks. const NUM_ARGS: usize = count_args!($($par)*); if args.len() != NUM_ARGS { - return Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS)); + return Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, pos)); } #[allow(unused_variables, unused_mut)] @@ -65,13 +66,13 @@ macro_rules! def_register { fn register_dynamic_fn(&mut self, name: &str, f: FN) { let fn_name = name.to_string(); - let fun = move |mut args: FnCallArgs| { + let fun = move |mut args: FnCallArgs, pos: Position| { // Check for length at the beginning to avoid // per-element bound checks. const NUM_ARGS: usize = count_args!($($par)*); if args.len() != NUM_ARGS { - return Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS)); + return Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, pos)); } #[allow(unused_variables, unused_mut)] diff --git a/src/parser.rs b/src/parser.rs index 001252d3..2037679c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -50,8 +50,8 @@ pub enum ParseErrorType { MissingRightBracket, MalformedCallExpr, MalformedIndexExpr, - VarExpectsIdentifier(Token), - FnMissingName(Token), + VarExpectsIdentifier, + FnMissingName, FnMissingParams, } @@ -59,25 +59,37 @@ type PERR = ParseErrorType; #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)] pub struct Position { - line: usize, - pos: usize, + pub line: usize, + pub pos: usize, } impl Position { - fn advance(&mut self) { + pub fn advance(&mut self) { self.pos += 1; } - fn rewind(&mut self) { + pub fn rewind(&mut self) { // Beware, should not rewind at zero position self.pos -= 1; } - fn new_line(&mut self) { + pub fn new_line(&mut self) { self.line += 1; self.pos = 0; } - fn eof() -> Self { + pub fn eof() -> Self { Self { line: 0, pos: 0 } } + pub fn is_eof(&self) -> bool { + self.line == 0 + } +} + +impl std::fmt::Display for Position { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + match self.line { + 0 => write!(f, "EOF"), + _ => write!(f, "line {}, position {}", self.line, self.pos), + } + } } #[derive(Debug, PartialEq, Clone)] @@ -93,6 +105,9 @@ impl ParseError { pub fn position(&self) -> usize { self.1.pos } + pub fn is_eof(&self) -> bool { + self.1.is_eof() + } } impl Error for ParseError { @@ -107,8 +122,8 @@ impl Error for ParseError { PERR::MissingRightBracket => "Expecting ']'", PERR::MalformedCallExpr => "Invalid expression in function call arguments", PERR::MalformedIndexExpr => "Invalid index in indexing expression", - PERR::VarExpectsIdentifier(_) => "Expecting name of a variable", - PERR::FnMissingName(_) => "Expecting name in function declaration", + PERR::VarExpectsIdentifier => "Expecting name of a variable", + PERR::FnMissingName => "Expecting name in function declaration", PERR::FnMissingParams => "Expecting parameters in function declaration", } } @@ -121,23 +136,12 @@ impl Error for ParseError { impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { - PERR::BadInput(ref s) => { - write!(f, "{}", s)?; - } - PERR::VarExpectsIdentifier(ref token) | PERR::FnMissingName(ref token) => match token { - Token::None => write!(f, "{}", self.description())?, - _ => write!( - f, - "{} (but gets {:?} token instead)", - self.description(), - token - )?, - }, + PERR::BadInput(ref s) => write!(f, "{}", s)?, _ => write!(f, "{}", self.description())?, } - if self.line() > 0 { - write!(f, " at line {}, position {}", self.line(), self.position()) + if !self.is_eof() { + write!(f, " ({})", self.1) } else { write!(f, " at the end of the script but there is no more input") } @@ -151,6 +155,7 @@ pub struct FnDef { pub name: String, pub params: Vec, pub body: Box, + pub pos: Position, } #[derive(Debug, Clone)] @@ -159,36 +164,55 @@ pub enum Stmt { While(Box, Box), Loop(Box), For(String, Box, Box), - Let(String, Option>), + Let(String, Option>, Position), Block(Vec), Expr(Box), - Break, - Return, - ReturnWithVal(Box), + Break(Position), + Return(Position), + ReturnWithVal(Box, Position), } #[derive(Debug, Clone)] pub enum Expr { - IntegerConstant(i64), - FloatConstant(f64), - Identifier(String), - CharConstant(char), - StringConstant(String), - FunctionCall(String, Vec, Option), + IntegerConstant(i64, Position), + FloatConstant(f64, Position), + Identifier(String, Position), + CharConstant(char, Position), + StringConstant(String, Position), + FunctionCall(String, Vec, Option, Position), Assignment(Box, Box), Dot(Box, Box), - Index(String, Box), - Array(Vec), + Index(String, Box, Position), + Array(Vec, Position), And(Box, Box), Or(Box, Box), - True, - False, - Unit, + True(Position), + False(Position), + Unit(Position), +} + +impl Expr { + pub fn position(&self) -> Position { + match self { + Expr::IntegerConstant(_, pos) + | Expr::FloatConstant(_, pos) + | Expr::Identifier(_, pos) + | Expr::CharConstant(_, pos) + | Expr::StringConstant(_, pos) + | Expr::FunctionCall(_, _, _, pos) + | Expr::Index(_, _, pos) + | Expr::Array(_, pos) + | Expr::True(pos) + | Expr::False(pos) + | Expr::Unit(pos) => *pos, + + Expr::Assignment(_, _) | Expr::Dot(_, _) | Expr::And(_, _) | Expr::Or(_, _) => panic!(), + } + } } #[derive(Debug, PartialEq, Clone)] pub enum Token { - None, IntegerConstant(i64), FloatConstant(f64), Identifier(String), @@ -972,11 +996,14 @@ fn get_precedence(token: &Token) -> i8 { } } -fn parse_paren_expr<'a>(input: &mut Peekable>) -> Result { +fn parse_paren_expr<'a>( + input: &mut Peekable>, + begin: Position, +) -> Result { match input.peek() { Some((Token::RightParen, _)) => { input.next(); - return Ok(Expr::Unit); + return Ok(Expr::Unit(begin)); } _ => (), } @@ -992,12 +1019,13 @@ fn parse_paren_expr<'a>(input: &mut Peekable>) -> Result( id: String, input: &mut Peekable>, + begin: Position, ) -> Result { let mut args = Vec::new(); if let Some(&(Token::RightParen, _)) = input.peek() { input.next(); - return Ok(Expr::FunctionCall(id, args, None)); + return Ok(Expr::FunctionCall(id, args, None, begin)); } loop { @@ -1006,7 +1034,7 @@ fn parse_call_expr<'a>( match input.peek() { Some(&(Token::RightParen, _)) => { input.next(); - return Ok(Expr::FunctionCall(id, args, None)); + return Ok(Expr::FunctionCall(id, args, None, begin)); } Some(&(Token::Comma, _)) => (), Some(&(_, pos)) => return Err(ParseError(PERR::MalformedCallExpr, pos)), @@ -1020,12 +1048,13 @@ fn parse_call_expr<'a>( fn parse_index_expr<'a>( id: String, input: &mut Peekable>, + begin: Position, ) -> Result { match parse_expr(input) { Ok(idx) => match input.peek() { Some(&(Token::RightBracket, _)) => { input.next(); - return Ok(Expr::Index(id, Box::new(idx))); + return Ok(Expr::Index(id, Box::new(idx), begin)); } Some(&(_, pos)) => return Err(ParseError(PERR::MalformedIndexExpr, pos)), None => return Err(ParseError(PERR::MalformedIndexExpr, Position::eof())), @@ -1040,21 +1069,26 @@ fn parse_index_expr<'a>( fn parse_ident_expr<'a>( id: String, input: &mut Peekable>, + begin: Position, ) -> Result { match input.peek() { - Some(&(Token::LeftParen, _)) => { + Some(&(Token::LeftParen, pos)) => { input.next(); - parse_call_expr(id, input) + parse_call_expr(id, input, pos) } - Some(&(Token::LeftBracket, _)) => { + Some(&(Token::LeftBracket, pos)) => { input.next(); - parse_index_expr(id, input) + parse_index_expr(id, input, pos) } - _ => Ok(Expr::Identifier(id)), + Some(_) => Ok(Expr::Identifier(id, begin)), + None => Ok(Expr::Identifier(id, Position::eof())), } } -fn parse_array_expr<'a>(input: &mut Peekable>) -> Result { +fn parse_array_expr<'a>( + input: &mut Peekable>, + begin: Position, +) -> Result { let mut arr = Vec::new(); let skip_contents = match input.peek() { @@ -1065,6 +1099,7 @@ fn parse_array_expr<'a>(input: &mut Peekable>) -> Result(input: &mut Peekable>) -> Result { input.next(); - Ok(Expr::Array(arr)) + Ok(Expr::Array(arr, begin)) } Some(&(_, pos)) => Err(ParseError(PERR::MissingRightBracket, pos)), None => Err(ParseError(PERR::MissingRightBracket, Position::eof())), @@ -1088,15 +1123,15 @@ fn parse_array_expr<'a>(input: &mut Peekable>) -> Result(input: &mut Peekable>) -> Result { match input.next() { Some((token, pos)) => match token { - Token::IntegerConstant(x) => Ok(Expr::IntegerConstant(x)), - Token::FloatConstant(x) => Ok(Expr::FloatConstant(x)), - Token::StringConst(s) => Ok(Expr::StringConstant(s)), - Token::CharConstant(c) => Ok(Expr::CharConstant(c)), - Token::Identifier(s) => parse_ident_expr(s, input), - Token::LeftParen => parse_paren_expr(input), - Token::LeftBracket => parse_array_expr(input), - Token::True => Ok(Expr::True), - Token::False => Ok(Expr::False), + Token::IntegerConstant(x) => Ok(Expr::IntegerConstant(x, pos)), + Token::FloatConstant(x) => Ok(Expr::FloatConstant(x, pos)), + Token::StringConst(s) => Ok(Expr::StringConstant(s, pos)), + Token::CharConstant(c) => Ok(Expr::CharConstant(c, pos)), + Token::Identifier(s) => parse_ident_expr(s, input, pos), + Token::LeftParen => parse_paren_expr(input, pos), + Token::LeftBracket => parse_array_expr(input, pos), + Token::True => Ok(Expr::True(pos)), + Token::False => Ok(Expr::False(pos)), Token::LexErr(le) => Err(ParseError(PERR::BadInput(le.to_string()), pos)), _ => Err(ParseError( PERR::BadInput(format!("Unexpected {:?} token", token)), @@ -1108,18 +1143,20 @@ fn parse_primary<'a>(input: &mut Peekable>) -> Result(input: &mut Peekable>) -> Result { - let token = match input.peek() { - Some((tok, _)) => tok.clone(), + let (token, pos) = match input.peek() { + Some((tok, tok_pos)) => (tok.clone(), *tok_pos), None => return Err(ParseError(PERR::InputPastEndOfFile, Position::eof())), }; match token { Token::UnaryMinus => { input.next(); + Ok(Expr::FunctionCall( "-".into(), vec![parse_primary(input)?], None, + pos, )) } Token::UnaryPlus => { @@ -1128,10 +1165,12 @@ fn parse_unary<'a>(input: &mut Peekable>) -> Result { input.next(); + Ok(Expr::FunctionCall( "!".into(), vec![parse_primary(input)?], Some(Box::new(false)), // NOT operator, when operating on invalid operand, defaults to false + pos, )) } _ => parse_primary(input), @@ -1157,6 +1196,8 @@ fn parse_binop<'a>( } if let Some((op_token, pos)) = input.next() { + input.peek(); + let mut rhs = parse_unary(input)?; let mut next_prec = -1; @@ -1173,118 +1214,175 @@ fn parse_binop<'a>( } lhs_curr = match op_token { - Token::Plus => Expr::FunctionCall("+".into(), vec![lhs_curr, rhs], None), - Token::Minus => Expr::FunctionCall("-".into(), vec![lhs_curr, rhs], None), - Token::Multiply => Expr::FunctionCall("*".into(), vec![lhs_curr, rhs], None), - Token::Divide => Expr::FunctionCall("/".into(), vec![lhs_curr, rhs], None), + Token::Plus => Expr::FunctionCall("+".into(), vec![lhs_curr, rhs], None, pos), + Token::Minus => Expr::FunctionCall("-".into(), vec![lhs_curr, rhs], None, pos), + Token::Multiply => Expr::FunctionCall("*".into(), vec![lhs_curr, rhs], None, pos), + Token::Divide => Expr::FunctionCall("/".into(), vec![lhs_curr, rhs], None, pos), Token::Equals => Expr::Assignment(Box::new(lhs_curr), Box::new(rhs)), Token::PlusAssign => { let lhs_copy = lhs_curr.clone(); Expr::Assignment( Box::new(lhs_curr), - Box::new(Expr::FunctionCall("+".into(), vec![lhs_copy, rhs], None)), + Box::new(Expr::FunctionCall( + "+".into(), + vec![lhs_copy, rhs], + None, + pos, + )), ) } Token::MinusAssign => { let lhs_copy = lhs_curr.clone(); Expr::Assignment( Box::new(lhs_curr), - Box::new(Expr::FunctionCall("-".into(), vec![lhs_copy, rhs], None)), + Box::new(Expr::FunctionCall( + "-".into(), + vec![lhs_copy, rhs], + None, + pos, + )), ) } Token::Period => Expr::Dot(Box::new(lhs_curr), Box::new(rhs)), // Comparison operators default to false when passed invalid operands Token::EqualsTo => { - Expr::FunctionCall("==".into(), vec![lhs_curr, rhs], Some(Box::new(false))) + Expr::FunctionCall("==".into(), vec![lhs_curr, rhs], Some(Box::new(false)), pos) } Token::NotEqualsTo => { - Expr::FunctionCall("!=".into(), vec![lhs_curr, rhs], Some(Box::new(false))) + Expr::FunctionCall("!=".into(), vec![lhs_curr, rhs], Some(Box::new(false)), pos) } Token::LessThan => { - Expr::FunctionCall("<".into(), vec![lhs_curr, rhs], Some(Box::new(false))) + Expr::FunctionCall("<".into(), vec![lhs_curr, rhs], Some(Box::new(false)), pos) } Token::LessThanEqualsTo => { - Expr::FunctionCall("<=".into(), vec![lhs_curr, rhs], Some(Box::new(false))) + Expr::FunctionCall("<=".into(), vec![lhs_curr, rhs], Some(Box::new(false)), pos) } Token::GreaterThan => { - Expr::FunctionCall(">".into(), vec![lhs_curr, rhs], Some(Box::new(false))) + Expr::FunctionCall(">".into(), vec![lhs_curr, rhs], Some(Box::new(false)), pos) } Token::GreaterThanEqualsTo => { - Expr::FunctionCall(">=".into(), vec![lhs_curr, rhs], Some(Box::new(false))) + Expr::FunctionCall(">=".into(), vec![lhs_curr, rhs], Some(Box::new(false)), pos) } Token::Or => Expr::Or(Box::new(lhs_curr), Box::new(rhs)), Token::And => Expr::And(Box::new(lhs_curr), Box::new(rhs)), - Token::XOr => Expr::FunctionCall("^".into(), vec![lhs_curr, rhs], None), + Token::XOr => Expr::FunctionCall("^".into(), vec![lhs_curr, rhs], None, pos), Token::OrAssign => { let lhs_copy = lhs_curr.clone(); Expr::Assignment( Box::new(lhs_curr), - Box::new(Expr::FunctionCall("|".into(), vec![lhs_copy, rhs], None)), + Box::new(Expr::FunctionCall( + "|".into(), + vec![lhs_copy, rhs], + None, + pos, + )), ) } Token::AndAssign => { let lhs_copy = lhs_curr.clone(); Expr::Assignment( Box::new(lhs_curr), - Box::new(Expr::FunctionCall("&".into(), vec![lhs_copy, rhs], None)), + Box::new(Expr::FunctionCall( + "&".into(), + vec![lhs_copy, rhs], + None, + pos, + )), ) } Token::XOrAssign => { let lhs_copy = lhs_curr.clone(); Expr::Assignment( Box::new(lhs_curr), - Box::new(Expr::FunctionCall("^".into(), vec![lhs_copy, rhs], None)), + Box::new(Expr::FunctionCall( + "^".into(), + vec![lhs_copy, rhs], + None, + pos, + )), ) } Token::MultiplyAssign => { let lhs_copy = lhs_curr.clone(); Expr::Assignment( Box::new(lhs_curr), - Box::new(Expr::FunctionCall("*".into(), vec![lhs_copy, rhs], None)), + Box::new(Expr::FunctionCall( + "*".into(), + vec![lhs_copy, rhs], + None, + pos, + )), ) } Token::DivideAssign => { let lhs_copy = lhs_curr.clone(); Expr::Assignment( Box::new(lhs_curr), - Box::new(Expr::FunctionCall("/".into(), vec![lhs_copy, rhs], None)), + Box::new(Expr::FunctionCall( + "/".into(), + vec![lhs_copy, rhs], + None, + pos, + )), ) } - Token::Pipe => Expr::FunctionCall("|".into(), vec![lhs_curr, rhs], None), - Token::LeftShift => Expr::FunctionCall("<<".into(), vec![lhs_curr, rhs], None), - Token::RightShift => Expr::FunctionCall(">>".into(), vec![lhs_curr, rhs], None), + Token::Pipe => Expr::FunctionCall("|".into(), vec![lhs_curr, rhs], None, pos), + Token::LeftShift => Expr::FunctionCall("<<".into(), vec![lhs_curr, rhs], None, pos), + Token::RightShift => { + Expr::FunctionCall(">>".into(), vec![lhs_curr, rhs], None, pos) + } Token::LeftShiftAssign => { let lhs_copy = lhs_curr.clone(); Expr::Assignment( Box::new(lhs_curr), - Box::new(Expr::FunctionCall("<<".into(), vec![lhs_copy, rhs], None)), + Box::new(Expr::FunctionCall( + "<<".into(), + vec![lhs_copy, rhs], + None, + pos, + )), ) } Token::RightShiftAssign => { let lhs_copy = lhs_curr.clone(); Expr::Assignment( Box::new(lhs_curr), - Box::new(Expr::FunctionCall(">>".into(), vec![lhs_copy, rhs], None)), + Box::new(Expr::FunctionCall( + ">>".into(), + vec![lhs_copy, rhs], + None, + pos, + )), ) } - Token::Ampersand => Expr::FunctionCall("&".into(), vec![lhs_curr, rhs], None), - Token::Modulo => Expr::FunctionCall("%".into(), vec![lhs_curr, rhs], None), + Token::Ampersand => Expr::FunctionCall("&".into(), vec![lhs_curr, rhs], None, pos), + Token::Modulo => Expr::FunctionCall("%".into(), vec![lhs_curr, rhs], None, pos), Token::ModuloAssign => { let lhs_copy = lhs_curr.clone(); Expr::Assignment( Box::new(lhs_curr), - Box::new(Expr::FunctionCall("%".into(), vec![lhs_copy, rhs], None)), + Box::new(Expr::FunctionCall( + "%".into(), + vec![lhs_copy, rhs], + None, + pos, + )), ) } - Token::PowerOf => Expr::FunctionCall("~".into(), vec![lhs_curr, rhs], None), + Token::PowerOf => Expr::FunctionCall("~".into(), vec![lhs_curr, rhs], None, pos), Token::PowerOfAssign => { let lhs_copy = lhs_curr.clone(); Expr::Assignment( Box::new(lhs_curr), - Box::new(Expr::FunctionCall("~".into(), vec![lhs_copy, rhs], None)), + Box::new(Expr::FunctionCall( + "~".into(), + vec![lhs_copy, rhs], + None, + pos, + )), ) } _ => return Err(ParseError(PERR::UnknownOperator, pos)), @@ -1345,24 +1443,14 @@ fn parse_for<'a>(input: &mut Peekable>) -> Result s, - Some((token, pos)) => return Err(ParseError(PERR::VarExpectsIdentifier(token), pos)), - None => { - return Err(ParseError( - PERR::VarExpectsIdentifier(Token::None), - Position::eof(), - )) - } + Some((_, pos)) => return Err(ParseError(PERR::VarExpectsIdentifier, pos)), + None => return Err(ParseError(PERR::VarExpectsIdentifier, Position::eof())), }; match input.next() { Some((Token::In, _)) => {} - Some((token, pos)) => return Err(ParseError(PERR::VarExpectsIdentifier(token), pos)), - None => { - return Err(ParseError( - PERR::VarExpectsIdentifier(Token::None), - Position::eof(), - )) - } + Some((_, pos)) => return Err(ParseError(PERR::VarExpectsIdentifier, pos)), + None => return Err(ParseError(PERR::VarExpectsIdentifier, Position::eof())), } let expr = parse_expr(input)?; @@ -1373,26 +1461,24 @@ fn parse_for<'a>(input: &mut Peekable>) -> Result(input: &mut Peekable>) -> Result { - input.next(); + let pos = match input.next() { + Some((_, tok_pos)) => tok_pos, + _ => return Err(ParseError(PERR::InputPastEndOfFile, Position::eof())), + }; let name = match input.next() { Some((Token::Identifier(s), _)) => s, - Some((token, pos)) => return Err(ParseError(PERR::VarExpectsIdentifier(token), pos)), - None => { - return Err(ParseError( - PERR::VarExpectsIdentifier(Token::None), - Position::eof(), - )) - } + Some((_, pos)) => return Err(ParseError(PERR::VarExpectsIdentifier, pos)), + None => return Err(ParseError(PERR::VarExpectsIdentifier, Position::eof())), }; match input.peek() { Some(&(Token::Equals, _)) => { input.next(); let initializer = parse_expr(input)?; - Ok(Stmt::Let(name, Some(Box::new(initializer)))) + Ok(Stmt::Let(name, Some(Box::new(initializer)), pos)) } - _ => Ok(Stmt::Let(name, None)), + _ => Ok(Stmt::Let(name, None, pos)), } } @@ -1446,18 +1532,19 @@ fn parse_stmt<'a>(input: &mut Peekable>) -> Result parse_while(input), Some(&(Token::Loop, _)) => parse_loop(input), Some(&(Token::For, _)) => parse_for(input), - Some(&(Token::Break, _)) => { + Some(&(Token::Break, pos)) => { input.next(); - Ok(Stmt::Break) + Ok(Stmt::Break(pos)) } Some(&(Token::Return, _)) => { input.next(); match input.peek() { - Some(&(Token::SemiColon, _)) => Ok(Stmt::Return), - _ => { + Some(&(Token::SemiColon, pos)) => Ok(Stmt::Return(pos)), + Some(&(_, pos)) => { let ret = parse_expr(input)?; - Ok(Stmt::ReturnWithVal(Box::new(ret))) + Ok(Stmt::ReturnWithVal(Box::new(ret), pos)) } + _ => parse_expr_stmt(input), } } Some(&(Token::LeftBrace, _)) => parse_block(input), @@ -1467,17 +1554,15 @@ fn parse_stmt<'a>(input: &mut Peekable>) -> Result(input: &mut Peekable>) -> Result { - input.next(); + let pos = match input.next() { + Some((_, tok_pos)) => tok_pos, + _ => return Err(ParseError(PERR::InputPastEndOfFile, Position::eof())), + }; let name = match input.next() { Some((Token::Identifier(s), _)) => s, - Some((token, pos)) => return Err(ParseError(PERR::FnMissingName(token), pos)), - None => { - return Err(ParseError( - PERR::FnMissingName(Token::None), - Position::eof(), - )) - } + Some((_, pos)) => return Err(ParseError(PERR::FnMissingName, pos)), + None => return Err(ParseError(PERR::FnMissingName, Position::eof())), }; match input.peek() { @@ -1518,6 +1603,7 @@ fn parse_fn<'a>(input: &mut Peekable>) -> Result