@@ -62,13 +62,50 @@ pub fn parse_expression(expr: &str) -> Result<ExprAST> {
6262 parser:: Parser :: new ( expr) ?. parse_stmt ( )
6363}
6464
65+ /// ## Usage
66+ ///
67+ /// Parse an expression and compile it directly to bytecode without keeping the AST.
68+ /// This is useful when you want to cache the compiled bytecode and execute it multiple times
69+ /// with different contexts, avoiding the parsing and compilation overhead.
70+ ///
71+ /// ``` rust
72+ /// use expression_engine::{parse_expression_to_bytecode, execute_program, create_context, Value};
73+ /// let input = "a + b * 2";
74+ /// let program = parse_expression_to_bytecode(input).unwrap();
75+ ///
76+ /// // Execute the same bytecode program multiple times with different contexts
77+ /// let mut ctx1 = create_context!("a" => 10, "b" => 5);
78+ /// let result1 = execute_program(&program, &mut ctx1).unwrap();
79+ /// assert_eq!(result1, Value::from(20));
80+ ///
81+ /// let mut ctx2 = create_context!("a" => 1, "b" => 2);
82+ /// let result2 = execute_program(&program, &mut ctx2).unwrap();
83+ /// assert_eq!(result2, Value::from(5));
84+ /// ```
6585pub fn parse_expression_to_bytecode ( expr : & str ) -> Result < bytecode:: Program > {
6686 init ( ) ;
6787 let ast = parser:: Parser :: new ( expr) ?. parse_stmt ( ) ?;
6888 bytecode:: compile_expression ( & ast)
6989}
7090
91+ /// ## Usage
92+ ///
93+ /// Execute a pre-compiled bytecode program with a given context.
94+ /// This function should be used together with `parse_expression_to_bytecode` when you need
95+ /// to execute the same expression multiple times with different contexts, which is more
96+ /// efficient than calling `execute` repeatedly.
97+ ///
98+ /// ``` rust
99+ /// use expression_engine::{parse_expression_to_bytecode, execute_program, create_context, Value};
100+ /// let input = "x * 2 + y";
101+ /// let program = parse_expression_to_bytecode(input).unwrap();
102+ ///
103+ /// let mut ctx = create_context!("x" => 5, "y" => 3);
104+ /// let result = execute_program(&program, &mut ctx).unwrap();
105+ /// assert_eq!(result, Value::from(13));
106+ /// ```
71107pub fn execute_program ( program : & bytecode:: Program , ctx : & mut Context ) -> Result < value:: Value > {
108+ init ( ) ;
72109 bytecode:: execute_program ( program, ctx)
73110}
74111
@@ -322,4 +359,68 @@ mod tests {
322359 _ => panic ! ( "Expected Error::DivByZero" ) ,
323360 }
324361 }
362+
363+ #[ test]
364+ fn test_parse_expression_to_bytecode ( ) {
365+ let input = "a + b * 2" ;
366+ let program = crate :: parse_expression_to_bytecode ( input) ;
367+ assert ! ( program. is_ok( ) ) ;
368+ let program = program. unwrap ( ) ;
369+ assert ! ( !program. instructions. is_empty( ) ) ;
370+ assert ! ( !program. constants. is_empty( ) ) ;
371+ }
372+
373+ #[ test]
374+ fn test_execute_program ( ) {
375+ let input = "x * 2 + y" ;
376+ let program = crate :: parse_expression_to_bytecode ( input) . unwrap ( ) ;
377+ let mut ctx = create_context ! ( "x" => 5 , "y" => 3 ) ;
378+ let result = crate :: execute_program ( & program, & mut ctx) . unwrap ( ) ;
379+ assert_eq ! ( result, Value :: from( 13 ) ) ;
380+ }
381+
382+ #[ test]
383+ fn test_parse_and_execute_bytecode_reuse ( ) {
384+ // Test that we can compile once and execute multiple times
385+ let input = "a + b * 2" ;
386+ let program = crate :: parse_expression_to_bytecode ( input) . unwrap ( ) ;
387+
388+ // Execute with first context
389+ let mut ctx1 = create_context ! ( "a" => 10 , "b" => 5 ) ;
390+ let result1 = crate :: execute_program ( & program, & mut ctx1) . unwrap ( ) ;
391+ assert_eq ! ( result1, Value :: from( 20 ) ) ;
392+
393+ // Execute with second context
394+ let mut ctx2 = create_context ! ( "a" => 1 , "b" => 2 ) ;
395+ let result2 = crate :: execute_program ( & program, & mut ctx2) . unwrap ( ) ;
396+ assert_eq ! ( result2, Value :: from( 5 ) ) ;
397+
398+ // Execute with third context
399+ let mut ctx3 = create_context ! ( "a" => 100 , "b" => 50 ) ;
400+ let result3 = crate :: execute_program ( & program, & mut ctx3) . unwrap ( ) ;
401+ assert_eq ! ( result3, Value :: from( 200 ) ) ;
402+ }
403+
404+ #[ test]
405+ fn test_execute_program_with_function ( ) {
406+ let input = "f(x) + y" ;
407+ let program = crate :: parse_expression_to_bytecode ( input) . unwrap ( ) ;
408+ let mut ctx = create_context ! (
409+ "x" => 10 ,
410+ "y" => 5 ,
411+ "f" => Arc :: new( |params| {
412+ let val = params[ 0 ] . clone( ) . integer( ) ?;
413+ Ok ( Value :: from( val * 2 ) )
414+ } )
415+ ) ;
416+ let result = crate :: execute_program ( & program, & mut ctx) . unwrap ( ) ;
417+ assert_eq ! ( result, Value :: from( 25 ) ) ;
418+ }
419+
420+ #[ test]
421+ fn test_parse_expression_to_bytecode_invalid ( ) {
422+ let input = "a + )" ; // Invalid expression - unmatched closing paren
423+ let result = crate :: parse_expression_to_bytecode ( input) ;
424+ assert ! ( result. is_err( ) ) ;
425+ }
325426}
0 commit comments