All symbols to start a custom syntax.
This commit is contained in:
parent
426055d4d3
commit
00f2b07d38
@ -12,6 +12,7 @@ Bug fixes
|
||||
* Syntax such as `foo.bar::baz` no longer panics, but returns a proper parse error.
|
||||
* `x += y` where `x` and `y` are `char` now works correctly.
|
||||
* Expressions such as `!inside` now parses correctly instead of as `!in` followed by `side`.
|
||||
* Custom syntax starting with symbols now works correctly and no longer raises a parse error.
|
||||
|
||||
Potentially breaking changes
|
||||
----------------------------
|
||||
|
@ -255,7 +255,8 @@ impl Engine {
|
||||
// Markers not in first position
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
CUSTOM_SYNTAX_MARKER_FLOAT if !segments.is_empty() => s.into(),
|
||||
// Standard or reserved keyword/symbol not in first position
|
||||
|
||||
// Keyword/symbol not in first position
|
||||
_ if !segments.is_empty() && token.is_some() => {
|
||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||
if (self
|
||||
@ -274,6 +275,7 @@ impl Engine {
|
||||
}
|
||||
s.into()
|
||||
}
|
||||
|
||||
// Standard keyword in first position but not disabled
|
||||
_ if segments.is_empty()
|
||||
&& token.as_ref().map_or(false, Token::is_standard_keyword)
|
||||
@ -291,8 +293,11 @@ impl Engine {
|
||||
)
|
||||
.into_err(Position::NONE));
|
||||
}
|
||||
// Identifier in first position
|
||||
_ if segments.is_empty() && is_valid_identifier(s) => {
|
||||
|
||||
// Identifier or symbol in first position
|
||||
_ if segments.is_empty()
|
||||
&& (is_valid_identifier(s) || is_reserved_keyword_or_symbol(s)) =>
|
||||
{
|
||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||
if self
|
||||
.disabled_symbols
|
||||
@ -310,6 +315,7 @@ impl Engine {
|
||||
}
|
||||
s.into()
|
||||
}
|
||||
|
||||
// Anything else is an error
|
||||
_ => {
|
||||
return Err(LexError::ImproperSymbol(
|
||||
@ -326,23 +332,20 @@ impl Engine {
|
||||
segments.push(seg);
|
||||
}
|
||||
|
||||
// If the syntax has no symbols, just ignore the registration
|
||||
// If the syntax has nothing, just ignore the registration
|
||||
if segments.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
|
||||
// The first keyword is the discriminator
|
||||
// The first keyword/symbol is the discriminator
|
||||
let key = segments[0].clone();
|
||||
|
||||
self.register_custom_syntax_with_state_raw(
|
||||
key,
|
||||
// Construct the parsing function
|
||||
move |stream, _, _| {
|
||||
if stream.len() >= segments.len() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(segments[stream.len()].clone()))
|
||||
}
|
||||
move |stream, _, _| match stream.len() {
|
||||
len if len >= segments.len() => Ok(None),
|
||||
len => Ok(Some(segments[len].clone())),
|
||||
},
|
||||
scope_may_be_changed,
|
||||
move |context, expressions, _| func(context, expressions),
|
||||
|
@ -3792,7 +3792,7 @@ impl Engine {
|
||||
(.., pos) => {
|
||||
return Err(PERR::MissingToken(
|
||||
Token::Pipe.into(),
|
||||
"to close the parameters list of anonymous function".into(),
|
||||
"to close the parameters list of anonymous function or closure".into(),
|
||||
)
|
||||
.into_err(pos))
|
||||
}
|
||||
|
@ -2066,7 +2066,7 @@ pub const fn is_id_continue(x: char) -> bool {
|
||||
x.is_ascii_alphanumeric() || x == '_'
|
||||
}
|
||||
|
||||
/// Is a piece of syntax a reserved keyword or symbol?
|
||||
/// Is a piece of syntax a reserved keyword or reserved symbol?
|
||||
#[must_use]
|
||||
pub fn is_reserved_keyword_or_symbol(syntax: &str) -> bool {
|
||||
match syntax {
|
||||
|
@ -251,6 +251,62 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_syntax_matrix() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
engine.disable_symbol("|");
|
||||
|
||||
engine.register_custom_syntax(
|
||||
[
|
||||
"@", //
|
||||
"|", "$expr$", "$expr$", "$expr$", "|", //
|
||||
"|", "$expr$", "$expr$", "$expr$", "|", //
|
||||
"|", "$expr$", "$expr$", "$expr$", "|",
|
||||
],
|
||||
false,
|
||||
|context, inputs| {
|
||||
let mut values = [[0; 3]; 3];
|
||||
|
||||
for y in 0..3 {
|
||||
for x in 0..3 {
|
||||
let offset = y * 3 + x;
|
||||
|
||||
match context.eval_expression_tree(&inputs[offset])?.as_int() {
|
||||
Ok(v) => values[y][x] = v,
|
||||
Err(typ) => {
|
||||
return Err(Box::new(EvalAltResult::ErrorMismatchDataType(
|
||||
"integer".to_string(),
|
||||
typ.to_string(),
|
||||
inputs[offset].position(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Dynamic::from(values))
|
||||
},
|
||||
)?;
|
||||
|
||||
let r = engine.eval::<[[INT; 3]; 3]>(
|
||||
"
|
||||
let a = 42;
|
||||
let b = 123;
|
||||
let c = 1;
|
||||
let d = 99;
|
||||
|
||||
@| a b 0 |
|
||||
| -b a 0 |
|
||||
| 0 0 c*d |
|
||||
",
|
||||
)?;
|
||||
|
||||
assert_eq!(r, [[42, 123, 0], [-123, 42, 0], [0, 0, 99]]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
|
Loading…
Reference in New Issue
Block a user