New custom syntax expression types.
This commit is contained in:
parent
c3eb6d65f6
commit
a5031969ca
@ -20,6 +20,7 @@ New features
|
|||||||
* New syntax for `for` statement to include counter variable.
|
* New syntax for `for` statement to include counter variable.
|
||||||
* An integer value can now be indexed to get/set a single bit.
|
* An integer value can now be indexed to get/set a single bit.
|
||||||
* The `bits` method of an integer can be used to iterate through its bits.
|
* The `bits` method of an integer can be used to iterate through its bits.
|
||||||
|
* New `$bool$`, `$int$`, `$float$` and `$string$` expression types for custom syntax.
|
||||||
|
|
||||||
|
|
||||||
Version 0.20.2
|
Version 0.20.2
|
||||||
|
10
src/ast.rs
10
src/ast.rs
@ -1819,11 +1819,11 @@ impl fmt::Debug for Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Expr {
|
impl Expr {
|
||||||
/// Get the [`Dynamic`] value of a constant expression.
|
/// Get the [`Dynamic`] value of a literal constant expression.
|
||||||
///
|
///
|
||||||
/// Returns [`None`] if the expression is not constant.
|
/// Returns [`None`] if the expression is not a literal constant.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_constant_value(&self) -> Option<Dynamic> {
|
pub fn get_literal_value(&self) -> Option<Dynamic> {
|
||||||
Some(match self {
|
Some(match self {
|
||||||
Self::DynamicConstant(x, _) => x.as_ref().clone(),
|
Self::DynamicConstant(x, _) => x.as_ref().clone(),
|
||||||
Self::IntegerConstant(x, _) => (*x).into(),
|
Self::IntegerConstant(x, _) => (*x).into(),
|
||||||
@ -1838,7 +1838,7 @@ impl Expr {
|
|||||||
Self::Array(x, _) if self.is_constant() => {
|
Self::Array(x, _) if self.is_constant() => {
|
||||||
let mut arr = Array::with_capacity(x.len());
|
let mut arr = Array::with_capacity(x.len());
|
||||||
arr.extend(x.iter().map(|v| {
|
arr.extend(x.iter().map(|v| {
|
||||||
v.get_constant_value()
|
v.get_literal_value()
|
||||||
.expect("never fails because a constant array always has a constant value")
|
.expect("never fails because a constant array always has a constant value")
|
||||||
}));
|
}));
|
||||||
Dynamic::from_array(arr)
|
Dynamic::from_array(arr)
|
||||||
@ -1850,7 +1850,7 @@ impl Expr {
|
|||||||
x.0.iter().for_each(|(k, v)| {
|
x.0.iter().for_each(|(k, v)| {
|
||||||
*map.get_mut(k.name.as_str())
|
*map.get_mut(k.name.as_str())
|
||||||
.expect("never fails because the template should contain all the keys") = v
|
.expect("never fails because the template should contain all the keys") = v
|
||||||
.get_constant_value()
|
.get_literal_value()
|
||||||
.expect("never fails because a constant map always has a constant value")
|
.expect("never fails because a constant map always has a constant value")
|
||||||
});
|
});
|
||||||
Dynamic::from_map(map)
|
Dynamic::from_map(map)
|
||||||
|
@ -208,7 +208,7 @@ fn optimize_stmt_block(
|
|||||||
state.push_var(
|
state.push_var(
|
||||||
&x.name,
|
&x.name,
|
||||||
AccessMode::ReadOnly,
|
AccessMode::ReadOnly,
|
||||||
value_expr.get_constant_value(),
|
value_expr.get_literal_value(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -454,7 +454,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
|
|
||||||
// switch const { ... }
|
// switch const { ... }
|
||||||
Stmt::Switch(match_expr, x, pos) if match_expr.is_constant() => {
|
Stmt::Switch(match_expr, x, pos) if match_expr.is_constant() => {
|
||||||
let value = match_expr.get_constant_value().unwrap();
|
let value = match_expr.get_literal_value().unwrap();
|
||||||
let hasher = &mut get_hasher();
|
let hasher = &mut get_hasher();
|
||||||
value.hash(hasher);
|
value.hash(hasher);
|
||||||
let hash = hasher.finish();
|
let hash = hasher.finish();
|
||||||
@ -841,7 +841,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Array(_, _) if expr.is_constant() => {
|
Expr::Array(_, _) if expr.is_constant() => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = Expr::DynamicConstant(Box::new(expr.get_constant_value().unwrap()), expr.position());
|
*expr = Expr::DynamicConstant(Box::new(expr.get_literal_value().unwrap()), expr.position());
|
||||||
}
|
}
|
||||||
// [ items .. ]
|
// [ items .. ]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -850,7 +850,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Map(_, _) if expr.is_constant() => {
|
Expr::Map(_, _) if expr.is_constant() => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = Expr::DynamicConstant(Box::new(expr.get_constant_value().unwrap()), expr.position());
|
*expr = Expr::DynamicConstant(Box::new(expr.get_literal_value().unwrap()), expr.position());
|
||||||
}
|
}
|
||||||
// #{ key:value, .. }
|
// #{ key:value, .. }
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -942,7 +942,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
|
|||||||
=> {
|
=> {
|
||||||
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| match e {
|
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| match e {
|
||||||
Expr::Stack(slot, _) => x.constants[*slot].clone(),
|
Expr::Stack(slot, _) => x.constants[*slot].clone(),
|
||||||
_ => e.get_constant_value().unwrap()
|
_ => e.get_literal_value().unwrap()
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
||||||
@ -968,7 +968,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
|
|||||||
|
|
||||||
// Move constant arguments
|
// Move constant arguments
|
||||||
for arg in x.args.iter_mut() {
|
for arg in x.args.iter_mut() {
|
||||||
if let Some(value) = arg.get_constant_value() {
|
if let Some(value) = arg.get_literal_value() {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
x.constants.push(value);
|
x.constants.push(value);
|
||||||
*arg = Expr::Stack(x.constants.len()-1, arg.position());
|
*arg = Expr::Stack(x.constants.len()-1, arg.position());
|
||||||
@ -991,7 +991,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
|
|||||||
if !has_script_fn {
|
if !has_script_fn {
|
||||||
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| match e {
|
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| match e {
|
||||||
Expr::Stack(slot, _) => x.constants[*slot].clone(),
|
Expr::Stack(slot, _) => x.constants[*slot].clone(),
|
||||||
_ => e.get_constant_value().unwrap()
|
_ => e.get_literal_value().unwrap()
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
// Save the typename of the first argument if it is `type_of()`
|
// Save the typename of the first argument if it is `type_of()`
|
||||||
@ -1025,7 +1025,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
|
|||||||
optimize_expr(arg, state, false);
|
optimize_expr(arg, state, false);
|
||||||
|
|
||||||
// Move constant arguments
|
// Move constant arguments
|
||||||
if let Some(value) = arg.get_constant_value() {
|
if let Some(value) = arg.get_literal_value() {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
x.constants.push(value);
|
x.constants.push(value);
|
||||||
*arg = Expr::Stack(x.constants.len()-1, arg.position());
|
*arg = Expr::Stack(x.constants.len()-1, arg.position());
|
||||||
|
@ -93,6 +93,8 @@ pub enum ParseErrorType {
|
|||||||
UnknownOperator(String),
|
UnknownOperator(String),
|
||||||
/// Expecting a particular token but not finding one. Wrapped values are the token and description.
|
/// Expecting a particular token but not finding one. Wrapped values are the token and description.
|
||||||
MissingToken(String, String),
|
MissingToken(String, String),
|
||||||
|
/// Expecting a particular symbol but not finding one. Wrapped value is the description.
|
||||||
|
MissingSymbol(String),
|
||||||
/// An expression in function call arguments `()` has syntax error. Wrapped value is the error
|
/// An expression in function call arguments `()` has syntax error. Wrapped value is the error
|
||||||
/// description (if any).
|
/// description (if any).
|
||||||
MalformedCallExpr(String),
|
MalformedCallExpr(String),
|
||||||
@ -196,6 +198,7 @@ impl ParseErrorType {
|
|||||||
Self::BadInput(err) => err.desc(),
|
Self::BadInput(err) => err.desc(),
|
||||||
Self::UnknownOperator(_) => "Unknown operator",
|
Self::UnknownOperator(_) => "Unknown operator",
|
||||||
Self::MissingToken(_, _) => "Expecting a certain token that is missing",
|
Self::MissingToken(_, _) => "Expecting a certain token that is missing",
|
||||||
|
Self::MissingSymbol(_) => "Expecting a certain symbol that is missing",
|
||||||
Self::MalformedCallExpr(_) => "Invalid expression in function call arguments",
|
Self::MalformedCallExpr(_) => "Invalid expression in function call arguments",
|
||||||
Self::MalformedIndexExpr(_) => "Invalid index in indexing expression",
|
Self::MalformedIndexExpr(_) => "Invalid index in indexing expression",
|
||||||
Self::MalformedInExpr(_) => "Invalid 'in' expression",
|
Self::MalformedInExpr(_) => "Invalid 'in' expression",
|
||||||
@ -268,6 +271,7 @@ impl fmt::Display for ParseErrorType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Self::MissingToken(token, s) => write!(f, "Expecting '{}' {}", token, s),
|
Self::MissingToken(token, s) => write!(f, "Expecting '{}' {}", token, s),
|
||||||
|
Self::MissingSymbol(s) => f.write_str(s),
|
||||||
|
|
||||||
Self::AssignmentToConstant(s) if s.is_empty() => f.write_str(self.desc()),
|
Self::AssignmentToConstant(s) if s.is_empty() => f.write_str(self.desc()),
|
||||||
Self::AssignmentToConstant(s) => write!(f, "Cannot assign to constant '{}'", s),
|
Self::AssignmentToConstant(s) => write!(f, "Cannot assign to constant '{}'", s),
|
||||||
|
@ -9,7 +9,10 @@ use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS};
|
|||||||
use crate::module::NamespaceRef;
|
use crate::module::NamespaceRef;
|
||||||
use crate::optimize::optimize_into_ast;
|
use crate::optimize::optimize_into_ast;
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::syntax::{CustomSyntax, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
|
use crate::syntax::{
|
||||||
|
CustomSyntax, MARKER_BLOCK, MARKER_BOOL, MARKER_EXPR, MARKER_IDENT, MARKER_INT, MARKER_STRING,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::token::{
|
use crate::token::{
|
||||||
is_keyword_function, is_valid_identifier, Token, TokenStream, TokenizerControl,
|
is_keyword_function, is_valid_identifier, Token, TokenStream, TokenizerControl,
|
||||||
};
|
};
|
||||||
@ -27,7 +30,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use crate::FLOAT;
|
use crate::{syntax::MARKER_FLOAT, FLOAT};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
use crate::FnAccess;
|
use crate::FnAccess;
|
||||||
@ -876,7 +879,7 @@ fn parse_switch(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let hash = if let Some(expr) = expr {
|
let hash = if let Some(expr) = expr {
|
||||||
if let Some(value) = expr.get_constant_value() {
|
if let Some(value) = expr.get_literal_value() {
|
||||||
let hasher = &mut get_hasher();
|
let hasher = &mut get_hasher();
|
||||||
value.hash(hasher);
|
value.hash(hasher);
|
||||||
let hash = hasher.finish();
|
let hash = hasher.finish();
|
||||||
@ -1917,6 +1920,60 @@ fn parse_custom_syntax(
|
|||||||
}
|
}
|
||||||
stmt => unreachable!("expecting Stmt::Block, but gets {:?}", stmt),
|
stmt => unreachable!("expecting Stmt::Block, but gets {:?}", stmt),
|
||||||
},
|
},
|
||||||
|
MARKER_BOOL => match input.next().expect(NEVER_ENDS) {
|
||||||
|
(b @ Token::True, pos) | (b @ Token::False, pos) => {
|
||||||
|
keywords.push(Expr::BoolConstant(b == Token::True, pos));
|
||||||
|
let keyword = state.get_identifier(MARKER_BOOL);
|
||||||
|
segments.push(keyword.clone().into());
|
||||||
|
tokens.push(keyword);
|
||||||
|
}
|
||||||
|
(_, pos) => {
|
||||||
|
return Err(
|
||||||
|
PERR::MissingSymbol("Expecting 'true' or 'false'".to_string())
|
||||||
|
.into_err(pos),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MARKER_INT => match input.next().expect(NEVER_ENDS) {
|
||||||
|
(Token::IntegerConstant(i), pos) => {
|
||||||
|
keywords.push(Expr::IntegerConstant(i, pos));
|
||||||
|
let keyword = state.get_identifier(MARKER_INT);
|
||||||
|
segments.push(keyword.clone().into());
|
||||||
|
tokens.push(keyword);
|
||||||
|
}
|
||||||
|
(_, pos) => {
|
||||||
|
return Err(
|
||||||
|
PERR::MissingSymbol("Expecting an integer number".to_string())
|
||||||
|
.into_err(pos),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
MARKER_FLOAT => match input.next().expect(NEVER_ENDS) {
|
||||||
|
(Token::FloatConstant(f), pos) => {
|
||||||
|
keywords.push(Expr::FloatConstant(f, pos));
|
||||||
|
let keyword = state.get_identifier(MARKER_FLOAT);
|
||||||
|
segments.push(keyword.clone().into());
|
||||||
|
tokens.push(keyword);
|
||||||
|
}
|
||||||
|
(_, pos) => {
|
||||||
|
return Err(PERR::MissingSymbol(
|
||||||
|
"Expecting a floating-point number".to_string(),
|
||||||
|
)
|
||||||
|
.into_err(pos))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MARKER_STRING => match input.next().expect(NEVER_ENDS) {
|
||||||
|
(Token::StringConstant(s), pos) => {
|
||||||
|
keywords.push(Expr::StringConstant(state.get_identifier(s).into(), pos));
|
||||||
|
let keyword = state.get_identifier(MARKER_STRING);
|
||||||
|
segments.push(keyword.clone().into());
|
||||||
|
tokens.push(keyword);
|
||||||
|
}
|
||||||
|
(_, pos) => {
|
||||||
|
return Err(PERR::MissingSymbol("Expecting a string".to_string()).into_err(pos))
|
||||||
|
}
|
||||||
|
},
|
||||||
s => match input.next().expect(NEVER_ENDS) {
|
s => match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
(t, _) if t.syntax().as_ref() == s => {
|
(t, _) if t.syntax().as_ref() == s => {
|
||||||
|
@ -5,8 +5,8 @@ use crate::engine::EvalContext;
|
|||||||
use crate::fn_native::SendSync;
|
use crate::fn_native::SendSync;
|
||||||
use crate::token::{is_valid_identifier, Token};
|
use crate::token::{is_valid_identifier, Token};
|
||||||
use crate::{
|
use crate::{
|
||||||
Engine, Identifier, ImmutableString, LexError, ParseError, Position, RhaiResult, Shared,
|
Dynamic, Engine, Identifier, ImmutableString, LexError, ParseError, Position, RhaiResult,
|
||||||
StaticVec,
|
Shared, StaticVec,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -14,6 +14,11 @@ use std::prelude::v1::*;
|
|||||||
pub const MARKER_EXPR: &str = "$expr$";
|
pub const MARKER_EXPR: &str = "$expr$";
|
||||||
pub const MARKER_BLOCK: &str = "$block$";
|
pub const MARKER_BLOCK: &str = "$block$";
|
||||||
pub const MARKER_IDENT: &str = "$ident$";
|
pub const MARKER_IDENT: &str = "$ident$";
|
||||||
|
pub const MARKER_STRING: &str = "$string$";
|
||||||
|
pub const MARKER_INT: &str = "$int$";
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
pub const MARKER_FLOAT: &str = "$float$";
|
||||||
|
pub const MARKER_BOOL: &str = "$bool$";
|
||||||
|
|
||||||
/// A general expression evaluation trait object.
|
/// A general expression evaluation trait object.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
@ -58,6 +63,11 @@ impl Expression<'_> {
|
|||||||
pub fn position(&self) -> Position {
|
pub fn position(&self) -> Position {
|
||||||
self.0.position()
|
self.0.position()
|
||||||
}
|
}
|
||||||
|
/// Get the value of this expression if it is a literal constant.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn get_literal_value(&self) -> Option<Dynamic> {
|
||||||
|
self.0.get_literal_value()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EvalContext<'_, '_, '_, '_, '_, '_, '_> {
|
impl EvalContext<'_, '_, '_, '_, '_, '_, '_> {
|
||||||
@ -132,7 +142,15 @@ impl Engine {
|
|||||||
|
|
||||||
let seg = match s {
|
let seg = match s {
|
||||||
// Markers not in first position
|
// Markers not in first position
|
||||||
MARKER_IDENT | MARKER_EXPR | MARKER_BLOCK if !segments.is_empty() => s.into(),
|
MARKER_IDENT | MARKER_EXPR | MARKER_BLOCK | MARKER_BOOL | MARKER_INT
|
||||||
|
| MARKER_STRING
|
||||||
|
if !segments.is_empty() =>
|
||||||
|
{
|
||||||
|
s.into()
|
||||||
|
}
|
||||||
|
// Markers not in first position
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
MARKER_FLOAT if !segments.is_empty() => s.into(),
|
||||||
// Standard or reserved keyword/symbol not in first position
|
// Standard or reserved keyword/symbol not in first position
|
||||||
s if !segments.is_empty() && token.is_some() => {
|
s 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
|
||||||
|
@ -19,19 +19,24 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
engine.register_custom_syntax(
|
engine.register_custom_syntax(
|
||||||
&[
|
&[
|
||||||
"exec", "[", "$ident$", "]", "->", "$block$", "while", "$expr$",
|
"exec", "[", "$ident$", ";", "$int$", "]", "->", "$block$", "while", "$expr$",
|
||||||
],
|
],
|
||||||
true,
|
true,
|
||||||
|context, inputs| {
|
|context, inputs| {
|
||||||
let var_name = inputs[0].get_variable_name().unwrap().to_string();
|
let var_name = inputs[0].get_variable_name().unwrap().to_string();
|
||||||
let stmt = inputs.get(1).unwrap();
|
let max = inputs[1].get_literal_value().unwrap().as_int().unwrap();
|
||||||
let condition = inputs.get(2).unwrap();
|
let stmt = inputs.get(2).unwrap();
|
||||||
|
let condition = inputs.get(3).unwrap();
|
||||||
|
|
||||||
context.scope_mut().push(var_name.clone(), 0 as INT);
|
context.scope_mut().push(var_name.clone(), 0 as INT);
|
||||||
|
|
||||||
let mut count: INT = 0;
|
let mut count: INT = 0;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
if count >= max {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
context.eval_expression_tree(stmt)?;
|
context.eval_expression_tree(stmt)?;
|
||||||
count += 1;
|
count += 1;
|
||||||
|
|
||||||
@ -63,17 +68,17 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"
|
"
|
||||||
let x = 0;
|
let x = 0;
|
||||||
let foo = (exec [x] -> { x += 2 } while x < 42) * 10;
|
let foo = (exec [x;15] -> { x += 2 } while x < 42) * 10;
|
||||||
foo
|
foo
|
||||||
"
|
"
|
||||||
)?,
|
)?,
|
||||||
210
|
150
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"
|
"
|
||||||
let x = 0;
|
let x = 0;
|
||||||
exec [x] -> { x += 1 } while x < 42;
|
exec [x;100] -> { x += 1 } while x < 42;
|
||||||
x
|
x
|
||||||
"
|
"
|
||||||
)?,
|
)?,
|
||||||
@ -82,7 +87,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"
|
"
|
||||||
exec [x] -> { x += 1 } while x < 42;
|
exec [x;100] -> { x += 1 } while x < 42;
|
||||||
x
|
x
|
||||||
"
|
"
|
||||||
)?,
|
)?,
|
||||||
@ -92,11 +97,11 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"
|
"
|
||||||
let foo = 123;
|
let foo = 123;
|
||||||
exec [x] -> { x += 1 } while x < 42;
|
exec [x;15] -> { x += 1 } while x < 42;
|
||||||
foo + x + x1 + x2 + x3
|
foo + x + x1 + x2 + x3
|
||||||
"
|
"
|
||||||
)?,
|
)?,
|
||||||
171
|
144
|
||||||
);
|
);
|
||||||
|
|
||||||
// The first symbol must be an identifier
|
// The first symbol must be an identifier
|
||||||
|
Loading…
Reference in New Issue
Block a user