Skip to content

Commit

Permalink
fix: correctly construct first and follow tables
Browse files Browse the repository at this point in the history
  • Loading branch information
Specy authored and umut-sahin committed Nov 19, 2024
1 parent 78c098e commit 7bd9e90
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 1 deletion.
7 changes: 7 additions & 0 deletions assets/grammars/correct/indirect_empty.lr
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
S -> A B

A -> C
C -> D
D -> ''

B -> 'x'
6 changes: 5 additions & 1 deletion src/tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ impl FirstTable {
/// Constructs the first table from the grammar.
pub fn construct(grammar: &Grammar) -> FirstTable {
let mut first_table = IndexMap::new();
let mut indirectly_empty_symbols = IndexSet::new();

let mut done = false;
while !done {
Expand All @@ -32,7 +33,9 @@ impl FirstTable {
.cloned(),
);
}
if !grammar.empty_symbols().contains(symbol) {
if !grammar.empty_symbols().contains(symbol)
&& !indirectly_empty_symbols.contains(symbol)
{
break;
}
},
Expand All @@ -42,6 +45,7 @@ impl FirstTable {
},
}
if index == rule.pattern().len() - 1 {
indirectly_empty_symbols.insert(rule.symbol());
possible_first_tokens.insert(Token::Empty);
}
}
Expand Down
1 change: 1 addition & 0 deletions tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub mod grammars {
pub const G9: &str = include_str!("../assets/grammars/correct/g9.lr");
pub const G10: &str = include_str!("../assets/grammars/correct/g10.lr");
pub const G11: &str = include_str!("../assets/grammars/correct/g11.lr");
pub const INDIRECT_EMPTY: &str = include_str!("../assets/grammars/correct/indirect_empty.lr");
pub const JSON: &str = include_str!("../assets/grammars/correct/json.lr");
pub const NOT_LALR: &str = include_str!("../assets/grammars/correct/not-lalr.lr");
pub const OPTIONAL: &str = include_str!("../assets/grammars/correct/optional.lr");
Expand Down
91 changes: 91 additions & 0 deletions tests/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,97 @@ fn raising_correct_error_when_creating_lalr_parser_for_non_lalr_grammar() {
}


#[test]
#[cfg_attr(target_family = "wasm", wasm_bindgen_test)]
fn correctly_creating_first_and_follow_sets_for_indirectly_empty_grammar() {
let grammar = Grammar::parse(common::grammars::INDIRECT_EMPTY).unwrap();
let parser = Parser::lr(grammar).unwrap();

let first_table = parser.first_table();
{
// +--------+-----------+
// | Symbol | First Set |
// +--------+-----------+
// | C | { ε } |
// | D | { ε } |
// | B | { 'x' } |
// | A | { ε } |
// | S | { 'x' } |
// +--------+-----------+

#[rustfmt::skip]
assert_eq!(
*first_table.deref(),
[
(
Symbol::from("C"),
[Token::Empty.into()].into(),
),
(
Symbol::from("D"),
[Token::Empty.into()].into(),
),
(
Symbol::from("B"),
[ConstantToken::from("x").into()].into(),
),
(
Symbol::from("A"),
[Token::Empty.into()].into(),
),
(
Symbol::from("S"),
[ConstantToken::from("x").into()].into(),
)
]
.into_iter()
.collect::<IndexMap<_, _>>()
);
}

let follow_table = parser.follow_table();
{
// +--------+------------+
// | Symbol | Follow Set |
// +--------+------------+
// | C | { 'x' } |
// | D | { 'x' } |
// | B | { $ } |
// | A | { 'x' } |
// | S | { $ } |
// +--------+------------+

#[rustfmt::skip]
assert_eq!(
*follow_table.deref(),
[
(
Symbol::from("C"),
[ConstantToken::from("x").into()].into(),
),
(
Symbol::from("D"),
[ConstantToken::from("x").into()].into(),
),
(
Symbol::from("B"),
[Token::Eof].into(),
),
(
Symbol::from("A"),
[ConstantToken::from("x").into()].into(),
),
(
Symbol::from("S"),
[Token::Eof].into(),
),
]
.into_iter()
.collect::<IndexMap<_, _>>()
);
}
}

#[test]
#[cfg_attr(target_family = "wasm", wasm_bindgen_test)]
fn correctly_creating_lr_parser_for_binary_addition_grammar() {
Expand Down

0 comments on commit 7bd9e90

Please sign in to comment.