Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 5 additions & 6 deletions engine/utils/ocaml_of_json_schema/ocaml_of_json_schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,7 @@ let variantNameOf = s => {
return v + "'";
return v;
};
let typeNameOf = s => s.replace(/[A-Z]/g, (l, i) => `${i ? '_' : ''}${l.toLowerCase()}`);
let fieldNameOf = s => {
let escapeOCamlKeywords = s => {
let ocaml_keywords = ["and", "as", "assert", "asr", "begin", "class", "constraint",
"do", "done", "downto", "else", "end", "exception", "external",
"false", "for", "fun", "function", "functor", "if", "in",
Expand All @@ -85,10 +84,10 @@ let fieldNameOf = s => {
"private", "rec", "sig", "struct", "then", "to", "true", "try",
"type", "val", "virtual", "when", "while", "with"
];
if (ocaml_keywords.includes(s))
return s + "'";
return s;
};
return ocaml_keywords.includes(s) ? s + "'" : s;
}
let typeNameOf = s => escapeOCamlKeywords(s.replace(/[A-Z]/g, (l, i) => `${i ? '_' : ''}${l.toLowerCase()}`));
let fieldNameOf = s => escapeOCamlKeywords(s);

let ensureUnique = (() => {
let cache = {};
Expand Down
2 changes: 2 additions & 0 deletions rust-engine/Bacon.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[jobs.clippy]
command = ["cargo", "clippy", "--no-deps"]
1 change: 1 addition & 0 deletions rust-engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ serde_stacker = "0.1.12"
pretty = "0.12"
itertools.workspace = true
derive_generic_visitor = { git = "https://github.com/W95Psp/derive-generic-visitor.git", branch = "visitable-group-add-attrs" }
pastey = "0.1.0"
65 changes: 64 additions & 1 deletion rust-engine/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ use proc_macro2::{Group, Ident, Span};
use quote::{ToTokens, quote};
use syn::{
Field, FieldsUnnamed, Token, parse_macro_input, parse_quote, punctuated::Punctuated,
token::Paren,
token::Paren, visit_mut::VisitMut,
};
use utils::*;

mod partial_application;
mod replace;

mod utils {
Expand Down Expand Up @@ -172,6 +173,68 @@ pub fn setup_error_handling_struct(_attr: TokenStream, item: TokenStream) -> Tok

#[proc_macro_attribute]
/// Replaces all occurrences of an identifier within the attached item.
///
/// For example, `#[replace(Name => A, B, C)]` will replace `Name` by `A, B, C`
/// in the item the proc-macro is applied on.
///
/// The special case `#[replace(Name => include(VisitableAstNodes))]` will
/// expand to a list of visitable AST nodes. This is useful in practice, as this
/// list is often repeated.
pub fn replace(attr: TokenStream, item: TokenStream) -> TokenStream {
replace::replace(attr, item)
}

/// An attribute procedural macro that creates a new `macro_rules!` definition
/// by partially applying an existing macro or function with a given token stream.
///
/// Usage:
/// ```rust,ignore
/// #[partial_apply(original_macro!, my_expression,)]
/// macro_rules! new_proxy_macro {
/// // This content is ignored and replaced by the proc macro.
/// }
/// ```
#[proc_macro_attribute]
pub fn partial_apply(attr: TokenStream, item: TokenStream) -> TokenStream {
partial_application::partial_apply(attr, item)
}

/// Prepend the body any associated function with the given attribute payload.
/// ```rust,ignore
/// #[prepend_associated_functions_with(println!("self is {self}");)]
/// impl Foo {
/// fn f(self) {}
/// }
/// ```
///
/// Expands to:
/// ```rust,ignore
/// impl Foo {
/// fn f(self) {
/// println!("self is {self}");
/// }
/// }
/// ```
#[proc_macro_attribute]
pub fn prepend_associated_functions_with(attr: TokenStream, item: TokenStream) -> TokenStream {
struct Visitor {
prefix: syn::Expr,
}
impl VisitMut for Visitor {
fn visit_item_impl_mut(&mut self, impl_block: &mut syn::ItemImpl) {
for item in &mut impl_block.items {
let syn::ImplItem::Fn(impl_item_fn) = item else {
continue;
};
impl_item_fn.block.stmts.insert(
0,
syn::Stmt::Expr(self.prefix.clone(), Some(Token![;](Span::mixed_site()))),
);
}
}
}
let mut item: syn::Item = parse_macro_input!(item);
let prefix = parse_macro_input!(attr);
Visitor { prefix }.visit_item_mut(&mut item);
quote! {#item}.into()
}
43 changes: 43 additions & 0 deletions rust-engine/macros/src/partial_application.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{ExprPath, Token, parse_macro_input};

struct PartialApplyArgs {
ident: ExprPath,
bang: Option<Token![!]>,
prefix: proc_macro2::TokenStream,
}

impl syn::parse::Parse for PartialApplyArgs {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let ident = input.parse()?;
let bang = input.parse()?;
input.parse::<syn::Token![,]>()?;
Ok(PartialApplyArgs {
ident,
bang,
prefix: input.parse()?,
})
}
}

/// See [`super::partial_apply`].
pub(crate) fn partial_apply(attr: TokenStream, item: TokenStream) -> TokenStream {
let PartialApplyArgs {
ident,
bang,
prefix,
} = parse_macro_input!(attr as PartialApplyArgs);
let input_macro = parse_macro_input!(item as syn::ItemMacro);
let macro_name = input_macro.ident;
let attrs = input_macro.attrs;
quote! {
#(#attrs)*
macro_rules! #macro_name {
($($rest:tt)*) => {
#ident #bang(#prefix $($rest)*)
};
}
}
.into()
}
39 changes: 38 additions & 1 deletion rust-engine/macros/src/replace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ extern crate proc_macro;

use proc_macro::TokenStream;
use proc_macro2::{Group, TokenStream as TokenStream2, TokenTree};
use quote::quote;
use syn::parse::{Parse, ParseStream, Result};
use syn::{Ident, Token, parse_macro_input};

mod kw {
syn::custom_keyword!(include);
}

fn replace_in_stream(
stream: TokenStream2,
target: &Ident,
Expand Down Expand Up @@ -37,9 +42,41 @@ impl Parse for AttributeArgs {
fn parse(input: ParseStream) -> Result<Self> {
let target: Ident = input.parse()?;
input.parse::<Token![=>]>()?;
let include_clause = |input: ParseStream| -> Result<Ident> {
input.parse::<kw::include>()?;
let content;
syn::parenthesized!(content in input);
content.parse()
}(input)
.ok();
Ok(AttributeArgs {
target,
replacement: input.parse::<TokenStream2>()?,
replacement: match include_clause {
Some(clause) => match clause.to_string().as_str() {
"VisitableAstNodes" => quote! {
Expr, Pat, ExprKind, PatKind, Ty, TyKind, Metadata, Literal,
LocalId, Lhs, Symbol, LoopKind, SafetyKind, Quote,
SpannedTy, BindingMode, PrimitiveTy, Region, ImplExpr,
IntKind, FloatKind, GenericValue, Arm, LoopState, ControlFlowKind,
DynTraitGoal, Attribute, QuoteContent, BorrowKind,
TraitGoal, ImplExprKind, IntSize, Signedness, Guard, AttributeKind,
GuardKind, ImplItem, ImplItemKind, TraitItem, TraitItemKind,
ItemQuoteOrigin, ItemQuoteOriginKind, ItemQuoteOriginPosition, GenericParamKind, ImplIdent,
ProjectionPredicate, GenericParam, Generics, DocCommentKind, Param, Variant, ItemKind, Item,
GenericConstraint, ErrorNode, Module,

ResugaredExprKind, ResugaredTyKind, ResugaredPatKind,
ResugaredImplItemKind, ResugaredTraitItemKind, ResugaredItemKind
}.into(),
_ => {
return Err(syn::Error::new_spanned(
clause,
format!("This is not a recognized include pragma."),
));
}
},
None => input.parse::<TokenStream2>()?,
},
})
}
}
Expand Down
Loading
Loading