use crate::collections::{map, Map};
use crate::grammar::free_variables::FreeVariables;
use crate::grammar::pattern::Pattern;
use crate::message::Content;
use crate::util::Sep;
use std::fmt::{Debug, Display, Error, Formatter};
use string_cache::DefaultAtom as Atom;
pub use crate::grammar::parse_tree::{
Annotation, InternToken, Lifetime, Name, NonterminalString, Path, Span, TerminalLiteral,
TerminalString, TypeBound, TypeParameter, Visibility,
};
#[derive(Clone, Debug)]
pub struct Grammar {
pub prefix: String,
pub algorithm: Algorithm,
pub uses_error_recovery: bool,
pub start_nonterminals: Map<NonterminalString, NonterminalString>,
pub uses: Vec<String>,
pub type_parameters: Vec<TypeParameter>,
pub parameters: Vec<Parameter>,
pub where_clauses: Vec<WhereClause>,
pub intern_token: Option<InternToken>,
pub action_fn_defns: Vec<ActionFnDefn>,
pub terminals: TerminalSet,
pub nonterminals: Map<NonterminalString, NonterminalData>,
pub token_span: Span,
pub conversions: Map<TerminalString, Pattern<TypeRepr>>,
pub types: Types,
pub module_attributes: Vec<String>,
}
#[allow(clippy::large_enum_variant)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum WhereClause {
Forall {
binder: Vec<TypeParameter>,
clause: Box<WhereClause>,
},
Bound {
subject: TypeRepr,
bound: TypeBound<TypeRepr>,
},
}
#[derive(Clone, Debug)]
pub struct TerminalSet {
pub all: Vec<TerminalString>,
pub bits: Map<TerminalString, usize>,
}
#[derive(Clone, Debug)]
pub struct NonterminalData {
pub name: NonterminalString,
pub visibility: Visibility,
pub span: Span,
pub annotations: Vec<Annotation>,
pub productions: Vec<Production>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Algorithm {
pub lalr: bool,
pub codegen: LrCodeGeneration,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum LrCodeGeneration {
TableDriven,
RecursiveAscent,
TestAll,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Parameter {
pub name: Atom,
pub ty: TypeRepr,
}
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Production {
pub nonterminal: NonterminalString,
pub symbols: Vec<Symbol>,
pub action: ActionFn,
pub span: Span,
}
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum Symbol {
Nonterminal(NonterminalString),
Terminal(TerminalString),
}
#[derive(Clone, PartialEq, Eq)]
pub struct ActionFnDefn {
pub fallible: bool,
pub ret_type: TypeRepr,
pub kind: ActionFnDefnKind,
}
#[derive(Clone, PartialEq, Eq)]
pub enum ActionFnDefnKind {
User(UserActionFnDefn),
Inline(InlineActionFnDefn),
Lookaround(LookaroundActionFnDefn),
}
#[derive(Clone, PartialEq, Eq)]
pub struct UserActionFnDefn {
pub arg_patterns: Vec<Name>,
pub arg_types: Vec<TypeRepr>,
pub code: String,
}
#[derive(Clone, PartialEq, Eq)]
pub struct InlineActionFnDefn {
pub action: ActionFn,
pub symbols: Vec<InlinedSymbol>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum LookaroundActionFnDefn {
Lookahead,
Lookbehind,
}
#[derive(Clone, PartialEq, Eq)]
pub enum InlinedSymbol {
Original(Symbol),
Inlined(ActionFn, Vec<Symbol>),
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum TypeRepr {
Tuple(Vec<TypeRepr>),
Slice(Box<TypeRepr>),
Nominal(NominalTypeRepr),
Associated {
type_parameter: Atom,
id: Atom,
},
Lifetime(Lifetime),
Ref {
lifetime: Option<Lifetime>,
mutable: bool,
referent: Box<TypeRepr>,
},
TraitObject(NominalTypeRepr),
Fn {
forall: Vec<TypeParameter>,
path: Path,
parameters: Vec<TypeRepr>,
ret: Option<Box<TypeRepr>>,
},
}
impl TypeRepr {
pub fn from_parameter(tp: &TypeParameter) -> Self {
match tp {
TypeParameter::Lifetime(l) => TypeRepr::Lifetime(l.clone()),
TypeParameter::Id(name) => TypeRepr::Nominal(NominalTypeRepr {
path: Path::from_id(name.clone()),
types: vec![],
}),
}
}
pub fn is_unit(&self) -> bool {
match *self {
TypeRepr::Tuple(ref v) => v.is_empty(),
_ => false,
}
}
pub fn usize() -> TypeRepr {
TypeRepr::Nominal(NominalTypeRepr {
path: Path::usize(),
types: vec![],
})
}
pub fn str() -> TypeRepr {
TypeRepr::Nominal(NominalTypeRepr {
path: Path::str(),
types: vec![],
})
}
pub fn bottom_up(&self, op: &mut impl FnMut(TypeRepr) -> TypeRepr) -> Self {
let result = match self {
TypeRepr::Tuple(types) => {
TypeRepr::Tuple(types.iter().map(|t| t.bottom_up(op)).collect())
}
TypeRepr::Slice(ty) => TypeRepr::Slice(Box::new(ty.bottom_up(op))),
TypeRepr::Nominal(NominalTypeRepr { path, types }) => {
TypeRepr::Nominal(NominalTypeRepr {
path: path.clone(),
types: types.iter().map(|t| t.bottom_up(op)).collect(),
})
}
TypeRepr::Associated { type_parameter, id } => TypeRepr::Associated {
type_parameter: type_parameter.clone(),
id: id.clone(),
},
TypeRepr::Lifetime(l) => TypeRepr::Lifetime(l.clone()),
TypeRepr::Ref {
lifetime,
mutable,
referent,
} => TypeRepr::Ref {
lifetime: lifetime.clone(),
mutable: *mutable,
referent: Box::new(referent.bottom_up(op)),
},
TypeRepr::TraitObject(NominalTypeRepr { path, types }) => {
TypeRepr::TraitObject(NominalTypeRepr {
path: path.clone(),
types: types.iter().map(|t| t.bottom_up(op)).collect(),
})
}
TypeRepr::Fn {
forall,
path,
parameters,
ret,
} => TypeRepr::Fn {
forall: forall.clone(),
path: path.clone(),
parameters: parameters.iter().map(|t| t.bottom_up(op)).collect(),
ret: ret.as_ref().map(|t| Box::new(t.bottom_up(op))),
},
};
op(result)
}
pub fn name_anonymous_lifetimes_and_compute_implied_outlives(
&self,
prefix: &str,
type_parameters: &mut Vec<TypeParameter>,
where_clauses: &mut Vec<WhereClause>,
) -> Self {
let fresh_lifetime_name = |type_parameters: &mut Vec<TypeParameter>| {
let len = type_parameters.len();
let name = Lifetime(Atom::from(format!("'{}{}", prefix, len)));
type_parameters.push(TypeParameter::Lifetime(name.clone()));
name
};
self.bottom_up(&mut |t| match t {
TypeRepr::Tuple { .. }
| TypeRepr::Slice { .. }
| TypeRepr::Nominal { .. }
| TypeRepr::Associated { .. }
| TypeRepr::TraitObject { .. }
| TypeRepr::Fn { .. } => t,
TypeRepr::Lifetime(l) => {
if l.is_anonymous() {
TypeRepr::Lifetime(fresh_lifetime_name(type_parameters))
} else {
TypeRepr::Lifetime(l)
}
}
TypeRepr::Ref {
mut lifetime,
mutable,
referent,
} => {
if lifetime.is_none() {
lifetime = Some(fresh_lifetime_name(type_parameters));
}
let l = lifetime.clone().unwrap();
for tp in referent.free_variables(type_parameters) {
let wc = WhereClause::Bound {
subject: TypeRepr::from_parameter(&tp),
bound: TypeBound::Lifetime(l.clone()),
};
if !where_clauses.contains(&wc) {
where_clauses.push(wc);
}
}
TypeRepr::Ref {
lifetime,
mutable,
referent,
}
}
})
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct NominalTypeRepr {
pub path: Path,
pub types: Vec<TypeRepr>,
}
#[derive(Clone, Debug)]
pub struct Types {
terminal_token_type: TypeRepr,
terminal_loc_type: Option<TypeRepr>,
error_type: Option<TypeRepr>,
terminal_types: Map<TerminalString, TypeRepr>,
nonterminal_types: Map<NonterminalString, TypeRepr>,
parse_error_type: TypeRepr,
error_recovery_type: TypeRepr,
}
impl Types {
pub fn new(
prefix: &str,
terminal_loc_type: Option<TypeRepr>,
error_type: Option<TypeRepr>,
terminal_token_type: TypeRepr,
) -> Types {
let mut types = Types {
terminal_loc_type,
error_type,
terminal_token_type,
terminal_types: map(),
nonterminal_types: map(),
parse_error_type: TypeRepr::Tuple(vec![]),
error_recovery_type: TypeRepr::Tuple(vec![]),
};
let args = vec![
types.terminal_loc_type(),
types.terminal_token_type().clone(),
types.error_type(),
];
types.parse_error_type = TypeRepr::Nominal(NominalTypeRepr {
path: Path {
absolute: false,
ids: vec![
Atom::from(format!("{}lalrpop_util", prefix)),
Atom::from("ParseError"),
],
},
types: args.clone(),
});
types.error_recovery_type = TypeRepr::Nominal(NominalTypeRepr {
path: Path {
absolute: false,
ids: vec![
Atom::from(format!("{}lalrpop_util", prefix)),
Atom::from("ErrorRecovery"),
],
},
types: args,
});
types
.terminal_types
.insert(TerminalString::Error, types.error_recovery_type.clone());
types
}
pub fn add_type(&mut self, nt_id: NonterminalString, ty: TypeRepr) {
assert!(self.nonterminal_types.insert(nt_id, ty).is_none());
}
pub fn add_term_type(&mut self, term: TerminalString, ty: TypeRepr) {
assert!(self.terminal_types.insert(term, ty).is_none());
}
pub fn terminal_token_type(&self) -> &TypeRepr {
&self.terminal_token_type
}
pub fn opt_terminal_loc_type(&self) -> Option<&TypeRepr> {
self.terminal_loc_type.as_ref()
}
pub fn terminal_loc_type(&self) -> TypeRepr {
self.terminal_loc_type
.clone()
.unwrap_or_else(|| TypeRepr::Tuple(vec![]))
}
pub fn error_type(&self) -> TypeRepr {
self.error_type.clone().unwrap_or_else(|| TypeRepr::Ref {
lifetime: Some(Lifetime::statik()),
mutable: false,
referent: Box::new(TypeRepr::str()),
})
}
pub fn terminal_type(&self, id: &TerminalString) -> &TypeRepr {
self.terminal_types
.get(id)
.unwrap_or(&self.terminal_token_type)
}
pub fn terminal_types(&self) -> Vec<TypeRepr> {
self.terminal_types.values().cloned().collect()
}
pub fn lookup_nonterminal_type(&self, id: &NonterminalString) -> Option<&TypeRepr> {
self.nonterminal_types.get(id)
}
pub fn nonterminal_type(&self, id: &NonterminalString) -> &TypeRepr {
&self.nonterminal_types[id]
}
pub fn nonterminal_types(&self) -> Vec<TypeRepr> {
self.nonterminal_types.values().cloned().collect()
}
pub fn parse_error_type(&self) -> &TypeRepr {
&self.parse_error_type
}
pub fn error_recovery_type(&self) -> &TypeRepr {
&self.error_recovery_type
}
pub fn triple_type(&self) -> TypeRepr {
self.spanned_type(self.terminal_token_type().clone())
}
pub fn spanned_type(&self, ty: TypeRepr) -> TypeRepr {
let location_type = self.terminal_loc_type();
TypeRepr::Tuple(vec![location_type.clone(), ty, location_type])
}
}
impl Display for WhereClause {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
match self {
WhereClause::Forall { binder, clause } => {
write!(fmt, "for<{}> {}", Sep(", ", binder), clause)
}
WhereClause::Bound { subject, bound } => write!(fmt, "{}: {}", subject, bound),
}
}
}
impl Display for Parameter {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
write!(fmt, "{}: {}", self.name, self.ty)
}
}
impl Display for TypeRepr {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
match *self {
TypeRepr::Tuple(ref types) => write!(fmt, "({})", Sep(", ", types)),
TypeRepr::Slice(ref ty) => write!(fmt, "[{}]", ty),
TypeRepr::Nominal(ref data) => write!(fmt, "{}", data),
TypeRepr::Associated {
ref type_parameter,
ref id,
} => write!(fmt, "{}::{}", type_parameter, id),
TypeRepr::Lifetime(ref id) => write!(fmt, "{}", id),
TypeRepr::Ref {
lifetime: None,
mutable: false,
ref referent,
} => write!(fmt, "&{}", referent),
TypeRepr::Ref {
lifetime: Some(ref l),
mutable: false,
ref referent,
} => write!(fmt, "&{} {}", l, referent),
TypeRepr::Ref {
lifetime: None,
mutable: true,
ref referent,
} => write!(fmt, "&mut {}", referent),
TypeRepr::Ref {
lifetime: Some(ref l),
mutable: true,
ref referent,
} => write!(fmt, "&{} mut {}", l, referent),
TypeRepr::TraitObject(ref data) => write!(fmt, "dyn {}", data),
TypeRepr::Fn {
ref forall,
ref path,
ref parameters,
ref ret,
} => {
write!(fmt, "dyn ")?;
if !forall.is_empty() {
write!(fmt, "for<{}> ", Sep(", ", forall),)?;
}
write!(fmt, "{}({})", path, Sep(", ", parameters))?;
if let Some(ret) = ret {
write!(fmt, " -> {}", ret)?;
}
Ok(())
}
}
}
}
impl Debug for TypeRepr {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
Display::fmt(self, fmt)
}
}
impl Display for NominalTypeRepr {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
if self.types.is_empty() {
write!(fmt, "{}", self.path)
} else {
write!(fmt, "{}<{}>", self.path, Sep(", ", &self.types))
}
}
}
impl Debug for NominalTypeRepr {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
Display::fmt(self, fmt)
}
}
#[derive(Copy, Clone, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub struct ActionFn(u32);
impl ActionFn {
pub fn new(x: usize) -> ActionFn {
ActionFn(x as u32)
}
pub fn index(self) -> usize {
self.0 as usize
}
}
impl Symbol {
pub fn is_terminal(&self) -> bool {
match *self {
Symbol::Terminal(..) => true,
Symbol::Nonterminal(..) => false,
}
}
pub fn ty<'ty>(&self, t: &'ty Types) -> &'ty TypeRepr {
match *self {
Symbol::Terminal(ref id) => t.terminal_type(id),
Symbol::Nonterminal(ref id) => t.nonterminal_type(id),
}
}
}
impl Display for Symbol {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
match self {
Symbol::Nonterminal(id) => write!(fmt, "{}", id),
Symbol::Terminal(id) => write!(fmt, "{}", id),
}
}
}
impl Debug for Symbol {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
Display::fmt(self, fmt)
}
}
impl From<Symbol> for Box<dyn Content> {
fn from(val: Symbol) -> Self {
match val {
Symbol::Nonterminal(nt) => nt.into(),
Symbol::Terminal(term) => term.into(),
}
}
}
impl Debug for Production {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
write!(
fmt,
"{} = {} => {:?};",
self.nonterminal,
Sep(", ", &self.symbols),
self.action,
)
}
}
impl Debug for ActionFnDefn {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
write!(fmt, "{}", self.to_fn_string("_"))
}
}
impl ActionFnDefn {
fn to_fn_string(&self, name: &str) -> String {
match self.kind {
ActionFnDefnKind::User(ref data) => data.to_fn_string(self, name),
ActionFnDefnKind::Inline(ref data) => data.to_fn_string(name),
ActionFnDefnKind::Lookaround(ref data) => format!("{:?}", data),
}
}
}
impl UserActionFnDefn {
fn to_fn_string(&self, defn: &ActionFnDefn, name: &str) -> String {
let arg_strings: Vec<String> = self
.arg_patterns
.iter()
.zip(self.arg_types.iter())
.map(|(name, ty)| format!("{}: {}", name, ty))
.collect();
format!(
"fn {}({}) -> {} {{ {} }}",
name,
Sep(", ", &arg_strings),
defn.ret_type,
self.code,
)
}
}
impl InlineActionFnDefn {
fn to_fn_string(&self, name: &str) -> String {
let arg_strings: Vec<String> = self
.symbols
.iter()
.map(|inline_sym| match *inline_sym {
InlinedSymbol::Original(ref s) => format!("{}", s),
InlinedSymbol::Inlined(a, ref s) => format!("{:?}({})", a, Sep(", ", s)),
})
.collect();
format!(
"fn {}(..) {{ {:?}({}) }}",
name,
self.action,
Sep(", ", &arg_strings),
)
}
}
impl Grammar {
pub fn pattern(&self, t: &TerminalString) -> &Pattern<TypeRepr> {
&self.conversions[t]
}
pub fn productions_for(&self, nonterminal: &NonterminalString) -> &[Production] {
match self.nonterminals.get(nonterminal) {
Some(v) => &v.productions[..],
None => &[], }
}
pub fn user_parameter_refs(&self) -> String {
let mut result = String::new();
for parameter in &self.parameters {
result.push_str(&format!("{}, ", parameter.name));
}
result
}
pub fn action_is_fallible(&self, f: ActionFn) -> bool {
self.action_fn_defns[f.index()].fallible
}
pub fn non_lifetime_type_parameters(&self) -> Vec<&TypeParameter> {
self.type_parameters
.iter()
.filter(|&tp| match *tp {
TypeParameter::Lifetime(_) => false,
TypeParameter::Id(_) => true,
})
.collect()
}
}
impl Default for Algorithm {
fn default() -> Self {
Algorithm {
lalr: false,
codegen: LrCodeGeneration::TableDriven,
}
}
}