Skip to content

Commit

Permalink
feat: WIP rework GLR reducer
Browse files Browse the repository at this point in the history
  • Loading branch information
igordejanovic committed Oct 14, 2023
1 parent b45ce82 commit cea56cb
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 145 deletions.
2 changes: 1 addition & 1 deletion rustemo-compiler/src/generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ impl<'g, 's> ParserGenerator<'g, 's> {
.collect();
ast.push(parse_quote! {
#[allow(clippy::enum_variant_names)]
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq)]
pub enum ProdKind {
#(#prodkind_variants),*
}
Expand Down
294 changes: 152 additions & 142 deletions rustemo/src/glr/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ where
S: State + Ord + Debug,
D: ParserDefinition<S, P, TK, NTK>,
TK: Copy + Default + PartialEq + Ord + Debug + 'i,
P: Copy + Debug + Into<NTK>,
P: Copy + Debug + Into<NTK> + PartialEq,
{
pub fn new(
definition: &'static D,
Expand Down Expand Up @@ -410,111 +410,46 @@ where
);
for path in self.find_reduction_paths(gss, &reduction) {
log!(" {} {path}", "Reducing over path:".green());
let root_head = gss.head(path.root_head);
let start_head = gss.head(match reduction.start {
let start_head = match reduction.start {
ReductionStart::Edge(e) => gss.start(e),
ReductionStart::Node(n) => n,
});
let start_position_before = start_head.position_before;
let start_location_pos_before = start_head.location_pos_before;
};
let token_kind_ahead =
start_head.token_ahead().as_ref().unwrap().kind;
let root_state = root_head.state();
gss.head(start_head).token_ahead().as_ref().unwrap().kind;
let root_state = gss.head(path.root_head).state();
let next_state =
self.definition.goto(root_state, production.into());

let solution = Rc::new(SPPFTree::NonTerm {
prod: production,
data: TreeData {
range: Range {
start: root_head.position_ahead,
end: start_position_before,
},
location: Location {
start: root_head.location_pos_ahead,
end: Some(start_location_pos_before),
},
layout: root_head.layout_ahead(),
},
children: path.parents,
});

if let Some(head) = subfrontier.get(&next_state) {
log!(
" {}",
format!(
"Head {} with the same state already exists.",
head.index()
)
.green()
);
log!(" {}",
format!(
"Register new solution for {} -> {}.",
head.index(),
path.root_head.index()
).green()
);
if let Some(edge) =
gss.add_solution(*head, path.root_head, solution)
// Get all non-error actions
let actions = self
.definition
.actions(next_state, token_kind_ahead)
.iter()
.take_while(|a| !matches!(a, Action::Error))
.collect::<Vec<_>>();

if actions.is_empty() {
log!(" No actions for new state {:?} and lookahead {:?}. Skipping.",
next_state, token_kind_ahead);
} else {
// Find a head with the same state or create new if it doesn't exist
let mut head_created = false;
let head = if let Some(head) = subfrontier.get(&next_state)
{
log!(
" {} {} -> {}.",
"Created edge".green(),
head.index(),
path.root_head.index()
" {}",
format!(
"Head {} with the same state already exists.",
head.index()
)
.green()
);
// Parent link was created -> register all non-empty
// reductions
for &action in self
.definition
.actions(next_state, token_kind_ahead)
.iter()
.take_while(|a| !matches!(a, Action::Error))
{
match action {
Action::Reduce(prod, length) if length > 0 => {
log!(
" {} {} -> {}: {:?}, len {}",
"Register new reduction".green(),
head.index(),
path.root_head.index(),
prod,
length
);
//log!(" {} {:?}, len = {}", "Register reduction for production:".green(), prod, length);
pending_reductions.push_back(Reduction {
start: ReductionStart::Edge(edge),
production: prod,
length,
})
}
_ => (),
}
}
*head
} else {
log!(" {}",
format!(
"Edge {} -> {} already exists. Not created.",
head.index(),
path.root_head.index()).green()
);
}
} else {
// No head with this state. We shall create one only if
// there is at least one action for the head. If not, we
// shall just report that.
let actions = self
.definition
.actions(next_state, token_kind_ahead)
.iter()
.take_while(|a| !matches!(a, Action::Error))
.collect::<Vec<_>>();
// No head with this state. Create one, create edge and
// register shifts and reductions.
if !actions.is_empty() {
let new_head = start_head.with_tok_state(
start_head.token_ahead().cloned().unwrap(),
// Create new head
let shead = gss.head(start_head);
let new_head = shead.with_tok_state(
shead.token_ahead().cloned().unwrap(),
next_state,
);
#[cfg(debug_assertions)]
Expand All @@ -527,73 +462,143 @@ where
new_head_idx.index(),
new_head_str
);
let edge = gss.add_solution(
new_head_idx,
path.root_head,
solution,
head_created = true;
new_head_idx
};

// Find an edge between the head and the root_head or create new
// if it doesn't exist
let mut edge_created = false;
let edge = if let Some(edge) =
gss.edge_between(head, path.root_head)
{
log!(
" {}",
format!(
"Edge {} -> {} already exists. Not created.",
head.index(),
path.root_head.index()
)
.green()
);
edge
} else {
// Create new edge
log!(
" {} {} -> {}.",
" {} {} -> {}.",
"Created edge".green(),
new_head_idx.index(),
head.index(),
path.root_head.index()
);

edge_created = true;
gss.add_parent(
head,
path.root_head,
Rc::new(Parent::new(path.root_head, head, vec![])),
)
};

// Register new solution and actions
if head_created
|| edge_created
// Don't register if we already have this production
// reduced between these heads
|| !gss.parent(edge).possibilities.borrow().iter().any(
|t| match **t {
SPPFTree::Term { .. } => false,
SPPFTree::NonTerm { prod, .. } => {
prod == production
}
},
)
{
log!(
" {} {}.",
"Preparing shifts/reduces for head".green(),
new_head_idx.index()
" {}",
format!(
"Register new solution for {} -> {}.",
head.index(),
path.root_head.index()
)
.green()
);

let root_head = gss.head(path.root_head);
let start_head = gss.head(start_head);
let solution = Rc::new(SPPFTree::NonTerm {
prod: production,
data: TreeData {
range: Range {
start: root_head.position_ahead,
end: start_head.position_before,
},
location: Location {
start: root_head.location_pos_ahead,
end: Some(start_head.location_pos_before),
},
layout: root_head.layout_ahead(),
},
children: path.parents,
});
gss.parent(edge)
.possibilities
.borrow_mut()
.push(solution);

// Register actions
for &action in actions {
match action {
Action::Reduce(production, length) => {
log!(
" {}: {:?}, len {}",
"Register new reduction".green(),
production,
length
);
pending_reductions.push_back(Reduction {
start: if length > 0 {
ReductionStart::Edge(
edge.expect(
"Edge must be created in add_solution!"))
if length > 0 || head_created {
log!(
" {}: {:?}, len {}",
"Register new reduction".green(),
production,
length
);
pending_reductions.push_back(
Reduction {
start: if length > 0 {
ReductionStart::Edge(edge)
} else {
ReductionStart::Node(new_head_idx)
ReductionStart::Node(head)
},
production,
length,
});
production,
length,
},
);
}
}
Action::Shift(s) => {
log!(
" {}",
format!(
"Adding head {} to pending shifts.",
new_head_idx.index()
)
.green()
);
pending_shifts.push((new_head_idx, s))
if head_created {
log!(
" {}",
format!(
"Adding head {} to pending shifts.",
head.index()
)
.green()
);
pending_shifts.push((head, s));
}
}
Action::Accept => {
log!(
" {}",
format!(
"Accepting head {}.",
new_head_idx.index()
)
.red()
);
accepted_heads.push(new_head_idx)
if head_created {
log!(
" {}",
format!(
"Accepting head {}.",
head.index()
)
.red()
);
accepted_heads.push(head)
}
}
Action::Error => panic!("Cannot happen!"),
}
}
} else {
log!(" No actions for reduced head. Not creating.");
}

}
}
}
Expand Down Expand Up @@ -815,7 +820,7 @@ where
I: Input + ?Sized + Debug,
L: Lexer<'i, GssHead<'i, I, S, TK>, S, TK, Input = I>,
S: State + Debug + Ord,
P: Copy + Debug + Into<NTK>,
P: Copy + Debug + Into<NTK> + PartialEq,
TK: Copy + Debug + Ord + Default + 'i,
D: ParserDefinition<S, P, TK, NTK>,
{
Expand Down Expand Up @@ -900,6 +905,11 @@ where
}

let forest = self.create_forest(gss, accepted_heads);
log!(
"\n{}. {}",
"Finished".red(),
format!("{} solutions found.", forest.solutions()).green()
);
Ok(forest)
}

Expand Down
2 changes: 1 addition & 1 deletion tests/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ fn main() {
),
(
"glr/special/right_nullable",
Box::new(|s| s.parser_algo(ParserAlgo::GLR).print_table(true)),
Box::new(|s| s.parser_algo(ParserAlgo::GLR)),
),
];

Expand Down
2 changes: 1 addition & 1 deletion tests/src/output_dir/output_dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ impl From<TokenKind> for usize {
}
}
#[allow(clippy::enum_variant_names)]
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq)]
pub enum ProdKind {
AP1,
B1P1,
Expand Down

0 comments on commit cea56cb

Please sign in to comment.