use super::{
Alternative, Grammar, IdlVersion, Production, ProductionId, SpecRef, Symbol, TokenKind,
};
const SR: SpecRef = SpecRef {
doc: "ZeroDDS Toy Arith Grammar",
section: "0.0",
};
const PROD_E: Production = Production {
id: ProductionId(0),
name: "expr",
spec_ref: SR,
alternatives: &[
Alternative {
name: Some("plus"),
symbols: &[
Symbol::Nonterminal(ProductionId(0)), Symbol::Terminal(TokenKind::Punct("+")),
Symbol::Nonterminal(ProductionId(1)), ],
note: None,
},
Alternative {
name: Some("just_term"),
symbols: &[Symbol::Nonterminal(ProductionId(1))],
note: None,
},
],
ast_hint: None,
};
const PROD_T: Production = Production {
id: ProductionId(1),
name: "term",
spec_ref: SR,
alternatives: &[
Alternative {
name: Some("times"),
symbols: &[
Symbol::Nonterminal(ProductionId(1)), Symbol::Terminal(TokenKind::Punct("*")),
Symbol::Nonterminal(ProductionId(2)), ],
note: None,
},
Alternative {
name: Some("just_factor"),
symbols: &[Symbol::Nonterminal(ProductionId(2))],
note: None,
},
],
ast_hint: None,
};
const PROD_F: Production = Production {
id: ProductionId(2),
name: "factor",
spec_ref: SR,
alternatives: &[
Alternative {
name: Some("number"),
symbols: &[Symbol::Terminal(TokenKind::Keyword("n"))],
note: None,
},
Alternative {
name: Some("paren"),
symbols: &[
Symbol::Terminal(TokenKind::Punct("(")),
Symbol::Nonterminal(ProductionId(0)), Symbol::Terminal(TokenKind::Punct(")")),
],
note: None,
},
],
ast_hint: None,
};
pub const TOY: Grammar = Grammar {
name: "toy_arith",
version: IdlVersion::V4_2,
productions: &[PROD_E, PROD_T, PROD_F],
start: ProductionId(0),
token_rules: &[],
};
#[cfg(test)]
mod tests {
use super::*;
use crate::grammar::validate::{Severity, ValidationIssue, validate};
#[test]
fn toy_grammar_has_no_errors_only_warnings() {
let report = validate(&TOY);
assert!(
!report.has_errors(),
"Errors: {:?}",
report.errors().collect::<Vec<_>>()
);
assert!(report.warnings().all(|i| i.severity() == Severity::Warning));
}
#[test]
fn toy_grammar_validator_reports_left_recursion() {
let report = validate(&TOY);
let lr_count = report
.issues()
.iter()
.filter(|i| matches!(i, ValidationIssue::LeftRecursion { .. }))
.count();
assert!(
lr_count >= 1,
"Toy-Grammar ist linksrekursiv (E und T) — mindestens eine LR-Warnung erwartet. Report: {:?}",
report.issues()
);
}
#[test]
fn toy_grammar_validator_reports_first_first_conflicts() {
let report = validate(&TOY);
let ffc_count = report
.issues()
.iter()
.filter(|i| matches!(i, ValidationIssue::FirstFirstConflict { .. }))
.count();
assert!(
ffc_count >= 2,
"Erwartet ≥2 FirstFirstConflicts (E und T). Report: {:?}",
report.issues()
);
}
#[test]
fn toy_grammar_starts_at_expression() {
let start = TOY.start_production();
assert!(start.is_some_and(|p| p.name == "expr"));
}
#[test]
fn toy_grammar_has_three_productions() {
assert_eq!(TOY.production_count(), 3);
}
#[test]
fn alternative_names_are_set() {
let production = TOY.production(ProductionId(0));
assert_eq!(
production.map(|p| p.alternatives.len()),
Some(2),
"E muss 2 Alternativen haben"
);
assert_eq!(
production.map(|p| p.alternatives[0].name),
Some(Some("plus"))
);
assert_eq!(
production.map(|p| p.alternatives[1].name),
Some(Some("just_term"))
);
}
}