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.rs395
1 files changed, 216 insertions, 179 deletions
diff --git a/chain/src/item/genins.rs b/chain/src/item/genins.rs
index f48c8a8..0c3f616 100644
--- a/chain/src/item/genins.rs
+++ b/chain/src/item/genins.rs
@@ -9,15 +9,17 @@
use super::*;
use crate::{
atom::{Atom, DefaultAtom},
- default::Error,
+ default::{BoTop, Error, Reducer},
item::default::DefaultForest,
Edge,
};
use grammar::{Error as GrammarError, GrammarLabel, GrammarLabelType, TNT};
-use graph::{builder::BuilderMut, labelled::binary::PLGBuilderMut, Graph, RedirectGraph};
+use graph::Graph;
use core::borrow::Borrow;
+use std::collections::HashSet;
+
/// Convert an error telling us that an index is out of bounds.
///
/// # Panics
@@ -151,8 +153,35 @@ impl DefaultForest<ForestLabel<GrammarLabel>> {
/// # Steps
///
/// This function performs the following steps.
+ ///
+ /// # Extra reductions
+ ///
+ /// If the label's true_source is different from its
+ /// forest_source, first splone the node of true_source, then
+ /// query the reducer by the key botop, whose bottom is
+ /// `true_source` and top is `forest_source`. The result is an
+ /// optional set of tuples (nt, rule) of unsigned integers. For
+ /// each tuple, find a parent which is labelled by `nt` and whose
+ /// parent is labelled by `rule`. Then proceed similarly.
+ ///
+ /// # Reductions
+ ///
+ /// Perform splone on the node of forest_source. Then query atom
+ /// for the reduction information by the key (label, atom_child),
+ /// where atom_child runs through every element of
+ /// atom_child_iter. The result is a list of unsigned integers.
+ /// For each unsigned integer `nt`, we find a parent which is
+ /// labelled by `nt`. The last parents found will be the parents
+ /// used in the next step.
+ ///
+ /// # Plant
+ ///
+ /// For parents as found in the previous step, for each node in
+ /// parents, perform splone with an open end, and then plant the
+ /// fragment under the result splone.
pub(crate) fn insert_item(
&mut self,
+ reducer: &Reducer,
label: Edge,
fragment: impl Borrow<DefaultForest<ForestLabel<GrammarLabel>>>,
atom_child_iter: impl Iterator<Item = usize> + ExactSizeIterator + Clone,
@@ -182,181 +211,12 @@ impl DefaultForest<ForestLabel<GrammarLabel>> {
let pos = fragment_root_label.label().start();
- if pavi != true_source {
- dbg!(label, pos);
-
- // Completing from true_source up to the pavi
- let true_source_node = match true_source {
- PaVi::Parent(node, edge) => {
- let nth_child = self.nth_child(node, edge)?;
- let nth_child_label = self
- .vertex_label(nth_child)?
- .ok_or(Error::NodeNoLabel(nth_child))?;
-
- let nth_child_degree = self.degree(nth_child)?;
- let nth_child_last = if nth_child_degree > 0 {
- nth_child_degree - 1
- } else {
- 0
- };
-
- if matches!(nth_child_label.label().label().tnt(), Some(TNT::Non(_)))
- && !nth_child_label.is_packed()
- {
- self.splone(nth_child, Some(pos), nth_child_last, false)?
- } else {
- nth_child
- }
- }
- PaVi::Virtual(nt, t, mut node) => {
- let node_label_start = self
- .vertex_label(node)?
- .ok_or(Error::NodeNoLabel(node))?
- .label()
- .start();
-
- let reduction_fragment = atom.generate_virtual_frags(nt, t, None);
-
- // FIXME: We shall "close" this fragment as well.
-
- for frag in reduction_fragment.into_iter().flatten() {
- let mut frag = frag.clone();
- frag.set_pos(node_label_start)?;
-
- // NOTE: The function `plant_if_needed`
- // assumes that we want to plant the fragment
- // as the first child of the node. This
- // assumption holds in this case, but not in
- // general.
-
- node = self.plant_if_needed(node, frag)?;
- }
-
- node
- }
- PaVi::Empty => {
- dbg!();
- root
- }
- };
-
- let true_source_pos = self
- .vertex_label(true_source_node)?
- .ok_or(Error::NodeNoLabel(true_source_node))?
- .label()
- .start();
-
- let top_node = match pavi {
- PaVi::Parent(node, edge) => self.nth_child(node, edge)?,
- PaVi::Virtual(_nt, _t, _node) => {
- dbg!(label);
-
- self.print_viz("dbg forest.gv").unwrap();
- fragment.print_viz("dbg fragment.gv").unwrap();
-
- unreachable!("I do not think this is possible")
- }
- PaVi::Empty => {
- unreachable!("The empty segment should not need to be reduced")
- }
- };
-
- let mut stack = vec![vec![(top_node, 0)]];
- let mut result_stack = Vec::new();
-
- while let Some(mut top_stack) = stack.pop() {
- let (node, _) = top_stack.pop().unwrap();
-
- let node_label = self.vertex_label(node)?.ok_or(Error::NodeNoLabel(node))?;
-
- if node_label.is_packed() {
- for node in self.children_of(node)? {
- let search_child = self.position_search(node, true_source_pos)?;
-
- if let Some((child, index)) = search_child {
- let mut top_stack = top_stack.clone();
- // Fix the previous element
- top_stack.push((node, index));
-
- if child == true_source_node {
- result_stack.push(top_stack);
- } else {
- top_stack.push((child, 0));
-
- stack.push(top_stack);
- }
- }
- }
- } else {
- let search_child = self.position_search(node, true_source_pos)?;
-
- if let Some((child, index)) = search_child {
- top_stack.push((node, index));
-
- if child == true_source_node {
- result_stack.push(top_stack);
- } else {
- top_stack.push((child, 0));
-
- stack.push(top_stack);
- }
- }
- }
- }
-
- // FIXME: We have to change the pavi as well, otherwise we
- // are going to use the wrong branch for planting later.
-
- for stack in result_stack {
- // dbg!(&stack);
- // self.print_viz("dbg forest.gv").unwrap();
- // panic!("test");
-
- let mut first_time = true;
-
- for (node, index) in stack.into_iter().rev() {
- if matches!(
- self.vertex_label(node)?
- .ok_or(Error::NodeNoLabel(node))?
- .label()
- .label()
- .tnt(),
- Some(TNT::Non(_))
- ) {
- let sploned = self.splone(node, Some(pos), index, false)?;
-
- if !first_time {
- let last_index = self.degree(sploned)? - 1;
-
- let last_child = self.nth_child(sploned, last_index)?;
-
- let last_child_label = self
- .vertex_label(last_child)?
- .ok_or(Error::NodeNoLabel(last_child))?
- .label();
-
- if last_child_label.end() != Some(pos) {
- let closed_label = GrammarLabel::new_closed(
- last_child_label.label(),
- last_child_label.start(),
- pos,
- );
-
- let closed_label_id = self
- .query_label(closed_label.into())
- .expect("last child closed label not found");
-
- let mut builder = PLGBuilderMut::from_graph_mut(&mut self.graph);
-
- builder.redirect(sploned, last_index, closed_label_id)?;
- }
- }
-
- first_time = false;
- }
- }
- }
- }
+ self.extra_reductions(
+ BoTop::new(pavi, true_source),
+ pos,
+ reducer.borrow(),
+ atom.borrow(),
+ )?;
// Ensure the last node in the PaVi is a terminal or a
// non-terminal node, as an extra safety guard during
@@ -399,6 +259,8 @@ impl DefaultForest<ForestLabel<GrammarLabel>> {
}
}
+ // TODO: Refactor this.
+
let is_empty_segment = pavi.is_empty();
let mut parents: Vec<Parent> = {
@@ -418,7 +280,7 @@ impl DefaultForest<ForestLabel<GrammarLabel>> {
if let Some(frag) = virtual_frag {
let mut frag = (*frag.get(0).unwrap()).clone();
- frag.set_pos(node_label.label().start())?;
+ frag.set_pos(node_label.label().start(), false)?;
let frag_nodes_len = frag.nodes_len();
@@ -435,7 +297,7 @@ impl DefaultForest<ForestLabel<GrammarLabel>> {
// assumption holds in this case, but
// not in general.
- self.plant_if_needed(node, frag)?;
+ self.plant_at_start(node, frag)?;
let rule_label_pos = self
.query_label(last_but_one_label)
@@ -685,6 +547,181 @@ impl DefaultForest<ForestLabel<GrammarLabel>> {
Ok(result)
}
+
+ /// Perform extra reductions.
+ ///
+ /// To be precise, this functions first splones the bottom node,
+ /// and then queries the reducer to find the next nodes to splone,
+ /// and repeats this process until either we find the top node, or
+ /// the reducer says to stop.
+ ///
+ /// One nuance to note is that after we splone a node, if we split
+ /// that node, then the splitted node will have a cloned parent,
+ /// see
+ /// [`split_node`][DefaultForest::<ForestLabel<GrammarLabel>>::split_node]
+ /// for details. So for each parent pointed out by the reducer,
+ /// we need to find its clone that has the splitted node as a
+ /// child.
+ fn extra_reductions(
+ &mut self,
+ botop: BoTop,
+ pos: usize,
+ reducer: &Reducer,
+ atom: &DefaultAtom,
+ ) -> Result<Vec<usize>, Error> {
+ if botop.bottom() == botop.top() {
+ return Ok(vec![self.node_from_pavi(botop.top())?]);
+ }
+
+ let top = botop.top();
+ let bottom = botop.bottom();
+
+ let bottom_closed = self.close_pavi(atom.borrow(), bottom, pos)?;
+
+ let top_node = self.node_from_pavi(top)?;
+ let bottom_node = self.node_from_pavi(bottom)?;
+
+ let mut stack = vec![bottom_node];
+
+ let mut seen_nodes: HashSet<usize> = Default::default();
+
+ let mut result = Vec::new();
+
+ while let Some(bottom) = stack.pop() {
+ if seen_nodes.contains(&bottom) {
+ continue;
+ }
+
+ seen_nodes.insert(bottom);
+
+ if let Some(set) = reducer.get(&(top_node, bottom)) {
+ // First splone the bottom, except for the bottom one.
+
+ let mut sploned = if bottom == bottom_node {
+ bottom_closed
+ } else {
+ let degree = self.degree(bottom)?;
+ let last_index = core::cmp::max(degree, 1) - 1;
+
+ self.splone(bottom, Some(pos), last_index, false)?
+ };
+
+ // Then add parents whose labels are what we want into
+ // the stack.
+
+ let mut label_set: HashSet<GrammarLabel> = HashSet::with_capacity(set.len());
+
+ for node in set.iter().copied() {
+ label_set.insert(
+ self.vertex_label(node)?
+ .ok_or(Error::NodeNoLabel(node))?
+ .label(),
+ );
+ }
+
+ let sploned_label = self
+ .vertex_label(sploned)?
+ .ok_or(Error::NodeNoLabel(sploned))?;
+
+ if sploned_label.clone_index().is_some() {
+ let mut parents = self.parents_of(sploned)?;
+
+ #[cfg(debug_assertions)]
+ if parents.len() != 1 {
+ panic!("assumption fails");
+ }
+
+ sploned = parents.next().unwrap().node();
+ }
+
+ for rule_parent in self.parents_of(sploned)? {
+ if self.degree(rule_parent.node())? != 1 {
+ panic!("assumption fails");
+ }
+
+ for parent in self.parents_of(rule_parent.node())? {
+ let parent_node = parent.node();
+
+ let parent_label = self
+ .vertex_label(parent_node)?
+ .ok_or(Error::NodeNoLabel(parent_node))?
+ .label();
+
+ if label_set.contains(&parent_label) {
+ stack.push(parent_node);
+ }
+ }
+ }
+ } else {
+ result.push(bottom);
+ }
+ }
+
+ Ok(result)
+ }
+
+ /// Set the end position of the node associated with `pavi` to be `pos`.
+ ///
+ /// The parameter `atom` is used to query the reduction fragment
+ /// if `pavi` is a virtual node.
+ fn close_pavi(&mut self, atom: &DefaultAtom, pavi: PaVi, pos: usize) -> Result<usize, Error> {
+ match pavi {
+ PaVi::Parent(node, edge) => {
+ let nth_child = self.nth_child(node, edge)?;
+ let nth_child_label = self
+ .vertex_label(nth_child)?
+ .ok_or(Error::NodeNoLabel(nth_child))?;
+
+ let nth_child_degree = self.degree(nth_child)?;
+ let nth_child_last = core::cmp::max(nth_child_degree, 1) - 1;
+
+ if matches!(nth_child_label.label().label().tnt(), Some(TNT::Non(_)))
+ && !nth_child_label.is_packed()
+ {
+ Ok(self.splone(nth_child, Some(pos), nth_child_last, false)?)
+ } else if nth_child_label.is_packed() {
+ // REVIEW: is this really correct?
+ dbg!("this should not really happen?");
+
+ let mut result: usize = nth_child;
+
+ for node in self.children_of(nth_child)?.collect::<Vec<_>>() {
+ let node_label =
+ self.vertex_label(node)?.ok_or(Error::NodeNoLabel(node))?;
+ let degree = self.degree(node)?;
+ let last_index = core::cmp::max(degree, 1) - 1;
+
+ if matches!(node_label.label().label().tnt(), Some(TNT::Non(_))) {
+ result = self.splone(node, Some(pos), last_index, false)?;
+ }
+ }
+
+ Ok(result)
+ } else {
+ Ok(nth_child)
+ }
+ }
+ PaVi::Virtual(nt, t, mut node) => {
+ let node_label_start = self
+ .vertex_label(node)?
+ .ok_or(Error::NodeNoLabel(node))?
+ .label()
+ .start();
+
+ let reduction_fragment = atom.generate_virtual_frags(nt, t, None);
+
+ for frag in reduction_fragment.into_iter().flatten() {
+ let mut frag = frag.clone();
+ frag.set_pos(node_label_start, true)?;
+
+ node = self.plant_at_start(node, frag)?;
+ }
+
+ Ok(node)
+ }
+ _ => self.root().ok_or(Error::IndexOutOfBounds(0, 0)),
+ }
+ }
}
#[cfg(test)]