summaryrefslogtreecommitdiff
path: root/chain/src/item/genins.rs
diff options
context:
space:
mode:
Diffstat (limited to 'chain/src/item/genins.rs')
-rw-r--r--chain/src/item/genins.rs231
1 files changed, 231 insertions, 0 deletions
diff --git a/chain/src/item/genins.rs b/chain/src/item/genins.rs
new file mode 100644
index 0000000..99d9202
--- /dev/null
+++ b/chain/src/item/genins.rs
@@ -0,0 +1,231 @@
+//! This module implements the generation and insertion of item
+//! derivation forests.
+//!
+//! This is used for the chain-rule machine to conveniently produce
+//! item derivations into a forest. This forest can serve as a rough
+//! approximation of the parse forests, and can be executed in other
+//! semirings later on.
+
+use super::*;
+use crate::{atom::DefaultAtom, default::Error, item::default::DefaultForest, Edge};
+use grammar::{Error as GrammarError, GrammarLabel, GrammarLabelType, TNT};
+use graph::Graph;
+
+use core::borrow::Borrow;
+use std::collections::HashSet as Set;
+
+/// A helper function to generate a fragment of forest.
+///
+/// It simply constructs a root node and then appends
+/// successive nodes as successive children of the previous
+/// node. Also the starting positions will all be set to the
+/// same position.
+///
+/// If the input is empty, this returns an empty forest;
+/// otherwise the result is not empty.
+pub fn generate_fragment(
+ labels: impl AsRef<[GrammarLabelType]>,
+ pos: usize,
+) -> Result<DefaultForest<ForestLabel<GrammarLabel>>, crate::default::Error> {
+ let mut labels_iter = labels.as_ref().iter();
+ let mut result: DefaultForest<ForestLabel<GrammarLabel>>;
+
+ if let Some(first_label) = labels_iter.next() {
+ result = DefaultForest::new_leaf(GrammarLabel::new(*first_label, pos));
+
+ let mut index = 0;
+
+ for label in labels_iter {
+ result.plant(
+ index,
+ DefaultForest::new_leaf(GrammarLabel::new(*label, pos)),
+ false,
+ )?;
+
+ // To prevent duplication of labels causing
+ // panics, we query the index of the new node.
+ index = result
+ .query_label(GrammarLabel::new(*label, pos).into())
+ // REVIEW: Perhaps a LabelNoNode error?
+ .ok_or(Error::Invalid)?;
+ }
+ } else {
+ result = Default::default();
+ }
+
+ Ok(result)
+}
+
+impl DefaultForest<ForestLabel<GrammarLabel>> {
+ /// Insert an item derivation forest into a recording forest.
+ ///
+ /// We need the help of other things just for finding the correct
+ /// places to insert these item fragments.
+ pub fn insert_item(
+ &mut self,
+ label: Edge,
+ fragment: impl Borrow<DefaultForest<ForestLabel<GrammarLabel>>>,
+ atom_child_iter: impl Iterator<Item = usize>,
+ atom: &DefaultAtom,
+ ) -> Result<PaSe, Error> {
+ /// Convert an error telling us that an index is out of bounds.
+ ///
+ /// # Panics
+ ///
+ /// The function panics if the error is not of the expected
+ /// kind.
+ fn index_out_of_bounds_conversion(ge: GrammarError) -> Error {
+ match ge {
+ GrammarError::IndexOutOfBounds(index, bound) => {
+ Error::IndexOutOfBounds(index, bound)
+ }
+ _ => Error::Invalid,
+ }
+ }
+
+ let fragment = fragment.borrow();
+
+ let pase = label.forest_source();
+
+ let parents: Vec<Parent> = {
+ let mut result = Vec::new();
+
+ if let Some(parent) = pase.parent() {
+ result.push(parent);
+ } else {
+ let (root, leaf) = pase.segment().unwrap();
+ let mut seen_nodes = Set::new();
+
+ let mut stack = vec![root];
+
+ while let Some(top) = stack.pop() {
+ if seen_nodes.contains(&top) {
+ continue;
+ }
+
+ seen_nodes.insert(top);
+
+ for (index, child) in self.children_of(top)?.enumerate() {
+ if child == leaf {
+ result.push(Parent::new(top, index));
+ } else {
+ stack.push(child);
+ }
+ }
+ }
+ }
+
+ result
+ };
+
+ for atom_child in atom_child_iter {
+ // Find reduction information.
+ let reduction_info = atom
+ .query_reduction(label.label(), atom_child)
+ .map_err(index_out_of_bounds_conversion)?;
+
+ let mut stack = parents.clone();
+ let mut second_stack = Vec::new();
+
+ // locate the nodes
+ for reduction_nt in reduction_info.iter().copied().flatten().rev() {
+ while let Some(node) = stack.pop() {
+ // REVIEW: A lot of labels appear here.
+ // Perhaps I need to refactor something?
+ if matches!(
+ self
+ .vertex_label(node.node())?
+ .ok_or_else(|| Error::NodeNoLabel(node.node()))?
+ .label()
+ .label(),
+ GrammarLabelType::TNT(TNT::Non(nt)) if nt == *reduction_nt
+ ) {
+ for parent in self.parents_of(node.node())? {
+ debug_assert!(matches!(
+ self.vertex_label(parent.node()),
+ Ok(Some(label)) if
+ label
+ .label()
+ .label()
+ .rule()
+ .is_some()));
+
+ second_stack.extend(self.parents_of(parent.node())?.filter(|n| {
+ matches!(self.vertex_label(n.node()),
+ Ok(Some(label))
+ if matches!(
+ label.label().label().tnt(),
+ Some(TNT::Non(_))))
+ }));
+ }
+ }
+ }
+
+ std::mem::swap(&mut stack, &mut second_stack);
+
+ if stack.is_empty() {
+ break;
+ }
+ }
+
+ if stack.is_empty() {
+ return Err(Error::CannotPlant);
+ }
+
+ for parent in stack.into_iter() {
+ if parent.edge() + 1 >= self.degree(parent.node())? {
+ self.plant(parent.node(), fragment, false)?;
+ } else {
+ let nth_child = self.nth_child(parent.node(), parent.edge() + 1)?;
+
+ if self.is_prefix(nth_child, fragment)? {
+ // do nothing
+ continue;
+ }
+
+ let cloned_node = self.clone_node(nth_child)?;
+
+ self.plant(cloned_node, fragment, false)?;
+ }
+ }
+ }
+
+ let result = if fragment.nodes_len() == 2 {
+ let root_label = fragment.vertex_label(0)?.unwrap();
+ let leaf_label = fragment.vertex_label(1)?.unwrap();
+
+ // it has been planted, so should be safe.
+ let node = self.query_label(root_label).unwrap();
+
+ let edge = {
+ let mut result = None;
+
+ for (index, child) in self.children_of(node)?.enumerate() {
+ if matches!(self.vertex_label(child)?, Some(child_label) if child_label == leaf_label)
+ {
+ result = Some(index);
+ break;
+ }
+ }
+
+ if let Some(result) = result {
+ result
+ } else {
+ unreachable!("the forest is wrongly planted");
+ }
+ };
+
+ PaSe::Parent(node, edge)
+ } else {
+ let root_label = fragment.vertex_label(0)?.unwrap();
+ let leaf_label = fragment.vertex_label(fragment.nodes_len() - 1)?.unwrap();
+
+ PaSe::Segment(
+ self.query_label(root_label).unwrap(),
+ self.query_label(leaf_label).unwrap(),
+ )
+ };
+
+ Ok(result)
+ }
+}