Add further static validation of named precedences
This commit is contained in:
parent
344797c110
commit
d8a235faa1
1 changed files with 133 additions and 8 deletions
|
|
@ -6,10 +6,10 @@ mod flatten_grammar;
|
|||
mod intern_symbols;
|
||||
mod process_inlines;
|
||||
|
||||
use super::Error;
|
||||
use super::{rules::Precedence, Error};
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::{hash_map, HashMap},
|
||||
collections::{hash_map, HashMap, HashSet},
|
||||
mem,
|
||||
};
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ pub(crate) fn prepare_grammar(
|
|||
InlinedProductionMap,
|
||||
AliasMap,
|
||||
)> {
|
||||
validate_precedence_orderings(&input_grammar.precedence_orderings)?;
|
||||
validate_named_precedences(input_grammar)?;
|
||||
|
||||
let interned_grammar = intern_symbols(input_grammar)?;
|
||||
let (syntax_grammar, lexical_grammar) = extract_tokens(interned_grammar)?;
|
||||
|
|
@ -69,12 +69,14 @@ pub(crate) fn prepare_grammar(
|
|||
Ok((syntax_grammar, lexical_grammar, inlines, default_aliases))
|
||||
}
|
||||
|
||||
/// Make sure that there are no conflicting orderings. For any two precedence
|
||||
/// names `a` and `b`, if `a` comes before `b` in some list, then it cannot come
|
||||
// *after* `b` in any list.
|
||||
fn validate_precedence_orderings(order_lists: &[Vec<String>]) -> Result<()> {
|
||||
/// Check that all of the named precedences used in the grammar are declared
|
||||
/// within the `precedences` lists, and also that there are no conflicting
|
||||
/// precedence orderings declared in those lists.
|
||||
fn validate_named_precedences(grammar: &InputGrammar) -> Result<()> {
|
||||
// For any two precedence names `a` and `b`, if `a` comes before `b`
|
||||
// in some list, then it cannot come *after* `b` in any list.
|
||||
let mut pairs = HashMap::new();
|
||||
for list in order_lists {
|
||||
for list in &grammar.precedence_orderings {
|
||||
for (i, mut name1) in list.iter().enumerate() {
|
||||
for mut name2 in list.iter().skip(i + 1) {
|
||||
if name2 == name1 {
|
||||
|
|
@ -101,5 +103,128 @@ fn validate_precedence_orderings(order_lists: &[Vec<String>]) -> Result<()> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that no rule contains a named precedence that is not present in
|
||||
// any of the `precedences` lists.
|
||||
fn validate(rule_name: &str, rule: &Rule, names: &HashSet<&String>) -> Result<()> {
|
||||
match rule {
|
||||
Rule::Repeat(rule) => validate(rule_name, rule, names),
|
||||
Rule::Seq(elements) | Rule::Choice(elements) => elements
|
||||
.iter()
|
||||
.map(|e| validate(rule_name, e, names))
|
||||
.collect(),
|
||||
Rule::Metadata { rule, params } => {
|
||||
if let Precedence::Name(n) = ¶ms.precedence {
|
||||
if !names.contains(n) {
|
||||
return Err(Error::new(format!(
|
||||
"Undeclared precedence '{}' in rule '{}'",
|
||||
n, rule_name
|
||||
)));
|
||||
}
|
||||
}
|
||||
validate(rule_name, rule, names)?;
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
let precedence_names = grammar
|
||||
.precedence_orderings
|
||||
.iter()
|
||||
.flat_map(|l| l.iter())
|
||||
.collect::<HashSet<&String>>();
|
||||
for variable in &grammar.variables {
|
||||
validate(&variable.name, &variable.rule, &precedence_names)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::generate::grammars::{InputGrammar, Variable, VariableType};
|
||||
|
||||
#[test]
|
||||
fn test_validate_named_precedences_with_undeclared_precedence() {
|
||||
let grammar = InputGrammar {
|
||||
name: String::new(),
|
||||
word_token: None,
|
||||
extra_symbols: vec![],
|
||||
external_tokens: vec![],
|
||||
supertype_symbols: vec![],
|
||||
expected_conflicts: vec![],
|
||||
variables_to_inline: vec![],
|
||||
precedence_orderings: vec![
|
||||
vec!["a".to_string(), "b".to_string()],
|
||||
vec!["b".to_string(), "c".to_string(), "d".to_string()],
|
||||
],
|
||||
variables: vec![
|
||||
Variable {
|
||||
name: "v1".to_string(),
|
||||
kind: VariableType::Named,
|
||||
rule: Rule::Seq(vec![
|
||||
Rule::prec_left(Precedence::Name("b".to_string()), Rule::string("w")),
|
||||
Rule::prec(Precedence::Name("c".to_string()), Rule::string("x")),
|
||||
]),
|
||||
},
|
||||
Variable {
|
||||
name: "v2".to_string(),
|
||||
kind: VariableType::Named,
|
||||
rule: Rule::repeat(Rule::Choice(vec![
|
||||
Rule::prec_left(Precedence::Name("omg".to_string()), Rule::string("y")),
|
||||
Rule::prec(Precedence::Name("c".to_string()), Rule::string("z")),
|
||||
])),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let result = validate_named_precedences(&grammar);
|
||||
assert_eq!(
|
||||
result.unwrap_err().message(),
|
||||
"Undeclared precedence 'omg' in rule 'v2'",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_named_precedences_with_conflicting_order() {
|
||||
let grammar = InputGrammar {
|
||||
name: String::new(),
|
||||
word_token: None,
|
||||
extra_symbols: vec![],
|
||||
external_tokens: vec![],
|
||||
supertype_symbols: vec![],
|
||||
expected_conflicts: vec![],
|
||||
variables_to_inline: vec![],
|
||||
precedence_orderings: vec![
|
||||
vec!["a".to_string(), "b".to_string()],
|
||||
vec!["b".to_string(), "c".to_string(), "a".to_string()],
|
||||
],
|
||||
variables: vec![
|
||||
Variable {
|
||||
name: "v1".to_string(),
|
||||
kind: VariableType::Named,
|
||||
rule: Rule::Seq(vec![
|
||||
Rule::prec_left(Precedence::Name("b".to_string()), Rule::string("w")),
|
||||
Rule::prec(Precedence::Name("c".to_string()), Rule::string("x")),
|
||||
]),
|
||||
},
|
||||
Variable {
|
||||
name: "v2".to_string(),
|
||||
kind: VariableType::Named,
|
||||
rule: Rule::repeat(Rule::Choice(vec![
|
||||
Rule::prec_left(Precedence::Name("a".to_string()), Rule::string("y")),
|
||||
Rule::prec(Precedence::Name("c".to_string()), Rule::string("z")),
|
||||
])),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let result = validate_named_precedences(&grammar);
|
||||
assert_eq!(
|
||||
result.unwrap_err().message(),
|
||||
"Conflicting orderings for precedences 'a' and 'b'",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue