From 7e87e79a41ef9557dd72d6e8fa01094a194a123a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Dec 2025 16:56:42 +0000 Subject: [PATCH 1/4] Initial plan From a80be6f95995417f183e567195d875cd5373fa34 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Dec 2025 17:01:44 +0000 Subject: [PATCH 2/4] Refactor: Extract duplicate token wrapping code in sexp.rs Co-authored-by: Giedriusj1 <1835596+Giedriusj1@users.noreply.github.com> --- src/sexp.rs | 66 +++++++++++++++++++++++------------------------------ 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/src/sexp.rs b/src/sexp.rs index a325172..0b486c0 100644 --- a/src/sexp.rs +++ b/src/sexp.rs @@ -137,6 +137,32 @@ impl std::fmt::Display for Sexp { } } +// Helper function to wrap tokens with special characters (quote/backtick/comma) +fn wrap_tokens_with_special_chars( + expanded_tokens: &mut Vec, + apostrophe_or_backtick_vec: &[lex::Token], +) { + for i in apostrophe_or_backtick_vec.iter() { + match i { + lex::Token::Apostrophe => { + expanded_tokens.push(lex::Token::LeftParen); + expanded_tokens.push(lex::Token::String("quote".to_string())); + } + lex::Token::Backtick => { + expanded_tokens.push(lex::Token::LeftParen); + expanded_tokens.push(lex::Token::String("backtick".to_string())); + } + lex::Token::Comma => { + expanded_tokens.push(lex::Token::LeftParen); + expanded_tokens.push(lex::Token::String("comma".to_string())); + } + _ => { + panic!("invalid syntax") + } + } + } +} + // This expands backticks into the quotation syntax fn expand_special_characters(tokens: &[lex::Token]) -> Vec { let mut expanded_tokens: Vec = vec![]; @@ -179,25 +205,7 @@ fn expand_special_characters(tokens: &[lex::Token]) -> Vec { let slice = &tokens[index + 1..index + skip + 2].to_vec().clone(); - for i in apostrophe_or_backtick_vec.iter() { - match i { - lex::Token::Apostrophe => { - expanded_tokens.push(lex::Token::LeftParen); - expanded_tokens.push(lex::Token::String("quote".to_string())); - } - lex::Token::Backtick => { - expanded_tokens.push(lex::Token::LeftParen); - expanded_tokens.push(lex::Token::String("backtick".to_string())); - } - lex::Token::Comma => { - expanded_tokens.push(lex::Token::LeftParen); - expanded_tokens.push(lex::Token::String("comma".to_string())); - } - _ => { - panic!("invalid syntax") - } - } - } + wrap_tokens_with_special_chars(&mut expanded_tokens, &apostrophe_or_backtick_vec); let mut expanded = expand_special_characters(slice); @@ -218,25 +226,7 @@ fn expand_special_characters(tokens: &[lex::Token]) -> Vec { } // If it's a symbol or a digit, then we need to wrap it in a quote lex::Token::String(_) | lex::Token::Digit(_) => { - for i in apostrophe_or_backtick_vec.iter() { - match i { - lex::Token::Apostrophe => { - expanded_tokens.push(lex::Token::LeftParen); - expanded_tokens.push(lex::Token::String("quote".to_string())); - } - lex::Token::Backtick => { - expanded_tokens.push(lex::Token::LeftParen); - expanded_tokens.push(lex::Token::String("backtick".to_string())); - } - lex::Token::Comma => { - expanded_tokens.push(lex::Token::LeftParen); - expanded_tokens.push(lex::Token::String("comma".to_string())); - } - _ => { - panic!("invalid syntax") - } - } - } + wrap_tokens_with_special_chars(&mut expanded_tokens, &apostrophe_or_backtick_vec); expanded_tokens.push(next_token.clone()); From b95614a748103823abb18588d37bc6058e232086 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Dec 2025 17:03:01 +0000 Subject: [PATCH 3/4] Refactor: Extract duplicate sequential statement evaluation in eval.rs Co-authored-by: Giedriusj1 <1835596+Giedriusj1@users.noreply.github.com> --- src/eval.rs | 56 +++++++++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/eval.rs b/src/eval.rs index 44109b9..f1120e4 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -24,6 +24,25 @@ fn eval_sexp_to_num(sexp: Rc, state: &mut EvalState) -> i64 { } } +// Helper function to evaluate a sequence of statements and return the last result +fn eval_statements_sequentially(statements: &[Rc], state: &mut EvalState) -> Rc { + let len = statements.len(); + + if len == 0 { + return Rc::new(Sexp::Nil); + } + + let mut result = Rc::new(Sexp::Nil); + for (i, statement) in statements.iter().enumerate() { + result = eval_sexp(statement.clone(), state); + if i < len - 1 { + // Only continue evaluating if not the last statement + continue; + } + } + result +} + fn get_from_fn_map_or_global(var_name: &str, state: &EvalState) -> Option> { // Check function scopes first for variable shadowing for var_map in state.fn_map.iter().rev() { @@ -137,23 +156,9 @@ pub(crate) fn eval_sexp(sexp: Rc, state: &mut EvalState) -> Rc { // Skip the actual "let", and the subsequent paramlist, // then eval all remaining and return last let statements = &l[2..]; - let len = statements.len(); - - if len > 0 { - let mut result = Rc::new(Sexp::Nil); - for (i, statement) in statements.iter().enumerate() { - result = eval_sexp(statement.clone(), state); - if i < len - 1 { - // Only continue evaluating if not the last statement - continue; - } - } - state.fn_map.pop(); - return result; - } else { - state.fn_map.pop(); - Rc::new(Sexp::Nil) - } + let result = eval_statements_sequentially(statements, state); + state.fn_map.pop(); + result } Sexp::Instrinsics(IntrinsicInstruction::Cdr) => { @@ -216,21 +221,12 @@ pub(crate) fn eval_sexp(sexp: Rc, state: &mut EvalState) -> Rc { Sexp::Instrinsics(IntrinsicInstruction::Progn) => { // Skip the actual "progn", then eval all and return last let statements = &l[1..]; - let len = statements.len(); - - if len == 0 { + + if statements.is_empty() { panic!("progn should have at least 2 arguments"); } - - let mut result = Rc::new(Sexp::Nil); - for (i, statement) in statements.iter().enumerate() { - result = eval_sexp(statement.clone(), state); - if i < len - 1 { - // Only continue evaluating if not the last statement - continue; - } - } - result + + eval_statements_sequentially(statements, state) } Sexp::Instrinsics(IntrinsicInstruction::Defmacro) => { let macro_name = match *l[1] { From 0af41d58978ab446bf24d771803fcee9b0afbe45 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Dec 2025 17:03:58 +0000 Subject: [PATCH 4/4] Refactor: Extract duplicate binary operations in eval.rs Co-authored-by: Giedriusj1 <1835596+Giedriusj1@users.noreply.github.com> --- src/eval.rs | 60 +++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/src/eval.rs b/src/eval.rs index f1120e4..12cf5fe 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -43,6 +43,30 @@ fn eval_statements_sequentially(statements: &[Rc], state: &mut EvalState) result } +// Helper function to perform binary arithmetic operations +fn eval_binary_arithmetic_op(l: &[Rc], state: &mut EvalState, op: F) -> Rc +where + F: Fn(i64, i64) -> i64, +{ + let left = eval_sexp_to_num(l[1].clone(), state); + let right = eval_sexp_to_num(l[2].clone(), state); + Rc::new(Sexp::Num(op(left, right))) +} + +// Helper function to perform binary comparison operations +fn eval_binary_comparison_op(l: &[Rc], state: &mut EvalState, op: F) -> Rc +where + F: Fn(i64, i64) -> bool, +{ + let left = eval_sexp_to_num(l[1].clone(), state); + let right = eval_sexp_to_num(l[2].clone(), state); + if op(left, right) { + Rc::new(Sexp::True) + } else { + Rc::new(Sexp::Nil) + } +} + fn get_from_fn_map_or_global(var_name: &str, state: &EvalState) -> Option> { // Check function scopes first for variable shadowing for var_map in state.fn_map.iter().rev() { @@ -357,35 +381,13 @@ pub(crate) fn eval_sexp(sexp: Rc, state: &mut EvalState) -> Rc { } result } - "-" => Rc::new(Sexp::Num( - eval_sexp_to_num(l[1].clone(), state) - eval_sexp_to_num(l[2].clone(), state), - )), - "+" => Rc::new(Sexp::Num( - eval_sexp_to_num(l[1].clone(), state) + eval_sexp_to_num(l[2].clone(), state), - )), - "*" => Rc::new(Sexp::Num( - eval_sexp_to_num(l[1].clone(), state) * eval_sexp_to_num(l[2].clone(), state), - )), - "<" => { - if eval_sexp_to_num(l[1].clone(), state) < eval_sexp_to_num(l[2].clone(), state) { - Rc::new(Sexp::True) - } else { - Rc::new(Sexp::Nil) - } - } - ">" => { - if eval_sexp_to_num(l[1].clone(), state) > eval_sexp_to_num(l[2].clone(), state) { - Rc::new(Sexp::True) - } else { - Rc::new(Sexp::Nil) - } - } - "%" => Rc::new(Sexp::Num( - eval_sexp_to_num(l[1].clone(), state) % eval_sexp_to_num(l[2].clone(), state), - )), - "/" => Rc::new(Sexp::Num( - eval_sexp_to_num(l[1].clone(), state) / eval_sexp_to_num(l[2].clone(), state), - )), + "-" => eval_binary_arithmetic_op(l, state, |a, b| a - b), + "+" => eval_binary_arithmetic_op(l, state, |a, b| a + b), + "*" => eval_binary_arithmetic_op(l, state, |a, b| a * b), + "<" => eval_binary_comparison_op(l, state, |a, b| a < b), + ">" => eval_binary_comparison_op(l, state, |a, b| a > b), + "%" => eval_binary_arithmetic_op(l, state, |a, b| a % b), + "/" => eval_binary_arithmetic_op(l, state, |a, b| a / b), "setq" => { if let Sexp::Sym(ref s) = *l[1] { let eval_value = eval_sexp(l[2].clone(), state);