From 3ddc0dfe02abf49a8d0942bd225cba374aff5a65 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Mon, 15 Jun 2015 10:33:11 -0400
Subject: [PATCH] sketch out macro expansion more

---
 src/grammar/parse_tree.rs     | 71 ++++++++++++++++++++++++++--
 src/normalize/macro_expand.rs | 87 +++++++++++++++++++++++++++++++++++
 src/normalize/mod.rs          |  2 +-
 src/normalize/util.rs         |  2 +
 src/parser/mod.rs             |  4 +-
 5 files changed, 160 insertions(+), 6 deletions(-)
 create mode 100644 src/normalize/macro_expand.rs
 create mode 100644 src/normalize/util.rs

diff --git a/src/grammar/parse_tree.rs b/src/grammar/parse_tree.rs
index 45e955a..f3722bb 100644
--- a/src/grammar/parse_tree.rs
+++ b/src/grammar/parse_tree.rs
@@ -60,6 +60,7 @@ grammar Type<'input, T> {
 */
 
 use intern::InternedString;
+use std::fmt::{Display, Formatter, Error};
 
 #[derive(Clone, Debug)]
 pub struct Grammar {
@@ -116,7 +117,18 @@ pub struct Alternative {
     pub condition: Option<Condition>,
 
     // => { code }
-    pub action: Option<String>,
+    pub action: Option<Action>,
+}
+
+#[derive(Clone, Debug)]
+pub enum Action {
+    // code provided by the user
+    User(String),
+
+    // an index into a side-list of action fns, which is setup to take
+    // all of the values in this alternative as arguments, dropping
+    // the ones it doesn't care about.
+    Fn(u32),
 }
 
 #[derive(Clone, Debug)]
@@ -136,7 +148,7 @@ pub enum Condition {
 
 #[derive(Clone, Debug)]
 pub enum Symbol {
-    // (<X> <Y>) etc
+    // (X Y)
     Expr(Vec<Symbol>),
 
     // "foo"
@@ -146,7 +158,7 @@ pub enum Symbol {
     Nonterminal(InternedString),
 
     // foo<..>
-    Macro(InternedString, Vec<Symbol>),
+    Macro(MacroSymbol),
 
     // X+
     Plus(Box<Symbol>),
@@ -163,3 +175,56 @@ pub enum Symbol {
     // ~x:X
     Name(InternedString, Box<Symbol>),
 }
+
+#[derive(Clone, Debug)]
+pub struct MacroSymbol {
+    pub name: InternedString,
+    pub args: Vec<Symbol>
+}
+
+impl Symbol {
+    pub fn canonical_form(&self) -> String {
+        format!("{}", self)
+    }
+}
+
+impl Display for Symbol {
+    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
+        match *self {
+            Symbol::Expr(ref symbols) =>
+                write!(fmt, "({})", Sep(" ", symbols)),
+            Symbol::Terminal(ref s) =>
+                write!(fmt, "\"{}\"", s.to_string()),
+            Symbol::Nonterminal(ref s) =>
+                write!(fmt, "{}", s),
+            Symbol::Macro(ref m) =>
+                write!(fmt, "{}<{}>", m.name, Sep(", ", &m.args)),
+            Symbol::Plus(ref s) =>
+                write!(fmt, "{}+", s),
+            Symbol::Question(ref s) =>
+                write!(fmt, "{}?", s),
+            Symbol::Star(ref s) =>
+                write!(fmt, "{}?", s),
+            Symbol::Choose(ref s) =>
+                write!(fmt, "~{}", s),
+            Symbol::Name(n, ref s) =>
+                write!(fmt, "~{}:{}", n, s),
+        }
+    }
+}
+
+struct Sep<S>(&'static str, S);
+
+impl<'a,S:Display> Display for Sep<&'a Vec<S>> {
+    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
+        let &Sep(sep, vec) = self;
+        let mut elems = vec.iter();
+        if let Some(elem) = elems.next() {
+            write!(fmt, "{}", elem);
+            while let Some(elem) = elems.next() {
+                write!(fmt, "{}{}", sep, elem);
+            }
+        }
+        Ok(())
+    }
+}
diff --git a/src/normalize/macro_expand.rs b/src/normalize/macro_expand.rs
new file mode 100644
index 0000000..938dab9
--- /dev/null
+++ b/src/normalize/macro_expand.rs
@@ -0,0 +1,87 @@
+use std::collections::HashMap;
+use grammar::parse_tree::{Grammar, GrammarItem, MacroSymbol, Symbol};
+
+pub fn expand_macros(input: pt::Grammar) {
+    let Grammar { type_name, items } = input;
+    let mut expander = MacroExpander::new();
+}
+
+struct MacroExpander {
+    expansion_stack: Vec<MacroSymbol>,
+    expansion_set: HashSet<InternedString>,
+}
+
+impl MacroExpander {
+    fn new(items: Vec<GrammarItem>) -> MacroExpander {
+        MacroExpander { items: items, expansion_stack: Vec::new(), expansion_set: HashSet::new() }
+    }
+
+    fn expand(&mut self, items: &mut Vec<GrammarItem>) {
+        let mut counter = 0;
+        loop {
+            // Find any macro uses in items added since last round and
+            // replace them in place with the expanded version:
+            for item in &mut items[counter..] {
+                self.replace(item);
+            }
+            counter = items.len();
+
+            // Drain macro queue:
+        }
+    }
+
+    fn replace_item(&mut self, item: &mut GrammarItem) {
+        match *item {
+            GrammarItem::TokenType(..) => { }
+            GrammarItem::Nonterminal(ref data) => {
+                // Ignore macro definitions. They will be expanded in
+                // due course.
+                if !data.args.is_empty() {
+                    return;
+                }
+
+                for alternative in &mut data.alternatives {
+                    self.replace_symbol(&mut alternative.symbol);
+                }
+            }
+        }
+    }
+
+    fn replace_symbol(&mut self, symbol: &mut Symbol) {
+        let key;
+
+        match *symbol {
+            Symbol::Macro(ref mut macro) => {
+                for sym in &mut macro.args {
+                    self.replace(sym);
+                }
+
+                key = symbol.canonical_form();
+                if self.expansion_set.insert(key) {
+                    self.expansion_stack.push(macro.clone());
+                }
+            }
+            Symbol::Expr(ref mut syms) => {
+                for sym in syms {
+                    self.replace_symbol(sym);
+                }
+                return;
+            }
+            Symbol::Terminal(_) |
+            Symbol::Nonterminal(_) => {
+                return;
+            }
+            Symbol::Plus(ref mut sym) |
+            Symbol::Question(ref mut sym) |
+            Symbol::Star(ref mut sym) |
+            Symbol::Choose(ref mut sym) |
+            Symbol::Name(_, ref mut sym) => {
+                self.replace_symbol(sym);
+                return;
+            }
+        }
+
+        // we only get here if this is a macro expansion
+        *symbol = Symbol::Nonterminal(intern(key));
+    }
+}
diff --git a/src/normalize/mod.rs b/src/normalize/mod.rs
index 47ebe94..b227358 100644
--- a/src/normalize/mod.rs
+++ b/src/normalize/mod.rs
@@ -6,7 +6,7 @@
 
 use grammar::parse_tree as pt;
 
-pub fn normalize(input: &pt::Grammar) -> Result<pt::Grammar> {
+pub fn normalize(input: pt::Grammar) -> Result<pt::Grammar> {
 }
 
 // These are executed *IN ORDER*:
diff --git a/src/normalize/util.rs b/src/normalize/util.rs
new file mode 100644
index 0000000..5e190ac
--- /dev/null
+++ b/src/normalize/util.rs
@@ -0,0 +1,2 @@
+pub struct SymbolKey {
+}
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 93309b9..be6556e 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -70,7 +70,7 @@ rusty_peg! {
         IF_COND: Condition =
             ("if" <c:COND>) => c;
 
-        ACTION: String = ("=>" <b:CODE>) => b;
+        ACTION: Action = ("=>" <b:CODE>) => Action::User(b);
 
         // Conditions
 
@@ -105,7 +105,7 @@ rusty_peg! {
             (<l:ID> "<" <m:{MACRO_ARG_START}> <n:[SYMBOL]> ">") => {
                 let mut args = m;
                 if let Some(n) = n { args.push(n); }
-                Symbol::Macro(l, args)
+                Symbol::Macro(MacroSymbol { name: l, args: args })
             };
 
         MACRO_ARG_START: Symbol = (<s:SYMBOL> ",") => s;