git.delta.rocks / jrsonnet / refs/commits / 349c41065b93

difftreelog

feat lazy values in builtin

Yaroslav Bolyukin2022-04-20parent: #c7fb888.patch.diff
in: master

8 files changed

modifiedcrates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth
1use 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;
2928
30pub 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}
468467
469#[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!(
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
4 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 )?
modifiedcrates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth
12use 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};
14
15#[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}
1427
15#[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}
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
28pub 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 frame
346 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 )
modifiedcrates/jrsonnet-evaluator/src/native.rsdiffbeforeafterboth
1#![allow(clippy::type_complexity)]1#![allow(clippy::type_complexity)]
22
3use 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;
1110
32 &self.params31 &self.params
33 }32 }
3433
35 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}
4943
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
1use std::convert::{TryFrom, TryInto};1use std::convert::{TryFrom, TryInto};
22
3use 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};
1314
14pub trait TypedObj: Typed {15pub trait TypedObj: Typed {
432 }433 }
433}434}
435
436impl Typed for Cc<FuncDesc> {
437 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);
438}
439impl TryFrom<Val> for Cc<FuncDesc> {
440 type Error = LocError;
441
442 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;
453
454 fn try_from(value: Cc<FuncDesc>) -> Result<Self, Self::Error> {
455 Ok(Self::Func(FuncVal::Normal(value)))
456 }
457}
458
434impl 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);
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
6 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};
1920
152 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}
172173
modifiedcrates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth
1use 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};
1313
14fn 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}
3535
36fn 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() >= 1
39 && path.segments.iter().last().unwrap().ident == needed
38}40}
41
39fn 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}
42
43trait 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 != after
52 }47 }
48 _ => None,
49 }
53}50}
5451
55fn 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() == 1
59 && path.segments.iter().next().unwrap().ident == "Option"
60 }
61
62 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 None
78 }71 })
79}72}
8073
81struct Field {74struct Field {
10194
102struct 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}
119
120enum 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}
134
135impl 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 }
161
162 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 }
169
170 (true, ty.clone())
171 } else {
172 (false, ty.clone())
173 };
174
175 Ok(Self::Normal {
176 ty,
177 is_option,
178 name: ident.to_string(),
179 // ident,
180 })
181 }
182}
126183
127#[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);
191
192 match builtin_inner(attr, item) {
193 Ok(v) => v.into(),
194 Err(e) => e.into_compile_error().into(),
195 }
196}
197
133 let mut fun: ItemFn = parse_macro_input!(item);198fn builtin_inner(attr: BuiltinAttrs, fun: ItemFn) -> syn::Result<TokenStream> {
134
135 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 };
144208
145 let params = fun209 let args = fun
146 .sig210 .sig
147 .inputs211 .inputs
148 .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,215
152 })
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 });
173229
174 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 loc
186 }}
187 } else if t.attrs.retain_had(|a| !a.path.is_ident("self")) {
188 quote! {{
189 self
190 }}
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 None
197 }246 }}
198 .into()
199 }247 } else {
248 quote! {{
249 let value = parsed.get(#name).expect("args shape is checked");
250 #eval
251 }}
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 None
211 }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()
216
217 ::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<_>>();
226270
227 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! {
234278
235 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 };
252296
253 (quote! {297 Ok(quote! {
254 #fun298 #fun
255 #[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 ];
270314
271 #static_ext315 #static_ext
279 fn params(&self) -> &[BuiltinParam] {323 fn params(&self) -> &[BuiltinParam] {
280 PARAMS324 PARAMS
281 }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)?;
284328
285 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}
294337
295#[derive(Default)]338#[derive(Default)]
296struct TypedAttr {339struct TypedAttr {
366 )409 )
367 }410 }
368411
369 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 }
451485
452 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}
459493
460#[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);
497
498 match derive_typed_inner(input) {
499 Ok(v) => v.into(),
500 Err(e) => e.to_compile_error().into(),
501 }
502}
503
461pub 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 };
471509
472 let ident = &input.ident;510 let ident = &input.ident;
473 let fields = match data511 let fields = data
474 .fields512 .fields
475 .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 };
482516
483 let typed = {517 let typed = {
484 let fields = fields518 let fields = fields
499 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);
501535
502 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