wagon_parser/parser/
wag.rs

1use std::fmt::Display;
2
3use crate::firstpass::{FirstPassState, FirstPassResult, WagCheckError};
4
5use super::{Parse, ParseResult, LexerBridge, Rewrite, SpannableNode, Spannable, Peek};
6use super::metadata::Metadata;
7use super::rule::Rule;
8use indexmap::IndexMap;
9
10use wagon_macros::new_unspanned;
11
12#[derive(PartialEq, Debug, Eq, Hash)]
13#[new_unspanned]
14/// The full WAG tree.
15/// 
16/// # Grammar
17/// <code>[Wag] -> [Metadata]? [Rule]*;</code>
18pub struct Wag {
19    /// Metadata associated with this WAG.
20	pub metadata: SpannableNode<Metadata>,
21    /// The actual grammar.
22	pub grammar: Vec<SpannableNode<Rule>>,
23}
24
25impl Parse for Wag {
26    fn parse(lexer: &mut LexerBridge) -> ParseResult<Self> {
27        let metadata = SpannableNode::parse(lexer)?;
28        let mut grammar = Vec::new();
29        while lexer.peek().is_some() {
30        	grammar.push(SpannableNode::parse(lexer)?);
31        }
32        Ok(Self {metadata, grammar})
33    }
34}
35
36/// Check all rules in the WAG for parameter errors, run rewrite on the children and combine rules with the same name into a single rule.
37impl Rewrite<()> for Wag {
38
39    fn rewrite(&mut self, depth: usize, state: &mut FirstPassState) -> FirstPassResult<()> {
40        fn handle_conflict(mut new_rule: SpannableNode<Rule>, map: &mut IndexMap<String, SpannableNode<Rule>>) -> FirstPassResult<()>{ // Combine rules for the same ident into a single rule
41            let ident = match &new_rule.node { // Get the identifier for this rule.
42                Rule::Analytic(s, ..) | Rule::Generate(s, ..) => s.clone(),
43                Rule::Import(..) | Rule::Exclude(..) => todo!(),
44            };
45            if let Some(orig) = map.get_mut(&ident) { // Check if we encountered this exact same identifier before.
46                match (&mut orig.node, &mut new_rule.node) {
47                    (Rule::Analytic(_, args1, v1), Rule::Analytic(_, args2, v2)) | (Rule::Generate(_, args1, v1), Rule::Generate(_, args2, v2)) => { // If both are analytic
48                        if args1 == args2 { // And they have the same attributes
49                            v1.extend(std::mem::take(v2)); // Merge this new rule into the original.
50                        } else { // If they have different attributes we have a problem
51                            return Err(WagCheckError::DisparateParameters { terminal: ident, offender: args1.to_owned(), expected: args2.to_owned(), span: new_rule.span()});
52                        }
53                    },
54                    _ => {map.insert(ident, new_rule);}
55                }
56            } else { // If this is a new rule, just add it to the map.
57                 map.insert(ident, new_rule);
58            };
59            Ok(())
60        }
61        let rules = std::mem::take(&mut self.grammar);
62        let mut map: IndexMap<String, SpannableNode<Rule>> = IndexMap::with_capacity(rules.len());
63        for mut rule in rules {
64            let new_rules = rule.rewrite(depth, state)?.0; // Rewrite the rule and get all the new rules that this has created.
65            handle_conflict(rule, &mut map)?; // Make sure this rule has no conflicts.
66            for new_rule in new_rules {
67                handle_conflict(new_rule, &mut map)?; // And the new rules don't either.
68            }
69        }
70        self.grammar.extend(map.into_values());
71        Ok(())
72    }
73
74}
75
76impl Display for Wag {
77    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78        self.metadata.fmt(f)?;
79        for rule in &self.grammar {
80            rule.fmt(f)?;
81        }
82        Ok(())
83    }
84}