1#![allow(clippy::useless_format, clippy::redundant_static_lifetimes, clippy::get_first)]
2#![cfg_attr(not(test), deny(unused_crate_dependencies))]
3mod tests;
4mod ascent_mir;
5mod utils;
6mod ascent_hir;
7mod scratchpad;
8mod ascent_codegen;
9mod ascent_syntax;
10mod test_errors;
11mod syn_utils;
12
13#[macro_use]
14extern crate quote;
15
16extern crate proc_macro;
17use ascent_syntax::{AscentProgram, desugar_ascent_program, parse_ascent_program};
18use derive_syn_parse::Parse;
19use itertools::Either;
20use proc_macro::TokenStream;
21use proc_macro2::Span;
22use syn::parse::{ParseStream, Parser};
23use syn::spanned::Spanned;
24use syn::{Attribute, Ident, Result, Token, parse_quote, parse_quote_spanned};
25use syn_utils::ResTokenStream2Ext;
26
27use crate::ascent_codegen::compile_mir;
28use crate::ascent_hir::compile_ascent_program_to_hir;
29use crate::ascent_mir::compile_hir_to_mir;
30
31#[proc_macro]
55pub fn ascent(input: TokenStream) -> TokenStream {
56 ascent_impl(input.into(), AscentMacroKind { is_ascent_run: false, is_parallel: false }).into_token_stream()
57}
58
59#[proc_macro]
63pub fn ascent_par(input: TokenStream) -> TokenStream {
64 ascent_impl(input.into(), AscentMacroKind { is_ascent_run: false, is_parallel: true }).into_token_stream()
65}
66
67#[proc_macro]
84pub fn ascent_run(input: TokenStream) -> TokenStream {
85 ascent_impl(input.into(), AscentMacroKind { is_ascent_run: true, is_parallel: false }).into_token_stream()
86}
87
88#[proc_macro]
90pub fn ascent_run_par(input: TokenStream) -> TokenStream {
91 ascent_impl(input.into(), AscentMacroKind { is_ascent_run: true, is_parallel: true }).into_token_stream()
92}
93
94#[proc_macro]
150pub fn ascent_source(input: TokenStream) -> TokenStream { ascent_source_impl(input.into()).into_token_stream() }
151
152fn ascent_source_impl(input: proc_macro2::TokenStream) -> Result<proc_macro2::TokenStream> {
153 #[derive(Parse)]
154 struct AscentSourceInput {
155 #[call(Attribute::parse_outer)]
156 attrs: Vec<Attribute>,
157 name: Ident,
158 colon: Token![:],
159 ascent_code: proc_macro2::TokenStream,
160 }
161
162 let AscentSourceInput { attrs, name, colon, ascent_code } = syn::parse2(input.into())?;
163
164 match Parser::parse2(
165 |input: ParseStream| parse_ascent_program(input, parse_quote!(::ascent::ascent_source)),
166 ascent_code.clone(),
167 )? {
168 itertools::Either::Left(_prog) => (),
169 itertools::Either::Right(mut include_source_call) => {
170 let before_tokens = include_source_call.before_tokens;
171 include_source_call.before_tokens = quote! {
172 #(#attrs)*
173 #name #colon
174 #before_tokens
175 };
176 return Err(syn::Error::new(
179 include_source_call.include_node.include_source_kw.span,
180 "`ascent_source`s cannot contain `include_source!`",
181 ))
182 },
183 };
184
185 if let Some(bad_attr) = attrs.iter().find(|attr| attr.path().get_ident().map_or(true, |ident| ident != "doc")) {
186 return Err(syn::Error::new(bad_attr.span(), "unexpected attribute. Only `doc` attribute is allowed"))
187 }
188
189 let macro_name = Ident::new(&format!("ascent_source_{name}"), name.span());
190
191 Ok(quote! {
192 #(#attrs)*
193 #[macro_export]
194 macro_rules! #macro_name {
195 ({$($cb: tt)*}, {$($before: tt)*}, {$($after: tt)*}) => {
196 $($cb)*! {
197 $($before)*
198 #ascent_code
199 $($after)*
200 }
201 }
202 }
203 pub use #macro_name as #name;
204 })
205}
206
207#[derive(Clone, Copy, Default)]
208pub(crate) struct AscentMacroKind {
209 pub is_ascent_run: bool,
210 pub is_parallel: bool,
211}
212
213impl AscentMacroKind {
214 pub fn name(&self) -> &'static str {
215 match (self.is_ascent_run, self.is_parallel) {
216 (false, false) => "ascent",
217 (false, true) => "ascent_par",
218 (true, false) => "ascent_run",
219 (true, true) => "ascent_run_par",
220 }
221 }
222
223 pub fn macro_path(&self, span: Span) -> syn::Path {
224 let name_ident = Ident::new(self.name(), span);
225 parse_quote_spanned! {span=>
226 ::ascent::#name_ident
227 }
228 }
229}
230
231pub(crate) fn ascent_impl(input: proc_macro2::TokenStream, kind: AscentMacroKind) -> Result<proc_macro2::TokenStream> {
232 let AscentMacroKind { is_ascent_run, is_parallel } = kind;
233 let prog = match Parser::parse2(
234 |input: ParseStream| parse_ascent_program(input, kind.macro_path(Span::call_site())),
235 input,
236 )? {
237 Either::Left(prog) => prog,
238 Either::Right(include_source_call) => return Ok(include_source_call.macro_call_output()),
239 };
240
241 let prog = desugar_ascent_program(prog)?;
242
243 let hir = compile_ascent_program_to_hir(&prog, is_parallel)?;
244
245 let mir = compile_hir_to_mir(&hir)?;
246
247 let code = compile_mir(&mir, is_ascent_run);
248
249 Ok(code)
250}