Allow non-custom reserved symbols in custom syntax.
This commit is contained in:
parent
f92cbe1f6d
commit
fbc2b1f13d
@ -12,6 +12,11 @@ New features
|
|||||||
|
|
||||||
* Added support for integer _ranges_ via the `..` and `..=` operators.
|
* Added support for integer _ranges_ via the `..` and `..=` operators.
|
||||||
|
|
||||||
|
Deprecated API's
|
||||||
|
----------------
|
||||||
|
|
||||||
|
* `Expression::get_variable_name` is deprecated in favor of the new `Expression::get_string_value`.
|
||||||
|
|
||||||
|
|
||||||
Version 1.3.1
|
Version 1.3.1
|
||||||
=============
|
=============
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
//! Module containing all deprecated API that will be removed in the next major version.
|
//! Module containing all deprecated API that will be removed in the next major version.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, Engine, EvalAltResult, FnPtr, ImmutableString, NativeCallContext, RhaiResult, Scope,
|
Dynamic, Engine, EvalAltResult, Expression, FnPtr, ImmutableString, NativeCallContext,
|
||||||
AST,
|
RhaiResult, Scope, AST,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -304,3 +304,19 @@ impl FnPtr {
|
|||||||
self.call_raw(context, this_ptr, arg_values)
|
self.call_raw(context, this_ptr, arg_values)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Expression<'_> {
|
||||||
|
/// If this expression is a variable name, return it. Otherwise [`None`].
|
||||||
|
///
|
||||||
|
/// # Deprecated
|
||||||
|
///
|
||||||
|
/// This method is deprecated. Use [`get_string_value`][Expression::get_string_value] instead.
|
||||||
|
///
|
||||||
|
/// This method will be removed in the next major version.
|
||||||
|
#[deprecated(since = "1.4.0", note = "use `get_string_value` instead")]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_variable_name(&self) -> Option<&str> {
|
||||||
|
self.get_string_value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -65,11 +65,17 @@ impl<'a> From<&'a Expr> for Expression<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Expression<'_> {
|
impl Expression<'_> {
|
||||||
/// If this expression is a variable name, return it. Otherwise [`None`].
|
/// Get the value of this expression if it is a variable name or a string constant.
|
||||||
|
///
|
||||||
|
/// Returns [`None`] also if the constant is not of the specified type.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_variable_name(&self) -> Option<&str> {
|
pub fn get_string_value(&self) -> Option<&str> {
|
||||||
self.0.get_variable_name(true)
|
match self.0 {
|
||||||
|
Expr::Variable(_, _, x) if x.1.is_none() => Some(x.2.as_str()),
|
||||||
|
Expr::StringConstant(x, _) => Some(x.as_str()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// Get the position of this expression.
|
/// Get the position of this expression.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -109,6 +115,9 @@ impl Expression<'_> {
|
|||||||
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Expr::StringConstant(x, _) => unsafe_try_cast(x.clone()).ok(),
|
Expr::StringConstant(x, _) => unsafe_try_cast(x.clone()).ok(),
|
||||||
|
Expr::Variable(_, _, x) => {
|
||||||
|
unsafe_try_cast(Into::<ImmutableString>::into(&x.2)).ok()
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1401,10 +1401,7 @@ fn parse_primary(
|
|||||||
let msg = format!("'{}' can only be used in functions", s);
|
let msg = format!("'{}' can only be used in functions", s);
|
||||||
return Err(LexError::ImproperSymbol(s.to_string(), msg).into_err(settings.pos));
|
return Err(LexError::ImproperSymbol(s.to_string(), msg).into_err(settings.pos));
|
||||||
}
|
}
|
||||||
_ if is_valid_identifier(s.chars()) => {
|
_ => return Err(PERR::Reserved(s.to_string()).into_err(settings.pos)),
|
||||||
return Err(PERR::Reserved(s.to_string()).into_err(settings.pos))
|
|
||||||
}
|
|
||||||
_ => return Err(LexError::UnexpectedInput(s.to_string()).into_err(settings.pos)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1698,12 +1695,12 @@ fn make_assignment_stmt(
|
|||||||
ref e => Some(e.position()),
|
ref e => Some(e.position()),
|
||||||
},
|
},
|
||||||
Expr::Index(x, term, _) | Expr::Dot(x, term, _) => match x.lhs {
|
Expr::Index(x, term, _) | Expr::Dot(x, term, _) => match x.lhs {
|
||||||
Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"),
|
Expr::Property(_) => unreachable!("unexpected `Expr::Property` in indexing"),
|
||||||
_ if !term => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _, _))),
|
_ if !term => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _, _))),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
Expr::Property(_) if parent_is_dot => None,
|
Expr::Property(_) if parent_is_dot => None,
|
||||||
Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"),
|
Expr::Property(_) => unreachable!("unexpected `Expr::Property` in indexing"),
|
||||||
e if parent_is_dot => Some(e.position()),
|
e if parent_is_dot => Some(e.position()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@ -2277,15 +2274,9 @@ fn parse_expr(
|
|||||||
settings.pos = input.peek().expect(NEVER_ENDS).1;
|
settings.pos = input.peek().expect(NEVER_ENDS).1;
|
||||||
|
|
||||||
// Parse expression normally.
|
// Parse expression normally.
|
||||||
|
let precedence = Precedence::new(1);
|
||||||
let lhs = parse_unary(input, state, lib, settings.level_up())?;
|
let lhs = parse_unary(input, state, lib, settings.level_up())?;
|
||||||
parse_binary_op(
|
parse_binary_op(input, state, lib, precedence, lhs, settings.level_up())
|
||||||
input,
|
|
||||||
state,
|
|
||||||
lib,
|
|
||||||
Precedence::new(1),
|
|
||||||
lhs,
|
|
||||||
settings.level_up(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an if statement.
|
/// Parse an if statement.
|
||||||
|
@ -2213,14 +2213,9 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||||||
)),
|
)),
|
||||||
// Reserved keyword/operator that is custom.
|
// Reserved keyword/operator that is custom.
|
||||||
(_, true) => Token::Custom(s),
|
(_, true) => Token::Custom(s),
|
||||||
// Reserved operator that is not custom.
|
|
||||||
(token, false) if !is_valid_identifier(token.chars()) => {
|
|
||||||
let msg = format!("'{}' is a reserved symbol", token);
|
|
||||||
Token::LexError(LERR::ImproperSymbol(s.to_string(), msg))
|
|
||||||
},
|
|
||||||
// Reserved keyword that is not custom and disabled.
|
// Reserved keyword that is not custom and disabled.
|
||||||
(token, false) if self.engine.disabled_symbols.contains(token) => {
|
(token, false) if self.engine.disabled_symbols.contains(token) => {
|
||||||
let msg = format!("reserved symbol '{}' is disabled", token);
|
let msg = format!("reserved {} '{}' is disabled", if is_valid_identifier(token.chars()) { "keyword"} else {"symbol"}, token);
|
||||||
Token::LexError(LERR::ImproperSymbol(s.to_string(), msg))
|
Token::LexError(LERR::ImproperSymbol(s.to_string(), msg))
|
||||||
},
|
},
|
||||||
// Reserved keyword/operator that is not custom.
|
// Reserved keyword/operator that is not custom.
|
||||||
@ -2230,14 +2225,14 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||||||
Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.contains_key(&*s) => {
|
Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.contains_key(&*s) => {
|
||||||
(Token::Custom(s), pos)
|
(Token::Custom(s), pos)
|
||||||
}
|
}
|
||||||
// Custom standard keyword/symbol - must be disabled
|
// Custom keyword/symbol - must be disabled
|
||||||
Some((token, pos)) if self.engine.custom_keywords.contains_key(&*token.syntax()) => {
|
Some((token, pos)) if self.engine.custom_keywords.contains_key(&*token.syntax()) => {
|
||||||
if self.engine.disabled_symbols.contains(&*token.syntax()) {
|
if self.engine.disabled_symbols.contains(&*token.syntax()) {
|
||||||
// Disabled standard keyword/symbol
|
// Disabled standard keyword/symbol
|
||||||
(Token::Custom(token.syntax().into()), pos)
|
(Token::Custom(token.syntax().into()), pos)
|
||||||
} else {
|
} else {
|
||||||
// Active standard keyword - should never be a custom keyword!
|
// Active standard keyword - should never be a custom keyword!
|
||||||
unreachable!("{:?} is an active keyword", token)
|
unreachable!("`{:?}` is an active keyword", token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Disabled symbol
|
// Disabled symbol
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
//! Module containing error definitions for the parsing process.
|
//! Module containing error definitions for the parsing process.
|
||||||
|
|
||||||
|
use crate::tokenizer::is_valid_identifier;
|
||||||
use crate::{EvalAltResult, Position};
|
use crate::{EvalAltResult, Position};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use core_error::Error;
|
use core_error::Error;
|
||||||
@ -120,7 +121,7 @@ pub enum ParseErrorType {
|
|||||||
PropertyExpected,
|
PropertyExpected,
|
||||||
/// Missing a variable name after the `let`, `const`, `for` or `catch` keywords.
|
/// Missing a variable name after the `let`, `const`, `for` or `catch` keywords.
|
||||||
VariableExpected,
|
VariableExpected,
|
||||||
/// An identifier is a reserved keyword.
|
/// An identifier is a reserved symbol.
|
||||||
Reserved(String),
|
Reserved(String),
|
||||||
/// An expression is of the wrong type.
|
/// An expression is of the wrong type.
|
||||||
/// Wrapped values are the type requested and type of the actual result.
|
/// Wrapped values are the type requested and type of the actual result.
|
||||||
@ -260,7 +261,8 @@ impl fmt::Display for ParseErrorType {
|
|||||||
},
|
},
|
||||||
|
|
||||||
Self::LiteralTooLarge(typ, max) => write!(f, "{} exceeds the maximum limit ({})", typ, max),
|
Self::LiteralTooLarge(typ, max) => write!(f, "{} exceeds the maximum limit ({})", typ, max),
|
||||||
Self::Reserved(s) => write!(f, "'{}' is a reserved keyword", s),
|
Self::Reserved(s) if is_valid_identifier(s.chars()) => write!(f, "'{}' is a reserved keyword", s),
|
||||||
|
Self::Reserved(s) => write!(f, "'{}' is a reserved symbol", s),
|
||||||
Self::UnexpectedEOF => f.write_str("Script is incomplete"),
|
Self::UnexpectedEOF => f.write_str("Script is incomplete"),
|
||||||
Self::WrongSwitchIntegerCase => f.write_str("Integer switch case cannot follow a range case"),
|
Self::WrongSwitchIntegerCase => f.write_str("Integer switch case cannot follow a range case"),
|
||||||
Self::WrongSwitchDefaultCase => f.write_str("Default switch case must be the last"),
|
Self::WrongSwitchDefaultCase => f.write_str("Default switch case must be the last"),
|
||||||
|
@ -25,13 +25,13 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
],
|
],
|
||||||
true,
|
true,
|
||||||
|context, inputs| {
|
|context, inputs| {
|
||||||
let var_name = inputs[0].get_variable_name().unwrap().to_string();
|
let var_name = inputs[0].get_string_value().unwrap();
|
||||||
let op = inputs[1].get_literal_value::<ImmutableString>().unwrap();
|
let op = inputs[1].get_literal_value::<ImmutableString>().unwrap();
|
||||||
let max = inputs[2].get_literal_value::<INT>().unwrap();
|
let max = inputs[2].get_literal_value::<INT>().unwrap();
|
||||||
let stmt = &inputs[3];
|
let stmt = &inputs[3];
|
||||||
let condition = &inputs[4];
|
let condition = &inputs[4];
|
||||||
|
|
||||||
context.scope_mut().push(var_name.clone(), 0 as INT);
|
context.scope_mut().push(var_name.to_string(), 0 as INT);
|
||||||
|
|
||||||
let mut count: INT = 0;
|
let mut count: INT = 0;
|
||||||
|
|
||||||
@ -151,14 +151,14 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
&["var", "$ident$", "=", "$expr$"],
|
&["var", "$ident$", "=", "$expr$"],
|
||||||
true,
|
true,
|
||||||
|context, inputs| {
|
|context, inputs| {
|
||||||
let var_name = inputs[0].get_variable_name().unwrap().to_string();
|
let var_name = inputs[0].get_string_value().unwrap();
|
||||||
let expr = &inputs[1];
|
let expr = &inputs[1];
|
||||||
|
|
||||||
// Evaluate the expression
|
// Evaluate the expression
|
||||||
let value = context.eval_expression_tree(expr)?;
|
let value = context.eval_expression_tree(expr)?;
|
||||||
|
|
||||||
if !context.scope().is_constant(&var_name).unwrap_or(false) {
|
if !context.scope().is_constant(var_name).unwrap_or(false) {
|
||||||
context.scope_mut().set_value(var_name, value);
|
context.scope_mut().set_value(var_name.to_string(), value);
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
} else {
|
} else {
|
||||||
Err(format!("variable {} is constant", var_name).into())
|
Err(format!("variable {} is constant", var_name).into())
|
||||||
@ -206,7 +206,7 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|context, inputs| {
|
|context, inputs| {
|
||||||
context.scope_mut().push("foo", 999 as INT);
|
context.scope_mut().push("foo", 999 as INT);
|
||||||
|
|
||||||
Ok(match inputs[0].get_variable_name().unwrap() {
|
Ok(match inputs[0].get_string_value().unwrap() {
|
||||||
"world"
|
"world"
|
||||||
if inputs
|
if inputs
|
||||||
.last()
|
.last()
|
||||||
@ -244,32 +244,31 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
|||||||
fn test_custom_syntax_raw2() -> Result<(), Box<EvalAltResult>> {
|
fn test_custom_syntax_raw2() -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
engine
|
engine.register_custom_syntax_raw(
|
||||||
.register_custom_operator("#", 255)?
|
"#",
|
||||||
.register_custom_syntax_raw(
|
|symbols, lookahead| match symbols.len() {
|
||||||
"#",
|
1 if lookahead == "-" => Ok(Some("$symbol$".into())),
|
||||||
|symbols, lookahead| match symbols.len() {
|
1 => Ok(Some("$int$".into())),
|
||||||
1 if lookahead == "-" => Ok(Some("$symbol$".into())),
|
2 if symbols[1] == "-" => Ok(Some("$int$".into())),
|
||||||
1 => Ok(Some("$int$".into())),
|
2 => Ok(None),
|
||||||
2 if symbols[1] == "-" => Ok(Some("$int$".into())),
|
3 => Ok(None),
|
||||||
2 => Ok(None),
|
_ => unreachable!(),
|
||||||
3 => Ok(None),
|
},
|
||||||
_ => unreachable!(),
|
false,
|
||||||
},
|
move |_, inputs| {
|
||||||
false,
|
let id = if inputs.len() == 2 {
|
||||||
move |_, inputs| {
|
-inputs[1].get_literal_value::<INT>().unwrap()
|
||||||
let id = if inputs.len() == 2 {
|
} else {
|
||||||
-inputs[1].get_literal_value::<INT>().unwrap()
|
inputs[0].get_literal_value::<INT>().unwrap()
|
||||||
} else {
|
};
|
||||||
inputs[0].get_literal_value::<INT>().unwrap()
|
Ok(id.into())
|
||||||
};
|
},
|
||||||
Ok(id.into())
|
);
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<INT>("#-1")?, -1);
|
||||||
assert_eq!(engine.eval::<INT>("let x = 41; x + #1")?, 42);
|
assert_eq!(engine.eval::<INT>("let x = 41; x + #1")?, 42);
|
||||||
assert_eq!(engine.eval::<INT>("#42/2")?, 21);
|
assert_eq!(engine.eval::<INT>("#42/2")?, 21);
|
||||||
assert_eq!(engine.eval::<INT>("#-1")?, -1);
|
assert_eq!(engine.eval::<INT>("sign(#1)")?, 1);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Engine, EvalAltResult, LexError, ParseErrorType, INT};
|
use rhai::{Engine, EvalAltResult, ParseErrorType, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tokens_disabled() {
|
fn test_tokens_disabled() {
|
||||||
@ -26,7 +26,7 @@ fn test_tokens_disabled() {
|
|||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.compile("let x = += 0;").expect_err("should error").0,
|
*engine.compile("let x = += 0;").expect_err("should error").0,
|
||||||
ParseErrorType::BadInput(LexError::UnexpectedInput(err)) if err == "+="
|
ParseErrorType::Reserved(err) if err == "+="
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user