#![cfg(feature = "internals")] use rhai::{ Dynamic, Engine, EvalAltResult, EvalState, Expression, Imports, LexError, Module, ParseError, ParseErrorType, Scope, INT, }; #[test] fn test_custom_syntax() -> Result<(), Box> { let mut engine = Engine::new(); engine.consume("while false {}")?; // Disable 'while' and make sure it still works with custom syntax engine.disable_symbol("while"); assert!(matches!( *engine.compile("while false {}").expect_err("should error").0, ParseErrorType::Reserved(err) if err == "while" )); assert!(matches!( *engine.compile("let while = 0").expect_err("should error").0, ParseErrorType::Reserved(err) if err == "while" )); engine .register_custom_syntax( &[ "do", "|", "$ident$", "|", "->", "$block$", "while", "$expr$", ], 1, |engine: &Engine, scope: &mut Scope, mods: &mut Imports, state: &mut EvalState, lib: &Module, this_ptr: &mut Option<&mut Dynamic>, inputs: &[Expression], level: usize| { let var_name = inputs[0].get_variable_name().unwrap().to_string(); let stmt = inputs.get(1).unwrap(); let expr = inputs.get(2).unwrap(); scope.push(var_name, 0 as INT); loop { engine.eval_expression_tree(scope, mods, state, lib, this_ptr, stmt, level)?; if !engine .eval_expression_tree(scope, mods, state, lib, this_ptr, expr, level)? .as_bool() .map_err(|_| { EvalAltResult::ErrorBooleanArgMismatch( "do-while".into(), expr.position(), ) })? { break; } } Ok(().into()) }, ) .unwrap(); // 'while' is now a custom keyword so this it can no longer be a variable engine.consume("let while = 0").expect_err("should error"); assert_eq!( engine.eval::( r" do |x| -> { x += 1 } while x < 42; x " )?, 42 ); // The first symbol must be an identifier assert!(matches!( *engine.register_custom_syntax(&["!"], 0, |_, _, _, _, _, _, _, _| Ok(().into())).expect_err("should error"), LexError::ImproperSymbol(s) if s == "!" )); Ok(()) }