Handle precedence and aliases properly when inlining variables

This commit is contained in:
Max Brunsfeld 2018-12-20 13:35:34 -08:00
parent d078c263b0
commit 988dc7de35

View file

@ -108,10 +108,25 @@ impl InlinedProductionMap {
.into_iter()
.map(|production_to_add| {
let mut inlined_production = item.production(grammar, &self).clone();
inlined_production.steps.splice(
step_index..step_index + 1,
production_to_add.steps.iter().cloned(),
);
let removed_step = inlined_production
.steps
.splice(
step_index..step_index + 1,
production_to_add.steps.iter().cloned(),
)
.next()
.unwrap();
let inserted_steps = &mut inlined_production.steps
[step_index..step_index + production_to_add.steps.len()];
if let Some(alias) = removed_step.alias {
for inserted_step in inserted_steps.iter_mut() {
inserted_step.alias = Some(alias.clone());
}
}
if let Some(last_inserted_step) = inserted_steps.last_mut() {
last_inserted_step.precedence = removed_step.precedence;
last_inserted_step.associativity = removed_step.associativity;
}
self.inlined_productions
.iter()
.position(|p| *p == inlined_production)
@ -129,8 +144,9 @@ impl InlinedProductionMap {
#[cfg(test)]
mod tests {
use super::*;
use crate::grammars::{ProductionStep, SyntaxVariable, VariableType};
use crate::rules::Symbol;
use crate::grammars::{LexicalGrammar, ProductionStep, SyntaxVariable, VariableType};
use crate::rules::{Alias, Associativity, Symbol};
use std::borrow::Borrow;
#[test]
fn test_basic_inlining() {
@ -142,7 +158,7 @@ mod tests {
variables_to_inline: vec![Symbol::non_terminal(1)],
variables: vec![
SyntaxVariable {
name: "var0".to_string(),
name: "non-terminal-0".to_string(),
kind: VariableType::Named,
productions: vec![Production {
dynamic_precedence: 0,
@ -154,7 +170,7 @@ mod tests {
}],
},
SyntaxVariable {
name: "var1".to_string(),
name: "non-terminal-1".to_string(),
kind: VariableType::Named,
productions: vec![
Production {
@ -176,34 +192,32 @@ mod tests {
let inline_map = InlinedProductionMap::new(&grammar);
// Nothing to inline at step 0.
assert_eq!(
display_items(
inline_map.inlined_items(ParseItem::Normal {
variable_index: 0,
production_index: 0,
step_index: 0
}),
&grammar,
&inline_map
),
None
);
assert!(inline_map
.inlined_items(ParseItem::Normal {
variable_index: 0,
production_index: 0,
step_index: 0
})
.is_none());
// Inlining variable 1 yields two productions.
assert_eq!(
display_items(
inline_map.inlined_items(ParseItem::Normal {
variable_index: 0,
production_index: 0,
step_index: 1
}),
inline_map
.inlined_items(ParseItem::Normal {
variable_index: 0,
production_index: 0,
step_index: 1
})
.unwrap(),
&grammar,
&inline_map
),
Some(vec![
"terminal-10 • terminal-12 terminal-13 terminal-11".to_string(),
"terminal-10 • terminal-14 terminal-11".to_string(),
])
vec![
"non-terminal-0 → terminal-10 • terminal-12 terminal-13 terminal-11"
.to_string(),
"non-terminal-0 → terminal-10 • terminal-14 terminal-11".to_string(),
]
);
}
@ -212,23 +226,21 @@ mod tests {
let grammar = SyntaxGrammar {
variables: vec![
SyntaxVariable {
name: "var0".to_string(),
name: "non-terminal-0".to_string(),
kind: VariableType::Named,
productions: vec![
Production {
dynamic_precedence: 0,
steps: vec![
ProductionStep::new(Symbol::terminal(10)),
ProductionStep::new(Symbol::non_terminal(1)), // inlined
ProductionStep::new(Symbol::terminal(11)),
ProductionStep::new(Symbol::non_terminal(2)), // inlined
ProductionStep::new(Symbol::terminal(12)),
],
},
],
productions: vec![Production {
dynamic_precedence: 0,
steps: vec![
ProductionStep::new(Symbol::terminal(10)),
ProductionStep::new(Symbol::non_terminal(1)), // inlined
ProductionStep::new(Symbol::terminal(11)),
ProductionStep::new(Symbol::non_terminal(2)), // inlined
ProductionStep::new(Symbol::terminal(12)),
],
}],
},
SyntaxVariable {
name: "var1".to_string(),
name: "non-terminal-1".to_string(),
kind: VariableType::Named,
productions: vec![
Production {
@ -245,7 +257,7 @@ mod tests {
],
},
SyntaxVariable {
name: "var2".to_string(),
name: "non-terminal-2".to_string(),
kind: VariableType::Named,
productions: vec![Production {
dynamic_precedence: 0,
@ -253,7 +265,7 @@ mod tests {
}],
},
SyntaxVariable {
name: "var3".to_string(),
name: "non-terminal-3".to_string(),
kind: VariableType::Named,
productions: vec![Production {
dynamic_precedence: 0,
@ -274,45 +286,156 @@ mod tests {
let inline_map = InlinedProductionMap::new(&grammar);
let items = inline_map.inlined_items(ParseItem::Normal {
variable_index: 0,
production_index: 0,
step_index: 1
}).unwrap().collect::<Vec<_>>();
let items = inline_map
.inlined_items(ParseItem::Normal {
variable_index: 0,
production_index: 0,
step_index: 1,
})
.unwrap()
.collect::<Vec<_>>();
assert_eq!(
display_items(Some(items.iter().cloned()), &grammar, &inline_map),
Some(vec![
"terminal-10 • terminal-13 terminal-11 non-terminal-2 terminal-12".to_string(),
"terminal-10 • terminal-16 terminal-14 terminal-11 non-terminal-2 terminal-12".to_string()
])
display_items(&items, &grammar, &inline_map),
vec![
"non-terminal-0 → terminal-10 • terminal-13 terminal-11 non-terminal-2 terminal-12".to_string(),
"non-terminal-0 → terminal-10 • terminal-16 terminal-14 terminal-11 non-terminal-2 terminal-12".to_string()
]
);
let item = items[0].successor().successor();
assert_eq!(
display_items(Some([item].iter().cloned()), &grammar, &inline_map),
Some(vec![
"terminal-10 terminal-13 terminal-11 • non-terminal-2 terminal-12".to_string(),
])
display_items(&[item], &grammar, &inline_map),
vec![
"non-terminal-0 → terminal-10 terminal-13 terminal-11 • non-terminal-2 terminal-12".to_string(),
]
);
assert_eq!(
display_items(inline_map.inlined_items(item), &grammar, &inline_map),
Some(vec![
"terminal-10 terminal-13 terminal-11 • terminal-15 terminal-12".to_string(),
])
display_items(inline_map.inlined_items(item).unwrap(), &grammar, &inline_map),
vec![
"non-terminal-0 → terminal-10 terminal-13 terminal-11 • terminal-15 terminal-12".to_string(),
]
);
}
#[test]
fn test_inlining_with_precedence_and_alias() {
let grammar = SyntaxGrammar {
variables_to_inline: vec![Symbol::non_terminal(1), Symbol::non_terminal(2)],
variables: vec![
SyntaxVariable {
name: "non-terminal-0".to_string(),
kind: VariableType::Named,
productions: vec![Production {
dynamic_precedence: 0,
steps: vec![
ProductionStep::new(Symbol::non_terminal(1)) // inlined
.with_prec(1, Some(Associativity::Left)),
ProductionStep::new(Symbol::terminal(10)),
ProductionStep::new(Symbol::non_terminal(2)), // inlined
],
}],
},
SyntaxVariable {
name: "non-terminal-1".to_string(),
kind: VariableType::Named,
productions: vec![Production {
dynamic_precedence: 0,
steps: vec![
ProductionStep::new(Symbol::terminal(11))
.with_prec(2, None)
.with_alias("inner_alias", true),
ProductionStep::new(Symbol::terminal(12)).with_prec(3, None),
],
}],
},
SyntaxVariable {
name: "non-terminal-2".to_string(),
kind: VariableType::Named,
productions: vec![Production {
dynamic_precedence: 0,
steps: vec![ProductionStep::new(Symbol::terminal(13))
.with_alias("outer_alias", true)],
}],
},
],
expected_conflicts: Vec::new(),
extra_tokens: Vec::new(),
external_tokens: Vec::new(),
word_token: None,
};
let inline_map = InlinedProductionMap::new(&grammar);
let items = inline_map
.inlined_items(ParseItem::Normal {
variable_index: 0,
production_index: 0,
step_index: 0,
})
.unwrap()
.collect::<Vec<_>>();
assert_eq!(
display_items(&items, &grammar, &inline_map)[0],
"non-terminal-0 → • terminal-11 terminal-12 terminal-10 non-terminal-2".to_string(),
);
// The first step in the inlined production retains its precedence and alias.
let item = items[0].successor();
assert_eq!(
display_items(&[item], &grammar, &inline_map)[0],
"non-terminal-0 → terminal-11 • terminal-12 terminal-10 non-terminal-2".to_string(),
);
assert_eq!(item.precedence(&grammar, &inline_map), 2);
assert_eq!(
items[0].step(&grammar, &inline_map).unwrap().alias,
Some(Alias {
value: "inner_alias".to_string(),
is_named: true,
})
);
// The final terminal of the inlined production inherits the precedence of
// the inlined step.
let item = item.successor();
assert_eq!(
display_items(&[item], &grammar, &inline_map)[0],
"non-terminal-0 → terminal-11 terminal-12 • terminal-10 non-terminal-2".to_string(),
);
assert_eq!(item.precedence(&grammar, &inline_map), 1);
let item = item.successor();
assert_eq!(
display_items(&[item], &grammar, &inline_map)[0],
"non-terminal-0 → terminal-11 terminal-12 terminal-10 • non-terminal-2".to_string(),
);
// All steps of the inlined production inherit their alias from the
// inlined step.
let items = inline_map.inlined_items(item).unwrap().collect::<Vec<_>>();
assert_eq!(
display_items(&items, &grammar, &inline_map)[0],
"non-terminal-0 → terminal-11 terminal-12 terminal-10 • terminal-13".to_string(),
);
assert_eq!(
items[0].step(&grammar, &inline_map).unwrap().alias,
Some(Alias {
value: "outer_alias".to_string(),
is_named: true,
})
)
}
fn display_items(
items: Option<impl Iterator<Item = ParseItem>>,
items: impl IntoIterator<Item = impl Borrow<ParseItem>>,
grammar: &SyntaxGrammar,
inline_map: &InlinedProductionMap,
) -> Option<Vec<String>> {
items.map(|items| {
items
.map(|item| format!("{}", item.with(grammar, inline_map)))
.collect()
})
) -> Vec<String> {
let lex = LexicalGrammar::default();
items
.into_iter()
.map(|item| format!("{}", item.borrow().display_with(grammar, &lex, inline_map)))
.collect()
}
}