From 80ca240073c17eadab62bda64d0bbfb1aebdac48 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 19 Dec 2024 13:25:01 -0600 Subject: [PATCH] refactor(path): Switch from recursive to iterative structures --- src/path/mod.rs | 168 +++++++++++++++++++++------------------------ src/path/parser.rs | 105 ++++++++++++++-------------- 2 files changed, 127 insertions(+), 146 deletions(-) diff --git a/src/path/mod.rs b/src/path/mod.rs index db79a813..333a4d57 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -7,15 +7,17 @@ use crate::value::{Value, ValueKind}; mod parser; #[derive(Debug, Eq, PartialEq, Clone, Hash)] -pub(crate) enum Expression { - Identifier(String), - Child(Box, String), - Subscript(Box, isize), +pub(crate) struct Expression { + root: String, + postfix: Vec, } impl Expression { pub(crate) fn root(root: String) -> Self { - Expression::Identifier(root) + Self { + root, + postfix: Vec::new(), + } } } @@ -29,6 +31,12 @@ impl FromStr for Expression { } } +#[derive(Debug, Eq, PartialEq, Clone, Hash)] +enum Postfix { + Key(String), + Index(isize), +} + #[derive(Debug)] struct ParseError(String); @@ -59,105 +67,83 @@ fn abs_index(index: isize, len: usize) -> Result { impl Expression { pub(crate) fn get(self, root: &Value) -> Option<&Value> { - match self { - Self::Identifier(key) => { - match root.kind { - // `x` access on a table is equivalent to: map[x] - ValueKind::Table(ref map) => map.get(&key), - - // all other variants return None - _ => None, + let ValueKind::Table(map) = &root.kind else { + return None; + }; + let mut child = map.get(&self.root)?; + for postfix in &self.postfix { + match postfix { + Postfix::Key(key) => { + let ValueKind::Table(map) = &child.kind else { + return None; + }; + child = map.get(key)?; } - } - - Self::Child(expr, key) => { - match expr.get(root) { - Some(child) => { - match child.kind { - // Access on a table is identical to Identifier, it just forwards - ValueKind::Table(ref map) => map.get(&key), - - // all other variants return None - _ => None, - } - } - - _ => None, + Postfix::Index(rel_index) => { + let ValueKind::Array(array) = &child.kind else { + return None; + }; + let index = abs_index(*rel_index, array.len()).ok()?; + child = array.get(index)?; } } - - Self::Subscript(expr, index) => match expr.get(root) { - Some(child) => match child.kind { - ValueKind::Array(ref array) => { - let index = abs_index(index, array.len()).ok()?; - array.get(index) - } - - _ => None, - }, - - _ => None, - }, } + Some(child) } pub(crate) fn get_mut_forcibly<'a>(&self, root: &'a mut Value) -> &'a mut Value { - match *self { - Self::Identifier(ref key) => { - if !matches!(root.kind, ValueKind::Table(_)) { - *root = Map::::new().into(); - } - let ValueKind::Table(ref mut map) = root.kind else { - unreachable!() - }; - - map.entry(key.clone()) - .or_insert_with(|| Value::new(None, ValueKind::Nil)) - } - - Self::Child(ref expr, ref key) => { - let child = expr.get_mut_forcibly(root); - - if !matches!(child.kind, ValueKind::Table(_)) { - *child = Map::::new().into(); - } - let ValueKind::Table(ref mut map) = child.kind else { - unreachable!() - }; - - map.entry(key.clone()) - .or_insert_with(|| Value::new(None, ValueKind::Nil)) - } - - Self::Subscript(ref expr, index) => { - let child = expr.get_mut_forcibly(root); + if !matches!(root.kind, ValueKind::Table(_)) { + *root = Map::::new().into(); + } + let ValueKind::Table(map) = &mut root.kind else { + unreachable!() + }; + let mut child = map + .entry(self.root.clone()) + .or_insert_with(|| Value::new(None, ValueKind::Nil)); + for postfix in &self.postfix { + match postfix { + Postfix::Key(key) => { + if !matches!(child.kind, ValueKind::Table(_)) { + *child = Map::::new().into(); + } + let ValueKind::Table(ref mut map) = child.kind else { + unreachable!() + }; - if !matches!(child.kind, ValueKind::Array(_)) { - *child = Vec::::new().into(); + child = map + .entry(key.clone()) + .or_insert_with(|| Value::new(None, ValueKind::Nil)); } - let ValueKind::Array(ref mut array) = child.kind else { - unreachable!() - }; - - let uindex = match abs_index(index, array.len()) { - Ok(uindex) => { - if uindex >= array.len() { - array.resize(uindex + 1, Value::new(None, ValueKind::Nil)); - } - uindex - } - Err(insertion) => { - array.splice( - 0..0, - (0..insertion).map(|_| Value::new(None, ValueKind::Nil)), - ); - 0 + Postfix::Index(rel_index) => { + if !matches!(child.kind, ValueKind::Array(_)) { + *child = Vec::::new().into(); } - }; + let ValueKind::Array(ref mut array) = child.kind else { + unreachable!() + }; + + let uindex = match abs_index(*rel_index, array.len()) { + Ok(uindex) => { + if uindex >= array.len() { + array.resize(uindex + 1, Value::new(None, ValueKind::Nil)); + } + uindex + } + Err(insertion) => { + array.splice( + 0..0, + (0..insertion).map(|_| Value::new(None, ValueKind::Nil)), + ); + 0 + } + }; - &mut array[uindex] + child = &mut array[uindex]; + } } } + child } pub(crate) fn set(&self, root: &mut Value, value: Value) { diff --git a/src/path/parser.rs b/src/path/parser.rs index f4246e0c..3b45fb77 100644 --- a/src/path/parser.rs +++ b/src/path/parser.rs @@ -17,6 +17,7 @@ use winnow::token::any; use winnow::token::take_while; use crate::path::Expression; +use crate::path::Postfix; pub(crate) fn from_str(input: &str) -> Result> { path.parse(input) @@ -24,33 +25,22 @@ pub(crate) fn from_str(input: &str) -> Result PResult { let root = ident.parse_next(i)?; - let expr = repeat(0.., postfix) - .fold( - || root.clone(), - |prev, cur| match cur { - Child::Key(k) => Expression::Child(Box::new(prev), k), - Child::Index(k) => Expression::Subscript(Box::new(prev), k), - }, - ) - .parse_next(i)?; + let postfix = repeat(0.., postfix).parse_next(i)?; + let expr = Expression { root, postfix }; Ok(expr) } -fn ident(i: &mut &str) -> PResult { - raw_ident.map(Expression::Identifier).parse_next(i) -} - -fn postfix(i: &mut &str) -> PResult { +fn postfix(i: &mut &str) -> PResult { dispatch! {any; '[' => cut_err( seq!( - integer.map(Child::Index), + integer.map(Postfix::Index), _: ']'.context(StrContext::Expected(StrContextValue::CharLiteral(']'))), ) .map(|(i,)| i) .context(StrContext::Label("subscript")) ), - '.' => cut_err(raw_ident.map(Child::Key)), + '.' => cut_err(ident.map(Postfix::Key)), _ => cut_err( fail .context(StrContext::Label("postfix")) @@ -61,14 +51,9 @@ fn postfix(i: &mut &str) -> PResult { .parse_next(i) } -enum Child { - Key(String), - Index(isize), -} - -fn raw_ident(i: &mut &str) -> PResult { +fn ident(i: &mut &str) -> PResult { take_while(1.., ('a'..='z', 'A'..='Z', '0'..='9', '_', '-')) - .map(ToString::to_string) + .map(ToOwned::to_owned) .context(StrContext::Label("identifier")) .context(StrContext::Expected(StrContextValue::Description( "ASCII alphanumeric", @@ -104,9 +89,10 @@ mod test { assert_data_eq!( parsed.to_debug(), str![[r#" -Identifier( - "abcd", -) +Expression { + root: "abcd", + postfix: [], +} "#]] ); @@ -118,9 +104,10 @@ Identifier( assert_data_eq!( parsed.to_debug(), str![[r#" -Identifier( - "abcd-efgh", -) +Expression { + root: "abcd-efgh", + postfix: [], +} "#]] ); @@ -132,12 +119,14 @@ Identifier( assert_data_eq!( parsed.to_debug(), str![[r#" -Child( - Identifier( - "abcd", - ), - "efgh", -) +Expression { + root: "abcd", + postfix: [ + Key( + "efgh", + ), + ], +} "#]] ); @@ -146,15 +135,17 @@ Child( assert_data_eq!( parsed.to_debug(), str![[r#" -Child( - Child( - Identifier( - "abcd", +Expression { + root: "abcd", + postfix: [ + Key( + "efgh", + ), + Key( + "ijkl", ), - "efgh", - ), - "ijkl", -) + ], +} "#]] ); @@ -166,12 +157,14 @@ Child( assert_data_eq!( parsed.to_debug(), str![[r#" -Subscript( - Identifier( - "abcd", - ), - 12, -) +Expression { + root: "abcd", + postfix: [ + Index( + 12, + ), + ], +} "#]] ); @@ -183,12 +176,14 @@ Subscript( assert_data_eq!( parsed.to_debug(), str![[r#" -Subscript( - Identifier( - "abcd", - ), - -1, -) +Expression { + root: "abcd", + postfix: [ + Index( + -1, + ), + ], +} "#]] );