1#![warn(clippy::pedantic, clippy::nursery, clippy::cargo)]
2#![deny(
3 clippy::use_self,
4 rust_2018_idioms,
5 missing_debug_implementations,
6 clippy::missing_panics_doc
7)]
8#![doc = include_str!("./../README.md")]
9use core::fmt;
10use std::collections::HashSet;
11
12use crate::custom::DotDotPlus;
13use custom::id;
14use proc_macro::TokenStream;
15use quote::{ToTokens, TokenStreamExt, quote};
16use rand::random;
17use syn::{
18 Ident, Token, ext::IdentExt, parenthesized, parse::Parse, parse_macro_input, spanned::Spanned,
19};
20
21#[derive(Clone, Debug, PartialEq)]
22struct MatchStruct {
23 binders: HashSet<Ident>,
24}
25mod custom {
26 use syn::{custom_keyword, custom_punctuation};
27
28 custom_punctuation!(DotDotPlus, ..+);
29 custom_keyword!(id);
30}
31struct NameAsSExpr(Ident, SExpr);
32impl Parse for NameAsSExpr {
33 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
34 let ident = Ident::parse_any(input)?;
35 input.parse::<Token![as]>()?;
36 let sexpr = input.parse()?;
37 Ok(Self(ident, sexpr))
38 }
39}
40#[derive(Debug, PartialEq, Clone)]
41enum SExpr {
42 Many(Box<Self>, MatchStruct),
43 ManyOne(Box<Self>, MatchStruct),
44 Symbol(Ident),
45 Identifier(Ident),
47 Empty,
48 Pair {
49 car: Box<Self>,
50 cdr: Box<Self>,
51 binders: MatchStruct,
52 },
53}
54impl fmt::Display for SExpr {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 match self {
57 Self::Many(sexpr, _) => write!(f, "{sexpr} ..."),
58 Self::ManyOne(sexpr, _) => write!(f, "{sexpr} ..+"),
59 Self::Symbol(ident) => write!(f, "{ident}"),
60 Self::Identifier(ident) => write!(f, "{ident}:id"),
61 Self::Empty => write!(f, "()"),
62 Self::Pair {
63 car,
64 cdr,
65 binders: _,
66 } => {
67 write!(f, "({car}")?;
68 let mut second: Self = *(cdr.clone());
69 while let Self::Pair {
70 car,
71 cdr,
72 binders: _,
73 } = second
74 {
75 write!(f, " {car}")?;
76 second = *cdr;
77 }
78 if second != Self::Empty {
79 write!(f, " . {second}")?;
80 }
81 write!(f, ")")
82 }
83 }
84 }
85}
86impl SExpr {
87 fn binders(&self) -> MatchStruct {
88 match self {
89 Self::Many(_, match_struct) | Self::ManyOne(_, match_struct) => match_struct.clone(),
90 Self::Symbol(ident) | Self::Identifier(ident) => MatchStruct {
91 binders: HashSet::from([ident.clone()]),
92 },
93 Self::Empty => MatchStruct {
94 binders: HashSet::new(),
95 },
96 Self::Pair {
97 car: _,
98 cdr: _,
99 binders,
100 } => binders.clone(),
101 }
102 }
103}
104impl Parse for SExpr {
105 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
106 let sexpr = if input.peek(Ident::peek_any) {
107 let ident = Ident::parse_any(input)?;
108 if input.peek(Token![:]) {
109 input.parse::<Token![:]>()?;
110 if input.peek(id) {
111 input.parse::<id>()?;
112 let ident = Ident::new(&(ident.to_string() + "_id"), ident.span());
113 Self::Identifier(ident)
114 } else {
115 return Err(input.error("unkown syntax expected `id` after `:`"));
116 }
117 } else if ident == "id" {
118 Self::Identifier(ident)
119 } else {
120 Self::Symbol(ident)
121 }
122 } else {
123 let paren_input;
124 parenthesized!(paren_input in input);
125 parse_paren(&paren_input)?
126 };
127 if input.peek(Token![...]) {
128 input.parse::<Token![...]>()?;
129
130 let binders = sexpr.binders();
131 Ok(Self::Many(Box::new(sexpr), binders))
132 } else if input.peek(DotDotPlus) {
133 input.parse::<DotDotPlus>()?;
134 let binders = sexpr.binders();
135 Ok(Self::ManyOne(Box::new(sexpr), binders))
136 } else {
137 Ok(sexpr)
138 }
139 }
140}
141
142fn parse_paren(input: &syn::parse::ParseBuffer<'_>) -> syn::Result<SExpr> {
143 if input.is_empty() {
144 Ok(SExpr::Empty)
145 } else {
146 let current = input
147 .parse::<SExpr>()
148 .map_err(|_| input.error("unterminated sexpr pair"))?;
149 let mut current_binders = current.binders();
150 if input.peek(Token![.]) && !(input.peek(Token![...]) || input.peek(DotDotPlus)) {
151 input.parse::<Token![.]>()?;
152 let end = input.parse::<SExpr>().map_err(|_| {
153 input.error("expected expression after improper list dots".to_string())
154 })?;
155 if input.is_empty() {
156 check_duplicates(input, &mut current_binders, &end)?;
157 Ok(SExpr::Pair {
158 car: Box::new(current),
159 cdr: Box::new(end),
160 binders: current_binders,
161 })
162 } else {
163 Err(input.error("expected nothing after last expression in improper list"))
164 }
165 } else if input.is_empty() && matches!(current, |SExpr::Many(_, _)| SExpr::ManyOne(_, _)) {
166 Ok(current)
167 } else {
168 let next = parse_paren(input)?;
169 check_duplicates(input, &mut current_binders, &next)?;
170 Ok(SExpr::Pair {
171 car: Box::new(current),
172 cdr: Box::new(next),
173 binders: current_binders,
174 })
175 }
176 }
177}
178
179fn check_duplicates(
180 input: &syn::parse::ParseBuffer<'_>,
181 current_binders: &mut MatchStruct,
182 next: &SExpr,
183) -> Result<(), syn::Error> {
184 next.binders().binders.into_iter().try_for_each(|binder| {
185 let message = format!("duplicate binder {binder}");
186 if current_binders.binders.insert(binder) {
187 Ok(())
188 } else {
189 Err(input.error(message))
190 }
191 })
192}
193
194impl ToTokens for SExpr {
195 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
196 match self {
197 Self::Many(sexpr, match_struct) => {
198 let binders = match_struct.binders.clone().into_iter();
199 let binders1 = match_struct.binders.clone().into_iter();
200 let token = quote! {
201 let res = s.fold_to_syntax_list::<Self, String>(
202 &mut |s, mut current| {
203 let mut this = Self::default();
204 #sexpr;
205 #( current.#binders = crate::ast::Ast::Pair(Box::new(crate::ast::Pair(this.#binders, current.#binders))); )*
206 Ok(current)
207 },
208 Self::default()
209 )?;
210
211 #( this.#binders1 = res.#binders1; )*
212 };
213 tokens.append_all(token);
214 }
215 Self::ManyOne(sexpr, match_struct) => {
216 let sexpr_string = format!("{sexpr}");
217 let binders = match_struct.binders.clone().into_iter();
218 let binders1 = match_struct.binders.clone().into_iter();
219 let token = quote! {
220 let sexpr = #sexpr_string;
221 let error = format!("expected at least one of {sexpr} {s}");
222 let res = s.fold_to_syntax_list::<(usize, Self), String>(
223 &mut |s, (i, mut current)| {
224 let next_i = if s == crate::ast::Ast::TheEmptyList { 0 } else { 1 } + i;
225 let mut this = Self::default();
226 #sexpr;
227 #( current.#binders = crate::ast::Ast::Pair(Box::new(Pair(this.#binders, current.#binders))); )*
228 Ok((next_i, current))
229 },
230 (0, Self::default())
231 )?;
232
233 if res.0 == 0 {
234 return Err(error)
235 }
236 #( this.#binders1 = res.1.#binders1; )*
237 };
238 tokens.append_all(token);
239 }
240 Self::Symbol(ident) => {
241 let token = quote! {
242 this.#ident = s;
243 };
244 tokens.append_all(token);
245 }
246 Self::Identifier(ident) => {
247 let ident_string = ident.to_string();
248 let token = quote! {
249 let ident = #ident_string;
250 if !s.identifier() {
251 return Err(format!("not an identifier {s} when matching identifier: {ident}"))
252 }
253 this.#ident = s;
254 };
255 tokens.append_all(token);
256 }
257 Self::Empty => {
258 let token = quote! {
259 let s = if let Ast::Syntax(s) = s { s.0 } else { s };
260 if s != crate::ast::Ast::TheEmptyList {
261 return Err(format!("bad syntax expected expected null {s}"))
262 }
263 };
264 tokens.append_all(token);
265 }
266 Self::Pair {
267 car,
268 cdr,
269 binders: _,
270 } => {
271 let this_string = self.to_string();
272 let token = quote! {
273 let s = if let Ast::Syntax(s) = s { s.0 } else { s };
274 let this_string = #this_string;
275 if let crate::ast::Ast::Pair(p) = s {
276 let crate::ast::Pair(car, cdr) = *p;
277 {
278 let s = car;
279 #car
280 }
281 {
282 let s = cdr;
283 #cdr
284 }
285 } else {
286 let this_string = #this_string;
287 return Err(format!("not a pair {s} when matching pair: {this_string}"))
288 }
289 };
290 tokens.append_all(token);
291 }
292 }
293 }
294}
295#[proc_macro]
296pub fn match_syntax_as(input: TokenStream) -> TokenStream {
297 let input = parse_macro_input!(input as NameAsSExpr);
298 let name = input.0;
299 let input = input.1;
300 let binders = input.binders().binders.into_iter();
301 let binders1 = input.binders().binders.into_iter();
302 quote! {
303 #[derive(Clone)]
304 struct #name {
305 #( #binders: crate::ast::Ast, )*
306 }
307 impl Default for #name {
308 fn default() -> Self {
309 Self {
310 #( #binders1: crate::ast::Ast::TheEmptyList, )*
311 }
312 }
313 }
314 impl #name {
315 fn matches(s: Ast) -> Result<Self, String> {
316 let mut this = Self::default();
317 #input
318 Ok(this)
319 }
320 }
321 }
324 .into()
325}
326#[proc_macro]
327pub fn match_syntax(input: TokenStream) -> TokenStream {
328 let input = parse_macro_input!(input as SExpr);
329 let binders = input.binders().binders.into_iter();
330 let binders1 = input.binders().binders.into_iter();
331 let name = syn::Ident::new(&format!("Matcher{}", random::<u64>()), input.span());
332 quote! {
333 {
334 #[derive(Clone)]
335 struct #name {
336 #( #binders: crate::ast::Ast, )*
337 }
338 impl Default for #name {
339 fn default() -> Self {
340 Self {
341 #( #binders1: crate::ast::Ast::TheEmptyList, )*
342 }
343 }
344 }
345 impl #name {
346 fn matches(s: Ast) -> Result<Self, String> {
347 let mut this = Self::default();
348 #input
349 Ok(this)
350 }
351 }
352 (#name::matches)
354 }
355 }
356 .into()
357}