summaryrefslogtreecommitdiff
path: root/dhall
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dhall_proc_macros/Cargo.toml6
-rw-r--r--dhall_proc_macros/src/lib.rs9
-rw-r--r--dhall_proc_macros/src/parser.rs398
-rw-r--r--dhall_syntax/Cargo.toml1
-rw-r--r--dhall_syntax/src/parser.rs209
5 files changed, 417 insertions, 206 deletions
diff --git a/dhall_proc_macros/Cargo.toml b/dhall_proc_macros/Cargo.toml
index df1eda8..b641a39 100644
--- a/dhall_proc_macros/Cargo.toml
+++ b/dhall_proc_macros/Cargo.toml
@@ -11,6 +11,6 @@ doctest = false
[dependencies]
itertools = "0.8.0"
-quote = "0.6.11"
-proc-macro2 = "0.4.27"
-syn = "0.15.29"
+quote = "1.0.2"
+proc-macro2 = "1.0.2"
+syn = { version = "1.0.5", features = ["full", "extra-traits"] }
diff --git a/dhall_proc_macros/src/lib.rs b/dhall_proc_macros/src/lib.rs
index 5304429..37c9985 100644
--- a/dhall_proc_macros/src/lib.rs
+++ b/dhall_proc_macros/src/lib.rs
@@ -6,6 +6,7 @@
extern crate proc_macro;
mod derive;
+mod parser;
use proc_macro::TokenStream;
@@ -13,3 +14,11 @@ use proc_macro::TokenStream;
pub fn derive_static_type(input: TokenStream) -> TokenStream {
derive::derive_static_type(input)
}
+
+#[proc_macro]
+pub fn make_parser(input: TokenStream) -> TokenStream {
+ TokenStream::from(match parser::make_parser(input) {
+ Ok(tokens) => tokens,
+ Err(err) => err.to_compile_error(),
+ })
+}
diff --git a/dhall_proc_macros/src/parser.rs b/dhall_proc_macros/src/parser.rs
new file mode 100644
index 0000000..bb4e894
--- /dev/null
+++ b/dhall_proc_macros/src/parser.rs
@@ -0,0 +1,398 @@
+use proc_macro2::{Span, TokenStream};
+use quote::quote;
+use syn::parse::{Parse, ParseStream, Result};
+use syn::punctuated::Punctuated;
+use syn::{bracketed, parenthesized, token, Expr, Ident, Pat, Token, Type};
+
+mod rule_kw {
+ syn::custom_keyword!(rule);
+ syn::custom_keyword!(captured_str);
+ syn::custom_keyword!(children);
+ syn::custom_keyword!(prec_climb);
+}
+
+#[derive(Debug, Clone)]
+struct Rules(Vec<Rule>);
+
+#[derive(Debug, Clone)]
+struct Rule {
+ rule_token: rule_kw::rule,
+ bang_token: Token![!],
+ paren_token: token::Paren,
+ name: Ident,
+ lt_token: token::Lt,
+ output_type: Type,
+ gt_token: token::Gt,
+ contents: RuleContents,
+ semi_token: Token![;],
+}
+
+#[derive(Debug, Clone)]
+enum RuleContents {
+ Empty,
+ CapturedString {
+ span: Option<Ident>,
+ captured_str_token: rule_kw::captured_str,
+ bang_token: Token![!],
+ paren_token: token::Paren,
+ pattern: Pat,
+ fat_arrow_token: Token![=>],
+ body: Expr,
+ },
+ Children {
+ span: Option<Ident>,
+ children_token: rule_kw::children,
+ bang_token: Token![!],
+ paren_token: token::Paren,
+ branches: Punctuated<ChildrenBranch, Token![,]>,
+ },
+ PrecClimb {
+ span: Option<Ident>,
+ prec_climb_token: rule_kw::prec_climb,
+ bang_token: Token![!],
+ paren_token: token::Paren,
+ child_rule: Ident,
+ comma_token: Token![,],
+ climber: Expr,
+ comma_token2: Token![,],
+ pattern: Pat,
+ fat_arrow_token: Token![=>],
+ body: Expr,
+ },
+}
+
+#[derive(Debug, Clone)]
+struct ChildrenBranch {
+ bracket_token: token::Bracket,
+ pattern_unparsed: TokenStream,
+ pattern: Punctuated<ChildrenBranchPatternItem, Token![,]>,
+ fat_arrow_token: Token![=>],
+ body: Expr,
+}
+
+#[derive(Debug, Clone)]
+enum ChildrenBranchPatternItem {
+ Single {
+ rule_name: Ident,
+ paren_token: token::Paren,
+ binder: Pat,
+ },
+ Multiple {
+ rule_name: Ident,
+ paren_token: token::Paren,
+ binder: Ident,
+ slice_token: Token![..],
+ },
+}
+
+impl Parse for Rules {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let mut rules = Vec::new();
+ while !input.is_empty() {
+ rules.push(input.parse()?)
+ }
+ Ok(Rules(rules))
+ }
+}
+
+impl Parse for Rule {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let contents;
+ Ok(Rule {
+ rule_token: input.parse()?,
+ bang_token: input.parse()?,
+ paren_token: parenthesized!(contents in input),
+ name: contents.parse()?,
+ lt_token: contents.parse()?,
+ output_type: contents.parse()?,
+ gt_token: contents.parse()?,
+ contents: contents.parse()?,
+ semi_token: input.parse()?,
+ })
+ }
+}
+
+impl Parse for RuleContents {
+ fn parse(input: ParseStream) -> Result<Self> {
+ if input.is_empty() {
+ return Ok(RuleContents::Empty);
+ }
+ let _: Token![;] = input.parse()?;
+ let span = if input.peek(Ident) && input.peek2(Token![;]) {
+ let span: Ident = input.parse()?;
+ let _: Token![;] = input.parse()?;
+ Some(span)
+ } else {
+ None
+ };
+
+ let lookahead = input.lookahead1();
+ if lookahead.peek(rule_kw::captured_str) {
+ let contents;
+ Ok(RuleContents::CapturedString {
+ span,
+ captured_str_token: input.parse()?,
+ bang_token: input.parse()?,
+ paren_token: parenthesized!(contents in input),
+ pattern: contents.parse()?,
+ fat_arrow_token: input.parse()?,
+ body: input.parse()?,
+ })
+ } else if lookahead.peek(rule_kw::children) {
+ let contents;
+ Ok(RuleContents::Children {
+ span,
+ children_token: input.parse()?,
+ bang_token: input.parse()?,
+ paren_token: parenthesized!(contents in input),
+ branches: Punctuated::parse_terminated(&contents)?,
+ })
+ } else if lookahead.peek(rule_kw::prec_climb) {
+ let contents;
+ Ok(RuleContents::PrecClimb {
+ span,
+ prec_climb_token: input.parse()?,
+ bang_token: input.parse()?,
+ paren_token: parenthesized!(contents in input),
+ child_rule: contents.parse()?,
+ comma_token: contents.parse()?,
+ climber: contents.parse()?,
+ comma_token2: contents.parse()?,
+ pattern: contents.parse()?,
+ fat_arrow_token: contents.parse()?,
+ body: contents.parse()?,
+ })
+ } else {
+ Err(lookahead.error())
+ }
+ }
+}
+
+impl Parse for ChildrenBranch {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let contents;
+ Ok(ChildrenBranch {
+ bracket_token: bracketed!(contents in input),
+ pattern_unparsed: contents.fork().parse()?,
+ pattern: Punctuated::parse_terminated(&contents)?,
+ fat_arrow_token: input.parse()?,
+ body: input.parse()?,
+ })
+ }
+}
+
+impl Parse for ChildrenBranchPatternItem {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let rule_name = input.parse()?;
+ let contents;
+ let paren_token = parenthesized!(contents in input);
+ if input.peek(Token![..]) {
+ Ok(ChildrenBranchPatternItem::Multiple {
+ rule_name,
+ paren_token,
+ binder: contents.parse()?,
+ slice_token: input.parse()?,
+ })
+ } else if input.is_empty() || input.peek(Token![,]) {
+ Ok(ChildrenBranchPatternItem::Single {
+ rule_name,
+ paren_token,
+ binder: contents.parse()?,
+ })
+ } else {
+ Err(input.error("expected `..` or nothing"))
+ }
+ }
+}
+
+fn make_construct_precclimbers(rules: &Rules) -> Result<TokenStream> {
+ let mut entries: Vec<TokenStream> = Vec::new();
+ for rule in &rules.0 {
+ if let RuleContents::PrecClimb { climber, .. } = &rule.contents {
+ let name = &rule.name;
+ entries.push(quote!(
+ map.insert(Rule::#name, #climber);
+ ))
+ }
+ }
+
+ Ok(quote!(
+ fn construct_precclimbers() -> HashMap<Rule, PrecClimber<Rule>> {
+ let mut map = HashMap::new();
+ #(#entries)*
+ map
+ }
+ ))
+}
+
+fn make_entrypoints(rules: &Rules) -> Result<TokenStream> {
+ let mut entries: Vec<TokenStream> = Vec::new();
+ for rule in &rules.0 {
+ let name = &rule.name;
+ let output_type = &rule.output_type;
+ entries.push(quote!(
+ #[allow(non_snake_case, dead_code)]
+ fn #name<'a>(
+ input: Rc<str>,
+ pair: Pair<'a, Rule>,
+ ) -> ParseResult<#output_type> {
+ let climbers = construct_precclimbers();
+ Parsers::#name((&climbers, input), pair)
+ }
+ ))
+ }
+
+ Ok(quote!(
+ struct EntryPoint;
+ impl EntryPoint {
+ #(#entries)*
+ }
+ ))
+}
+
+fn make_parser_branch(branch: &ChildrenBranch) -> TokenStream {
+ let ChildrenBranch {
+ pattern,
+ body,
+ pattern_unparsed,
+ ..
+ } = branch;
+ let variable_pattern = Ident::new("variable_pattern", Span::call_site());
+ let match_pat = pattern.iter().map(|item| match item {
+ ChildrenBranchPatternItem::Single { rule_name, .. } => {
+ quote!(Rule::#rule_name)
+ }
+ ChildrenBranchPatternItem::Multiple { .. } => {
+ quote!(#variable_pattern..)
+ }
+ });
+ let match_filter = pattern.iter().map(|item| match item {
+ ChildrenBranchPatternItem::Single { .. } => quote!(true &&),
+ ChildrenBranchPatternItem::Multiple { rule_name, .. } => {
+ quote!(#variable_pattern.iter().all(|r| r == &Rule::#rule_name) &&)
+ }
+ });
+ quote!(
+ [#(#match_pat),*] if #(#match_filter)* true => {
+ parse_children!((climbers, input.clone()), iter;
+ [#pattern_unparsed] => {
+ #[allow(unused_variables)]
+ let res: Result<_, String> = try { #body };
+ res.map_err(|msg|
+ custom_parse_error(&pair, msg)
+ )
+ }
+ )
+ }
+ )
+}
+
+fn make_parser_expr(rule: &Rule) -> Result<TokenStream> {
+ let name = &rule.name;
+ let expr = match &rule.contents {
+ RuleContents::Empty => quote!(Ok(())),
+ RuleContents::CapturedString { pattern, body, .. } => quote!(
+ let #pattern = pair.as_str();
+ let res: Result<_, String> = try { #body };
+ res.map_err(|msg| custom_parse_error(&pair, msg))
+ ),
+ RuleContents::PrecClimb {
+ child_rule,
+ pattern,
+ body,
+ ..
+ } => quote!(
+ let climber = climbers.get(&Rule::#name).unwrap();
+ climber.climb(
+ pair.clone().into_inner(),
+ |p| Parsers::#child_rule((climbers, input.clone()), p),
+ |l, op, r| {
+ let #pattern = (l?, op, r?);
+ let res: Result<_, String> = try { #body };
+ res.map_err(|msg| custom_parse_error(&pair, msg))
+ },
+ )
+ ),
+ RuleContents::Children { branches, .. } => {
+ let branches = branches.iter().map(make_parser_branch);
+ quote!(
+ let children_rules: Vec<Rule> = pair
+ .clone()
+ .into_inner()
+ .map(|p| p.as_rule())
+ .collect();
+
+ #[allow(unused_mut)]
+ let mut iter = pair.clone().into_inner();
+
+ #[allow(unreachable_code)]
+ match children_rules.as_slice() {
+ #(#branches,)*
+ [..] => Err(custom_parse_error(
+ &pair,
+ format!("Unexpected children: {:?}", children_rules)
+ )),
+ }
+ )
+ }
+ };
+ Ok(expr)
+}
+
+fn make_parsers(rules: &Rules) -> Result<TokenStream> {
+ let mut entries: Vec<TokenStream> = Vec::new();
+ for rule in &rules.0 {
+ let span_def = match &rule.contents {
+ RuleContents::CapturedString {
+ span: Some(span), ..
+ }
+ | RuleContents::Children {
+ span: Some(span), ..
+ }
+ | RuleContents::PrecClimb {
+ span: Some(span), ..
+ } => Some(quote!(
+ let #span = Span::make(input.clone(), pair.as_span());
+ )),
+ _ => None,
+ };
+
+ let name = &rule.name;
+ let output_type = &rule.output_type;
+ let expr = make_parser_expr(rule)?;
+
+ entries.push(quote!(
+ #[allow(non_snake_case, dead_code)]
+ fn #name<'a>(
+ (climbers, input): (&HashMap<Rule, PrecClimber<Rule>>, Rc<str>),
+ pair: Pair<'a, Rule>,
+ ) -> ParseResult<#output_type> {
+ #span_def
+ #expr
+ }
+ ))
+ }
+
+ Ok(quote!(
+ struct Parsers;
+ impl Parsers {
+ #(#entries)*
+ }
+ ))
+}
+
+pub fn make_parser(
+ input: proc_macro::TokenStream,
+) -> Result<proc_macro2::TokenStream> {
+ let rules: Rules = syn::parse_macro_input::parse(input.clone())?;
+
+ let construct_precclimbers = make_construct_precclimbers(&rules)?;
+ let entrypoints = make_entrypoints(&rules)?;
+ let parsers = make_parsers(&rules)?;
+
+ Ok(quote!(
+ #construct_precclimbers
+ #entrypoints
+ #parsers
+ ))
+}
diff --git a/dhall_syntax/Cargo.toml b/dhall_syntax/Cargo.toml
index 1da10c7..62ecced 100644
--- a/dhall_syntax/Cargo.toml
+++ b/dhall_syntax/Cargo.toml
@@ -16,3 +16,4 @@ either = "1.5.2"
take_mut = "0.2.2"
hex = "0.3.2"
dhall_generated_parser = { path = "../dhall_generated_parser" }
+dhall_proc_macros = { path = "../dhall_proc_macros" }
diff --git a/dhall_syntax/src/parser.rs b/dhall_syntax/src/parser.rs
index 53fd68a..4fd6f57 100644
--- a/dhall_syntax/src/parser.rs
+++ b/dhall_syntax/src/parser.rs
@@ -189,203 +189,6 @@ macro_rules! parse_children {
};
}
-macro_rules! make_parser {
- (@children_pattern,
- $varpat:ident,
- ($($acc:tt)*),
- [$variant:ident ($x:pat), $($rest:tt)*]
- ) => (
- make_parser!(@children_pattern,
- $varpat,
- ($($acc)* , Rule::$variant),
- [$($rest)*]
- )
- );
- (@children_pattern,
- $varpat:ident,
- ($($acc:tt)*),
- [$variant:ident ($x:ident).., $($rest:tt)*]
- ) => (
- make_parser!(@children_pattern,
- $varpat,
- ($($acc)* , $varpat..),
- [$($rest)*]
- )
- );
- (@children_pattern,
- $varpat:ident,
- (, $($acc:tt)*), [$(,)*]
- ) => ([$($acc)*]);
- (@children_pattern,
- $varpat:ident,
- ($($acc:tt)*), [$(,)*]
- ) => ([$($acc)*]);
-
- (@children_filter,
- $varpat:ident,
- [$variant:ident ($x:pat), $($rest:tt)*]
- ) => (
- make_parser!(@children_filter, $varpat, [$($rest)*])
- );
- (@children_filter,
- $varpat:ident,
- [$variant:ident ($x:ident).., $($rest:tt)*]
- ) => (
- $varpat.iter().all(|r| r == &Rule::$variant) &&
- make_parser!(@children_filter, $varpat, [$($rest)*])
- );
- (@children_filter, $varpat:ident, [$(,)*]) => (true);
-
- (@body,
- ($climbers:expr, $input:expr, $pair:expr),
- rule!(
- $name:ident<$o:ty>;
- $span:ident;
- captured_str!($x:pat) => $body:expr
- )
- ) => ({
- let $span = Span::make($input.clone(), $pair.as_span());
- let $x = $pair.as_str();
- let res: Result<_, String> = try { $body };
- res.map_err(|msg| custom_parse_error(&$pair, msg))
- });
- (@body,
- ($climbers:expr, $input:expr, $pair:expr),
- rule!(
- $name:ident<$o:ty>;
- $span:ident;
- children!( $( [$($args:tt)*] => $body:expr ),* $(,)* )
- )
- ) => ({
- let children_rules: Vec<Rule> = $pair
- .clone()
- .into_inner()
- .map(|p| p.as_rule())
- .collect();
-
- let $span = Span::make($input.clone(), $pair.as_span());
- #[allow(unused_mut)]
- let mut iter = $pair.clone().into_inner();
-
- #[allow(unreachable_code)]
- match children_rules.as_slice() {
- $(
- make_parser!(@children_pattern, x, (), [$($args)*,])
- if make_parser!(@children_filter, x, [$($args)*,])
- => {
- parse_children!(($climbers, $input.clone()), iter;
- [$($args)*] => {
- let res: Result<_, String> = try { $body };
- res.map_err(|msg| custom_parse_error(&$pair, msg))
- }
- )
- }
- ,
- )*
- [..] => Err(custom_parse_error(
- &$pair,
- format!("Unexpected children: {:?}", children_rules)
- )),
- }
- });
- (@body,
- ($climbers:expr, $input:expr, $pair:expr),
- rule!(
- $name:ident<$o:ty>;
- prec_climb!(
- $other_rule:ident,
- $_climber:expr,
- $args:pat => $body:expr $(,)*
- )
- )
- ) => ({
- let climber = $climbers.get(&Rule::$name).unwrap();
- climber.climb(
- $pair.clone().into_inner(),
- |p| Parsers::$other_rule(($climbers, $input.clone()), p),
- |l, op, r| {
- let $args = (l?, op, r?);
- let res: Result<_, String> = try { $body };
- res.map_err(|msg| custom_parse_error(&$pair, msg))
- },
- )
- });
- (@body,
- ($($things:tt)*),
- rule!(
- $name:ident<$o:ty>;
- $($args:tt)*
- )
- ) => ({
- make_parser!(@body,
- ($($things)*),
- rule!(
- $name<$o>;
- _span;
- $($args)*
- )
- )
- });
- (@body,
- ($($things:tt)*),
- rule!($name:ident<$o:ty>)
- ) => ({
- Ok(())
- });
-
- (@construct_climber,
- ($map:expr),
- rule!(
- $name:ident<$o:ty>;
- prec_climb!($other_rule:ident, $climber:expr, $($_rest:tt)* )
- )
- ) => ({
- $map.insert(Rule::$name, $climber)
- });
- (@construct_climber, ($($things:tt)*), $($args:tt)*) => (());
-
- ($( $submac:ident!( $name:ident<$o:ty> $($args:tt)* ); )*) => (
- struct Parsers;
-
- impl Parsers {
- $(
- #[allow(non_snake_case, unused_variables, clippy::let_unit_value)]
- fn $name<'a>(
- (climbers, input): (&HashMap<Rule, PrecClimber<Rule>>, Rc<str>),
- pair: Pair<'a, Rule>,
- ) -> ParseResult<$o> {
- make_parser!(@body, (climbers, input, pair),
- $submac!( $name<$o> $($args)* ))
- }
- )*
- }
-
- fn construct_precclimbers() -> HashMap<Rule, PrecClimber<Rule>> {
- let mut map = HashMap::new();
- $(
- make_parser!(@construct_climber, (map),
- $submac!( $name<$o> $($args)* ));
- )*
- map
- }
-
- struct EntryPoint;
-
- impl EntryPoint {
- $(
- #[allow(non_snake_case, dead_code)]
- fn $name<'a>(
- input: Rc<str>,
- pair: Pair<'a, Rule>,
- ) -> ParseResult<$o> {
- let climbers = construct_precclimbers();
- Parsers::$name((&climbers, input), pair)
- }
- )*
- }
- );
-}
-
// Trim the shared indent off of a vec of lines, as defined by the Dhall semantics of multiline
// literals.
fn trim_indent(lines: &mut Vec<ParsedText>) {
@@ -427,7 +230,7 @@ fn trim_indent(lines: &mut Vec<ParsedText>) {
}
}
-make_parser! {
+dhall_proc_macros::make_parser! {
rule!(EOI<()>);
rule!(simple_label<Label>;
@@ -629,9 +432,9 @@ make_parser! {
rule!(double_literal<core::Double>; children!(
[numeric_double_literal(n)] => n,
- [minus_infinity_literal(n)] => std::f64::NEG_INFINITY.into(),
- [plus_infinity_literal(n)] => std::f64::INFINITY.into(),
- [NaN(n)] => std::f64::NAN.into(),
+ [minus_infinity_literal(_)] => std::f64::NEG_INFINITY.into(),
+ [plus_infinity_literal(_)] => std::f64::INFINITY.into(),
+ [NaN(_)] => std::f64::NAN.into(),
));
rule!(natural_literal<core::Natural>;
@@ -977,7 +780,7 @@ make_parser! {
[import_expression(e)] => e,
));
- rule!(import_expression<ParsedExpr>; span;
+ rule!(import_expression<ParsedExpr>;
children!(
[selector_expression(e)] => e,
[import(e)] => e,
@@ -996,7 +799,7 @@ make_parser! {
rule!(selector<Either<Label, DupTreeSet<Label>>>; children!(
[label(l)] => Either::Left(l),
[labels(ls)] => Either::Right(ls),
- [expression(e)] => unimplemented!("selection by expression"), // TODO
+ [expression(_e)] => unimplemented!("selection by expression"), // TODO
));
rule!(labels<DupTreeSet<Label>>; children!(