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.
|
* 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.
|
* `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`.
|
* 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
|
Potentially breaking changes
|
||||||
----------------------------
|
----------------------------
|
||||||
|
@ -255,7 +255,8 @@ impl Engine {
|
|||||||
// Markers not in first position
|
// Markers not in first position
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
CUSTOM_SYNTAX_MARKER_FLOAT if !segments.is_empty() => s.into(),
|
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() => {
|
_ if !segments.is_empty() && token.is_some() => {
|
||||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||||
if (self
|
if (self
|
||||||
@ -274,6 +275,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
s.into()
|
s.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Standard keyword in first position but not disabled
|
// Standard keyword in first position but not disabled
|
||||||
_ if segments.is_empty()
|
_ if segments.is_empty()
|
||||||
&& token.as_ref().map_or(false, Token::is_standard_keyword)
|
&& token.as_ref().map_or(false, Token::is_standard_keyword)
|
||||||
@ -291,8 +293,11 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
.into_err(Position::NONE));
|
.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
|
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||||
if self
|
if self
|
||||||
.disabled_symbols
|
.disabled_symbols
|
||||||
@ -310,6 +315,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
s.into()
|
s.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Anything else is an error
|
// Anything else is an error
|
||||||
_ => {
|
_ => {
|
||||||
return Err(LexError::ImproperSymbol(
|
return Err(LexError::ImproperSymbol(
|
||||||
@ -326,23 +332,20 @@ impl Engine {
|
|||||||
segments.push(seg);
|
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() {
|
if segments.is_empty() {
|
||||||
return Ok(self);
|
return Ok(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The first keyword is the discriminator
|
// The first keyword/symbol is the discriminator
|
||||||
let key = segments[0].clone();
|
let key = segments[0].clone();
|
||||||
|
|
||||||
self.register_custom_syntax_with_state_raw(
|
self.register_custom_syntax_with_state_raw(
|
||||||
key,
|
key,
|
||||||
// Construct the parsing function
|
// Construct the parsing function
|
||||||
move |stream, _, _| {
|
move |stream, _, _| match stream.len() {
|
||||||
if stream.len() >= segments.len() {
|
len if len >= segments.len() => Ok(None),
|
||||||
Ok(None)
|
len => Ok(Some(segments[len].clone())),
|
||||||
} else {
|
|
||||||
Ok(Some(segments[stream.len()].clone()))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
scope_may_be_changed,
|
scope_may_be_changed,
|
||||||
move |context, expressions, _| func(context, expressions),
|
move |context, expressions, _| func(context, expressions),
|
||||||
|
@ -3792,7 +3792,7 @@ impl Engine {
|
|||||||
(.., pos) => {
|
(.., pos) => {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
Token::Pipe.into(),
|
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))
|
.into_err(pos))
|
||||||
}
|
}
|
||||||
|
@ -2066,7 +2066,7 @@ pub const fn is_id_continue(x: char) -> bool {
|
|||||||
x.is_ascii_alphanumeric() || x == '_'
|
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]
|
#[must_use]
|
||||||
pub fn is_reserved_keyword_or_symbol(syntax: &str) -> bool {
|
pub fn is_reserved_keyword_or_symbol(syntax: &str) -> bool {
|
||||||
match syntax {
|
match syntax {
|
||||||
|
@ -251,6 +251,62 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
Ok(())
|
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]
|
#[test]
|
||||||
fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
Loading…
Reference in New Issue
Block a user