From a535e9d9d48eb00210970aa9234fbda6700b6e83 Mon Sep 17 00:00:00 2001 From: Kai Schmidt Date: Thu, 28 Nov 2024 13:51:42 -0800 Subject: [PATCH] fix some bugs in array/uasm --- src/array.rs | 74 ++++++++++++++++++++++++++++-------- src/lib.rs | 71 +++++++++++++++++++--------------- src/tree.rs | 22 ++++++++--- tests/error.ua | 101 ------------------------------------------------- 4 files changed, 117 insertions(+), 151 deletions(-) delete mode 100644 tests/error.ua diff --git a/src/array.rs b/src/array.rs index 44f1f7117..fa6ae7cff 100644 --- a/src/array.rs +++ b/src/array.rs @@ -1411,24 +1411,68 @@ trait ArrayValueSer: ArrayValue + fmt::Debug { } } -macro_rules! array_value_ser { - ($ty:ty) => { - impl ArrayValueSer for $ty { - type Scalar = $ty; - type Collection = CowSlice<$ty>; - fn make_collection(data: CowSlice) -> Self::Collection { - data - } - fn make_data(collection: Self::Collection) -> CowSlice { - collection - } +impl ArrayValueSer for u8 { + type Scalar = u8; + type Collection = CowSlice; + fn make_collection(data: CowSlice) -> Self::Collection { + data + } + fn make_data(collection: Self::Collection) -> CowSlice { + collection + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +enum BoxCollection { + #[serde(rename = "empty_boxes")] + Empty([Boxed; 0]), + #[serde(untagged)] + List(CowSlice), +} + +impl ArrayValueSer for Boxed { + type Scalar = Boxed; + type Collection = BoxCollection; + fn make_collection(data: CowSlice) -> Self::Collection { + if data.is_empty() { + BoxCollection::Empty([]) + } else { + BoxCollection::List(data) } - }; + } + fn make_data(collection: Self::Collection) -> CowSlice { + match collection { + BoxCollection::Empty(_) => CowSlice::new(), + BoxCollection::List(data) => data, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +enum ComplexCollection { + #[serde(rename = "empty_complex")] + Empty([Complex; 0]), + #[serde(untagged)] + List(CowSlice), } -array_value_ser!(u8); -array_value_ser!(Boxed); -array_value_ser!(Complex); +impl ArrayValueSer for Complex { + type Scalar = Complex; + type Collection = ComplexCollection; + fn make_collection(data: CowSlice) -> Self::Collection { + if data.is_empty() { + ComplexCollection::Empty([]) + } else { + ComplexCollection::List(data) + } + } + fn make_data(collection: Self::Collection) -> CowSlice { + match collection { + ComplexCollection::Empty(_) => CowSlice::new(), + ComplexCollection::List(data) => data, + } + } +} impl ArrayValueSer for f64 { type Scalar = F64Rep; diff --git a/src/lib.rs b/src/lib.rs index f3180d974..9efdca4c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -264,41 +264,52 @@ mod tests { #[test] #[cfg(feature = "native_sys")] - fn errors() { + fn error_dont_crash() { use super::*; - for path in test_files(|path| { - (path.file_stem().unwrap()) - .to_string_lossy() - .contains("error") - }) { - let mut code = std::fs::read_to_string(&path).unwrap(); - if code.contains('\r') { - code = code.replace('\r', ""); - } - for section in code.split("\n\n") { - let mut env = Uiua::with_native_sys(); - let mut comp = Compiler::new(); - let res = comp - .load_str_src(section, &path) - .and_then(|comp| env.run_asm(comp.finish())); - if res.is_ok() - && comp - .take_diagnostics() - .into_iter() - .filter(|diag| diag.kind > DiagnosticKind::Advice) - .count() - == 0 - { - panic!( - "Test succeeded when it should have failed in {}:\n{}", - path.display(), - section - ); - } + let path = Path::new("tests_special/error.ua"); + let mut code = std::fs::read_to_string(path).unwrap(); + if code.contains('\r') { + code = code.replace('\r', ""); + } + for section in code.split("\n\n") { + let mut env = Uiua::with_native_sys(); + let mut comp = Compiler::new(); + let res = comp + .load_str_src(section, path) + .and_then(|comp| env.run_asm(comp.finish())); + if res.is_ok() + && comp + .take_diagnostics() + .into_iter() + .filter(|diag| diag.kind > DiagnosticKind::Advice) + .count() + == 0 + { + panic!( + "Test succeeded when it should have failed in {}:\n{}", + path.display(), + section + ); } } } + #[test] + #[cfg(feature = "native_sys")] + fn assembly_round_trip() { + use super::*; + let path = Path::new("tests_special/uasm.ua"); + let mut comp = Compiler::new(); + comp.load_file(path).unwrap(); + let asm = comp.finish(); + let root = asm.root.clone(); + let uasm = asm.to_uasm(); + let asm = Assembly::from_uasm(&uasm).unwrap(); + assert_eq!(asm.root, root); + let mut env = Uiua::with_native_sys(); + env.run_asm(asm).unwrap(); + } + #[test] fn lsp_spans() { use super::*; diff --git a/src/tree.rs b/src/tree.rs index 51b98d9d6..9c58f7f1a 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -452,8 +452,8 @@ impl Node { f: impl FnOnce(&mut Node) -> T + Copy, ) -> Option { let mut this_node = match target { - Some(i) => &asm.functions[i], - None => &*node, + Some(i) => asm.functions[i].inner(), + None => node.inner(), }; if let Some(i) = sub { this_node = this_node.get(i)?; @@ -470,11 +470,11 @@ impl Node { Node::Call(func, _) => recurse(Some(func.index), None, node, asm, f), _ => { let mut node = match target { - Some(i) => &mut asm.functions.make_mut()[i], - None => node, + Some(i) => asm.functions.make_mut()[i].inner_mut(), + None => node.inner_mut(), }; if let Some(i) = sub { - node = node.as_mut_slice().get_mut(i)?; + node = node.as_mut_slice().get_mut(i)?.inner_mut(); } Some(f(node)) } @@ -482,6 +482,18 @@ impl Node { } recurse(None, None, self, asm, f) } + fn inner(&self) -> &Node { + match self { + Node::TrackCaller(inner) | Node::NoInline(inner) => inner, + node => node, + } + } + fn inner_mut(&mut self) -> &mut Node { + match self { + Node::TrackCaller(inner) | Node::NoInline(inner) => Arc::make_mut(inner), + node => node, + } + } pub(crate) fn hash_with_span(&self, hasher: &mut impl Hasher) { self.hash(hasher); if let Some(span) = self.span() { diff --git a/tests/error.ua b/tests/error.ua deleted file mode 100644 index 9a763e7ce..000000000 --- a/tests/error.ua +++ /dev/null @@ -1,101 +0,0 @@ -⊟ [1 2 3] 4_5 - -≡⊟ [1 2 3] [4 5] - -≡⊟ [1 2 3] [] -≡(⊂⊂) [1 2 3] [4 5 6] [] - -∵⊟ [1 2 3] [4 5] - -⊡ ↯0_2e [1] - -Ex ~ "example" -Ex~RangeDiff - -F! ← F!^0 -F!+ - -regex "([a-z]" "hello world" - -"Hello"_"Worrld" - -⍜⊏() ⊚. [ - [0 0 1 0 0] - [0 0 0 0 1] - [1 0 0 0 0] - [0 0 0 1 0] - [0 1 0 0 0]] - -F ← [⋅⋅◌⍢(-1:0|>0)]1 -F"oops" - -F ← [⋅◌ ⍥0] 1 -F "oops" - -F ← ⌊×10[◌◌⍥gen]⧻:0 - -⊕⊢ [0 2 2] [1 2 3] - -+ ¤[1 2 3] [10_20_30_40 50_60_70_80] - -⇡⇡50 - -x ← &sc -F! ←^ °⋕x◌ -F!5 - -⇡1e20 - -⬚0≡(++) [1] [2 3 4] [5 6] - -5 -⊸∘◌ - -F ← |39 ⍥+ - -⍜⊏(?≡⊂:9?) [0 2] [1_2 3_4 5_6 7_8] - -┌─╴M - F ↚ +1 -└─╴ -M!F 5 - -d ← gen [] ⚂ -D! ←^ $"_" d ◌ -◌D!4 - -! ← ^0 -!(^0) - -F ← |0 spawnF -F - -┌─╴Foo - |Bar {a} - |Baz {a} -└─╴ -⍤⤙≍ 5 Foo~Baz~a Foo~Bar 5 - -°(⊂0?) - -⟜(@a @b) - -⊂?⟜(°△⍜⊢¬+1)⇡13 - -⊂:?⟜(°△⍜⊢¬+1)⇡13 - -# Experimental! -quote(⊂⊂:⊂⊂,⊸:-7’+1⊡5."quote(⊂⊂:⊂⊂,⊸:-7’+1⊡5.") - -°binary 0_1_10 - -comptime(⍤@@@@) - -F! ← ^2 - -⬚0⌝⊏ ¯2 5 -⬚0⌝⊡ ¯2 5 - -°¤ 1 - -°[∘] 1