difftreelog
feat lazy values in builtin
in: master
8 files changed
crates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -1,4 +1,4 @@
-use crate::function::StaticBuiltin;
+use crate::function::{CallLocation, StaticBuiltin};
use crate::typed::{Any, PositiveF64, VecVal, M1};
use crate::{
builtin::manifest::{manifest_yaml_ex, ManifestYamlOptions},
@@ -12,7 +12,6 @@
use crate::{Either, ObjValue};
use format::{format_arr, format_obj};
use jrsonnet_interner::IStr;
-use jrsonnet_parser::ExprLocation;
use serde::Deserialize;
use serde_yaml::DeserializingQuirks;
use std::collections::HashMap;
@@ -29,7 +28,7 @@
pub fn std_format(str: IStr, vals: Val) -> Result<String> {
push_frame(
- None,
+ CallLocation::native(),
|| format!("std.format of {}", str),
|| {
Ok(match vals {
@@ -467,9 +466,9 @@
}
#[jrsonnet_macros::builtin]
-fn builtin_trace(#[location] loc: Option<&ExprLocation>, str: IStr, rest: Any) -> Result<Any> {
+fn builtin_trace(loc: CallLocation, str: IStr, rest: Any) -> Result<Any> {
eprint!("TRACE:");
- if let Some(loc) = loc {
+ if let Some(loc) = loc.0 {
with_state(|s| {
let locs = s.map_source_locations(&loc.0, &[loc.1]);
eprint!(
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -4,6 +4,7 @@
builtin::{std_slice, BUILTINS},
error::Error::*,
evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},
+ function::CallLocation,
gc::TraceBox,
push_frame, throw, with_state, ArrValue, Bindable, Context, ContextCreator, FuncDesc, FuncVal,
FutureWrapper, GcHashMap, LazyBinding, LazyVal, LazyValValue, ObjValue, ObjValueBuilder,
@@ -12,8 +13,8 @@
use gcmodule::{Cc, Trace};
use jrsonnet_interner::IStr;
use jrsonnet_parser::{
- ArgsDesc, AssertStmt, BindSpec, CompSpec, Expr, ExprLocation, FieldMember, ForSpecData,
- IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc,
+ ArgsDesc, AssertStmt, BindSpec, CompSpec, Expr, FieldMember, ForSpecData, IfSpecData,
+ LiteralType, LocExpr, Member, ObjBody, ParamsDesc,
};
use jrsonnet_types::ValType;
pub mod operator;
@@ -192,7 +193,7 @@
Ok(match field_name {
jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),
jrsonnet_parser::FieldName::Dyn(expr) => push_frame(
- Some(&expr.1),
+ CallLocation::new(&expr.1),
|| "evaluating field name".to_string(),
|| {
let value = evaluate(context, expr)?;
@@ -442,7 +443,7 @@
context: Context,
value: &LocExpr,
args: &ArgsDesc,
- loc: Option<&ExprLocation>,
+ loc: CallLocation,
tailstrict: bool,
) -> Result<Val> {
let value = evaluate(context.clone(), value)?;
@@ -463,13 +464,13 @@
let value = &assertion.0;
let msg = &assertion.1;
let assertion_result = push_frame(
- Some(&value.1),
+ CallLocation::new(&value.1),
|| "assertion condition".to_owned(),
|| bool::try_from(evaluate(context.clone(), value)?),
)?;
if !assertion_result {
push_frame(
- Some(&value.1),
+ CallLocation::new(&value.1),
|| "assertion failure".to_owned(),
|| {
if let Some(msg) = msg {
@@ -519,7 +520,7 @@
BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,
UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,
Var(name) => push_frame(
- Some(loc),
+ CallLocation::new(loc),
|| format!("variable <{}> access", name),
|| context.binding(name.clone())?.evaluate(),
)?,
@@ -528,7 +529,7 @@
(Val::Obj(v), Val::Str(s)) => {
let sn = s.clone();
push_frame(
- Some(loc),
+ CallLocation::new(loc),
|| format!("field <{}> access", sn),
|| {
if let Some(v) = v.get(s.clone())? {
@@ -625,7 +626,7 @@
&Val::Obj(evaluate_object(context, t)?),
)?,
Apply(value, args, tailstrict) => {
- evaluate_apply(context, value, args, Some(loc), *tailstrict)?
+ evaluate_apply(context, value, args, CallLocation::new(loc), *tailstrict)?
}
Function(params, body) => {
evaluate_method(context, "anonymous".into(), params.clone(), body.clone())
@@ -640,7 +641,7 @@
evaluate(context, returned)?
}
ErrorStmt(e) => push_frame(
- Some(loc),
+ CallLocation::new(loc),
|| "error statement".to_owned(),
|| throw!(RuntimeError(IStr::try_from(evaluate(context, e)?)?,)),
)?,
@@ -650,7 +651,7 @@
cond_else,
} => {
if push_frame(
- Some(loc),
+ CallLocation::new(loc),
|| "if condition".to_owned(),
|| bool::try_from(evaluate(context.clone(), &cond.0)?),
)? {
@@ -689,7 +690,7 @@
let mut import_location = tmp.to_path_buf();
import_location.pop();
push_frame(
- Some(loc),
+ CallLocation::new(loc),
|| format!("import {:?}", path),
|| with_state(|s| s.import_file(&import_location, path)),
)?
crates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function.rs
+++ b/crates/jrsonnet-evaluator/src/function.rs
@@ -12,6 +12,19 @@
use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};
use std::{borrow::Cow, collections::HashMap, convert::TryFrom};
+#[derive(Clone, Copy)]
+pub struct CallLocation<'l>(pub Option<&'l ExprLocation>);
+impl<'l> CallLocation<'l> {
+ pub fn new(loc: &'l ExprLocation) -> Self {
+ Self(Some(loc))
+ }
+}
+impl CallLocation<'static> {
+ pub fn native() -> Self {
+ Self(None)
+ }
+}
+
#[derive(Trace)]
struct EvaluateLazyVal {
context: Context,
@@ -383,12 +396,7 @@
pub trait Builtin: Trace {
fn name(&self) -> &str;
fn params(&self) -> &[BuiltinParam];
- fn call(
- &self,
- context: Context,
- loc: Option<&ExprLocation>,
- args: &dyn ArgsLike,
- ) -> Result<Val>;
+ fn call(&self, context: Context, loc: CallLocation, args: &dyn ArgsLike) -> Result<Val>;
}
pub trait StaticBuiltin: Builtin + Send + Sync
crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -28,7 +28,7 @@
pub use dynamic::*;
use error::{Error::*, LocError, Result, StackTraceElement};
pub use evaluate::*;
-use function::{Builtin, TlaArg};
+use function::{Builtin, CallLocation, TlaArg};
use gc::{GcHashMap, TraceBox};
use gcmodule::{Cc, Trace, Weak};
pub use import::*;
@@ -173,6 +173,7 @@
/// Global state is fine here.
pub(crate) static EVAL_STATE: RefCell<Option<EvaluationState>> = RefCell::new(None)
}
+
pub(crate) fn with_state<T>(f: impl FnOnce(&EvaluationState) -> T) -> T {
EVAL_STATE.with(|s| {
f(s.borrow().as_ref().expect(
@@ -181,7 +182,7 @@
})
}
pub fn push_frame<T>(
- e: Option<&ExprLocation>,
+ e: CallLocation,
frame_desc: impl FnOnce() -> String,
f: impl FnOnce() -> Result<T>,
) -> Result<T> {
@@ -345,7 +346,7 @@
/// Executes code creating a new stack frame
pub fn push<T>(
&self,
- e: Option<&ExprLocation>,
+ e: CallLocation,
frame_desc: impl FnOnce() -> String,
f: impl FnOnce() -> Result<T>,
) -> Result<T> {
@@ -368,7 +369,7 @@
}
if let Err(mut err) = result {
err.trace_mut().0.push(StackTraceElement {
- location: e.cloned(),
+ location: e.0.cloned(),
desc: frame_desc(),
});
return Err(err);
@@ -512,7 +513,7 @@
|| {
func.evaluate(
self.create_default_context(),
- None,
+ CallLocation::native(),
&self.settings().tla_vars,
true,
)
crates/jrsonnet-evaluator/src/native.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/native.rs
+++ b/crates/jrsonnet-evaluator/src/native.rs
@@ -1,11 +1,10 @@
#![allow(clippy::type_complexity)]
-use crate::function::{parse_builtin_call, ArgsLike, Builtin, BuiltinParam};
+use crate::function::{parse_builtin_call, ArgsLike, Builtin, BuiltinParam, CallLocation};
use crate::gc::TraceBox;
use crate::Context;
use crate::{error::Result, Val};
use gcmodule::Trace;
-use jrsonnet_parser::ExprLocation;
use std::path::Path;
use std::rc::Rc;
@@ -32,18 +31,13 @@
&self.params
}
- fn call(
- &self,
- context: Context,
- loc: Option<&ExprLocation>,
- args: &dyn ArgsLike,
- ) -> Result<Val> {
+ fn call(&self, context: Context, loc: CallLocation, args: &dyn ArgsLike) -> Result<Val> {
let args = parse_builtin_call(context, &self.params, args, true)?;
let mut out_args = Vec::with_capacity(self.params.len());
for p in self.params.iter() {
out_args.push(args[&p.name].evaluate()?);
}
- self.handler.call(loc.map(|l| l.0.clone()), &out_args)
+ self.handler.call(loc.0.map(|l| l.0.clone()), &out_args)
}
}
crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -1,5 +1,6 @@
use std::convert::{TryFrom, TryInto};
+use gcmodule::Cc;
use jrsonnet_interner::IStr;
pub use jrsonnet_macros::Typed;
use jrsonnet_types::{ComplexValType, ValType};
@@ -8,7 +9,7 @@
error::{Error::*, LocError, Result},
throw,
typed::CheckType,
- ArrValue, FuncVal, IndexableVal, ObjValue, ObjValueBuilder, Val,
+ ArrValue, FuncDesc, FuncVal, IndexableVal, ObjValue, ObjValueBuilder, Val,
};
pub trait TypedObj: Typed {
@@ -431,6 +432,30 @@
Ok(Self::Func(value))
}
}
+
+impl Typed for Cc<FuncDesc> {
+ const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);
+}
+impl TryFrom<Val> for Cc<FuncDesc> {
+ type Error = LocError;
+
+ fn try_from(value: Val) -> Result<Self, Self::Error> {
+ <Self as Typed>::TYPE.check(&value)?;
+ match value {
+ Val::Func(FuncVal::Normal(desc)) => Ok(desc.clone()),
+ Val::Func(_) => throw!(RuntimeError("expected normal function, not builtin".into())),
+ _ => unreachable!(),
+ }
+ }
+}
+impl TryFrom<Cc<FuncDesc>> for Val {
+ type Error = LocError;
+
+ fn try_from(value: Cc<FuncDesc>) -> Result<Self, Self::Error> {
+ Ok(Self::Func(FuncVal::Normal(value)))
+ }
+}
+
impl Typed for ObjValue {
const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj);
}
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -6,14 +6,15 @@
error::{Error::*, LocError},
evaluate,
function::{
- parse_default_function_call, parse_function_call, ArgsLike, Builtin, StaticBuiltin,
+ parse_default_function_call, parse_function_call, ArgsLike, Builtin, CallLocation,
+ StaticBuiltin,
},
gc::TraceBox,
throw, Context, ObjValue, Result,
};
use gcmodule::{Cc, Trace};
use jrsonnet_interner::IStr;
-use jrsonnet_parser::{ExprLocation, LocExpr, ParamsDesc};
+use jrsonnet_parser::{LocExpr, ParamsDesc};
use jrsonnet_types::ValType;
use std::{cell::RefCell, fmt::Debug, rc::Rc};
@@ -152,7 +153,7 @@
pub fn evaluate(
&self,
call_ctx: Context,
- loc: Option<&ExprLocation>,
+ loc: CallLocation,
args: &dyn ArgsLike,
tailstrict: bool,
) -> Result<Val> {
@@ -166,7 +167,7 @@
}
}
pub fn evaluate_simple(&self, args: &dyn ArgsLike) -> Result<Val> {
- self.evaluate(Context::default(), None, args, true)
+ self.evaluate(Context::default(), CallLocation::native(), args, true)
}
}
crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth1use proc_macro2::TokenStream;1use proc_macro2::TokenStream;2use quote::{quote, quote_spanned};2use quote::quote;3use syn::{3use syn::{4 parenthesized,4 parenthesized,5 parse::{Parse, ParseStream},5 parse::{Parse, ParseStream},6 parse_macro_input,6 parse_macro_input,7 punctuated::Punctuated,7 punctuated::Punctuated,8 spanned::Spanned,8 spanned::Spanned,9 token::Comma,9 token::Comma,10 Attribute, DeriveInput, Error, FnArg, GenericArgument, Ident, ItemFn, LitStr, Pat, PatType,10 Attribute, DeriveInput, Error, FnArg, GenericArgument, Ident, ItemFn, LitStr, Pat, Path,11 Path, PathArguments, Result, Token, Type,11 PathArguments, Result, ReturnType, Token, Type,12};12};131314fn parse_attr<A: Parse, I>(attrs: &[Attribute], ident: I) -> Result<Option<A>>14fn parse_attr<A: Parse, I>(attrs: &[Attribute], ident: I) -> Result<Option<A>>33 Ok(Some(attr))33 Ok(Some(attr))34}34}353536fn is_location_arg(t: &PatType) -> bool {36fn path_is(path: &Path, needed: &str) -> bool {37 t.attrs.iter().any(|a| a.path.is_ident("location"))37 path.leading_colon.is_none()38 && path.segments.len() >= 139 && path.segments.iter().last().unwrap().ident == needed38}40}4139fn is_self_arg(t: &PatType) -> bool {42fn type_is_path<'ty>(ty: &'ty Type, needed: &str) -> Option<&'ty PathArguments> {40 t.attrs.iter().any(|a| a.path.is_ident("self"))43 match ty {41}4243trait RetainHad<T> {44 fn retain_had(&mut self, h: impl FnMut(&T) -> bool) -> bool;45}46impl<T> RetainHad<T> for Vec<T> {47 fn retain_had(&mut self, h: impl FnMut(&T) -> bool) -> bool {44 Type::Path(path) if path.qself.is_none() && path_is(&path.path, needed) => {48 let before = self.len();45 let args = &path.path.segments.iter().last().unwrap().arguments;49 self.retain(h);50 let after = self.len();46 Some(args)51 before != after52 }47 }48 _ => None,49 }53}50}545155fn extract_type_from_option(ty: &Type) -> Option<&Type> {52fn extract_type_from_option(ty: &Type) -> Result<Option<&Type>> {56 fn path_is_option(path: &Path) -> bool {57 path.leading_colon.is_none()58 && path.segments.len() == 159 && path.segments.iter().next().unwrap().ident == "Option"60 }6162 match ty {63 Type::Path(typepath) if typepath.qself.is_none() && path_is_option(&typepath.path) => {53 Ok(if let Some(args) = type_is_path(ty, "Option") {64 // Get the first segment of the path (there is only one, in fact: "Option"):65 let type_params = &typepath.path.segments.iter().next().unwrap().arguments;66 // It should have only on angle-bracketed param ("<String>"):54 // It should have only on angle-bracketed param ("<String>"):67 let generic_arg = match type_params {55 let generic_arg = match args {68 PathArguments::AngleBracketed(params) => params.args.iter().next().unwrap(),56 PathArguments::AngleBracketed(params) => params.args.iter().next().unwrap(),69 _ => panic!("missing option generic"),57 _ => return Err(Error::new(args.span(), "missing option generic")),70 };58 };71 // This argument must be a type:59 // This argument must be a type:72 match generic_arg {60 match generic_arg {73 GenericArgument::Type(ty) => Some(ty),61 GenericArgument::Type(ty) => Some(ty),74 _ => panic!("option generic should be a type"),62 _ => {63 return Err(Error::new(64 generic_arg.span(),65 "option generic should be a type",66 ))67 }75 }68 }76 }69 } else {77 _ => None,70 None78 }71 })79}72}807381struct Field {74struct Field {10194102struct EmptyAttr;95struct EmptyAttr;103impl Parse for EmptyAttr {96impl Parse for EmptyAttr {104 fn parse(input: ParseStream) -> Result<Self> {97 fn parse(_input: ParseStream) -> Result<Self> {105 Ok(Self)98 Ok(Self)106 }99 }107}100}124 }117 }125}118}119120enum ArgInfo {121 Normal {122 ty: Type,123 is_option: bool,124 name: String,125 // ident: Ident,126 },127 Lazy {128 is_option: bool,129 name: String,130 },131 Location,132 This,133}134135impl ArgInfo {136 fn parse(arg: &FnArg) -> Result<Self> {137 let typed = match arg {138 FnArg::Receiver(_) => unreachable!(),139 FnArg::Typed(a) => a,140 };141 let ident = match &typed.pat as &Pat {142 Pat::Ident(i) => i.ident.clone(),143 _ => {144 return Err(Error::new(145 typed.pat.span(),146 "arg should be plain identifier",147 ))148 }149 };150 let ty = &typed.ty as &Type;151 if type_is_path(&ty, "CallLocation").is_some() {152 return Ok(Self::Location);153 } else if type_is_path(&ty, "Self").is_some() {154 return Ok(Self::This);155 } else if type_is_path(&ty, "LazyVal").is_some() {156 return Ok(Self::Lazy {157 is_option: false,158 name: ident.to_string(),159 });160 }161162 let (is_option, ty) = if let Some(ty) = extract_type_from_option(&ty)? {163 if type_is_path(&ty, "LazyVal").is_some() {164 return Ok(Self::Lazy {165 is_option: true,166 name: ident.to_string(),167 });168 }169170 (true, ty.clone())171 } else {172 (false, ty.clone())173 };174175 Ok(Self::Normal {176 ty,177 is_option,178 name: ident.to_string(),179 // ident,180 })181 }182}126183127#[proc_macro_attribute]184#[proc_macro_attribute]128pub fn builtin(185pub fn builtin(129 attr: proc_macro::TokenStream,186 attr: proc_macro::TokenStream,130 item: proc_macro::TokenStream,187 item: proc_macro::TokenStream,131) -> proc_macro::TokenStream {188) -> proc_macro::TokenStream {132 let attrs = parse_macro_input!(attr as BuiltinAttrs);189 let attr = parse_macro_input!(attr as BuiltinAttrs);190 let item: ItemFn = parse_macro_input!(item);191192 match builtin_inner(attr, item) {193 Ok(v) => v.into(),194 Err(e) => e.into_compile_error().into(),195 }196}197133 let mut fun: ItemFn = parse_macro_input!(item);198fn builtin_inner(attr: BuiltinAttrs, fun: ItemFn) -> syn::Result<TokenStream> {134135 let result = match fun.sig.output {199 let result = match fun.sig.output {136 syn::ReturnType::Default => {200 ReturnType::Default => {137 return quote_spanned! { fun.sig.span() =>201 return Err(Error::new(138 compile_error!("builtins should return something");202 fun.sig.span(),139 }203 "builtin should return something",140 .into()204 ))141 }205 }142 syn::ReturnType::Type(_, ref ty) => ty.clone(),206 ReturnType::Type(_, ref ty) => ty.clone(),143 };207 };144208145 let params = fun209 let args = fun146 .sig210 .sig147 .inputs211 .inputs148 .iter()212 .iter()149 .map(|i| match i {213 .map(|a| ArgInfo::parse(a))150 FnArg::Receiver(_) => unreachable!(),214 .collect::<Result<Vec<_>>>()?;151 FnArg::Typed(t) => t,215152 })153 .filter(|a| !is_location_arg(a) && !is_self_arg(a))154 .map(|t| {155 let ident = match &t.pat as &Pat {216 let params_desc = args.iter().flat_map(|a| match a {156 Pat::Ident(i) => i.ident.to_string(),217 ArgInfo::Normal {157 _ => {218 is_option, name, ..158 return quote_spanned! { t.pat.span() =>219 }159 compile_error!("args should be plain identifiers")160 }161 .into()162 }163 };164 let optional = extract_type_from_option(&t.ty).is_some();165 quote! {220 | ArgInfo::Lazy { is_option, name } => Some(quote! {166 BuiltinParam {221 BuiltinParam {167 name: std::borrow::Cow::Borrowed(#ident),222 name: std::borrow::Cow::Borrowed(#name),168 has_default: #optional,223 has_default: #is_option,169 }224 }170 }225 }),171 })172 .collect::<Vec<_>>();226 ArgInfo::Location => None,227 ArgInfo::This => None,228 });173229174 let args = fun230 let pass = args.iter().map(|a| match a {175 .sig231 ArgInfo::Normal {176 .inputs232 ty,177 .iter_mut()233 is_option,178 .map(|i| match i {234 name,179 FnArg::Receiver(_) => unreachable!(),235 // ident,180 FnArg::Typed(t) => t,181 })182 .map(|t| {183 if t.attrs.retain_had(|a| !a.path.is_ident("location")) {184 quote! {{185 loc186 }}187 } else if t.attrs.retain_had(|a| !a.path.is_ident("self")) {188 quote! {{189 self190 }}191 } else {192 let ident = match &t.pat as &Pat {236 } => {237 let eval = quote! {::jrsonnet_evaluator::push_description_frame(238 || format!("argument <{}> evaluation", #name),193 Pat::Ident(i) => i.ident.to_string(),239 || <#ty>::try_from(value.evaluate()?),240 )?};194 _ => {241 if *is_option {195 return quote_spanned! { t.pat.span() =>242 quote! {if let Some(value) = parsed.get(#name) {196 compile_error!("args should be plain identifiers")243 Some(#eval)244 } else {245 None197 }246 }}198 .into()199 }247 } else {248 quote! {{249 let value = parsed.get(#name).expect("args shape is checked");250 #eval251 }}252 }200 };253 }201 let ty = &t.ty;254 ArgInfo::Lazy { is_option, name } => {202 if let Some(opt_ty) = extract_type_from_option(&t.ty) {255 if *is_option {203 quote! {{256 quote! {if let Some(value) = parsed.get(#name) {204 if let Some(value) = parsed.get(#ident) {205 Some(::jrsonnet_evaluator::push_description_frame(257 Some(value.clone())206 || format!("argument <{}> evaluation", #ident),207 || <#opt_ty>::try_from(value.evaluate()?),208 )?)209 } else {258 } else {210 None259 None211 }260 }}212 }}213 } else {261 } else {214 quote! {{262 quote! {215 let value = parsed.get(#ident).unwrap();263 parsed.get(#name).expect("args shape is correct").clone()216217 ::jrsonnet_evaluator::push_description_frame(218 || format!("argument <{}> evaluation", #ident),219 || <#ty>::try_from(value.evaluate()?),220 )?221 }}264 }222 }265 }266 }267 ArgInfo::Location => quote! {location},268 ArgInfo::This => quote! {self},223 }269 });224 })225 .collect::<Vec<_>>();226270227 let fields = attrs.fields.iter().map(|field| {271 let fields = attr.fields.iter().map(|field| {228 let name = &field.name;272 let name = &field.name;229 let ty = &field.ty;273 let ty = &field.ty;230 quote! {274 quote! {234278235 let name = &fun.sig.ident;279 let name = &fun.sig.ident;236 let vis = &fun.vis;280 let vis = &fun.vis;237 let static_ext = if attrs.fields.is_empty() {281 let static_ext = if attr.fields.is_empty() {238 quote! {282 quote! {239 impl #name {283 impl #name {240 pub const INST: &'static dyn StaticBuiltin = &#name {};284 pub const INST: &'static dyn StaticBuiltin = &#name {};244 } else {288 } else {245 quote! {}289 quote! {}246 };290 };247 let static_derive_copy = if attrs.fields.is_empty() {291 let static_derive_copy = if attr.fields.is_empty() {248 quote! {, Copy}292 quote! {, Copy}249 } else {293 } else {250 quote! {}294 quote! {}251 };295 };252296253 (quote! {297 Ok(quote! {254 #fun298 #fun255 #[doc(hidden)]299 #[doc(hidden)]256 #[allow(non_camel_case_types)]300 #[allow(non_camel_case_types)]260 }304 }261 const _: () = {305 const _: () = {262 use ::jrsonnet_evaluator::{306 use ::jrsonnet_evaluator::{263 function::{Builtin, StaticBuiltin, BuiltinParam, ArgsLike, parse_builtin_call},307 function::{Builtin, CallLocation, StaticBuiltin, BuiltinParam, ArgsLike, parse_builtin_call},264 error::Result, Context,308 error::Result, Context,265 parser::ExprLocation,309 parser::ExprLocation,266 };310 };267 const PARAMS: &'static [BuiltinParam] = &[311 const PARAMS: &'static [BuiltinParam] = &[268 #(#params),*312 #(#params_desc),*269 ];313 ];270314271 #static_ext315 #static_ext279 fn params(&self) -> &[BuiltinParam] {323 fn params(&self) -> &[BuiltinParam] {280 PARAMS324 PARAMS281 }325 }282 fn call(&self, context: Context, loc: Option<&ExprLocation>, args: &dyn ArgsLike) -> Result<Val> {326 fn call(&self, context: Context, location: CallLocation, args: &dyn ArgsLike) -> Result<Val> {283 let parsed = parse_builtin_call(context, &PARAMS, args, false)?;327 let parsed = parse_builtin_call(context, &PARAMS, args, false)?;284328285 let result: #result = #name(#(#args),*);329 let result: #result = #name(#(#pass),*);286 let result = result?;330 let result = result?;287 result.try_into()331 result.try_into()288 }332 }289 }333 }290 };334 };291 })335 })292 .into()336}293}294337295#[derive(Default)]338#[derive(Default)]296struct TypedAttr {339struct TypedAttr {366 )409 )367 }410 }368411369 fn expand_shallow_field(&self) -> Option<TokenStream> {370 if self.is_option() {371 return None;372 }373 let name = self.name()?;374 Some(quote! {375 (#name, ComplexValType::Any)376 })377 }378 fn expand_field(&self) -> Option<TokenStream> {412 fn expand_field(&self) -> Option<TokenStream> {379 if self.is_option() {413 if self.is_option() {380 return None;414 return None;450 }484 }451485452 fn as_option(&self) -> Option<&Type> {486 fn as_option(&self) -> Option<&Type> {453 extract_type_from_option(&self.0.ty)487 extract_type_from_option(&self.0.ty).unwrap()454 }488 }455 fn is_option(&self) -> bool {489 fn is_option(&self) -> bool {456 self.as_option().is_some()490 self.as_option().is_some()457 }491 }458}492}459493460#[proc_macro_derive(Typed, attributes(typed))]494#[proc_macro_derive(Typed, attributes(typed))]495pub fn derive_typed(item: proc_macro::TokenStream) -> proc_macro::TokenStream {496 let input = parse_macro_input!(item as DeriveInput);497498 match derive_typed_inner(input) {499 Ok(v) => v.into(),500 Err(e) => e.to_compile_error().into(),501 }502}503461pub fn derive_typed(item: proc_macro::TokenStream) -> proc_macro::TokenStream {504fn derive_typed_inner(input: DeriveInput) -> Result<TokenStream> {462 let input = parse_macro_input!(item as DeriveInput);463 let data = match &input.data {505 let data = match &input.data {464 syn::Data::Struct(s) => s,506 syn::Data::Struct(s) => s,465 _ => {507 _ => return Err(Error::new(input.span(), "only structs supported")),466 return syn::Error::new(input.span(), "only structs supported")467 .to_compile_error()468 .into()469 }470 };508 };471509472 let ident = &input.ident;510 let ident = &input.ident;473 let fields = match data511 let fields = data474 .fields512 .fields475 .iter()513 .iter()476 .map(TypedField::try_new)514 .map(TypedField::try_new)477 .collect::<Result<Vec<_>>>()515 .collect::<Result<Vec<_>>>()?;478 {479 Ok(v) => v,480 Err(e) => return e.to_compile_error().into(),481 };482516483 let typed = {517 let typed = {484 let fields = fields518 let fields = fields499 let fields_parse = fields.iter().map(TypedField::expand_parse);533 let fields_parse = fields.iter().map(TypedField::expand_parse);500 let fields_serialize = fields.iter().map(TypedField::expand_serialize);534 let fields_serialize = fields.iter().map(TypedField::expand_serialize);501535502 quote! {536 Ok(quote! {503 const _: () = {537 const _: () = {504 use ::jrsonnet_evaluator::{538 use ::jrsonnet_evaluator::{505 typed::{ComplexValType, Typed, TypedObj, CheckType},539 typed::{ComplexValType, Typed, TypedObj, CheckType},540 }574 }541 ()575 ()542 };576 };543 }577 })544 .into()545}578}546579