Allow non-custom reserved symbols in custom syntax.

This commit is contained in:
Stephen Chung
2021-12-16 18:01:49 +08:00
parent f92cbe1f6d
commit fbc2b1f13d
8 changed files with 77 additions and 60 deletions

View File

@@ -1,8 +1,8 @@
//! Module containing all deprecated API that will be removed in the next major version.
use crate::{
Dynamic, Engine, EvalAltResult, FnPtr, ImmutableString, NativeCallContext, RhaiResult, Scope,
AST,
Dynamic, Engine, EvalAltResult, Expression, FnPtr, ImmutableString, NativeCallContext,
RhaiResult, Scope, AST,
};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
@@ -304,3 +304,19 @@ impl FnPtr {
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()
}
}

View File

@@ -65,11 +65,17 @@ impl<'a> From<&'a Expr> for Expression<'a> {
}
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)]
#[must_use]
pub fn get_variable_name(&self) -> Option<&str> {
self.0.get_variable_name(true)
pub fn get_string_value(&self) -> Option<&str> {
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.
#[inline(always)]
@@ -109,6 +115,9 @@ impl Expression<'_> {
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
return match self.0 {
Expr::StringConstant(x, _) => unsafe_try_cast(x.clone()).ok(),
Expr::Variable(_, _, x) => {
unsafe_try_cast(Into::<ImmutableString>::into(&x.2)).ok()
}
_ => None,
};
}

View File

@@ -1401,10 +1401,7 @@ fn parse_primary(
let msg = format!("'{}' can only be used in functions", s);
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(LexError::UnexpectedInput(s.to_string()).into_err(settings.pos)),
_ => return Err(PERR::Reserved(s.to_string()).into_err(settings.pos)),
}
}
@@ -1698,12 +1695,12 @@ fn make_assignment_stmt(
ref e => Some(e.position()),
},
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(_, _, _))),
_ => 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()),
_ => None,
}
@@ -2277,15 +2274,9 @@ fn parse_expr(
settings.pos = input.peek().expect(NEVER_ENDS).1;
// Parse expression normally.
let precedence = Precedence::new(1);
let lhs = parse_unary(input, state, lib, settings.level_up())?;
parse_binary_op(
input,
state,
lib,
Precedence::new(1),
lhs,
settings.level_up(),
)
parse_binary_op(input, state, lib, precedence, lhs, settings.level_up())
}
/// Parse an if statement.

View File

@@ -2213,14 +2213,9 @@ impl<'a> Iterator for TokenIterator<'a> {
)),
// Reserved keyword/operator that is custom.
(_, 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.
(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))
},
// 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) => {
(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()) => {
if self.engine.disabled_symbols.contains(&*token.syntax()) {
// Disabled standard keyword/symbol
(Token::Custom(token.syntax().into()), pos)
} else {
// Active standard keyword - should never be a custom keyword!
unreachable!("{:?} is an active keyword", token)
unreachable!("`{:?}` is an active keyword", token)
}
}
// Disabled symbol

View File

@@ -1,5 +1,6 @@
//! Module containing error definitions for the parsing process.
use crate::tokenizer::is_valid_identifier;
use crate::{EvalAltResult, Position};
#[cfg(feature = "no_std")]
use core_error::Error;
@@ -120,7 +121,7 @@ pub enum ParseErrorType {
PropertyExpected,
/// Missing a variable name after the `let`, `const`, `for` or `catch` keywords.
VariableExpected,
/// An identifier is a reserved keyword.
/// An identifier is a reserved symbol.
Reserved(String),
/// An expression is of the wrong type.
/// 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::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::WrongSwitchIntegerCase => f.write_str("Integer switch case cannot follow a range case"),
Self::WrongSwitchDefaultCase => f.write_str("Default switch case must be the last"),