Skip to content

Commit dd3065a

Browse files
committed
Update macro crate to get return types from native rust functions (WIP)
1 parent 8d65a6c commit dd3065a

3 files changed

Lines changed: 94 additions & 10 deletions

File tree

ndc_lib/src/interpreter/function.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ impl FunctionBody {
114114
Self::Memoized { function, .. } => function.arity(),
115115
}
116116
}
117+
117118
pub fn generic(
118119
type_signature: TypeSignature,
119120
return_type: StaticType,
@@ -556,7 +557,8 @@ impl fmt::Display for StaticType {
556557
Self::Sequence => write!(f, "Sequence"),
557558
Self::List => write!(f, "List"),
558559
Self::String => write!(f, "String"),
559-
Self::Tuple(tup) => write!(f, "tuple<{}>", tup.iter().join(", ")),
560+
Self::Tuple(tup) if tup.is_empty() => write!(f, "()"),
561+
Self::Tuple(tup) => write!(f, "Tuple<{}>", tup.iter().join(", ")),
560562
Self::Map => write!(f, "Map"),
561563
Self::Iterator => write!(f, "Iterator"),
562564
Self::MinHeap => write!(f, "MinHeap"),

ndc_macros/src/convert.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub struct Argument {
1212
}
1313
pub trait TypeConverter {
1414
fn matches(&self, ty: &syn::Type) -> bool;
15+
fn static_type(&self) -> TokenStream;
1516
fn convert(
1617
&self,
1718
temp_var: syn::Ident,
@@ -26,14 +27,18 @@ impl TypeConverter for MutRefString {
2627
is_ref_mut(ty) && is_string(ty)
2728
}
2829

30+
fn static_type(&self) -> TokenStream {
31+
quote! { crate::interpreter::function::StaticType::String }
32+
}
33+
2934
fn convert(
3035
&self,
3136
temp_var: syn::Ident,
3237
original_name: &str,
3338
argument_var_name: syn::Ident,
3439
) -> Vec<Argument> {
3540
vec![Argument {
36-
param_type: quote! { crate::interpreter::function::StaticType::String },
41+
param_type: self.static_type(),
3742
param_name: quote! { #original_name },
3843
argument: quote! { #argument_var_name },
3944
initialize_code: quote! {
@@ -53,14 +58,18 @@ impl TypeConverter for InternalMap {
5358
path_ends_with(ty, "MapRepr")
5459
}
5560

61+
fn static_type(&self) -> TokenStream {
62+
quote! { crate::interpreter::function::StaticType::Map }
63+
}
64+
5665
fn convert(
5766
&self,
5867
temp_var: syn::Ident,
5968
original_name: &str,
6069
argument_var_name: syn::Ident,
6170
) -> Vec<Argument> {
6271
vec![Argument {
63-
param_type: quote! { crate::interpreter::function::StaticType::Map },
72+
param_type: self.static_type(),
6473
param_name: quote! { #original_name },
6574
argument: quote! { #argument_var_name },
6675
initialize_code: quote! {
@@ -79,14 +88,18 @@ impl TypeConverter for InternalString {
7988
path_ends_with(ty, "StringRepr")
8089
}
8190

91+
fn static_type(&self) -> TokenStream {
92+
quote! { crate::interpreter::function::StaticType::String }
93+
}
94+
8295
fn convert(
8396
&self,
8497
temp_var: syn::Ident,
8598
original_name: &str,
8699
argument_var_name: syn::Ident,
87100
) -> Vec<Argument> {
88101
vec![Argument {
89-
param_type: quote! { crate::interpreter::function::StaticType::String },
102+
param_type: self.static_type(),
90103
param_name: quote! { #original_name },
91104
argument: quote! { #argument_var_name },
92105
initialize_code: quote! {
@@ -106,14 +119,18 @@ impl TypeConverter for InternalList {
106119
path_ends_with(ty, "ListRepr")
107120
}
108121

122+
fn static_type(&self) -> TokenStream {
123+
quote! { crate::interpreter::function::StaticType::List }
124+
}
125+
109126
fn convert(
110127
&self,
111128
temp_var: syn::Ident,
112129
original_name: &str,
113130
argument_var_name: syn::Ident,
114131
) -> Vec<Argument> {
115132
vec![Argument {
116-
param_type: quote! { crate::interpreter::function::StaticType::List },
133+
param_type: self.static_type(),
117134
param_name: quote! { #original_name },
118135
argument: quote! { #argument_var_name },
119136
initialize_code: quote! {

ndc_macros/src/function.rs

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use itertools::Itertools;
77
use proc_macro2::TokenStream;
88
use quote::{format_ident, quote};
99
use std::fmt::Write;
10+
use syn::spanned::Spanned;
1011

1112
pub struct WrappedFunction {
1213
pub function_declaration: TokenStream,
@@ -20,7 +21,7 @@ pub fn wrap_function(function: &syn::ItemFn) -> Vec<WrappedFunction> {
2021
&original_identifier.to_string(),
2122
)];
2223

23-
let mut docs_buf = String::new();
24+
let mut documentation_buffer = String::new();
2425
for attr in &function.attrs {
2526
if attr.path().is_ident("function") {
2627
attr.parse_nested_meta(|meta| {
@@ -42,7 +43,8 @@ pub fn wrap_function(function: &syn::ItemFn) -> Vec<WrappedFunction> {
4243
&& let syn::Expr::Lit(expr) = &meta.value
4344
&& let syn::Lit::Str(lit_str) = &expr.lit
4445
{
45-
writeln!(docs_buf, "{}", lit_str.value().trim()).expect("failed to write docs");
46+
writeln!(documentation_buffer, "{}", lit_str.value().trim())
47+
.expect("failed to write docs");
4648
}
4749
}
4850

@@ -54,7 +56,7 @@ pub fn wrap_function(function: &syn::ItemFn) -> Vec<WrappedFunction> {
5456
}
5557

5658
// TODO: CONTINUE HERE
57-
let return_type = quote! { crate::interpreter::function::StaticType::Any }; // RIP ROP RAP
59+
let return_type = map_return_type(&function.sig.output);
5860

5961
// If the function has no argument then the cartesian product stuff below doesn't work
6062
if function.sig.inputs.is_empty() {
@@ -67,7 +69,7 @@ pub fn wrap_function(function: &syn::ItemFn) -> Vec<WrappedFunction> {
6769
function_name,
6870
vec![],
6971
return_type.clone(),
70-
&docs_buf,
72+
&documentation_buffer,
7173
)
7274
})
7375
.collect();
@@ -102,7 +104,7 @@ pub fn wrap_function(function: &syn::ItemFn) -> Vec<WrappedFunction> {
102104
function_name,
103105
args,
104106
return_type.clone(),
105-
&docs_buf,
107+
&documentation_buffer,
106108
);
107109
variation_id += 1;
108110
wrapped
@@ -112,6 +114,69 @@ pub fn wrap_function(function: &syn::ItemFn) -> Vec<WrappedFunction> {
112114
.collect()
113115
}
114116

117+
fn map_return_type(output: &syn::ReturnType) -> TokenStream {
118+
let convert = build();
119+
match output {
120+
syn::ReturnType::Default => {
121+
// in case return type is not specified (for closures rust defaults to type inference which doesn't help us here)
122+
quote! { crate::interpreter::function::StaticType::Tuple(vec![]) }
123+
}
124+
syn::ReturnType::Type(_, ty) => map_type(ty),
125+
}
126+
}
127+
128+
fn map_type(ty: &syn::Type) -> TokenStream {
129+
match ty {
130+
syn::Type::Path(p) => map_type_path(p),
131+
syn::Type::Reference(r) => map_type(r.elem.as_ref()),
132+
// add more variants when needed
133+
_ => quote::quote! { crate::interpreter::function::StaticType::Any },
134+
}
135+
}
136+
137+
fn map_type_path(p: &syn::TypePath) -> TokenStream {
138+
let segment = p.path.segments.last().unwrap();
139+
140+
match segment.ident.to_string().as_str() {
141+
// Primitive single identifiers
142+
"i32" | "i64" | "isize" | "u32" | "u64" => {
143+
quote::quote! { crate::interpreter::function::StaticType::Int }
144+
}
145+
"f32" | "f64" => {
146+
quote::quote! { crate::interpreter::function::StaticType::Float }
147+
}
148+
"bool" => {
149+
quote::quote! { crate::interpreter::function::StaticType::Bool }
150+
}
151+
"String" | "str" => {
152+
quote::quote! { crate::interpreter::function::StaticType::String }
153+
}
154+
155+
// Generic wrappers like anyhow::Result<T>, std::result::Result<T>
156+
// "HashMap" =>
157+
"Result" => match &segment.arguments {
158+
syn::PathArguments::AngleBracketed(args) => {
159+
// Extract the first generic argument
160+
if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
161+
// Recurse on T
162+
map_type(inner_ty)
163+
} else {
164+
panic!("Result without generic arguments");
165+
}
166+
}
167+
168+
_ => {
169+
panic!("Result return type found without syn::PathArguments::AngleBracketed");
170+
}
171+
},
172+
173+
// Fallback
174+
_ => {
175+
quote::quote! { crate::interpreter::function::StaticType::Any }
176+
}
177+
}
178+
}
179+
115180
/// Wraps an original rust function `function` in an outer function with the identifier `identifier`
116181
/// It's registered with the environment as `register_as_function_name`
117182
/// The argument translations mapping is defined by `input_arguments`

0 commit comments

Comments
 (0)