From 40feacdeb90786b49ea9e0510ba22ff7af79e020 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 31 Jan 2019 21:09:43 +0300 Subject: split macros across crates --- crates/ra_hir/Cargo.toml | 3 +- crates/ra_hir/src/macros.rs | 1 - crates/ra_macros/Cargo.toml | 9 -- crates/ra_macros/src/lib.rs | 17 ---- crates/ra_macros/src/mbe.rs | 72 --------------- crates/ra_macros/src/mbe_expander.rs | 169 ----------------------------------- crates/ra_macros/src/mbe_parser.rs | 94 ------------------- crates/ra_macros/src/tt.rs | 119 ------------------------ crates/ra_macros/src/tt_cursor.rs | 93 ------------------- crates/ra_mbe/Cargo.toml | 10 +++ crates/ra_mbe/src/lib.rs | 88 ++++++++++++++++++ crates/ra_mbe/src/mbe_expander.rs | 169 +++++++++++++++++++++++++++++++++++ crates/ra_mbe/src/mbe_parser.rs | 93 +++++++++++++++++++ crates/ra_mbe/src/tt_cursor.rs | 91 +++++++++++++++++++ crates/ra_tt/Cargo.toml | 9 ++ crates/ra_tt/src/lib.rs | 131 +++++++++++++++++++++++++++ 16 files changed, 593 insertions(+), 575 deletions(-) delete mode 100644 crates/ra_macros/Cargo.toml delete mode 100644 crates/ra_macros/src/lib.rs delete mode 100644 crates/ra_macros/src/mbe.rs delete mode 100644 crates/ra_macros/src/mbe_expander.rs delete mode 100644 crates/ra_macros/src/mbe_parser.rs delete mode 100644 crates/ra_macros/src/tt.rs delete mode 100644 crates/ra_macros/src/tt_cursor.rs create mode 100644 crates/ra_mbe/Cargo.toml create mode 100644 crates/ra_mbe/src/lib.rs create mode 100644 crates/ra_mbe/src/mbe_expander.rs create mode 100644 crates/ra_mbe/src/mbe_parser.rs create mode 100644 crates/ra_mbe/src/tt_cursor.rs create mode 100644 crates/ra_tt/Cargo.toml create mode 100644 crates/ra_tt/src/lib.rs (limited to 'crates') diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml index f0988acc8..4309a05d6 100644 --- a/crates/ra_hir/Cargo.toml +++ b/crates/ra_hir/Cargo.toml @@ -16,7 +16,8 @@ join_to_string = "0.1.3" ra_syntax = { path = "../ra_syntax" } ra_arena = { path = "../ra_arena" } ra_db = { path = "../ra_db" } -ra_macros = { path = "../ra_macros" } +mbe = { path = "../ra_mbe", package = "ra_mbe" } +tt = { path = "../ra_tt", package = "ra_tt" } test_utils = { path = "../test_utils" } [dev-dependencies] diff --git a/crates/ra_hir/src/macros.rs b/crates/ra_hir/src/macros.rs index e6ba8c08f..ffcf1c3f9 100644 --- a/crates/ra_hir/src/macros.rs +++ b/crates/ra_hir/src/macros.rs @@ -14,7 +14,6 @@ use ra_syntax::{ SyntaxKind::*, ast::{self, NameOwner}, }; -use ra_macros::{tt, mbe}; use crate::{HirDatabase, MacroCallId}; diff --git a/crates/ra_macros/Cargo.toml b/crates/ra_macros/Cargo.toml deleted file mode 100644 index 7d3cb055c..000000000 --- a/crates/ra_macros/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -edition = "2018" -name = "ra_macros" -version = "0.1.0" -authors = ["Aleksey Kladov "] - -[dependencies] -rustc-hash = "1.0.0" -smol_str = "0.1.9" diff --git a/crates/ra_macros/src/lib.rs b/crates/ra_macros/src/lib.rs deleted file mode 100644 index e35a056cc..000000000 --- a/crates/ra_macros/src/lib.rs +++ /dev/null @@ -1,17 +0,0 @@ -macro_rules! impl_froms { - ($e:ident: $($v:ident), *) => { - $( - impl From<$v> for $e { - fn from(it: $v) -> $e { - $e::$v(it) - } - } - )* - } -} - -pub mod tt; -pub mod mbe; -mod tt_cursor; -mod mbe_parser; -mod mbe_expander; diff --git a/crates/ra_macros/src/mbe.rs b/crates/ra_macros/src/mbe.rs deleted file mode 100644 index d4106a41c..000000000 --- a/crates/ra_macros/src/mbe.rs +++ /dev/null @@ -1,72 +0,0 @@ -use smol_str::SmolStr; - -pub(crate) use crate::tt::{Delimiter, Punct}; - -pub use crate::{ - mbe_parser::parse, - mbe_expander::exapnd, -}; - -#[derive(Debug)] -pub struct MacroRules { - pub(crate) rules: Vec, -} - -#[derive(Debug)] -pub(crate) struct Rule { - pub(crate) lhs: Subtree, - pub(crate) rhs: Subtree, -} - -#[derive(Debug)] -pub(crate) enum TokenTree { - Leaf(Leaf), - Subtree(Subtree), - Repeat(Repeat), -} -impl_froms!(TokenTree: Leaf, Subtree, Repeat); - -#[derive(Debug)] -pub(crate) enum Leaf { - Literal(Literal), - Punct(Punct), - Ident(Ident), - Var(Var), -} -impl_froms!(Leaf: Literal, Punct, Ident, Var); - -#[derive(Debug)] -pub(crate) struct Subtree { - pub(crate) delimiter: Delimiter, - pub(crate) token_trees: Vec, -} - -#[derive(Debug)] -pub(crate) struct Repeat { - pub(crate) subtree: Subtree, - pub(crate) kind: RepeatKind, - pub(crate) separator: Option, -} - -#[derive(Debug)] -pub(crate) enum RepeatKind { - ZeroOrMore, - OneOrMore, - ZeroOrOne, -} - -#[derive(Debug)] -pub(crate) struct Literal { - pub(crate) text: SmolStr, -} - -#[derive(Debug)] -pub(crate) struct Ident { - pub(crate) text: SmolStr, -} - -#[derive(Debug)] -pub(crate) struct Var { - pub(crate) text: SmolStr, - pub(crate) kind: Option, -} diff --git a/crates/ra_macros/src/mbe_expander.rs b/crates/ra_macros/src/mbe_expander.rs deleted file mode 100644 index 5d5363261..000000000 --- a/crates/ra_macros/src/mbe_expander.rs +++ /dev/null @@ -1,169 +0,0 @@ -use rustc_hash::FxHashMap; -use smol_str::SmolStr; - -use crate::{mbe, tt, tt_cursor::TtCursor}; - -pub fn exapnd(rules: &mbe::MacroRules, input: &tt::Subtree) -> Option { - rules.rules.iter().find_map(|it| expand_rule(it, input)) -} - -fn expand_rule(rule: &mbe::Rule, input: &tt::Subtree) -> Option { - let mut input = TtCursor::new(input); - let bindings = match_lhs(&rule.lhs, &mut input)?; - expand_subtree(&rule.rhs, &bindings, &mut Vec::new()) -} - -#[derive(Debug, Default)] -struct Bindings { - inner: FxHashMap, -} - -#[derive(Debug)] -enum Binding { - Simple(tt::TokenTree), - Nested(Vec), -} - -impl Bindings { - fn get(&self, name: &SmolStr, nesting: &[usize]) -> Option<&tt::TokenTree> { - let mut b = self.inner.get(name)?; - for &idx in nesting.iter() { - b = match b { - Binding::Simple(_) => break, - Binding::Nested(bs) => bs.get(idx)?, - }; - } - match b { - Binding::Simple(it) => Some(it), - Binding::Nested(_) => None, - } - } - fn push_nested(&mut self, nested: Bindings) -> Option<()> { - for (key, value) in nested.inner { - if !self.inner.contains_key(&key) { - self.inner.insert(key.clone(), Binding::Nested(Vec::new())); - } - match self.inner.get_mut(&key) { - Some(Binding::Nested(it)) => it.push(value), - _ => return None, - } - } - Some(()) - } -} - -fn match_lhs(pattern: &mbe::Subtree, input: &mut TtCursor) -> Option { - let mut res = Bindings::default(); - for pat in pattern.token_trees.iter() { - match pat { - mbe::TokenTree::Leaf(leaf) => match leaf { - mbe::Leaf::Var(mbe::Var { text, kind }) => { - let kind = kind.clone()?; - match kind.as_str() { - "ident" => { - let ident = input.eat_ident()?.clone(); - res.inner.insert( - text.clone(), - Binding::Simple(tt::Leaf::from(ident).into()), - ); - } - _ => return None, - } - } - mbe::Leaf::Punct(punct) => { - if input.eat_punct()? != punct { - return None; - } - } - _ => return None, - }, - mbe::TokenTree::Repeat(mbe::Repeat { - subtree, - kind: _, - separator, - }) => { - while let Some(nested) = match_lhs(subtree, input) { - res.push_nested(nested)?; - if separator.is_some() && !input.is_eof() { - input.eat_punct()?; - } - } - } - _ => {} - } - } - Some(res) -} - -/* - -macro_rules! impl_froms { - ($e:ident: $($v:ident),*) => { - $( - impl From<$v> for $e { - fn from(it: $v) -> $e { - $e::$v(it) - } - } - )* - } -} - -impl_froms! (Foo: Bar, Baz) - -*/ - -fn expand_subtree( - template: &mbe::Subtree, - bindings: &Bindings, - nesting: &mut Vec, -) -> Option { - let token_trees = template - .token_trees - .iter() - .map(|it| expand_tt(it, bindings, nesting)) - .collect::>>()?; - - Some(tt::Subtree { - token_trees, - delimiter: template.delimiter, - }) -} - -fn expand_tt( - template: &mbe::TokenTree, - bindings: &Bindings, - nesting: &mut Vec, -) -> Option { - let res: tt::TokenTree = match template { - mbe::TokenTree::Subtree(subtree) => expand_subtree(subtree, bindings, nesting)?.into(), - mbe::TokenTree::Repeat(repeat) => { - let mut token_trees = Vec::new(); - nesting.push(0); - while let Some(t) = expand_subtree(&repeat.subtree, bindings, nesting) { - let idx = nesting.pop().unwrap(); - nesting.push(idx + 1); - token_trees.push(t.into()) - } - nesting.pop().unwrap(); - tt::Subtree { - token_trees, - delimiter: tt::Delimiter::None, - } - .into() - } - mbe::TokenTree::Leaf(leaf) => match leaf { - mbe::Leaf::Ident(ident) => tt::Leaf::from(tt::Ident { - text: ident.text.clone(), - }) - .into(), - mbe::Leaf::Punct(punct) => tt::Leaf::from(punct.clone()).into(), - mbe::Leaf::Var(v) => bindings.get(&v.text, nesting)?.clone(), - mbe::Leaf::Literal(l) => tt::Leaf::from(tt::Literal { - text: l.text.clone(), - }) - .into(), - }, - }; - Some(res) -} diff --git a/crates/ra_macros/src/mbe_parser.rs b/crates/ra_macros/src/mbe_parser.rs deleted file mode 100644 index 483594590..000000000 --- a/crates/ra_macros/src/mbe_parser.rs +++ /dev/null @@ -1,94 +0,0 @@ -use crate::{tt, mbe}; -use crate::tt_cursor::TtCursor; - -/// This module parses a raw `tt::TokenStream` into macro-by-example token -/// stream. This is a *mostly* identify function, expect for handling of -/// `$var:tt_kind` and `$(repeat),*` constructs. - -pub fn parse(tt: &tt::Subtree) -> Option { - let mut parser = TtCursor::new(tt); - let mut rules = Vec::new(); - while !parser.is_eof() { - rules.push(parse_rule(&mut parser)?) - } - Some(mbe::MacroRules { rules }) -} - -fn parse_rule(p: &mut TtCursor) -> Option { - let lhs = parse_subtree(p.eat_subtree()?)?; - p.expect_char('=')?; - p.expect_char('>')?; - let rhs = parse_subtree(p.eat_subtree()?)?; - Some(mbe::Rule { lhs, rhs }) -} - -fn parse_subtree(tt: &tt::Subtree) -> Option { - let mut token_trees = Vec::new(); - let mut p = TtCursor::new(tt); - while let Some(tt) = p.eat() { - let child: mbe::TokenTree = match tt { - tt::TokenTree::Leaf(leaf) => match leaf { - tt::Leaf::Punct(tt::Punct { char: '$', .. }) => { - if p.at_ident().is_some() { - mbe::Leaf::from(parse_var(&mut p)?).into() - } else { - parse_repeat(&mut p)?.into() - } - } - tt::Leaf::Punct(punct) => mbe::Leaf::from(*punct).into(), - tt::Leaf::Ident(tt::Ident { text }) => { - mbe::Leaf::from(mbe::Ident { text: text.clone() }).into() - } - tt::Leaf::Literal(tt::Literal { text }) => { - mbe::Leaf::from(mbe::Literal { text: text.clone() }).into() - } - }, - tt::TokenTree::Subtree(subtree) => parse_subtree(subtree)?.into(), - }; - token_trees.push(child); - } - Some(mbe::Subtree { - token_trees, - delimiter: tt.delimiter, - }) -} - -fn parse_var(p: &mut TtCursor) -> Option { - let ident = p.eat_ident().unwrap(); - let text = ident.text.clone(); - let kind = if p.at_char(':') { - p.bump(); - if let Some(ident) = p.eat_ident() { - Some(ident.text.clone()) - } else { - p.rev_bump(); - None - } - } else { - None - }; - Some(mbe::Var { text, kind }) -} - -fn parse_repeat(p: &mut TtCursor) -> Option { - let subtree = p.eat_subtree().unwrap(); - let subtree = parse_subtree(subtree)?; - let sep = p.eat_punct()?; - let (separator, rep) = match sep.char { - '*' | '+' | '?' => (None, sep.char), - char => (Some(char), p.eat_punct()?.char), - }; - - let kind = match rep { - '*' => mbe::RepeatKind::ZeroOrMore, - '+' => mbe::RepeatKind::OneOrMore, - '?' => mbe::RepeatKind::ZeroOrOne, - _ => return None, - }; - p.bump(); - Some(mbe::Repeat { - subtree, - kind, - separator, - }) -} diff --git a/crates/ra_macros/src/tt.rs b/crates/ra_macros/src/tt.rs deleted file mode 100644 index 2855bae51..000000000 --- a/crates/ra_macros/src/tt.rs +++ /dev/null @@ -1,119 +0,0 @@ -use std::fmt; - -use smol_str::SmolStr; - -#[derive(Debug, Clone)] -pub enum TokenTree { - Leaf(Leaf), - Subtree(Subtree), -} -impl_froms!(TokenTree: Leaf, Subtree); - -#[derive(Debug, Clone)] -pub enum Leaf { - Literal(Literal), - Punct(Punct), - Ident(Ident), -} -impl_froms!(Leaf: Literal, Punct, Ident); - -#[derive(Debug, Clone)] -pub struct Subtree { - pub delimiter: Delimiter, - pub token_trees: Vec, -} - -#[derive(Clone, Copy, Debug)] -pub enum Delimiter { - Parenthesis, - Brace, - Bracket, - None, -} - -#[derive(Debug, Clone)] -pub struct Literal { - pub text: SmolStr, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Punct { - pub char: char, - pub spacing: Spacing, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Spacing { - Alone, - Joint, -} - -#[derive(Debug, Clone)] -pub struct Ident { - pub text: SmolStr, -} - -impl fmt::Display for TokenTree { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - TokenTree::Leaf(it) => fmt::Display::fmt(it, f), - TokenTree::Subtree(it) => fmt::Display::fmt(it, f), - } - } -} - -impl fmt::Display for Subtree { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let (l, r) = match self.delimiter { - Delimiter::Parenthesis => ("(", ")"), - Delimiter::Brace => ("{", "}"), - Delimiter::Bracket => ("[", "]"), - Delimiter::None => ("", ""), - }; - f.write_str(l)?; - let mut needs_space = false; - for tt in self.token_trees.iter() { - if needs_space { - f.write_str(" ")?; - } - needs_space = true; - match tt { - TokenTree::Leaf(Leaf::Punct(p)) => { - needs_space = p.spacing == Spacing::Alone; - fmt::Display::fmt(p, f)? - } - tt => fmt::Display::fmt(tt, f)?, - } - } - f.write_str(r)?; - Ok(()) - } -} - -impl fmt::Display for Leaf { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Leaf::Ident(it) => fmt::Display::fmt(it, f), - Leaf::Literal(it) => fmt::Display::fmt(it, f), - Leaf::Punct(it) => fmt::Display::fmt(it, f), - } - } -} - -impl fmt::Display for Ident { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self.text, f) - } -} - -impl fmt::Display for Literal { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self.text, f) - } -} - -impl fmt::Display for Punct { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self.char, f) - } -} diff --git a/crates/ra_macros/src/tt_cursor.rs b/crates/ra_macros/src/tt_cursor.rs deleted file mode 100644 index 046770a0f..000000000 --- a/crates/ra_macros/src/tt_cursor.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::tt; - -#[derive(Clone)] -pub(crate) struct TtCursor<'a> { - subtree: &'a tt::Subtree, - pos: usize, -} - -impl<'a> TtCursor<'a> { - pub(crate) fn new(subtree: &'a tt::Subtree) -> TtCursor<'a> { - TtCursor { subtree, pos: 0 } - } - - pub(crate) fn is_eof(&self) -> bool { - self.pos == self.subtree.token_trees.len() - } - - pub(crate) fn current(&self) -> Option<&'a tt::TokenTree> { - self.subtree.token_trees.get(self.pos) - } - - pub(crate) fn at_punct(&self) -> Option<&'a tt::Punct> { - match self.current() { - Some(tt::TokenTree::Leaf(tt::Leaf::Punct(it))) => Some(it), - _ => None, - } - } - - pub(crate) fn at_char(&self, char: char) -> bool { - match self.at_punct() { - Some(tt::Punct { char: c, .. }) if *c == char => true, - _ => false, - } - } - - pub(crate) fn at_ident(&mut self) -> Option<&'a tt::Ident> { - match self.current() { - Some(tt::TokenTree::Leaf(tt::Leaf::Ident(i))) => Some(i), - _ => None, - } - } - - pub(crate) fn bump(&mut self) { - self.pos += 1; - } - pub(crate) fn rev_bump(&mut self) { - self.pos -= 1; - } - - pub(crate) fn eat(&mut self) -> Option<&'a tt::TokenTree> { - match self.current() { - Some(it) => { - self.bump(); - Some(it) - } - None => None, - } - } - - pub(crate) fn eat_subtree(&mut self) -> Option<&'a tt::Subtree> { - match self.current()? { - tt::TokenTree::Subtree(sub) => { - self.bump(); - Some(sub) - } - _ => return None, - } - } - - pub(crate) fn eat_punct(&mut self) -> Option<&'a tt::Punct> { - if let Some(it) = self.at_punct() { - self.bump(); - return Some(it); - } - None - } - - pub(crate) fn eat_ident(&mut self) -> Option<&'a tt::Ident> { - if let Some(i) = self.at_ident() { - self.bump(); - return Some(i); - } - None - } - - pub(crate) fn expect_char(&mut self, char: char) -> Option<()> { - if self.at_char(char) { - self.bump(); - return Some(()); - } - None - } -} diff --git a/crates/ra_mbe/Cargo.toml b/crates/ra_mbe/Cargo.toml new file mode 100644 index 000000000..b7f03cd38 --- /dev/null +++ b/crates/ra_mbe/Cargo.toml @@ -0,0 +1,10 @@ +[package] +edition = "2018" +name = "ra_mbe" +version = "0.1.0" +authors = ["Aleksey Kladov "] + +[dependencies] +tt = { path = "../ra_tt", package = "ra_tt" } +rustc-hash = "1.0.0" +smol_str = "0.1.9" diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs new file mode 100644 index 000000000..38bf3431a --- /dev/null +++ b/crates/ra_mbe/src/lib.rs @@ -0,0 +1,88 @@ +macro_rules! impl_froms { + ($e:ident: $($v:ident), *) => { + $( + impl From<$v> for $e { + fn from(it: $v) -> $e { + $e::$v(it) + } + } + )* + } +} + +mod tt_cursor; +mod mbe_parser; +mod mbe_expander; + +use smol_str::SmolStr; + +pub use tt::{Delimiter, Punct}; + +pub use crate::{ + mbe_parser::parse, + mbe_expander::exapnd, +}; + +#[derive(Debug)] +pub struct MacroRules { + pub(crate) rules: Vec, +} + +#[derive(Debug)] +pub(crate) struct Rule { + pub(crate) lhs: Subtree, + pub(crate) rhs: Subtree, +} + +#[derive(Debug)] +pub(crate) enum TokenTree { + Leaf(Leaf), + Subtree(Subtree), + Repeat(Repeat), +} +impl_froms!(TokenTree: Leaf, Subtree, Repeat); + +#[derive(Debug)] +pub(crate) enum Leaf { + Literal(Literal), + Punct(Punct), + Ident(Ident), + Var(Var), +} +impl_froms!(Leaf: Literal, Punct, Ident, Var); + +#[derive(Debug)] +pub(crate) struct Subtree { + pub(crate) delimiter: Delimiter, + pub(crate) token_trees: Vec, +} + +#[derive(Debug)] +pub(crate) struct Repeat { + pub(crate) subtree: Subtree, + pub(crate) kind: RepeatKind, + pub(crate) separator: Option, +} + +#[derive(Debug)] +pub(crate) enum RepeatKind { + ZeroOrMore, + OneOrMore, + ZeroOrOne, +} + +#[derive(Debug)] +pub(crate) struct Literal { + pub(crate) text: SmolStr, +} + +#[derive(Debug)] +pub(crate) struct Ident { + pub(crate) text: SmolStr, +} + +#[derive(Debug)] +pub(crate) struct Var { + pub(crate) text: SmolStr, + pub(crate) kind: Option, +} diff --git a/crates/ra_mbe/src/mbe_expander.rs b/crates/ra_mbe/src/mbe_expander.rs new file mode 100644 index 000000000..92ad26889 --- /dev/null +++ b/crates/ra_mbe/src/mbe_expander.rs @@ -0,0 +1,169 @@ +use rustc_hash::FxHashMap; +use smol_str::SmolStr; + +use crate::{self as mbe, tt_cursor::TtCursor}; + +pub fn exapnd(rules: &mbe::MacroRules, input: &tt::Subtree) -> Option { + rules.rules.iter().find_map(|it| expand_rule(it, input)) +} + +fn expand_rule(rule: &mbe::Rule, input: &tt::Subtree) -> Option { + let mut input = TtCursor::new(input); + let bindings = match_lhs(&rule.lhs, &mut input)?; + expand_subtree(&rule.rhs, &bindings, &mut Vec::new()) +} + +#[derive(Debug, Default)] +struct Bindings { + inner: FxHashMap, +} + +#[derive(Debug)] +enum Binding { + Simple(tt::TokenTree), + Nested(Vec), +} + +impl Bindings { + fn get(&self, name: &SmolStr, nesting: &[usize]) -> Option<&tt::TokenTree> { + let mut b = self.inner.get(name)?; + for &idx in nesting.iter() { + b = match b { + Binding::Simple(_) => break, + Binding::Nested(bs) => bs.get(idx)?, + }; + } + match b { + Binding::Simple(it) => Some(it), + Binding::Nested(_) => None, + } + } + fn push_nested(&mut self, nested: Bindings) -> Option<()> { + for (key, value) in nested.inner { + if !self.inner.contains_key(&key) { + self.inner.insert(key.clone(), Binding::Nested(Vec::new())); + } + match self.inner.get_mut(&key) { + Some(Binding::Nested(it)) => it.push(value), + _ => return None, + } + } + Some(()) + } +} + +fn match_lhs(pattern: &mbe::Subtree, input: &mut TtCursor) -> Option { + let mut res = Bindings::default(); + for pat in pattern.token_trees.iter() { + match pat { + mbe::TokenTree::Leaf(leaf) => match leaf { + mbe::Leaf::Var(mbe::Var { text, kind }) => { + let kind = kind.clone()?; + match kind.as_str() { + "ident" => { + let ident = input.eat_ident()?.clone(); + res.inner.insert( + text.clone(), + Binding::Simple(tt::Leaf::from(ident).into()), + ); + } + _ => return None, + } + } + mbe::Leaf::Punct(punct) => { + if input.eat_punct()? != punct { + return None; + } + } + _ => return None, + }, + mbe::TokenTree::Repeat(mbe::Repeat { + subtree, + kind: _, + separator, + }) => { + while let Some(nested) = match_lhs(subtree, input) { + res.push_nested(nested)?; + if separator.is_some() && !input.is_eof() { + input.eat_punct()?; + } + } + } + _ => {} + } + } + Some(res) +} + +/* + +macro_rules! impl_froms { + ($e:ident: $($v:ident),*) => { + $( + impl From<$v> for $e { + fn from(it: $v) -> $e { + $e::$v(it) + } + } + )* + } +} + +impl_froms! (Foo: Bar, Baz) + +*/ + +fn expand_subtree( + template: &mbe::Subtree, + bindings: &Bindings, + nesting: &mut Vec, +) -> Option { + let token_trees = template + .token_trees + .iter() + .map(|it| expand_tt(it, bindings, nesting)) + .collect::>>()?; + + Some(tt::Subtree { + token_trees, + delimiter: template.delimiter, + }) +} + +fn expand_tt( + template: &mbe::TokenTree, + bindings: &Bindings, + nesting: &mut Vec, +) -> Option { + let res: tt::TokenTree = match template { + mbe::TokenTree::Subtree(subtree) => expand_subtree(subtree, bindings, nesting)?.into(), + mbe::TokenTree::Repeat(repeat) => { + let mut token_trees = Vec::new(); + nesting.push(0); + while let Some(t) = expand_subtree(&repeat.subtree, bindings, nesting) { + let idx = nesting.pop().unwrap(); + nesting.push(idx + 1); + token_trees.push(t.into()) + } + nesting.pop().unwrap(); + tt::Subtree { + token_trees, + delimiter: tt::Delimiter::None, + } + .into() + } + mbe::TokenTree::Leaf(leaf) => match leaf { + mbe::Leaf::Ident(ident) => tt::Leaf::from(tt::Ident { + text: ident.text.clone(), + }) + .into(), + mbe::Leaf::Punct(punct) => tt::Leaf::from(punct.clone()).into(), + mbe::Leaf::Var(v) => bindings.get(&v.text, nesting)?.clone(), + mbe::Leaf::Literal(l) => tt::Leaf::from(tt::Literal { + text: l.text.clone(), + }) + .into(), + }, + }; + Some(res) +} diff --git a/crates/ra_mbe/src/mbe_parser.rs b/crates/ra_mbe/src/mbe_parser.rs new file mode 100644 index 000000000..a70ed1d52 --- /dev/null +++ b/crates/ra_mbe/src/mbe_parser.rs @@ -0,0 +1,93 @@ +use crate::{self as mbe, tt_cursor::TtCursor}; + +/// This module parses a raw `tt::TokenStream` into macro-by-example token +/// stream. This is a *mostly* identify function, expect for handling of +/// `$var:tt_kind` and `$(repeat),*` constructs. + +pub fn parse(tt: &tt::Subtree) -> Option { + let mut parser = TtCursor::new(tt); + let mut rules = Vec::new(); + while !parser.is_eof() { + rules.push(parse_rule(&mut parser)?) + } + Some(mbe::MacroRules { rules }) +} + +fn parse_rule(p: &mut TtCursor) -> Option { + let lhs = parse_subtree(p.eat_subtree()?)?; + p.expect_char('=')?; + p.expect_char('>')?; + let rhs = parse_subtree(p.eat_subtree()?)?; + Some(mbe::Rule { lhs, rhs }) +} + +fn parse_subtree(tt: &tt::Subtree) -> Option { + let mut token_trees = Vec::new(); + let mut p = TtCursor::new(tt); + while let Some(tt) = p.eat() { + let child: mbe::TokenTree = match tt { + tt::TokenTree::Leaf(leaf) => match leaf { + tt::Leaf::Punct(tt::Punct { char: '$', .. }) => { + if p.at_ident().is_some() { + mbe::Leaf::from(parse_var(&mut p)?).into() + } else { + parse_repeat(&mut p)?.into() + } + } + tt::Leaf::Punct(punct) => mbe::Leaf::from(*punct).into(), + tt::Leaf::Ident(tt::Ident { text }) => { + mbe::Leaf::from(mbe::Ident { text: text.clone() }).into() + } + tt::Leaf::Literal(tt::Literal { text }) => { + mbe::Leaf::from(mbe::Literal { text: text.clone() }).into() + } + }, + tt::TokenTree::Subtree(subtree) => parse_subtree(&subtree)?.into(), + }; + token_trees.push(child); + } + Some(mbe::Subtree { + token_trees, + delimiter: tt.delimiter, + }) +} + +fn parse_var(p: &mut TtCursor) -> Option { + let ident = p.eat_ident().unwrap(); + let text = ident.text.clone(); + let kind = if p.at_char(':') { + p.bump(); + if let Some(ident) = p.eat_ident() { + Some(ident.text.clone()) + } else { + p.rev_bump(); + None + } + } else { + None + }; + Some(mbe::Var { text, kind }) +} + +fn parse_repeat(p: &mut TtCursor) -> Option { + let subtree = p.eat_subtree().unwrap(); + let subtree = parse_subtree(subtree)?; + let sep = p.eat_punct()?; + let (separator, rep) = match sep.char { + '*' | '+' | '?' => (None, sep.char), + char => (Some(char), p.eat_punct()?.char), + }; + + let kind = match rep { + '*' => mbe::RepeatKind::ZeroOrMore, + '+' => mbe::RepeatKind::OneOrMore, + '?' => mbe::RepeatKind::ZeroOrOne, + _ => return None, + }; + p.bump(); + Some(mbe::Repeat { + subtree, + kind, + separator, + }) +} diff --git a/crates/ra_mbe/src/tt_cursor.rs b/crates/ra_mbe/src/tt_cursor.rs new file mode 100644 index 000000000..30c8eda67 --- /dev/null +++ b/crates/ra_mbe/src/tt_cursor.rs @@ -0,0 +1,91 @@ +#[derive(Clone)] +pub(crate) struct TtCursor<'a> { + subtree: &'a tt::Subtree, + pos: usize, +} + +impl<'a> TtCursor<'a> { + pub(crate) fn new(subtree: &'a tt::Subtree) -> TtCursor<'a> { + TtCursor { subtree, pos: 0 } + } + + pub(crate) fn is_eof(&self) -> bool { + self.pos == self.subtree.token_trees.len() + } + + pub(crate) fn current(&self) -> Option<&'a tt::TokenTree> { + self.subtree.token_trees.get(self.pos) + } + + pub(crate) fn at_punct(&self) -> Option<&'a tt::Punct> { + match self.current() { + Some(tt::TokenTree::Leaf(tt::Leaf::Punct(it))) => Some(it), + _ => None, + } + } + + pub(crate) fn at_char(&self, char: char) -> bool { + match self.at_punct() { + Some(tt::Punct { char: c, .. }) if *c == char => true, + _ => false, + } + } + + pub(crate) fn at_ident(&mut self) -> Option<&'a tt::Ident> { + match self.current() { + Some(tt::TokenTree::Leaf(tt::Leaf::Ident(i))) => Some(i), + _ => None, + } + } + + pub(crate) fn bump(&mut self) { + self.pos += 1; + } + pub(crate) fn rev_bump(&mut self) { + self.pos -= 1; + } + + pub(crate) fn eat(&mut self) -> Option<&'a tt::TokenTree> { + match self.current() { + Some(it) => { + self.bump(); + Some(it) + } + None => None, + } + } + + pub(crate) fn eat_subtree(&mut self) -> Option<&'a tt::Subtree> { + match self.current()? { + tt::TokenTree::Subtree(sub) => { + self.bump(); + Some(sub) + } + _ => return None, + } + } + + pub(crate) fn eat_punct(&mut self) -> Option<&'a tt::Punct> { + if let Some(it) = self.at_punct() { + self.bump(); + return Some(it); + } + None + } + + pub(crate) fn eat_ident(&mut self) -> Option<&'a tt::Ident> { + if let Some(i) = self.at_ident() { + self.bump(); + return Some(i); + } + None + } + + pub(crate) fn expect_char(&mut self, char: char) -> Option<()> { + if self.at_char(char) { + self.bump(); + return Some(()); + } + None + } +} diff --git a/crates/ra_tt/Cargo.toml b/crates/ra_tt/Cargo.toml new file mode 100644 index 000000000..357a5c5a3 --- /dev/null +++ b/crates/ra_tt/Cargo.toml @@ -0,0 +1,9 @@ +[package] +edition = "2018" +name = "ra_tt" +version = "0.1.0" +authors = ["Aleksey Kladov "] + +[dependencies] +rustc-hash = "1.0.0" +smol_str = "0.1.9" diff --git a/crates/ra_tt/src/lib.rs b/crates/ra_tt/src/lib.rs new file mode 100644 index 000000000..d7c3c62bf --- /dev/null +++ b/crates/ra_tt/src/lib.rs @@ -0,0 +1,131 @@ +macro_rules! impl_froms { + ($e:ident: $($v:ident), *) => { + $( + impl From<$v> for $e { + fn from(it: $v) -> $e { + $e::$v(it) + } + } + )* + } +} + +use std::fmt; + +use smol_str::SmolStr; + +#[derive(Debug, Clone)] +pub enum TokenTree { + Leaf(Leaf), + Subtree(Subtree), +} +impl_froms!(TokenTree: Leaf, Subtree); + +#[derive(Debug, Clone)] +pub enum Leaf { + Literal(Literal), + Punct(Punct), + Ident(Ident), +} +impl_froms!(Leaf: Literal, Punct, Ident); + +#[derive(Debug, Clone)] +pub struct Subtree { + pub delimiter: Delimiter, + pub token_trees: Vec, +} + +#[derive(Clone, Copy, Debug)] +pub enum Delimiter { + Parenthesis, + Brace, + Bracket, + None, +} + +#[derive(Debug, Clone)] +pub struct Literal { + pub text: SmolStr, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Punct { + pub char: char, + pub spacing: Spacing, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Spacing { + Alone, + Joint, +} + +#[derive(Debug, Clone)] +pub struct Ident { + pub text: SmolStr, +} + +impl fmt::Display for TokenTree { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + TokenTree::Leaf(it) => fmt::Display::fmt(it, f), + TokenTree::Subtree(it) => fmt::Display::fmt(it, f), + } + } +} + +impl fmt::Display for Subtree { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let (l, r) = match self.delimiter { + Delimiter::Parenthesis => ("(", ")"), + Delimiter::Brace => ("{", "}"), + Delimiter::Bracket => ("[", "]"), + Delimiter::None => ("", ""), + }; + f.write_str(l)?; + let mut needs_space = false; + for tt in self.token_trees.iter() { + if needs_space { + f.write_str(" ")?; + } + needs_space = true; + match tt { + TokenTree::Leaf(Leaf::Punct(p)) => { + needs_space = p.spacing == Spacing::Alone; + fmt::Display::fmt(p, f)? + } + tt => fmt::Display::fmt(tt, f)?, + } + } + f.write_str(r)?; + Ok(()) + } +} + +impl fmt::Display for Leaf { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Leaf::Ident(it) => fmt::Display::fmt(it, f), + Leaf::Literal(it) => fmt::Display::fmt(it, f), + Leaf::Punct(it) => fmt::Display::fmt(it, f), + } + } +} + +impl fmt::Display for Ident { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.text, f) + } +} + +impl fmt::Display for Literal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.text, f) + } +} + +impl fmt::Display for Punct { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.char, f) + } +} -- cgit v1.2.3