From fd116b676e6a4fbf835004e104761c8a7a936f74 Mon Sep 17 00:00:00 2001 From: Kai Schmidt Date: Wed, 27 Nov 2024 10:22:19 -0800 Subject: [PATCH] fix a bug in pre-evaluation --- src/compile/mod.rs | 6 +++--- src/compile/modifier.rs | 23 ++++++++++++++--------- src/compile/pre_eval.rs | 8 ++++---- tests/macros.ua | 6 ++++++ 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/compile/mod.rs b/src/compile/mod.rs index fbde39577..59ebe4690 100644 --- a/src/compile/mod.rs +++ b/src/compile/mod.rs @@ -330,7 +330,7 @@ impl Compiler { let input: EcoString = fs::read_to_string(path) .map_err(|e| UiuaErrorKind::Load(path.into(), e.into()))? .into(); - // _ = crate::lsp::Spans::from_input(&input); + _ = crate::lsp::Spans::from_input(&input); self.asm.inputs.files.insert(path.into(), input.clone()); self.load_impl(&input, InputSrc::File(path.into())) } @@ -661,12 +661,12 @@ code: } // Try to evaluate at comptime // This can be done when: - // - the pre-eval mode is not `Line` + // - the pre-eval mode is greater that `Line` // - there are at least as many push nodes preceding the current line as there are arguments to the line // - the words create no bindings if precomp && error_count_after == error_count_before - && self.pre_eval_mode != PreEvalMode::Line + && self.pre_eval_mode > PreEvalMode::Line && !line_node.is_empty() && binding_count_before == binding_count_after && root_len_before == self.asm.root.len() diff --git a/src/compile/modifier.rs b/src/compile/modifier.rs index 21fdc4a7e..1b310da22 100644 --- a/src/compile/modifier.rs +++ b/src/compile/modifier.rs @@ -1177,7 +1177,7 @@ impl Compiler { }) .collect(); - let mut code = String::new(); + let mut code: Option = None; (|| -> UiuaResult { if let Some(index) = self.node_unbound_index(&mac.root.node) { let name = self.scope.names.iter().find_map(|(name, local)| { @@ -1225,8 +1225,9 @@ impl Compiler { Ok(strings) => strings, Err(_) => val.representation().lines().map(Into::into).collect(), }; + let code = code.get_or_insert_with(String::new); for s in strings { - if s.chars().last().is_some_and(|c| !c.is_whitespace()) { + if code.chars().last().is_some_and(|c| !c.is_whitespace()) { code.push(' '); } code.push_str(&s); @@ -1247,14 +1248,18 @@ impl Compiler { .map_err(|e| e.trace_macro(mac_name.clone(), modifier_span.clone()))?; // Quote - self.code_meta - .macro_expansions - .insert(full_span, (mac_name.clone(), code.clone())); - self.suppress_diagnostics(|comp| { - comp.temp_scope(mac.names, None, |comp| { - comp.quote(&code, mac_name, &modifier_span) + if let Some(code) = code { + self.code_meta + .macro_expansions + .insert(full_span, (mac_name.clone(), code.clone())); + self.suppress_diagnostics(|comp| { + comp.temp_scope(mac.names, None, |comp| { + comp.quote(&code, mac_name, &modifier_span) + }) }) - }) + } else { + Ok(Node::empty()) + } } fn node_unbound_index(&self, node: &Node) -> Option { match node { diff --git a/src/compile/pre_eval.rs b/src/compile/pre_eval.rs index 6ae8b5946..a85e92866 100644 --- a/src/compile/pre_eval.rs +++ b/src/compile/pre_eval.rs @@ -13,15 +13,15 @@ use super::*; pub enum PreEvalMode { /// Does not evalute pure constants and expressions at comptime, but still evaluates `comptime` Lazy, - /// Evaluate as much as possible at compile time, even impure expressions - /// - /// Recursive functions and certain system functions are not evaluated - Lsp, /// Pre-evaluate each line, but not multiple lines together Line, /// The normal mode. Tries to evaluate pure, time-bounded constants and expressions at comptime #[default] Normal, + /// Evaluate as much as possible at compile time, even impure expressions + /// + /// Recursive functions and certain system functions are not evaluated + Lsp, } const MAX_PRE_EVAL_ELEMS: usize = 1000; diff --git a/tests/macros.ua b/tests/macros.ua index 863f2973e..3ba96b89e 100644 --- a/tests/macros.ua +++ b/tests/macros.ua @@ -81,6 +81,12 @@ M! ←^ ⋅"∘" @a @b ⍤⤙≍ "b" [⋅M!∘] +Join! ←^ ↯:@⊂↥0-1⋕ +{Join!0 "a" "bc" "def"} +{Join!1 "a" "bc" "def"} +{Join!2 "a" "bc" "def"} +◌◌◌ + # Experimental! # Inline macros