mod builtins;
mod expand;
mod hygiene;
mod pattern;
pub use builtins::*;
pub use expand::*;
pub use hygiene::*;
pub use pattern::*;
use std::collections::HashMap;
use std::sync::Arc;
use crate::lexer::{Delimiter, Span, Token, TokenKind};
use thiserror::Error;
#[derive(Debug, Clone)]
pub enum TokenTree {
Token(Token),
Delimited {
delimiter: Delimiter,
open_span: Span,
tokens: Vec<TokenTree>,
close_span: Span,
},
}
impl TokenTree {
pub fn span(&self) -> Span {
match self {
TokenTree::Token(t) => t.span,
TokenTree::Delimited {
open_span,
close_span,
..
} => open_span.merge(close_span),
}
}
pub fn is_token(&self) -> bool {
matches!(self, TokenTree::Token(_))
}
pub fn is_delimited(&self) -> bool {
matches!(self, TokenTree::Delimited { .. })
}
pub fn as_token(&self) -> Option<&Token> {
match self {
TokenTree::Token(t) => Some(t),
_ => None,
}
}
pub fn as_delimited(&self) -> Option<(&Delimiter, &[TokenTree])> {
match self {
TokenTree::Delimited {
delimiter, tokens, ..
} => Some((delimiter, tokens)),
_ => None,
}
}
}
pub fn tokens_to_tree(tokens: &[Token]) -> Vec<TokenTree> {
let mut result = Vec::new();
let mut i = 0;
while i < tokens.len() {
match &tokens[i].kind {
TokenKind::OpenDelim(delim) => {
let open_span = tokens[i].span;
let delim = *delim;
i += 1;
let (inner, close_idx) = collect_until_close(&tokens[i..], delim);
let inner_trees = tokens_to_tree(&inner);
let close_span = if i + close_idx < tokens.len() {
tokens[i + close_idx].span
} else {
open_span
};
result.push(TokenTree::Delimited {
delimiter: delim,
open_span,
tokens: inner_trees,
close_span,
});
i += close_idx + 1;
}
TokenKind::CloseDelim(_) => {
i += 1;
}
TokenKind::Eof => {
i += 1;
}
_ => {
result.push(TokenTree::Token(tokens[i].clone()));
i += 1;
}
}
}
result
}
fn collect_until_close(tokens: &[Token], open_delim: Delimiter) -> (Vec<Token>, usize) {
let mut result = Vec::new();
let mut depth = 1;
let mut i = 0;
while i < tokens.len() && depth > 0 {
match &tokens[i].kind {
TokenKind::OpenDelim(d) if *d == open_delim => {
depth += 1;
result.push(tokens[i].clone());
}
TokenKind::CloseDelim(d) if *d == open_delim => {
depth -= 1;
if depth > 0 {
result.push(tokens[i].clone());
}
}
_ => {
result.push(tokens[i].clone());
}
}
i += 1;
}
(result, i.saturating_sub(1))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MacroId(pub u32);
impl MacroId {
pub fn fresh() -> Self {
use std::sync::atomic::{AtomicU32, Ordering};
static COUNTER: AtomicU32 = AtomicU32::new(0);
MacroId(COUNTER.fetch_add(1, Ordering::SeqCst))
}
}
#[derive(Debug, Clone)]
pub struct MacroDef {
pub id: MacroId,
pub name: Arc<str>,
pub rules: Vec<MacroRule>,
pub is_exported: bool,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct MacroRule {
pub pattern: MacroPattern,
pub expansion: MacroExpansion,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct MacroPattern {
pub elements: Vec<PatternElement>,
}
#[derive(Debug, Clone)]
pub enum PatternElement {
Token(TokenKind),
MetaVar { name: Arc<str>, kind: MetaVarKind },
Repetition {
elements: Vec<PatternElement>,
separator: Option<TokenKind>,
repetition: RepetitionKind,
},
Delimited {
delimiter: Delimiter,
elements: Vec<PatternElement>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MetaVarKind {
Expr,
Type,
Ident,
Path,
Pat,
Stmt,
Block,
Item,
Meta,
TokenTree,
Literal,
Lifetime,
Vis,
}
impl MetaVarKind {
pub fn from_str(s: &str) -> Option<Self> {
match s {
"expr" => Some(Self::Expr),
"ty" => Some(Self::Type),
"ident" => Some(Self::Ident),
"path" => Some(Self::Path),
"pat" | "pat_param" => Some(Self::Pat),
"stmt" => Some(Self::Stmt),
"block" => Some(Self::Block),
"item" => Some(Self::Item),
"meta" => Some(Self::Meta),
"tt" => Some(Self::TokenTree),
"literal" | "lit" => Some(Self::Literal),
"lifetime" => Some(Self::Lifetime),
"vis" => Some(Self::Vis),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RepetitionKind {
ZeroOrMore,
OneOrMore,
ZeroOrOne,
}
#[derive(Debug, Clone)]
pub struct MacroExpansion {
pub elements: Vec<ExpansionElement>,
}
#[derive(Debug, Clone)]
pub enum ExpansionElement {
Token(TokenKind, Span),
MetaVar(Arc<str>),
Repetition {
elements: Vec<ExpansionElement>,
separator: Option<TokenKind>,
repetition: RepetitionKind,
},
Delimited {
delimiter: Delimiter,
elements: Vec<ExpansionElement>,
span: Span,
},
}
#[derive(Debug, Clone, Default)]
pub struct MacroContext {
pub macros: HashMap<MacroId, MacroDef>,
pub macro_names: HashMap<Arc<str>, MacroId>,
scopes: Vec<MacroScope>,
}
#[derive(Debug, Clone, Default)]
pub struct MacroScope {
local_macros: HashMap<Arc<str>, MacroId>,
}
impl MacroContext {
pub fn new() -> Self {
let mut ctx = Self::default();
ctx.scopes.push(MacroScope::default());
ctx
}
pub fn register_macro(&mut self, def: MacroDef) {
let id = def.id;
let name = def.name.clone();
self.macros.insert(id, def);
self.macro_names.insert(name, id);
}
pub fn lookup_macro(&self, name: &str) -> Option<&MacroDef> {
for scope in self.scopes.iter().rev() {
if let Some(id) = scope.local_macros.get(name) {
return self.macros.get(id);
}
}
self.macro_names
.get(name)
.and_then(|id| self.macros.get(id))
}
pub fn push_scope(&mut self) {
self.scopes.push(MacroScope::default());
}
pub fn pop_scope(&mut self) {
if self.scopes.len() > 1 {
self.scopes.pop();
}
}
pub fn define_local(&mut self, name: Arc<str>, id: MacroId) {
if let Some(scope) = self.scopes.last_mut() {
scope.local_macros.insert(name, id);
}
}
}
#[derive(Debug, Clone, Error)]
pub enum MacroError {
#[error("macro `{name}` not found")]
MacroNotFound { name: String },
#[error("no rules matched for macro `{name}`")]
NoMatchingRule { name: String },
#[error("metavariable `{name}` not found")]
MetaVarNotFound { name: String },
#[error("invalid metavariable kind `{kind}`")]
InvalidMetaVarKind { kind: String },
#[error("repetition mismatch: `{name}` does not repeat with `{other}`")]
RepetitionMismatch { name: String, other: String },
#[error("unexpected token: expected `{expected}`, found `{found:?}`")]
UnexpectedToken { expected: String, found: TokenKind },
#[error("macro recursion limit ({limit}) exceeded")]
RecursionLimit { limit: u32 },
#[error("parse error: {message}")]
ParseError { message: String },
}
pub type MacroResult<T> = Result<T, MacroError>;