From 28fa61886307d6e9b736c4a6d1e52df54b443f62 Mon Sep 17 00:00:00 2001 From: ashyan_spada Date: Fri, 10 Apr 2026 23:30:25 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E9=9D=A2=E5=90=91=E4=BA=91=E4=BB=A3?= =?UTF-8?q?=E7=90=86=E4=BC=9A=E8=AF=9D=E7=9A=84=20VS=20Code=20=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E6=A3=80=E6=9F=A5=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 0 -> 6148 bytes src/lib.rs | 10 ++++++++++ 2 files changed, 10 insertions(+) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..7632f5079692515a7a2ef38dfe7d863a2c8f30f0 GIT binary patch literal 6148 zcmeHLF-yZh6ndN@-p7g^}kqI#B~{dH-Yiwsv)2 z(f$6f{%%`&Hw1S$6>_7z(1rmtwCphdkd9C}Jl@nk%Nv}-#5!S=Fv*V6_`uGH&oT6% z2@&|v()r|GB_Ab1<@0q>cz)j+^U?oZN)d#RpnNC&&KzNoTq*-X|I-#y->bc=)*o{3~=|_ zy;*vyE>8N@+&!y5+`i^}*Va%OWW}gFR!^q7lwr-LmM;i2qfx*pU=$cDK<@_!JE1Ev z7RX;6IPfC?qK#(W@S4{OM28HBuEba%TCg!O6;VuO@`%C2bhJA%&XpJo6w`t6%(#xs z%;X7$@$6`KcsMXupc#z?C!>H-V5$^gwpaJ6xFuOzQ=6k& w>tNr*PL1MXfqV)MvK;dZD#i4_0&nPZ=mOD|7z;!THuFP(%U}khz@IAc4V((cE&u=k literal 0 HcmV?d00001 diff --git a/src/lib.rs b/src/lib.rs index 521d0b5..e533b89 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,16 @@ pub fn parse_expression(expr: &str) -> Result { parser::Parser::new(expr)?.parse_stmt() } +pub fn parse_expression_to_bytecode(expr: &str) -> Result { + init(); + let ast = parser::Parser::new(expr)?.parse_stmt()?; + bytecode::compile_expression(&ast) +} + +pub fn execute_program(program: &bytecode::Program, ctx: &mut Context) -> Result { + bytecode::execute_program(program, ctx) +} + /// ## Usage /// /// You can register some inner functions in advance via this method From ffb2dc5cd50f56e98e690f71613fa551aaef7fda Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Fri, 10 Apr 2026 15:34:42 +0000 Subject: [PATCH 2/2] Improve API design: Add documentation, tests, and init() call for bytecode functions Agent-Logs-Url: https://github.com/ashyanSpada/expression_engine_rs/sessions/6c599b7f-b7f6-4f2e-a732-61b10b9c4971 Co-authored-by: ashyanSpada <22587148+ashyanSpada@users.noreply.github.com> --- src/lib.rs | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index e533b89..be92912 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,13 +62,50 @@ pub fn parse_expression(expr: &str) -> Result { parser::Parser::new(expr)?.parse_stmt() } +/// ## Usage +/// +/// Parse an expression and compile it directly to bytecode without keeping the AST. +/// This is useful when you want to cache the compiled bytecode and execute it multiple times +/// with different contexts, avoiding the parsing and compilation overhead. +/// +/// ``` rust +/// use expression_engine::{parse_expression_to_bytecode, execute_program, create_context, Value}; +/// let input = "a + b * 2"; +/// let program = parse_expression_to_bytecode(input).unwrap(); +/// +/// // Execute the same bytecode program multiple times with different contexts +/// let mut ctx1 = create_context!("a" => 10, "b" => 5); +/// let result1 = execute_program(&program, &mut ctx1).unwrap(); +/// assert_eq!(result1, Value::from(20)); +/// +/// let mut ctx2 = create_context!("a" => 1, "b" => 2); +/// let result2 = execute_program(&program, &mut ctx2).unwrap(); +/// assert_eq!(result2, Value::from(5)); +/// ``` pub fn parse_expression_to_bytecode(expr: &str) -> Result { init(); let ast = parser::Parser::new(expr)?.parse_stmt()?; bytecode::compile_expression(&ast) } +/// ## Usage +/// +/// Execute a pre-compiled bytecode program with a given context. +/// This function should be used together with `parse_expression_to_bytecode` when you need +/// to execute the same expression multiple times with different contexts, which is more +/// efficient than calling `execute` repeatedly. +/// +/// ``` rust +/// use expression_engine::{parse_expression_to_bytecode, execute_program, create_context, Value}; +/// let input = "x * 2 + y"; +/// let program = parse_expression_to_bytecode(input).unwrap(); +/// +/// let mut ctx = create_context!("x" => 5, "y" => 3); +/// let result = execute_program(&program, &mut ctx).unwrap(); +/// assert_eq!(result, Value::from(13)); +/// ``` pub fn execute_program(program: &bytecode::Program, ctx: &mut Context) -> Result { + init(); bytecode::execute_program(program, ctx) } @@ -322,4 +359,68 @@ mod tests { _ => panic!("Expected Error::DivByZero"), } } + + #[test] + fn test_parse_expression_to_bytecode() { + let input = "a + b * 2"; + let program = crate::parse_expression_to_bytecode(input); + assert!(program.is_ok()); + let program = program.unwrap(); + assert!(!program.instructions.is_empty()); + assert!(!program.constants.is_empty()); + } + + #[test] + fn test_execute_program() { + let input = "x * 2 + y"; + let program = crate::parse_expression_to_bytecode(input).unwrap(); + let mut ctx = create_context!("x" => 5, "y" => 3); + let result = crate::execute_program(&program, &mut ctx).unwrap(); + assert_eq!(result, Value::from(13)); + } + + #[test] + fn test_parse_and_execute_bytecode_reuse() { + // Test that we can compile once and execute multiple times + let input = "a + b * 2"; + let program = crate::parse_expression_to_bytecode(input).unwrap(); + + // Execute with first context + let mut ctx1 = create_context!("a" => 10, "b" => 5); + let result1 = crate::execute_program(&program, &mut ctx1).unwrap(); + assert_eq!(result1, Value::from(20)); + + // Execute with second context + let mut ctx2 = create_context!("a" => 1, "b" => 2); + let result2 = crate::execute_program(&program, &mut ctx2).unwrap(); + assert_eq!(result2, Value::from(5)); + + // Execute with third context + let mut ctx3 = create_context!("a" => 100, "b" => 50); + let result3 = crate::execute_program(&program, &mut ctx3).unwrap(); + assert_eq!(result3, Value::from(200)); + } + + #[test] + fn test_execute_program_with_function() { + let input = "f(x) + y"; + let program = crate::parse_expression_to_bytecode(input).unwrap(); + let mut ctx = create_context!( + "x" => 10, + "y" => 5, + "f" => Arc::new(|params| { + let val = params[0].clone().integer()?; + Ok(Value::from(val * 2)) + }) + ); + let result = crate::execute_program(&program, &mut ctx).unwrap(); + assert_eq!(result, Value::from(25)); + } + + #[test] + fn test_parse_expression_to_bytecode_invalid() { + let input = "a + )"; // Invalid expression - unmatched closing paren + let result = crate::parse_expression_to_bytecode(input); + assert!(result.is_err()); + } }