use crate::lexer::{self, Token, to_stream};
use crate::error::HtmlParseError;
use crate::html::{Node, Element};
use crate::declare::Declare;
use crate::map::StringyMap;
use proc_macro2::{Delimiter, Ident, Literal, Group, TokenTree};
use lalrpop_util::ParseError;
use crate::span;
grammar;
/// Match a B separated list of zero or more A, return a list of A.
Separated<A, B>: Vec<A> = {
<v:(<A> B)*> <e:A?> => match e {
None => v,
Some(e) => {
let mut v = v;
v.push(e);
v
}
}
}
/// Match a B separated list of one or more A, return a list of tokens, including the Bs.
/// Both A and B must resolve to a Token.
SeparatedInc<A, B>: Vec<Token> = {
<v:(A B)*> <e:A> => {
let mut out = Vec::new();
for (a, b) in v {
out.push(a);
out.push(b);
}
out.push(e);
out
}
}
Ident: Ident = IdentToken => {
match <> {
Token::Ident(ident) => ident,
_ => unreachable!()
}
};
Literal: Literal = LiteralToken => {
match <> {
Token::Literal(literal) => literal,
_ => unreachable!()
}
};
GroupToken = {
BraceGroupToken,
BracketGroupToken,
ParenGroupToken,
};
/// A kebab case HTML ident, converted to a snake case ident.
HtmlIdent: Ident = {
<init:(<Ident> "-")*> <last:Ident> => {
let mut init = init;
init.push(last);
let (span, name) = init.into_iter().fold((None, String::new()), |(span, name), token| {
(
match span {
None => Some(token.span().unstable()),
Some(span) => {
#[cfg(can_join_spans)]
{
span.join(token.span().unstable())
}
#[cfg(not(can_join_spans))]
{
Some(span)
}
}
},
if name.is_empty() {
name + &token.to_string()
} else {
name + "_" + &token.to_string()
}
)
});
Ident::new(&name, span::from_unstable(span.unwrap()))
}
};
// The HTML macro
/// An approximation of a Rust expression.
BareExpression: Token = "&"? (IdentToken ":" ":")* SeparatedInc<IdentToken, "."> ParenGroupToken? => {
let (reference, left, right, args) = (<>);
let mut out = Vec::new();
if let Some(reference) = reference {
out.push(reference);
}
for (ident, c1, c2) in left {
out.push(ident);
out.push(c1);
out.push(c2);
}
out.extend(right);
if let Some(args) = args {
out.push(args);
}
Group::new(Delimiter::Brace, to_stream(out)).into()
};
AttrValue: Token = {
LiteralToken,
GroupToken,
BareExpression,
};
Attr: (Ident, Token) = <name:HtmlIdent> "=" <value:AttrValue> => (name, value);
Attrs: StringyMap<Ident, TokenTree> = Attr* => <>.into();
OpeningTag: (Ident, StringyMap<Ident, TokenTree>) = "<" <HtmlIdent> <Attrs> ">";
ClosingTag: Ident = "<" "/" <HtmlIdent> ">";
SingleTag: Element = "<" <name:HtmlIdent> <attributes:Attrs> "/" ">" => {
Element {
name,
attributes,
children: Vec::new(),
}
};
ParentTag: Element = <opening:OpeningTag> <children:Node*> <closing:ClosingTag> =>? {
let (name, attributes) = opening;
let closing_name = closing.to_string();
if closing_name == name.to_string() {
Ok(Element {
name,
attributes,
children,
})
} else {
Err(ParseError::User { error: HtmlParseError::TagMismatch {
open: name.into(),
close: closing.into(),
}})
}
};
Element = {
SingleTag,
ParentTag,
};
TextNode = Literal;
CodeBlock: Group = BraceGroupToken => match <> {
Token::Group(_, group) => group,
_ => unreachable!()
};
Node: Node = {
Element => Node::Element(<>),
TextNode => Node::Text(<>),
CodeBlock => Node::Block(<>),
};
pub NodeWithType: (Node, Option<Vec<Token>>) = {
Node => (<>, None),
<Node> ":" <TypeSpec> => {
let (node, spec) = (<>);
(node, Some(spec))
},
};
pub NodeWithBump: (Ident, Node) = {
<Ident> "," <Node>,
};
// The declare macro
TypePath: Vec<Token> = {
IdentToken => vec![<>],
TypePath ":" ":" IdentToken => {
let (mut path, c1, c2, last) = (<>);
path.push(c1);
path.push(c2);
path.push(last);
path
}
};
Reference: Vec<Token> = "&" ("'" IdentToken)? => {
let (amp, lifetime) = (<>);
let mut out = vec![amp];
if let Some((tick, ident)) = lifetime {
out.push(tick);
out.push(ident);
}
out
};
TypeArgs: Vec<Token> = {
TypeSpec,
TypeArgs "," TypeSpec => {
let (mut args, comma, last) = (<>);
args.push(comma);
args.extend(last);
args
}
};
TypeArgList: Vec<Token> = "<" TypeArgs ">" => {
let (left, mut args, right) = (<>);
args.insert(0, left);
args.push(right);
args
};
FnReturnType: Vec<Token> = "-" ">" TypeSpec => {
let (dash, right, spec) = (<>);
let mut out = vec![dash, right];
out.extend(spec);
out
};
FnArgList: Vec<Token> = ParenGroupToken FnReturnType? => {
let (args, rt) = (<>);
let mut out = vec![args];
if let Some(rt) = rt {
out.extend(rt);
}
out
};
TypeArgSpec = {
TypeArgList,
FnArgList,
};
TypeSpec: Vec<Token> = Reference? TypePath TypeArgSpec? => {
let (reference, path, args) = (<>);
let mut out = Vec::new();
if let Some(reference) = reference {
out.extend(reference);
}
out.extend(path);
if let Some(args) = args {
out.extend(args);
}
out
};
TypeDecl: (Ident, Vec<Token>) = <HtmlIdent> ":" <TypeSpec>;
TypeDecls: Vec<(Ident, Vec<Token>)> = {
TypeDecl => vec![<>],
<decls:TypeDecls> "," <decl:TypeDecl> => {
let mut decls = decls;
decls.push(decl);
decls
},
};
Attributes = "{" <TypeDecls> ","? "}";
TypePathList = "[" <Separated<TypePath, ",">> "]";
IdentList = "[" <Separated<Ident, ",">> "]";
Groups = "in" <TypePathList>;
Children: (Vec<Ident>, Option<Vec<Token>>) = "with" <req:IdentList?> <opt:TypePath?> => {
(req.unwrap_or_else(|| Vec::new()), opt)
};
Declaration: Declare = <name:HtmlIdent> <attrs:Attributes?> <groups:Groups?> <children:Children?> ";" => {
let mut decl = Declare::new(name);
if let Some(attrs) = attrs {
for (key, value) in attrs {
decl.attrs.insert(key, to_stream(value));
}
}
if let Some(groups) = groups {
for group in groups {
decl.traits.push(to_stream(group));
}
}
if let Some((req_children, opt_children)) = children {
decl.req_children = req_children;
decl.opt_children = opt_children.map(to_stream);
}
decl
};
pub Declarations = Declaration*;
extern {
type Location = usize;
type Error = HtmlParseError;
enum lexer::Token {
"<" => Token::Punct('<', _),
">" => Token::Punct('>', _),
"/" => Token::Punct('/', _),
"=" => Token::Punct('=', _),
"-" => Token::Punct('-', _),
":" => Token::Punct(':', _),
"." => Token::Punct('.', _),
"," => Token::Punct(',', _),
"&" => Token::Punct('&', _),
"'" => Token::Punct('\'', _),
";" => Token::Punct(';', _),
"{" => Token::GroupOpen(Delimiter::Brace, _),
"}" => Token::GroupClose(Delimiter::Brace, _),
"[" => Token::GroupOpen(Delimiter::Bracket, _),
"]" => Token::GroupClose(Delimiter::Bracket, _),
"in" => Token::Keyword(lexer::Keyword::In, _),
"with" => Token::Keyword(lexer::Keyword::With, _),
IdentToken => Token::Ident(_),
LiteralToken => Token::Literal(_),
ParenGroupToken => Token::Group(Delimiter::Parenthesis, _),
BraceGroupToken => Token::Group(Delimiter::Brace, _),
BracketGroupToken => Token::Group(Delimiter::Bracket, _),
}
}