difftreelog
feat lazy values in builtin
in: master
8 files changed
crates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth1use crate::function::StaticBuiltin;1use crate::function::{CallLocation, StaticBuiltin};2use crate::typed::{Any, PositiveF64, VecVal, M1};2use crate::typed::{Any, PositiveF64, VecVal, M1};3use crate::{3use crate::{4 builtin::manifest::{manifest_yaml_ex, ManifestYamlOptions},4 builtin::manifest::{manifest_yaml_ex, ManifestYamlOptions},12use crate::{Either, ObjValue};12use crate::{Either, ObjValue};13use format::{format_arr, format_obj};13use format::{format_arr, format_obj};14use jrsonnet_interner::IStr;14use jrsonnet_interner::IStr;15use jrsonnet_parser::ExprLocation;16use serde::Deserialize;15use serde::Deserialize;17use serde_yaml::DeserializingQuirks;16use serde_yaml::DeserializingQuirks;18use std::collections::HashMap;17use std::collections::HashMap;292830pub fn std_format(str: IStr, vals: Val) -> Result<String> {29pub fn std_format(str: IStr, vals: Val) -> Result<String> {31 push_frame(30 push_frame(32 None,31 CallLocation::native(),33 || format!("std.format of {}", str),32 || format!("std.format of {}", str),34 || {33 || {35 Ok(match vals {34 Ok(match vals {467}466}468467469#[jrsonnet_macros::builtin]468#[jrsonnet_macros::builtin]470fn builtin_trace(#[location] loc: Option<&ExprLocation>, str: IStr, rest: Any) -> Result<Any> {469fn builtin_trace(loc: CallLocation, str: IStr, rest: Any) -> Result<Any> {471 eprint!("TRACE:");470 eprint!("TRACE:");472 if let Some(loc) = loc {471 if let Some(loc) = loc.0 {473 with_state(|s| {472 with_state(|s| {474 let locs = s.map_source_locations(&loc.0, &[loc.1]);473 let locs = s.map_source_locations(&loc.0, &[loc.1]);475 eprint!(474 eprint!(crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth4 builtin::{std_slice, BUILTINS},4 builtin::{std_slice, BUILTINS},5 error::Error::*,5 error::Error::*,6 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},6 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},7 function::CallLocation,7 gc::TraceBox,8 gc::TraceBox,8 push_frame, throw, with_state, ArrValue, Bindable, Context, ContextCreator, FuncDesc, FuncVal,9 push_frame, throw, with_state, ArrValue, Bindable, Context, ContextCreator, FuncDesc, FuncVal,9 FutureWrapper, GcHashMap, LazyBinding, LazyVal, LazyValValue, ObjValue, ObjValueBuilder,10 FutureWrapper, GcHashMap, LazyBinding, LazyVal, LazyValValue, ObjValue, ObjValueBuilder,12use gcmodule::{Cc, Trace};13use gcmodule::{Cc, Trace};13use jrsonnet_interner::IStr;14use jrsonnet_interner::IStr;14use jrsonnet_parser::{15use jrsonnet_parser::{15 ArgsDesc, AssertStmt, BindSpec, CompSpec, Expr, ExprLocation, FieldMember, ForSpecData,16 ArgsDesc, AssertStmt, BindSpec, CompSpec, Expr, FieldMember, ForSpecData, IfSpecData,16 IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc,17 LiteralType, LocExpr, Member, ObjBody, ParamsDesc,17};18};18use jrsonnet_types::ValType;19use jrsonnet_types::ValType;192 Ok(match field_name {193 Ok(match field_name {193 jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),194 jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),194 jrsonnet_parser::FieldName::Dyn(expr) => push_frame(195 jrsonnet_parser::FieldName::Dyn(expr) => push_frame(195 Some(&expr.1),196 CallLocation::new(&expr.1),196 || "evaluating field name".to_string(),197 || "evaluating field name".to_string(),197 || {198 || {198 let value = evaluate(context, expr)?;199 let value = evaluate(context, expr)?;442 context: Context,443 context: Context,443 value: &LocExpr,444 value: &LocExpr,444 args: &ArgsDesc,445 args: &ArgsDesc,445 loc: Option<&ExprLocation>,446 loc: CallLocation,446 tailstrict: bool,447 tailstrict: bool,447) -> Result<Val> {448) -> Result<Val> {448 let value = evaluate(context.clone(), value)?;449 let value = evaluate(context.clone(), value)?;463 let value = &assertion.0;464 let value = &assertion.0;464 let msg = &assertion.1;465 let msg = &assertion.1;465 let assertion_result = push_frame(466 let assertion_result = push_frame(466 Some(&value.1),467 CallLocation::new(&value.1),467 || "assertion condition".to_owned(),468 || "assertion condition".to_owned(),468 || bool::try_from(evaluate(context.clone(), value)?),469 || bool::try_from(evaluate(context.clone(), value)?),469 )?;470 )?;470 if !assertion_result {471 if !assertion_result {471 push_frame(472 push_frame(472 Some(&value.1),473 CallLocation::new(&value.1),473 || "assertion failure".to_owned(),474 || "assertion failure".to_owned(),474 || {475 || {475 if let Some(msg) = msg {476 if let Some(msg) = msg {519 BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,520 BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,520 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,521 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,521 Var(name) => push_frame(522 Var(name) => push_frame(522 Some(loc),523 CallLocation::new(loc),523 || format!("variable <{}> access", name),524 || format!("variable <{}> access", name),524 || context.binding(name.clone())?.evaluate(),525 || context.binding(name.clone())?.evaluate(),525 )?,526 )?,528 (Val::Obj(v), Val::Str(s)) => {529 (Val::Obj(v), Val::Str(s)) => {529 let sn = s.clone();530 let sn = s.clone();530 push_frame(531 push_frame(531 Some(loc),532 CallLocation::new(loc),532 || format!("field <{}> access", sn),533 || format!("field <{}> access", sn),533 || {534 || {534 if let Some(v) = v.get(s.clone())? {535 if let Some(v) = v.get(s.clone())? {625 &Val::Obj(evaluate_object(context, t)?),626 &Val::Obj(evaluate_object(context, t)?),626 )?,627 )?,627 Apply(value, args, tailstrict) => {628 Apply(value, args, tailstrict) => {628 evaluate_apply(context, value, args, Some(loc), *tailstrict)?629 evaluate_apply(context, value, args, CallLocation::new(loc), *tailstrict)?629 }630 }630 Function(params, body) => {631 Function(params, body) => {631 evaluate_method(context, "anonymous".into(), params.clone(), body.clone())632 evaluate_method(context, "anonymous".into(), params.clone(), body.clone())640 evaluate(context, returned)?641 evaluate(context, returned)?641 }642 }642 ErrorStmt(e) => push_frame(643 ErrorStmt(e) => push_frame(643 Some(loc),644 CallLocation::new(loc),644 || "error statement".to_owned(),645 || "error statement".to_owned(),645 || throw!(RuntimeError(IStr::try_from(evaluate(context, e)?)?,)),646 || throw!(RuntimeError(IStr::try_from(evaluate(context, e)?)?,)),646 )?,647 )?,650 cond_else,651 cond_else,651 } => {652 } => {652 if push_frame(653 if push_frame(653 Some(loc),654 CallLocation::new(loc),654 || "if condition".to_owned(),655 || "if condition".to_owned(),655 || bool::try_from(evaluate(context.clone(), &cond.0)?),656 || bool::try_from(evaluate(context.clone(), &cond.0)?),656 )? {657 )? {689 let mut import_location = tmp.to_path_buf();690 let mut import_location = tmp.to_path_buf();690 import_location.pop();691 import_location.pop();691 push_frame(692 push_frame(692 Some(loc),693 CallLocation::new(loc),693 || format!("import {:?}", path),694 || format!("import {:?}", path),694 || with_state(|s| s.import_file(&import_location, path)),695 || with_state(|s| s.import_file(&import_location, path)),695 )?696 )?crates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth12use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};12use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};13use std::{borrow::Cow, collections::HashMap, convert::TryFrom};13use std::{borrow::Cow, collections::HashMap, convert::TryFrom};1415#[derive(Clone, Copy)]16pub struct CallLocation<'l>(pub Option<&'l ExprLocation>);17impl<'l> CallLocation<'l> {18 pub fn new(loc: &'l ExprLocation) -> Self {19 Self(Some(loc))20 }21}22impl CallLocation<'static> {23 pub fn native() -> Self {24 Self(None)25 }26}142715#[derive(Trace)]28#[derive(Trace)]16struct EvaluateLazyVal {29struct EvaluateLazyVal {383pub trait Builtin: Trace {396pub trait Builtin: Trace {384 fn name(&self) -> &str;397 fn name(&self) -> &str;385 fn params(&self) -> &[BuiltinParam];398 fn params(&self) -> &[BuiltinParam];386 fn call(399 fn call(&self, context: Context, loc: CallLocation, args: &dyn ArgsLike) -> Result<Val>;387 &self,388 context: Context,389 loc: Option<&ExprLocation>,390 args: &dyn ArgsLike,391 ) -> Result<Val>;392}400}crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth28pub use dynamic::*;28pub use dynamic::*;29use error::{Error::*, LocError, Result, StackTraceElement};29use error::{Error::*, LocError, Result, StackTraceElement};30pub use evaluate::*;30pub use evaluate::*;31use function::{Builtin, TlaArg};31use function::{Builtin, CallLocation, TlaArg};32use gc::{GcHashMap, TraceBox};32use gc::{GcHashMap, TraceBox};33use gcmodule::{Cc, Trace, Weak};33use gcmodule::{Cc, Trace, Weak};34pub use import::*;34pub use import::*;181 })182 })182}183}183pub fn push_frame<T>(184pub fn push_frame<T>(184 e: Option<&ExprLocation>,185 e: CallLocation,185 frame_desc: impl FnOnce() -> String,186 frame_desc: impl FnOnce() -> String,186 f: impl FnOnce() -> Result<T>,187 f: impl FnOnce() -> Result<T>,187) -> Result<T> {188) -> Result<T> {345 /// Executes code creating a new stack frame346 /// Executes code creating a new stack frame346 pub fn push<T>(347 pub fn push<T>(347 &self,348 &self,348 e: Option<&ExprLocation>,349 e: CallLocation,349 frame_desc: impl FnOnce() -> String,350 frame_desc: impl FnOnce() -> String,350 f: impl FnOnce() -> Result<T>,351 f: impl FnOnce() -> Result<T>,351 ) -> Result<T> {352 ) -> Result<T> {368 }369 }369 if let Err(mut err) = result {370 if let Err(mut err) = result {370 err.trace_mut().0.push(StackTraceElement {371 err.trace_mut().0.push(StackTraceElement {371 location: e.cloned(),372 location: e.0.cloned(),372 desc: frame_desc(),373 desc: frame_desc(),373 });374 });374 return Err(err);375 return Err(err);512 || {513 || {513 func.evaluate(514 func.evaluate(514 self.create_default_context(),515 self.create_default_context(),515 None,516 CallLocation::native(),516 &self.settings().tla_vars,517 &self.settings().tla_vars,517 true,518 true,518 )519 )crates/jrsonnet-evaluator/src/native.rsdiffbeforeafterboth1#![allow(clippy::type_complexity)]1#![allow(clippy::type_complexity)]223use crate::function::{parse_builtin_call, ArgsLike, Builtin, BuiltinParam};3use crate::function::{parse_builtin_call, ArgsLike, Builtin, BuiltinParam, CallLocation};4use crate::gc::TraceBox;4use crate::gc::TraceBox;5use crate::Context;5use crate::Context;6use crate::{error::Result, Val};6use crate::{error::Result, Val};7use gcmodule::Trace;7use gcmodule::Trace;8use jrsonnet_parser::ExprLocation;9use std::path::Path;8use std::path::Path;10use std::rc::Rc;9use std::rc::Rc;111032 &self.params31 &self.params33 }32 }343335 fn call(34 fn call(&self, context: Context, loc: CallLocation, args: &dyn ArgsLike) -> Result<Val> {36 &self,37 context: Context,38 loc: Option<&ExprLocation>,39 args: &dyn ArgsLike,40 ) -> Result<Val> {41 let args = parse_builtin_call(context, &self.params, args, true)?;35 let args = parse_builtin_call(context, &self.params, args, true)?;42 let mut out_args = Vec::with_capacity(self.params.len());36 let mut out_args = Vec::with_capacity(self.params.len());43 for p in self.params.iter() {37 for p in self.params.iter() {44 out_args.push(args[&p.name].evaluate()?);38 out_args.push(args[&p.name].evaluate()?);45 }39 }46 self.handler.call(loc.map(|l| l.0.clone()), &out_args)40 self.handler.call(loc.0.map(|l| l.0.clone()), &out_args)47 }41 }48}42}4943crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth1use std::convert::{TryFrom, TryInto};1use std::convert::{TryFrom, TryInto};223use gcmodule::Cc;3use jrsonnet_interner::IStr;4use jrsonnet_interner::IStr;4pub use jrsonnet_macros::Typed;5pub use jrsonnet_macros::Typed;5use jrsonnet_types::{ComplexValType, ValType};6use jrsonnet_types::{ComplexValType, ValType};8 error::{Error::*, LocError, Result},9 error::{Error::*, LocError, Result},9 throw,10 throw,10 typed::CheckType,11 typed::CheckType,11 ArrValue, FuncVal, IndexableVal, ObjValue, ObjValueBuilder, Val,12 ArrValue, FuncDesc, FuncVal, IndexableVal, ObjValue, ObjValueBuilder, Val,12};13};131414pub trait TypedObj: Typed {15pub trait TypedObj: Typed {432 }433 }433}434}435436impl Typed for Cc<FuncDesc> {437 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);438}439impl TryFrom<Val> for Cc<FuncDesc> {440 type Error = LocError;441442 fn try_from(value: Val) -> Result<Self, Self::Error> {443 <Self as Typed>::TYPE.check(&value)?;444 match value {445 Val::Func(FuncVal::Normal(desc)) => Ok(desc.clone()),446 Val::Func(_) => throw!(RuntimeError("expected normal function, not builtin".into())),447 _ => unreachable!(),448 }449 }450}451impl TryFrom<Cc<FuncDesc>> for Val {452 type Error = LocError;453454 fn try_from(value: Cc<FuncDesc>) -> Result<Self, Self::Error> {455 Ok(Self::Func(FuncVal::Normal(value)))456 }457}458434impl Typed for ObjValue {459impl Typed for ObjValue {435 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj);460 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj);crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth6 error::{Error::*, LocError},6 error::{Error::*, LocError},7 evaluate,7 evaluate,8 function::{8 function::{9 parse_default_function_call, parse_function_call, ArgsLike, Builtin, StaticBuiltin,9 parse_default_function_call, parse_function_call, ArgsLike, Builtin, CallLocation,10 StaticBuiltin,10 },11 },11 gc::TraceBox,12 gc::TraceBox,12 throw, Context, ObjValue, Result,13 throw, Context, ObjValue, Result,13};14};14use gcmodule::{Cc, Trace};15use gcmodule::{Cc, Trace};15use jrsonnet_interner::IStr;16use jrsonnet_interner::IStr;16use jrsonnet_parser::{ExprLocation, LocExpr, ParamsDesc};17use jrsonnet_parser::{LocExpr, ParamsDesc};17use jrsonnet_types::ValType;18use jrsonnet_types::ValType;18use std::{cell::RefCell, fmt::Debug, rc::Rc};19use std::{cell::RefCell, fmt::Debug, rc::Rc};1920152 pub fn evaluate(153 pub fn evaluate(153 &self,154 &self,154 call_ctx: Context,155 call_ctx: Context,155 loc: Option<&ExprLocation>,156 loc: CallLocation,156 args: &dyn ArgsLike,157 args: &dyn ArgsLike,157 tailstrict: bool,158 tailstrict: bool,158 ) -> Result<Val> {159 ) -> Result<Val> {166 }167 }167 }168 }168 pub fn evaluate_simple(&self, args: &dyn ArgsLike) -> Result<Val> {169 pub fn evaluate_simple(&self, args: &dyn ArgsLike) -> Result<Val> {169 self.evaluate(Context::default(), None, args, true)170 self.evaluate(Context::default(), CallLocation::native(), args, true)170 }171 }171}172}172173crates/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