Skip to content

Commit

Permalink
refactor(path): Switch from recursive to iterative structures
Browse files Browse the repository at this point in the history
  • Loading branch information
epage committed Dec 19, 2024
1 parent c397a10 commit 80ca240
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 146 deletions.
168 changes: 77 additions & 91 deletions src/path/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self>, String),
Subscript(Box<Self>, isize),
pub(crate) struct Expression {
root: String,
postfix: Vec<Postfix>,
}

impl Expression {
pub(crate) fn root(root: String) -> Self {
Expression::Identifier(root)
Self {
root,
postfix: Vec::new(),
}
}
}

Expand All @@ -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);

Expand Down Expand Up @@ -59,105 +67,83 @@ fn abs_index(index: isize, len: usize) -> Result<usize, usize> {

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::<String, Value>::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::<String, Value>::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::<String, Value>::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::<String, Value>::new().into();
}
let ValueKind::Table(ref mut map) = child.kind else {
unreachable!()
};

if !matches!(child.kind, ValueKind::Array(_)) {
*child = Vec::<Value>::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::<Value>::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) {
Expand Down
105 changes: 50 additions & 55 deletions src/path/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,40 +17,30 @@ 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<Expression, ParseError<&str, ContextError>> {
path.parse(input)
}

fn path(i: &mut &str) -> PResult<Expression> {
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<Expression> {
raw_ident.map(Expression::Identifier).parse_next(i)
}

fn postfix(i: &mut &str) -> PResult<Child> {
fn postfix(i: &mut &str) -> PResult<Postfix> {
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"))
Expand All @@ -61,14 +51,9 @@ fn postfix(i: &mut &str) -> PResult<Child> {
.parse_next(i)
}

enum Child {
Key(String),
Index(isize),
}

fn raw_ident(i: &mut &str) -> PResult<String> {
fn ident(i: &mut &str) -> PResult<String> {
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",
Expand Down Expand Up @@ -104,9 +89,10 @@ mod test {
assert_data_eq!(
parsed.to_debug(),
str![[r#"
Identifier(
"abcd",
)
Expression {
root: "abcd",
postfix: [],
}
"#]]
);
Expand All @@ -118,9 +104,10 @@ Identifier(
assert_data_eq!(
parsed.to_debug(),
str![[r#"
Identifier(
"abcd-efgh",
)
Expression {
root: "abcd-efgh",
postfix: [],
}
"#]]
);
Expand All @@ -132,12 +119,14 @@ Identifier(
assert_data_eq!(
parsed.to_debug(),
str![[r#"
Child(
Identifier(
"abcd",
),
"efgh",
)
Expression {
root: "abcd",
postfix: [
Key(
"efgh",
),
],
}
"#]]
);
Expand All @@ -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",
)
],
}
"#]]
);
Expand All @@ -166,12 +157,14 @@ Child(
assert_data_eq!(
parsed.to_debug(),
str![[r#"
Subscript(
Identifier(
"abcd",
),
12,
)
Expression {
root: "abcd",
postfix: [
Index(
12,
),
],
}
"#]]
);
Expand All @@ -183,12 +176,14 @@ Subscript(
assert_data_eq!(
parsed.to_debug(),
str![[r#"
Subscript(
Identifier(
"abcd",
),
-1,
)
Expression {
root: "abcd",
postfix: [
Index(
-1,
),
],
}
"#]]
);
Expand Down

0 comments on commit 80ca240

Please sign in to comment.