Merge branch 'master' into plugins
This commit is contained in:
commit
96e5a1c56d
@ -13,6 +13,7 @@ Breaking changes
|
|||||||
* Callback closure passed to `Engine::on_progress` now takes `&u64` instead of `u64` to be consistent with other callback signatures.
|
* Callback closure passed to `Engine::on_progress` now takes `&u64` instead of `u64` to be consistent with other callback signatures.
|
||||||
* `Engine::register_indexer` is renamed to `Engine::register_indexer_get`.
|
* `Engine::register_indexer` is renamed to `Engine::register_indexer_get`.
|
||||||
* `Module::set_indexer_fn` is renamed to `Module::set_indexer_get_fn`.
|
* `Module::set_indexer_fn` is renamed to `Module::set_indexer_get_fn`.
|
||||||
|
* The tuple `ParseError` now exposes the internal fields and the `ParseError::error_type` and `ParseError::position` methods are removed. The first tuple field is the `ParseErrorType` and the second tuple field is the `Position`.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
@ -7,28 +7,27 @@ use std::io::{stdin, stdout, Write};
|
|||||||
|
|
||||||
fn print_error(input: &str, err: EvalAltResult) {
|
fn print_error(input: &str, err: EvalAltResult) {
|
||||||
let lines: Vec<_> = input.trim().split('\n').collect();
|
let lines: Vec<_> = input.trim().split('\n').collect();
|
||||||
|
let pos = err.position();
|
||||||
|
|
||||||
let line_no = if lines.len() > 1 {
|
let line_no = if lines.len() > 1 {
|
||||||
match err.position() {
|
if pos.is_none() {
|
||||||
p if p.is_none() => "".to_string(),
|
"".to_string()
|
||||||
p => format!("{}: ", p.line().unwrap()),
|
} else {
|
||||||
|
format!("{}: ", pos.line().unwrap())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Print error
|
// Print error
|
||||||
let pos = err.position();
|
|
||||||
let pos_text = format!(" ({})", pos);
|
let pos_text = format!(" ({})", pos);
|
||||||
|
|
||||||
match pos {
|
if pos.is_none() {
|
||||||
p if p.is_none() => {
|
|
||||||
// No position
|
// No position
|
||||||
println!("{}", err);
|
println!("{}", err);
|
||||||
}
|
} else {
|
||||||
p => {
|
|
||||||
// Specific position
|
// Specific position
|
||||||
println!("{}{}", line_no, lines[p.line().unwrap() - 1]);
|
println!("{}{}", line_no, lines[pos.line().unwrap() - 1]);
|
||||||
|
|
||||||
let err_text = match err {
|
let err_text = match err {
|
||||||
EvalAltResult::ErrorRuntime(err, _) if !err.is_empty() => {
|
EvalAltResult::ErrorRuntime(err, _) if !err.is_empty() => {
|
||||||
@ -40,11 +39,10 @@ fn print_error(input: &str, err: EvalAltResult) {
|
|||||||
println!(
|
println!(
|
||||||
"{0:>1$} {2}",
|
"{0:>1$} {2}",
|
||||||
"^",
|
"^",
|
||||||
line_no.len() + p.position().unwrap(),
|
line_no.len() + pos.position().unwrap(),
|
||||||
err_text.replace(&pos_text, "")
|
err_text.replace(&pos_text, "")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_help() {
|
fn print_help() {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Engine, EvalAltResult};
|
use rhai::{Engine, EvalAltResult, Position};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
use rhai::OptimizationLevel;
|
use rhai::OptimizationLevel;
|
||||||
@ -6,15 +6,17 @@ use rhai::OptimizationLevel;
|
|||||||
use std::{env, fs::File, io::Read, process::exit};
|
use std::{env, fs::File, io::Read, process::exit};
|
||||||
|
|
||||||
fn eprint_error(input: &str, err: EvalAltResult) {
|
fn eprint_error(input: &str, err: EvalAltResult) {
|
||||||
fn eprint_line(lines: &[&str], line: usize, pos: usize, err: &str) {
|
fn eprint_line(lines: &[&str], pos: Position, err: &str) {
|
||||||
|
let line = pos.line().unwrap();
|
||||||
|
|
||||||
let line_no = format!("{}: ", line);
|
let line_no = format!("{}: ", line);
|
||||||
let pos_text = format!(" (line {}, position {})", line, pos);
|
let pos_text = format!(" ({})", pos);
|
||||||
|
|
||||||
eprintln!("{}{}", line_no, lines[line - 1]);
|
eprintln!("{}{}", line_no, lines[line - 1]);
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{:>1$} {2}",
|
"{:>1$} {2}",
|
||||||
"^",
|
"^",
|
||||||
line_no.len() + pos,
|
line_no.len() + pos.position().unwrap(),
|
||||||
err.replace(&pos_text, "")
|
err.replace(&pos_text, "")
|
||||||
);
|
);
|
||||||
eprintln!("");
|
eprintln!("");
|
||||||
@ -25,12 +27,10 @@ fn eprint_error(input: &str, err: EvalAltResult) {
|
|||||||
// Print error
|
// Print error
|
||||||
let pos = err.position();
|
let pos = err.position();
|
||||||
|
|
||||||
match pos {
|
if pos.is_none() {
|
||||||
p if p.is_none() => {
|
|
||||||
// No position
|
// No position
|
||||||
eprintln!("{}", err);
|
eprintln!("{}", err);
|
||||||
}
|
} else {
|
||||||
p => {
|
|
||||||
// Specific position
|
// Specific position
|
||||||
let err_text = match err {
|
let err_text = match err {
|
||||||
EvalAltResult::ErrorRuntime(err, _) if !err.is_empty() => {
|
EvalAltResult::ErrorRuntime(err, _) if !err.is_empty() => {
|
||||||
@ -39,8 +39,7 @@ fn eprint_error(input: &str, err: EvalAltResult) {
|
|||||||
err => err.to_string(),
|
err => err.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
eprint_line(&lines, p.line().unwrap(), p.position().unwrap(), &err_text)
|
eprint_line(&lines, pos, &err_text)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -871,9 +871,7 @@ impl Engine {
|
|||||||
|
|
||||||
// If new functions are defined within the eval string, it is an error
|
// If new functions are defined within the eval string, it is an error
|
||||||
if ast.lib().num_fn() != 0 {
|
if ast.lib().num_fn() != 0 {
|
||||||
return Err(Box::new(EvalAltResult::ErrorParsing(
|
return Err(ParseErrorType::WrongFnDefinition.into());
|
||||||
ParseErrorType::WrongFnDefinition.into_err(Position::none()),
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let statements = mem::take(ast.statements_mut());
|
let statements = mem::take(ast.statements_mut());
|
||||||
|
191
src/error.rs
191
src/error.rs
@ -1,5 +1,6 @@
|
|||||||
//! Module containing error definitions for the parsing process.
|
//! Module containing error definitions for the parsing process.
|
||||||
|
|
||||||
|
use crate::result::EvalAltResult;
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
|
|
||||||
use crate::stdlib::{boxed::Box, char, error::Error, fmt, string::String};
|
use crate::stdlib::{boxed::Box, char, error::Error, fmt, string::String};
|
||||||
@ -123,113 +124,111 @@ impl ParseErrorType {
|
|||||||
pub(crate) fn into_err(self, pos: Position) -> ParseError {
|
pub(crate) fn into_err(self, pos: Position) -> ParseError {
|
||||||
ParseError(Box::new(self), pos)
|
ParseError(Box::new(self), pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn desc(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::BadInput(p) => p,
|
||||||
|
Self::UnexpectedEOF => "Script is incomplete",
|
||||||
|
Self::UnknownOperator(_) => "Unknown operator",
|
||||||
|
Self::MissingToken(_, _) => "Expecting a certain token that is missing",
|
||||||
|
Self::MalformedCallExpr(_) => "Invalid expression in function call arguments",
|
||||||
|
Self::MalformedIndexExpr(_) => "Invalid index in indexing expression",
|
||||||
|
Self::MalformedInExpr(_) => "Invalid 'in' expression",
|
||||||
|
Self::DuplicatedProperty(_) => "Duplicated property in object map literal",
|
||||||
|
Self::ForbiddenConstantExpr(_) => "Expecting a constant",
|
||||||
|
Self::PropertyExpected => "Expecting name of a property",
|
||||||
|
Self::VariableExpected => "Expecting name of a variable",
|
||||||
|
Self::ExprExpected(_) => "Expecting an expression",
|
||||||
|
Self::FnMissingName => "Expecting name in function declaration",
|
||||||
|
Self::FnMissingParams(_) => "Expecting parameters in function declaration",
|
||||||
|
Self::FnDuplicatedParam(_,_) => "Duplicated parameters in function declaration",
|
||||||
|
Self::FnMissingBody(_) => "Expecting body statement block for function declaration",
|
||||||
|
Self::WrongFnDefinition => "Function definitions must be at global level and cannot be inside a block or another function",
|
||||||
|
Self::DuplicatedExport(_) => "Duplicated variable/function in export statement",
|
||||||
|
Self::WrongExport => "Export statement can only appear at global level",
|
||||||
|
Self::AssignmentToCopy => "Only a copy of the value is change with this assignment",
|
||||||
|
Self::AssignmentToConstant(_) => "Cannot assign to a constant value",
|
||||||
|
Self::ExprTooDeep => "Expression exceeds maximum complexity",
|
||||||
|
Self::LoopBreak => "Break statement should only be used inside a loop"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ParseErrorType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::BadInput(s) | ParseErrorType::MalformedCallExpr(s) => {
|
||||||
|
write!(f, "{}", if s.is_empty() { self.desc() } else { s })
|
||||||
|
}
|
||||||
|
Self::ForbiddenConstantExpr(s) => {
|
||||||
|
write!(f, "Expecting a constant to assign to '{}'", s)
|
||||||
|
}
|
||||||
|
Self::UnknownOperator(s) => write!(f, "{}: '{}'", self.desc(), s),
|
||||||
|
|
||||||
|
Self::MalformedIndexExpr(s) => {
|
||||||
|
write!(f, "{}", if s.is_empty() { self.desc() } else { s })
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::MalformedInExpr(s) => write!(f, "{}", if s.is_empty() { self.desc() } else { s }),
|
||||||
|
|
||||||
|
Self::DuplicatedProperty(s) => {
|
||||||
|
write!(f, "Duplicated property '{}' for object map literal", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::ExprExpected(s) => write!(f, "Expecting {} expression", s),
|
||||||
|
|
||||||
|
Self::FnMissingParams(s) => write!(f, "Expecting parameters for function '{}'", s),
|
||||||
|
|
||||||
|
Self::FnMissingBody(s) => {
|
||||||
|
write!(f, "Expecting body statement block for function '{}'", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::FnDuplicatedParam(s, arg) => {
|
||||||
|
write!(f, "Duplicated parameter '{}' for function '{}'", arg, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::DuplicatedExport(s) => write!(
|
||||||
|
f,
|
||||||
|
"Duplicated variable/function '{}' in export statement",
|
||||||
|
s
|
||||||
|
),
|
||||||
|
|
||||||
|
Self::MissingToken(token, s) => write!(f, "Expecting '{}' {}", token, s),
|
||||||
|
|
||||||
|
Self::AssignmentToConstant(s) if s.is_empty() => write!(f, "{}", self.desc()),
|
||||||
|
Self::AssignmentToConstant(s) => write!(f, "Cannot assign to constant '{}'", s),
|
||||||
|
_ => write!(f, "{}", self.desc()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error when parsing a script.
|
/// Error when parsing a script.
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||||
pub struct ParseError(pub(crate) Box<ParseErrorType>, pub(crate) Position);
|
pub struct ParseError(pub Box<ParseErrorType>, pub Position);
|
||||||
|
|
||||||
impl ParseError {
|
|
||||||
/// Get the parse error.
|
|
||||||
pub fn error_type(&self) -> &ParseErrorType {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the location in the script of the error.
|
|
||||||
pub fn position(&self) -> Position {
|
|
||||||
self.1
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn desc(&self) -> &str {
|
|
||||||
match self.0.as_ref() {
|
|
||||||
ParseErrorType::BadInput(p) => p,
|
|
||||||
ParseErrorType::UnexpectedEOF => "Script is incomplete",
|
|
||||||
ParseErrorType::UnknownOperator(_) => "Unknown operator",
|
|
||||||
ParseErrorType::MissingToken(_, _) => "Expecting a certain token that is missing",
|
|
||||||
ParseErrorType::MalformedCallExpr(_) => "Invalid expression in function call arguments",
|
|
||||||
ParseErrorType::MalformedIndexExpr(_) => "Invalid index in indexing expression",
|
|
||||||
ParseErrorType::MalformedInExpr(_) => "Invalid 'in' expression",
|
|
||||||
ParseErrorType::DuplicatedProperty(_) => "Duplicated property in object map literal",
|
|
||||||
ParseErrorType::ForbiddenConstantExpr(_) => "Expecting a constant",
|
|
||||||
ParseErrorType::PropertyExpected => "Expecting name of a property",
|
|
||||||
ParseErrorType::VariableExpected => "Expecting name of a variable",
|
|
||||||
ParseErrorType::ExprExpected(_) => "Expecting an expression",
|
|
||||||
ParseErrorType::FnMissingName => "Expecting name in function declaration",
|
|
||||||
ParseErrorType::FnMissingParams(_) => "Expecting parameters in function declaration",
|
|
||||||
ParseErrorType::FnDuplicatedParam(_,_) => "Duplicated parameters in function declaration",
|
|
||||||
ParseErrorType::FnMissingBody(_) => "Expecting body statement block for function declaration",
|
|
||||||
ParseErrorType::WrongFnDefinition => "Function definitions must be at global level and cannot be inside a block or another function",
|
|
||||||
ParseErrorType::DuplicatedExport(_) => "Duplicated variable/function in export statement",
|
|
||||||
ParseErrorType::WrongExport => "Export statement can only appear at global level",
|
|
||||||
ParseErrorType::AssignmentToCopy => "Only a copy of the value is change with this assignment",
|
|
||||||
ParseErrorType::AssignmentToConstant(_) => "Cannot assign to a constant value",
|
|
||||||
ParseErrorType::ExprTooDeep => "Expression exceeds maximum complexity",
|
|
||||||
ParseErrorType::LoopBreak => "Break statement should only be used inside a loop"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for ParseError {}
|
impl Error for ParseError {}
|
||||||
|
|
||||||
impl fmt::Display for ParseError {
|
impl fmt::Display for ParseError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self.0.as_ref() {
|
fmt::Display::fmt(&self.0, f)?;
|
||||||
ParseErrorType::BadInput(s) | ParseErrorType::MalformedCallExpr(s) => {
|
|
||||||
write!(f, "{}", if s.is_empty() { self.desc() } else { s })?
|
|
||||||
}
|
|
||||||
ParseErrorType::ForbiddenConstantExpr(s) => {
|
|
||||||
write!(f, "Expecting a constant to assign to '{}'", s)?
|
|
||||||
}
|
|
||||||
ParseErrorType::UnknownOperator(s) => write!(f, "{}: '{}'", self.desc(), s)?,
|
|
||||||
|
|
||||||
ParseErrorType::MalformedIndexExpr(s) => {
|
|
||||||
write!(f, "{}", if s.is_empty() { self.desc() } else { s })?
|
|
||||||
}
|
|
||||||
|
|
||||||
ParseErrorType::MalformedInExpr(s) => {
|
|
||||||
write!(f, "{}", if s.is_empty() { self.desc() } else { s })?
|
|
||||||
}
|
|
||||||
|
|
||||||
ParseErrorType::DuplicatedProperty(s) => {
|
|
||||||
write!(f, "Duplicated property '{}' for object map literal", s)?
|
|
||||||
}
|
|
||||||
|
|
||||||
ParseErrorType::ExprExpected(s) => write!(f, "Expecting {} expression", s)?,
|
|
||||||
|
|
||||||
ParseErrorType::FnMissingParams(s) => {
|
|
||||||
write!(f, "Expecting parameters for function '{}'", s)?
|
|
||||||
}
|
|
||||||
|
|
||||||
ParseErrorType::FnMissingBody(s) => {
|
|
||||||
write!(f, "Expecting body statement block for function '{}'", s)?
|
|
||||||
}
|
|
||||||
|
|
||||||
ParseErrorType::FnDuplicatedParam(s, arg) => {
|
|
||||||
write!(f, "Duplicated parameter '{}' for function '{}'", arg, s)?
|
|
||||||
}
|
|
||||||
|
|
||||||
ParseErrorType::DuplicatedExport(s) => write!(
|
|
||||||
f,
|
|
||||||
"Duplicated variable/function '{}' in export statement",
|
|
||||||
s
|
|
||||||
)?,
|
|
||||||
|
|
||||||
ParseErrorType::MissingToken(token, s) => write!(f, "Expecting '{}' {}", token, s)?,
|
|
||||||
|
|
||||||
ParseErrorType::AssignmentToConstant(s) if s.is_empty() => {
|
|
||||||
write!(f, "{}", self.desc())?
|
|
||||||
}
|
|
||||||
ParseErrorType::AssignmentToConstant(s) => {
|
|
||||||
write!(f, "Cannot assign to constant '{}'", s)?
|
|
||||||
}
|
|
||||||
_ => write!(f, "{}", self.desc())?,
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.1.is_none() {
|
|
||||||
// Do not write any position if None
|
// Do not write any position if None
|
||||||
Ok(())
|
if !self.1.is_none() {
|
||||||
} else {
|
write!(f, " ({})", self.1)?;
|
||||||
write!(f, " ({})", self.1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ParseErrorType> for Box<EvalAltResult> {
|
||||||
|
fn from(err: ParseErrorType) -> Self {
|
||||||
|
Box::new(EvalAltResult::ErrorParsing(err, Position::none()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ParseError> for Box<EvalAltResult> {
|
||||||
|
fn from(err: ParseError) -> Self {
|
||||||
|
Box::new(EvalAltResult::ErrorParsing(*err.0, err.1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ use crate::stdlib::{
|
|||||||
char,
|
char,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
format,
|
format,
|
||||||
iter::{empty, Peekable},
|
iter::empty,
|
||||||
mem,
|
mem,
|
||||||
num::NonZeroUsize,
|
num::NonZeroUsize,
|
||||||
ops::{Add, Deref, DerefMut},
|
ops::{Add, Deref, DerefMut},
|
||||||
@ -751,15 +751,13 @@ fn parse_paren_expr(
|
|||||||
// ( xxx )
|
// ( xxx )
|
||||||
(Token::RightParen, _) => Ok(expr),
|
(Token::RightParen, _) => Ok(expr),
|
||||||
// ( <error>
|
// ( <error>
|
||||||
(Token::LexError(err), pos) => {
|
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
|
||||||
return Err(PERR::BadInput(err.to_string()).into_err(settings.pos))
|
|
||||||
}
|
|
||||||
// ( xxx ???
|
// ( xxx ???
|
||||||
(_, pos) => Err(PERR::MissingToken(
|
(_, pos) => Err(PERR::MissingToken(
|
||||||
Token::RightParen.into(),
|
Token::RightParen.into(),
|
||||||
"for a matching ( in this expression".into(),
|
"for a matching ( in this expression".into(),
|
||||||
)
|
)
|
||||||
.into_err(settings.pos)),
|
.into_err(pos)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -769,10 +767,9 @@ fn parse_call_expr(
|
|||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
id: String,
|
id: String,
|
||||||
mut modules: Option<Box<ModuleRef>>,
|
mut modules: Option<Box<ModuleRef>>,
|
||||||
mut settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
let (token, token_pos) = input.peek().unwrap();
|
let (token, _) = input.peek().unwrap();
|
||||||
settings.pos = *token_pos;
|
|
||||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||||
|
|
||||||
let mut args = StaticVec::new();
|
let mut args = StaticVec::new();
|
||||||
@ -888,7 +885,7 @@ fn parse_index_chain(
|
|||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
lhs: Expr,
|
lhs: Expr,
|
||||||
settings: ParseSettings,
|
mut settings: ParseSettings,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||||
|
|
||||||
@ -1031,11 +1028,12 @@ fn parse_index_chain(
|
|||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
// If another indexing level, right-bind it
|
// If another indexing level, right-bind it
|
||||||
(Token::LeftBracket, _) => {
|
(Token::LeftBracket, _) => {
|
||||||
let idx_pos = eat_token(input, Token::LeftBracket);
|
let prev_pos = settings.pos;
|
||||||
|
settings.pos = eat_token(input, Token::LeftBracket);
|
||||||
// Recursively parse the indexing chain, right-binding each
|
// Recursively parse the indexing chain, right-binding each
|
||||||
let idx_expr = parse_index_chain(input, state, idx_expr, settings.level_up())?;
|
let idx_expr = parse_index_chain(input, state, idx_expr, settings.level_up())?;
|
||||||
// Indexing binds to right
|
// Indexing binds to right
|
||||||
Ok(Expr::Index(Box::new((lhs, idx_expr, settings.pos))))
|
Ok(Expr::Index(Box::new((lhs, idx_expr, prev_pos))))
|
||||||
}
|
}
|
||||||
// Otherwise terminate the indexing chain
|
// Otherwise terminate the indexing chain
|
||||||
_ => {
|
_ => {
|
||||||
@ -1257,11 +1255,13 @@ fn parse_primary(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (token, token_pos) = input.next().unwrap();
|
let (token, token_pos) = input.next().unwrap();
|
||||||
|
settings.pos = token_pos;
|
||||||
|
|
||||||
root_expr = match (root_expr, token) {
|
root_expr = match (root_expr, token) {
|
||||||
// Function call
|
// Function call
|
||||||
(Expr::Variable(x), Token::LeftParen) => {
|
(Expr::Variable(x), Token::LeftParen) => {
|
||||||
let ((name, pos), modules, _, _) = *x;
|
let ((name, pos), modules, _, _) = *x;
|
||||||
|
settings.pos = pos;
|
||||||
parse_call_expr(input, state, name, modules, settings.level_up())?
|
parse_call_expr(input, state, name, modules, settings.level_up())?
|
||||||
}
|
}
|
||||||
(Expr::Property(_), _) => unreachable!(),
|
(Expr::Property(_), _) => unreachable!(),
|
||||||
@ -1269,6 +1269,7 @@ fn parse_primary(
|
|||||||
(Expr::Variable(x), Token::DoubleColon) => match input.next().unwrap() {
|
(Expr::Variable(x), Token::DoubleColon) => match input.next().unwrap() {
|
||||||
(Token::Identifier(id2), pos2) => {
|
(Token::Identifier(id2), pos2) => {
|
||||||
let ((name, pos), mut modules, _, index) = *x;
|
let ((name, pos), mut modules, _, index) = *x;
|
||||||
|
|
||||||
if let Some(ref mut modules) = modules {
|
if let Some(ref mut modules) = modules {
|
||||||
modules.push((name, pos));
|
modules.push((name, pos));
|
||||||
} else {
|
} else {
|
||||||
@ -1284,7 +1285,6 @@ fn parse_primary(
|
|||||||
// Indexing
|
// Indexing
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
(expr, Token::LeftBracket) => {
|
(expr, Token::LeftBracket) => {
|
||||||
settings.pos = token_pos;
|
|
||||||
parse_index_chain(input, state, expr, settings.level_up())?
|
parse_index_chain(input, state, expr, settings.level_up())?
|
||||||
}
|
}
|
||||||
// Unknown postfix operator
|
// Unknown postfix operator
|
||||||
@ -1416,9 +1416,11 @@ fn make_assignment_stmt<'a>(
|
|||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
match &lhs {
|
match &lhs {
|
||||||
|
// var (non-indexed) = rhs
|
||||||
Expr::Variable(x) if x.3.is_none() => {
|
Expr::Variable(x) if x.3.is_none() => {
|
||||||
Ok(Expr::Assignment(Box::new((lhs, fn_name.into(), rhs, pos))))
|
Ok(Expr::Assignment(Box::new((lhs, fn_name.into(), rhs, pos))))
|
||||||
}
|
}
|
||||||
|
// var (indexed) = rhs
|
||||||
Expr::Variable(x) => {
|
Expr::Variable(x) => {
|
||||||
let ((name, name_pos), _, _, index) = x.as_ref();
|
let ((name, name_pos), _, _, index) = x.as_ref();
|
||||||
match state.stack[(state.len() - index.unwrap().get())].1 {
|
match state.stack[(state.len() - index.unwrap().get())].1 {
|
||||||
@ -1432,10 +1434,13 @@ fn make_assignment_stmt<'a>(
|
|||||||
ScopeEntryType::Module => unreachable!(),
|
ScopeEntryType::Module => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// xxx[???] = rhs, xxx.??? = rhs
|
||||||
Expr::Index(x) | Expr::Dot(x) => match &x.0 {
|
Expr::Index(x) | Expr::Dot(x) => match &x.0 {
|
||||||
|
// var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs
|
||||||
Expr::Variable(x) if x.3.is_none() => {
|
Expr::Variable(x) if x.3.is_none() => {
|
||||||
Ok(Expr::Assignment(Box::new((lhs, fn_name.into(), rhs, pos))))
|
Ok(Expr::Assignment(Box::new((lhs, fn_name.into(), rhs, pos))))
|
||||||
}
|
}
|
||||||
|
// var[???] (indexed) = rhs, var.??? (indexed) = rhs
|
||||||
Expr::Variable(x) => {
|
Expr::Variable(x) => {
|
||||||
let ((name, name_pos), _, _, index) = x.as_ref();
|
let ((name, name_pos), _, _, index) = x.as_ref();
|
||||||
match state.stack[(state.len() - index.unwrap().get())].1 {
|
match state.stack[(state.len() - index.unwrap().get())].1 {
|
||||||
@ -1449,11 +1454,18 @@ fn make_assignment_stmt<'a>(
|
|||||||
ScopeEntryType::Module => unreachable!(),
|
ScopeEntryType::Module => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// expr[???] = rhs, expr.??? = rhs
|
||||||
_ => Err(PERR::AssignmentToCopy.into_err(x.0.position())),
|
_ => Err(PERR::AssignmentToCopy.into_err(x.0.position())),
|
||||||
},
|
},
|
||||||
|
// const_expr = rhs
|
||||||
expr if expr.is_constant() => {
|
expr if expr.is_constant() => {
|
||||||
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position()))
|
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position()))
|
||||||
}
|
}
|
||||||
|
// ??? && ??? = rhs, ??? || ??? = rhs
|
||||||
|
Expr::And(_) | Expr::Or(_) => {
|
||||||
|
Err(PERR::BadInput("Possibly a typo of '=='?".to_string()).into_err(pos))
|
||||||
|
}
|
||||||
|
// expr = rhs
|
||||||
_ => Err(PERR::AssignmentToCopy.into_err(lhs.position())),
|
_ => Err(PERR::AssignmentToCopy.into_err(lhs.position())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
148
src/result.rs
148
src/result.rs
@ -1,7 +1,7 @@
|
|||||||
//! Module containing error definitions for the evaluation process.
|
//! Module containing error definitions for the evaluation process.
|
||||||
|
|
||||||
use crate::any::Dynamic;
|
use crate::any::Dynamic;
|
||||||
use crate::error::ParseError;
|
use crate::error::ParseErrorType;
|
||||||
use crate::parser::INT;
|
use crate::parser::INT;
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ use crate::stdlib::path::PathBuf;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum EvalAltResult {
|
pub enum EvalAltResult {
|
||||||
/// Syntax error.
|
/// Syntax error.
|
||||||
ErrorParsing(ParseError),
|
ErrorParsing(ParseErrorType, Position),
|
||||||
|
|
||||||
/// Error reading from a script file. Wrapped value is the path of the script file.
|
/// Error reading from a script file. Wrapped value is the path of the script file.
|
||||||
///
|
///
|
||||||
@ -101,7 +101,7 @@ impl EvalAltResult {
|
|||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Self::ErrorReadingScriptFile(_, _, _) => "Cannot read from script file",
|
Self::ErrorReadingScriptFile(_, _, _) => "Cannot read from script file",
|
||||||
|
|
||||||
Self::ErrorParsing(p) => p.desc(),
|
Self::ErrorParsing(p, _) => p.desc(),
|
||||||
Self::ErrorInFunctionCall(_, _, _) => "Error in called function",
|
Self::ErrorInFunctionCall(_, _, _) => "Error in called function",
|
||||||
Self::ErrorFunctionNotFound(_, _) => "Function not found",
|
Self::ErrorFunctionNotFound(_, _) => "Function not found",
|
||||||
Self::ErrorBooleanArgMismatch(_, _) => "Boolean operator expects boolean operands",
|
Self::ErrorBooleanArgMismatch(_, _) => "Boolean operator expects boolean operands",
|
||||||
@ -153,95 +153,89 @@ impl Error for EvalAltResult {}
|
|||||||
impl fmt::Display for EvalAltResult {
|
impl fmt::Display for EvalAltResult {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let desc = self.desc();
|
let desc = self.desc();
|
||||||
|
let pos = self.position();
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Self::ErrorReadingScriptFile(path, pos, err) if pos.is_none() => {
|
Self::ErrorReadingScriptFile(path, _, err) => {
|
||||||
write!(f, "{} '{}': {}", desc, path.display(), err)
|
write!(f, "{} '{}': {}", desc, path.display(), err)?
|
||||||
}
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
Self::ErrorReadingScriptFile(path, pos, err) => {
|
|
||||||
write!(f, "{} '{}': {} ({})", desc, path.display(), err, pos)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::ErrorParsing(p) => write!(f, "Syntax error: {}", p),
|
Self::ErrorParsing(p, _) => write!(f, "Syntax error: {}", p)?,
|
||||||
|
|
||||||
Self::ErrorInFunctionCall(s, err, pos) => {
|
Self::ErrorInFunctionCall(s, err, _) => {
|
||||||
write!(f, "Error in call to function '{}' ({}): {}", s, pos, err)
|
write!(f, "Error in call to function '{}' : {}", s, err)?
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::ErrorFunctionNotFound(s, pos)
|
Self::ErrorFunctionNotFound(s, _)
|
||||||
| Self::ErrorVariableNotFound(s, pos)
|
| Self::ErrorVariableNotFound(s, _)
|
||||||
| Self::ErrorModuleNotFound(s, pos) => write!(f, "{}: '{}' ({})", desc, s, pos),
|
| Self::ErrorModuleNotFound(s, _) => write!(f, "{}: '{}'", desc, s)?,
|
||||||
|
|
||||||
Self::ErrorDotExpr(s, pos) if !s.is_empty() => write!(f, "{} {} ({})", desc, s, pos),
|
Self::ErrorDotExpr(s, _) if !s.is_empty() => write!(f, "{} {}", desc, s)?,
|
||||||
|
|
||||||
Self::ErrorIndexingType(_, pos)
|
Self::ErrorIndexingType(_, _)
|
||||||
| Self::ErrorNumericIndexExpr(pos)
|
| Self::ErrorNumericIndexExpr(_)
|
||||||
| Self::ErrorStringIndexExpr(pos)
|
| Self::ErrorStringIndexExpr(_)
|
||||||
| Self::ErrorImportExpr(pos)
|
| Self::ErrorImportExpr(_)
|
||||||
| Self::ErrorLogicGuard(pos)
|
| Self::ErrorLogicGuard(_)
|
||||||
| Self::ErrorFor(pos)
|
| Self::ErrorFor(_)
|
||||||
| Self::ErrorAssignmentToUnknownLHS(pos)
|
| Self::ErrorAssignmentToUnknownLHS(_)
|
||||||
| Self::ErrorInExpr(pos)
|
| Self::ErrorInExpr(_)
|
||||||
| Self::ErrorDotExpr(_, pos)
|
| Self::ErrorDotExpr(_, _)
|
||||||
| Self::ErrorTooManyOperations(pos)
|
| Self::ErrorTooManyOperations(_)
|
||||||
| Self::ErrorTooManyModules(pos)
|
| Self::ErrorTooManyModules(_)
|
||||||
| Self::ErrorStackOverflow(pos)
|
| Self::ErrorStackOverflow(_)
|
||||||
| Self::ErrorTerminated(pos) => write!(f, "{} ({})", desc, pos),
|
| Self::ErrorTerminated(_) => write!(f, "{}", desc)?,
|
||||||
|
|
||||||
Self::ErrorRuntime(s, pos) => {
|
Self::ErrorRuntime(s, _) => write!(f, "{}", if s.is_empty() { desc } else { s })?,
|
||||||
write!(f, "{} ({})", if s.is_empty() { desc } else { s }, pos)
|
|
||||||
|
Self::ErrorAssignmentToConstant(s, _) => write!(f, "{}: '{}'", desc, s)?,
|
||||||
|
Self::ErrorMismatchOutputType(s, _) => write!(f, "{}: {}", desc, s)?,
|
||||||
|
Self::ErrorArithmetic(s, _) => write!(f, "{}", s)?,
|
||||||
|
|
||||||
|
Self::ErrorLoopBreak(_, _) => write!(f, "{}", desc)?,
|
||||||
|
Self::Return(_, _) => write!(f, "{}", desc)?,
|
||||||
|
|
||||||
|
Self::ErrorBooleanArgMismatch(op, _) => {
|
||||||
|
write!(f, "{} operator expects boolean operands", op)?
|
||||||
}
|
}
|
||||||
|
Self::ErrorCharMismatch(_) => write!(f, "string indexing expects a character value")?,
|
||||||
Self::ErrorAssignmentToConstant(s, pos) => write!(f, "{}: '{}' ({})", desc, s, pos),
|
Self::ErrorArrayBounds(_, index, _) if *index < 0 => {
|
||||||
Self::ErrorMismatchOutputType(s, pos) => write!(f, "{}: {} ({})", desc, s, pos),
|
write!(f, "{}: {} < 0", desc, index)?
|
||||||
Self::ErrorArithmetic(s, pos) => write!(f, "{} ({})", s, pos),
|
|
||||||
|
|
||||||
Self::ErrorLoopBreak(_, pos) => write!(f, "{} ({})", desc, pos),
|
|
||||||
Self::Return(_, pos) => write!(f, "{} ({})", desc, pos),
|
|
||||||
|
|
||||||
Self::ErrorBooleanArgMismatch(op, pos) => {
|
|
||||||
write!(f, "{} operator expects boolean operands ({})", op, pos)
|
|
||||||
}
|
}
|
||||||
Self::ErrorCharMismatch(pos) => {
|
Self::ErrorArrayBounds(0, _, _) => write!(f, "{}", desc)?,
|
||||||
write!(f, "string indexing expects a character value ({})", pos)
|
Self::ErrorArrayBounds(1, index, _) => write!(
|
||||||
}
|
|
||||||
Self::ErrorArrayBounds(_, index, pos) if *index < 0 => {
|
|
||||||
write!(f, "{}: {} < 0 ({})", desc, index, pos)
|
|
||||||
}
|
|
||||||
Self::ErrorArrayBounds(0, _, pos) => write!(f, "{} ({})", desc, pos),
|
|
||||||
Self::ErrorArrayBounds(1, index, pos) => write!(
|
|
||||||
f,
|
f,
|
||||||
"Array index {} is out of bounds: only one element in the array ({})",
|
"Array index {} is out of bounds: only one element in the array",
|
||||||
index, pos
|
index
|
||||||
),
|
)?,
|
||||||
Self::ErrorArrayBounds(max, index, pos) => write!(
|
Self::ErrorArrayBounds(max, index, _) => write!(
|
||||||
f,
|
f,
|
||||||
"Array index {} is out of bounds: only {} elements in the array ({})",
|
"Array index {} is out of bounds: only {} elements in the array",
|
||||||
index, max, pos
|
index, max
|
||||||
),
|
)?,
|
||||||
Self::ErrorStringBounds(_, index, pos) if *index < 0 => {
|
Self::ErrorStringBounds(_, index, _) if *index < 0 => {
|
||||||
write!(f, "{}: {} < 0 ({})", desc, index, pos)
|
write!(f, "{}: {} < 0", desc, index)?
|
||||||
}
|
}
|
||||||
Self::ErrorStringBounds(0, _, pos) => write!(f, "{} ({})", desc, pos),
|
Self::ErrorStringBounds(0, _, _) => write!(f, "{}", desc)?,
|
||||||
Self::ErrorStringBounds(1, index, pos) => write!(
|
Self::ErrorStringBounds(1, index, _) => write!(
|
||||||
f,
|
f,
|
||||||
"String index {} is out of bounds: only one character in the string ({})",
|
"String index {} is out of bounds: only one character in the string",
|
||||||
index, pos
|
index
|
||||||
),
|
)?,
|
||||||
Self::ErrorStringBounds(max, index, pos) => write!(
|
Self::ErrorStringBounds(max, index, _) => write!(
|
||||||
f,
|
f,
|
||||||
"String index {} is out of bounds: only {} characters in the string ({})",
|
"String index {} is out of bounds: only {} characters in the string",
|
||||||
index, max, pos
|
index, max
|
||||||
),
|
)?,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ParseError> for Box<EvalAltResult> {
|
// Do not write any position if None
|
||||||
fn from(err: ParseError) -> Self {
|
if !pos.is_none() {
|
||||||
Box::new(EvalAltResult::ErrorParsing(err))
|
write!(f, " ({})", pos)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,9 +255,8 @@ impl EvalAltResult {
|
|||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Self::ErrorReadingScriptFile(_, pos, _) => *pos,
|
Self::ErrorReadingScriptFile(_, pos, _) => *pos,
|
||||||
|
|
||||||
Self::ErrorParsing(err) => err.position(),
|
Self::ErrorParsing(_, pos)
|
||||||
|
| Self::ErrorFunctionNotFound(_, pos)
|
||||||
Self::ErrorFunctionNotFound(_, pos)
|
|
||||||
| Self::ErrorInFunctionCall(_, _, pos)
|
| Self::ErrorInFunctionCall(_, _, pos)
|
||||||
| Self::ErrorBooleanArgMismatch(_, pos)
|
| Self::ErrorBooleanArgMismatch(_, pos)
|
||||||
| Self::ErrorCharMismatch(pos)
|
| Self::ErrorCharMismatch(pos)
|
||||||
@ -299,9 +292,8 @@ impl EvalAltResult {
|
|||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Self::ErrorReadingScriptFile(_, pos, _) => *pos = new_position,
|
Self::ErrorReadingScriptFile(_, pos, _) => *pos = new_position,
|
||||||
|
|
||||||
Self::ErrorParsing(err) => err.1 = new_position,
|
Self::ErrorParsing(_, pos)
|
||||||
|
| Self::ErrorFunctionNotFound(_, pos)
|
||||||
Self::ErrorFunctionNotFound(_, pos)
|
|
||||||
| Self::ErrorInFunctionCall(_, _, pos)
|
| Self::ErrorInFunctionCall(_, _, pos)
|
||||||
| Self::ErrorBooleanArgMismatch(_, pos)
|
| Self::ErrorBooleanArgMismatch(_, pos)
|
||||||
| Self::ErrorCharMismatch(pos)
|
| Self::ErrorCharMismatch(pos)
|
||||||
|
@ -1,19 +1,17 @@
|
|||||||
#![cfg(not(feature = "no_function"))]
|
#![cfg(not(feature = "no_function"))]
|
||||||
use rhai::{Engine, EvalAltResult, Func, ParseErrorType, Scope, INT};
|
use rhai::{Engine, EvalAltResult, Func, ParseError, ParseErrorType, Scope, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fn() -> Result<(), Box<EvalAltResult>> {
|
fn test_fn() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
// Expect duplicated parameters error
|
// Expect duplicated parameters error
|
||||||
match engine
|
assert!(matches!(
|
||||||
|
engine
|
||||||
.compile("fn hello(x, x) { x }")
|
.compile("fn hello(x, x) { x }")
|
||||||
.expect_err("should be error")
|
.expect_err("should be error"),
|
||||||
.error_type()
|
ParseError(x, _) if *x == ParseErrorType::FnDuplicatedParam("hello".to_string(), "x".to_string())
|
||||||
{
|
));
|
||||||
ParseErrorType::FnDuplicatedParam(f, p) if f == "hello" && p == "x" => (),
|
|
||||||
_ => assert!(false, "wrong error"),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -7,14 +7,16 @@ fn test_constant() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(engine.eval::<INT>("const x = 123; x")?, 123);
|
assert_eq!(engine.eval::<INT>("const x = 123; x")?, 123);
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.eval::<INT>("const x = 123; x = 42;").expect_err("expects error"),
|
*engine
|
||||||
EvalAltResult::ErrorParsing(err) if err.error_type() == &ParseErrorType::AssignmentToConstant("x".to_string())
|
.eval::<INT>("const x = 123; x = 42;")
|
||||||
|
.expect_err("expects error"),
|
||||||
|
EvalAltResult::ErrorParsing(ParseErrorType::AssignmentToConstant(x), _) if x == "x"
|
||||||
));
|
));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.eval::<INT>("const x = [1, 2, 3, 4, 5]; x[2] = 42;").expect_err("expects error"),
|
*engine.eval::<INT>("const x = [1, 2, 3, 4, 5]; x[2] = 42;").expect_err("expects error"),
|
||||||
EvalAltResult::ErrorParsing(err) if err.error_type() == &ParseErrorType::AssignmentToConstant("x".to_string())
|
EvalAltResult::ErrorParsing(ParseErrorType::AssignmentToConstant(x), _) if x == "x"
|
||||||
));
|
));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -21,6 +21,8 @@ fn test_expressions() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(engine.eval_expression::<()>("x = 42").is_err());
|
assert!(engine.eval_expression::<()>("x = 42").is_err());
|
||||||
assert!(engine.compile_expression("let x = 42").is_err());
|
assert!(engine.compile_expression("let x = 42").is_err());
|
||||||
|
|
||||||
|
engine.compile("40 + { let x = 2; x }")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Engine, EvalAltResult, INT};
|
use rhai::{Engine, EvalAltResult, ParseError, ParseErrorType, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_loop() -> Result<(), Box<EvalAltResult>> {
|
fn test_loop() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -26,5 +26,15 @@ fn test_loop() -> Result<(), Box<EvalAltResult>> {
|
|||||||
21
|
21
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
engine.compile("let x = 0; break;").expect_err("should error"),
|
||||||
|
ParseError(x, _) if *x == ParseErrorType::LoopBreak
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
engine.compile("let x = 0; if x > 0 { continue; }").expect_err("should error"),
|
||||||
|
ParseError(x, _) if *x == ParseErrorType::LoopBreak
|
||||||
|
));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#![cfg(not(feature = "no_module"))]
|
#![cfg(not(feature = "no_module"))]
|
||||||
use rhai::{module_resolvers, Engine, EvalAltResult, Module, Scope, INT};
|
use rhai::{
|
||||||
|
module_resolvers, Engine, EvalAltResult, Module, ParseError, ParseErrorType, Scope, INT,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_module() {
|
fn test_module() {
|
||||||
@ -231,3 +233,20 @@ fn test_module_from_ast() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_export() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
engine.compile(r"let x = 10; { export x; }").expect_err("should error"),
|
||||||
|
ParseError(x, _) if *x == ParseErrorType::WrongExport
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
engine.compile(r"fn abc(x) { export x; }").expect_err("should error"),
|
||||||
|
ParseError(x, _) if *x == ParseErrorType::WrongExport
|
||||||
|
));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user