Skip to content

Commit

Permalink
Merge pull request #622 from epage/path
Browse files Browse the repository at this point in the history
refactor(path): Switch from recursive to iterative structures
  • Loading branch information
epage authored Dec 19, 2024
2 parents fdef5c5 + 80ca240 commit b509e7e
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 202 deletions.
253 changes: 89 additions & 164 deletions src/path/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,18 @@ 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 {
Self {
root,
postfix: Vec::new(),
}
}
}

impl FromStr for Expression {
Expand All @@ -23,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 @@ -53,186 +67,97 @@ 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(id) => {
match root.kind {
// `x` access on a table is equivalent to: map[x]
ValueKind::Table(ref map) => map.get(&id),

// 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(value) => {
match value.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(value) => match value.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) -> Option<&'a mut Value> {
match *self {
Self::Identifier(ref id) => match root.kind {
ValueKind::Table(ref mut map) => Some(
map.entry(id.clone())
.or_insert_with(|| Value::new(None, ValueKind::Nil)),
),

_ => None,
},

Self::Child(ref expr, ref key) => match expr.get_mut_forcibly(root) {
Some(value) => {
if let ValueKind::Table(ref mut map) = value.kind {
Some(
map.entry(key.clone())
.or_insert_with(|| Value::new(None, ValueKind::Nil)),
)
} else {
*value = Map::<String, Value>::new().into();

if let ValueKind::Table(ref mut map) = value.kind {
Some(
map.entry(key.clone())
.or_insert_with(|| Value::new(None, ValueKind::Nil)),
)
} else {
unreachable!();
}
pub(crate) fn get_mut_forcibly<'a>(&self, root: &'a mut Value) -> &'a mut Value {
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();
}
}

_ => None,
},
let ValueKind::Table(ref mut map) = child.kind else {
unreachable!()
};

Self::Subscript(ref expr, index) => match expr.get_mut_forcibly(root) {
Some(value) => {
match value.kind {
ValueKind::Array(_) => (),
_ => *value = Vec::<Value>::new().into(),
child = map
.entry(key.clone())
.or_insert_with(|| Value::new(None, ValueKind::Nil));
}
Postfix::Index(rel_index) => {
if !matches!(child.kind, ValueKind::Array(_)) {
*child = Vec::<Value>::new().into();
}

match value.kind {
ValueKind::Array(ref mut array) => {
let index = abs_index(index, array.len()).ok()?;

if index >= array.len() {
array.resize(index + 1, Value::new(None, ValueKind::Nil));
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));
}

Some(&mut array[index])
uindex
}
Err(insertion) => {
array.splice(
0..0,
(0..insertion).map(|_| Value::new(None, ValueKind::Nil)),
);
0
}
};

_ => None,
}
child = &mut array[uindex];
}
_ => None,
},
}
}
child
}

pub(crate) fn set(&self, root: &mut Value, value: Value) {
match *self {
Self::Identifier(ref id) => {
// Ensure that root is a table
match root.kind {
ValueKind::Table(_) => {}

_ => {
*root = Map::<String, Value>::new().into();
}
}

match value.kind {
ValueKind::Table(ref incoming_map) => {
// Pull out another table
let target = if let ValueKind::Table(ref mut map) = root.kind {
map.entry(id.clone())
.or_insert_with(|| Map::<String, Value>::new().into())
} else {
unreachable!();
};

// Continue the deep merge
for (key, val) in incoming_map {
Self::Identifier(key.clone()).set(target, val.clone());
}
}

_ => {
if let ValueKind::Table(ref mut map) = root.kind {
// Just do a simple set
if let Some(existing) = map.get_mut(id) {
*existing = value;
} else {
map.insert(id.clone(), value);
}
}
}
}
}

Self::Child(ref expr, ref key) => {
if let Some(parent) = expr.get_mut_forcibly(root) {
if !matches!(parent.kind, ValueKind::Table(_)) {
// Didn't find a table. Oh well. Make a table and do this anyway
*parent = Map::<String, Value>::new().into();
}
Self::Identifier(key.clone()).set(parent, value);
let parent = self.get_mut_forcibly(root);
match value.kind {
ValueKind::Table(ref incoming_map) => {
// Continue the deep merge
for (key, val) in incoming_map {
Self::root(key.clone()).set(parent, val.clone());
}
}

Self::Subscript(ref expr, index) => {
if let Some(parent) = expr.get_mut_forcibly(root) {
if !matches!(parent.kind, ValueKind::Array(_)) {
*parent = Vec::<Value>::new().into();
}

if let ValueKind::Array(ref mut array) = parent.kind {
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
}
};

array[uindex] = value;
}
}
_ => {
*parent = value;
}
}
}
Expand Down
Loading

0 comments on commit b509e7e

Please sign in to comment.