blob: 5e06e3f5ce5164fd336fd46cd82e2814ce59f7a6 [file] [log] [blame]
//! Discrete tokens that can be parsed out by synom.
//!
//! This module contains a number of useful tokens like `+=` and `/` along with
//! keywords like `crate` and such. These structures are used to track the spans
//! of these tokens and all implment the `ToTokens` and `Synom` traits when the
//! corresponding feature is activated.
use span::Span;
macro_rules! tokens {
(
ops: {
$(($($op:tt)*),)*
}
delim: {
$(($($delim:tt)*),)*
}
syms: {
$(($($sym:tt)*),)*
}
) => (
$(op! { $($op)* })*
$(delim! { $($delim)* })*
$(sym! { $($sym)* })*
)
}
macro_rules! op {
(pub struct $name:ident($($contents:tt)*) => $s:expr) => {
#[cfg_attr(feature = "clone-impls", derive(Copy, Clone))]
#[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
#[derive(Default)]
pub struct $name(pub $($contents)*);
#[cfg(feature = "printing")]
impl ::quote::ToTokens for $name {
fn to_tokens(&self, tokens: &mut ::quote::Tokens) {
printing::op($s, &self.0, tokens);
}
}
#[cfg(feature = "parsing")]
impl ::Synom for $name {
fn parse(tokens: $crate::Cursor) -> $crate::PResult<$name> {
parsing::op($s, tokens, $name)
}
}
}
}
macro_rules! sym {
(pub struct $name:ident => $s:expr) => {
#[cfg_attr(feature = "clone-impls", derive(Copy, Clone))]
#[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
#[derive(Default)]
pub struct $name(pub Span);
#[cfg(feature = "printing")]
impl ::quote::ToTokens for $name {
fn to_tokens(&self, tokens: &mut ::quote::Tokens) {
printing::sym($s, &self.0, tokens);
}
}
#[cfg(feature = "parsing")]
impl ::Synom for $name {
fn parse(tokens: $crate::Cursor) -> $crate::PResult<$name> {
parsing::sym($s, tokens, $name)
}
}
}
}
macro_rules! delim {
(pub struct $name:ident => $s:expr) => {
#[cfg_attr(feature = "clone-impls", derive(Copy, Clone))]
#[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
#[derive(Default)]
pub struct $name(pub Span);
impl $name {
#[cfg(feature = "printing")]
pub fn surround<F>(&self,
tokens: &mut ::quote::Tokens,
f: F)
where F: FnOnce(&mut ::quote::Tokens)
{
printing::delim($s, &self.0, tokens, f);
}
#[cfg(feature = "parsing")]
pub fn parse<F, R>(tokens: $crate::Cursor, f: F) -> $crate::PResult<(R, $name)>
where F: FnOnce($crate::Cursor) -> $crate::PResult<R>
{
parsing::delim($s, tokens, $name, f)
}
}
}
}
tokens! {
ops: {
(pub struct Add([Span; 1]) => "+"),
(pub struct AddEq([Span; 2]) => "+="),
(pub struct And([Span; 1]) => "&"),
(pub struct AndAnd([Span; 2]) => "&&"),
(pub struct AndEq([Span; 2]) => "&="),
(pub struct At([Span; 1]) => "@"),
(pub struct Bang([Span; 1]) => "!"),
(pub struct Caret([Span; 1]) => "^"),
(pub struct CaretEq([Span; 2]) => "^="),
(pub struct Colon([Span; 1]) => ":"),
(pub struct Colon2([Span; 2]) => "::"),
(pub struct Comma([Span; 1]) => ","),
(pub struct Div([Span; 1]) => "/"),
(pub struct DivEq([Span; 2]) => "/="),
(pub struct Dot([Span; 1]) => "."),
(pub struct Dot2([Span; 2]) => ".."),
(pub struct Dot3([Span; 3]) => "..."),
(pub struct Eq([Span; 1]) => "="),
(pub struct EqEq([Span; 2]) => "=="),
(pub struct Ge([Span; 2]) => ">="),
(pub struct Gt([Span; 1]) => ">"),
(pub struct Le([Span; 2]) => "<="),
(pub struct Lt([Span; 1]) => "<"),
(pub struct MulEq([Span; 2]) => "*="),
(pub struct Ne([Span; 2]) => "!="),
(pub struct Or([Span; 1]) => "|"),
(pub struct OrEq([Span; 2]) => "|="),
(pub struct OrOr([Span; 2]) => "||"),
(pub struct Pound([Span; 1]) => "#"),
(pub struct Question([Span; 1]) => "?"),
(pub struct RArrow([Span; 2]) => "->"),
(pub struct Rem([Span; 1]) => "%"),
(pub struct RemEq([Span; 2]) => "%="),
(pub struct Rocket([Span; 2]) => "=>"),
(pub struct Semi([Span; 1]) => ";"),
(pub struct Shl([Span; 2]) => "<<"),
(pub struct ShlEq([Span; 3]) => "<<="),
(pub struct Shr([Span; 2]) => ">>"),
(pub struct ShrEq([Span; 3]) => ">>="),
(pub struct Star([Span; 1]) => "*"),
(pub struct Sub([Span; 1]) => "-"),
(pub struct SubEq([Span; 2]) => "-="),
(pub struct Underscore([Span; 1]) => "_"),
}
delim: {
(pub struct Brace => "{"),
(pub struct Bracket => "["),
(pub struct Paren => "("),
}
syms: {
(pub struct As => "as"),
(pub struct Box_ => "box"),
(pub struct Break => "break"),
(pub struct CapSelf => "Self"),
(pub struct Catch => "catch"),
(pub struct Const => "const"),
(pub struct Continue => "continue"),
(pub struct Crate => "crate"),
(pub struct Default_ => "default"),
(pub struct Do => "do"),
(pub struct Else => "else"),
(pub struct Enum => "enum"),
(pub struct Extern => "extern"),
(pub struct Fn_ => "fn"),
(pub struct For => "for"),
(pub struct If => "if"),
(pub struct Impl => "impl"),
(pub struct In => "in"),
(pub struct Let => "let"),
(pub struct Loop => "loop"),
(pub struct Match => "match"),
(pub struct Mod => "mod"),
(pub struct Move => "move"),
(pub struct Mut => "mut"),
(pub struct Pub => "pub"),
(pub struct Ref => "ref"),
(pub struct Return => "return"),
(pub struct Self_ => "self"),
(pub struct Static => "static"),
(pub struct Struct => "struct"),
(pub struct Super => "super"),
(pub struct Trait => "trait"),
(pub struct Type => "type"),
(pub struct Union => "union"),
(pub struct Unsafe => "unsafe"),
(pub struct Use => "use"),
(pub struct Where => "where"),
(pub struct While => "while"),
}
}
#[cfg(feature = "parsing")]
mod parsing {
use proc_macro2::{TokenTree, TokenKind, Delimiter, OpKind};
use {PResult, Cursor, parse_error};
use span::Span;
pub trait FromSpans: Sized {
fn from_spans(spans: &[Span]) -> Self;
}
impl FromSpans for [Span; 1] {
fn from_spans(spans: &[Span]) -> Self {
[spans[0]]
}
}
impl FromSpans for [Span; 2] {
fn from_spans(spans: &[Span]) -> Self {
[spans[0], spans[1]]
}
}
impl FromSpans for [Span; 3] {
fn from_spans(spans: &[Span]) -> Self {
[spans[0], spans[1], spans[2]]
}
}
pub fn op<'a, T, R>(s: &str,
tokens: Cursor<'a>,
new: fn(T) -> R)
-> PResult<'a, R>
where T: FromSpans,
{
let mut spans = [Span::default(); 3];
assert!(s.len() <= spans.len());
let chars = s.chars();
let mut it = tokens.iter();
for (i, (ch, slot)) in chars.zip(&mut spans).enumerate() {
let tok = match it.next() {
Some(tok) => tok,
_ => return parse_error(),
};
let kind = match tok.kind {
TokenKind::Op(c, kind) if c == ch => kind,
_ => return parse_error(),
};
if i != s.len() - 1 {
match kind {
OpKind::Joint => {}
OpKind::Alone => return parse_error(),
}
}
*slot = Span(tok.span);
}
Ok((it.as_slice(), new(T::from_spans(&spans))))
}
pub fn sym<'a, T>(sym: &str,
tokens: Cursor<'a>,
new: fn(Span) -> T)
-> PResult<'a, T>
{
let mut tokens = tokens.iter();
let (span, s) = match tokens.next() {
Some(&TokenTree { span, kind: TokenKind::Word(sym) }) => (span, sym),
_ => return parse_error(),
};
if s.as_str() == sym {
Ok((tokens.as_slice(), new(Span(span))))
} else {
parse_error()
}
}
pub fn delim<'a, F, R, T>(delim: &str,
tokens: Cursor<'a>,
new: fn(Span) -> T,
f: F)
-> PResult<'a, (R, T)>
where F: FnOnce(Cursor) -> PResult<R>
{
let delim = match delim {
"(" => Delimiter::Parenthesis,
"{" => Delimiter::Brace,
"[" => Delimiter::Bracket,
_ => panic!("unknown delimiter: {}", delim),
};
let mut tokens = tokens.iter();
let (span, d, others) = match tokens.next() {
Some(&TokenTree { span, kind: TokenKind::Sequence(d, ref rest) }) => {
(span, d, rest)
}
_ => return parse_error(),
};
match (delim, d) {
(Delimiter::Parenthesis, Delimiter::Parenthesis) |
(Delimiter::Brace, Delimiter::Brace) |
(Delimiter::Bracket, Delimiter::Bracket) => {}
_ => return parse_error(),
}
// TODO: Need a custom type to avoid this allocation every time we try
// this branch. (issue dtolnay/syn#148)
let rest = others.clone().into_iter().collect::<Vec<_>>();
match f(&rest) {
Ok((remaining, ret)) => {
if remaining.is_empty() {
Ok((tokens.as_slice(), (ret, new(Span(span)))))
} else {
parse_error()
}
}
Err(err) => Err(err),
}
}
}
#[cfg(feature = "printing")]
mod printing {
use proc_macro2::{TokenTree, TokenKind, OpKind};
use quote::Tokens;
use span::Span;
pub fn op(s: &str, spans: &[Span], tokens: &mut Tokens) {
assert_eq!(s.len(), spans.len());
let mut chars = s.chars();
let mut spans = spans.iter();
let ch = chars.next_back().unwrap();
let span = spans.next_back().unwrap();
for (ch, span) in chars.zip(spans) {
tokens.append(TokenTree {
span: span.0,
kind: TokenKind::Op(ch, OpKind::Joint),
});
}
tokens.append(TokenTree {
span: span.0,
kind: TokenKind::Op(ch, OpKind::Alone),
});
}
pub fn sym(s: &str, span: &Span, tokens: &mut Tokens) {
tokens.append(TokenTree {
span: span.0,
kind: TokenKind::Word(s.into()),
});
}
pub fn delim<F>(s: &str, span: &Span, tokens: &mut Tokens, f: F)
where F: FnOnce(&mut Tokens)
{
tokens.append_delimited(s, span.0, f)
}
}