difftreelog
feat(macro) pass call location to builtins
in: master
8 files changed
cmds/jrsonnet-fmt/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/cmds/jrsonnet-fmt/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "jrsonnet-fmt"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+dprint-core = "0.47.1"
+jrsonnet-parser = { path = "../../crates/jrsonnet-parser" }
cmds/jrsonnet-fmt/src/main.rsdiffbeforeafterboth--- /dev/null
+++ b/cmds/jrsonnet-fmt/src/main.rs
@@ -0,0 +1,373 @@
+use std::path::PathBuf;
+
+use dprint_core::formatting::{PrintItems, PrintOptions, Signal};
+use jrsonnet_parser::{
+ ArgsDesc, BinaryOpType, BindSpec, Expr, FieldName, LocExpr, Member, ObjBody, Param, ParamsDesc,
+ ParserSettings, Visibility,
+};
+
+pub trait Printable {
+ fn print(&self) -> PrintItems;
+}
+
+macro_rules! pi {
+ (@i; $($t:tt)*) => {{
+ let mut o = PrintItems::new();
+ pi!(@s; o: $($t)*);
+ o
+ }};
+ (@s; $o:ident: str($e:expr) $($t:tt)*) => {{
+ $o.push_str($e);
+ pi!(@s; $o: $($t)*);
+ }};
+ (@s; $o:ident: nl $($t:tt)*) => {{
+ $o.push_signal(Signal::NewLine);
+ pi!(@s; $o: $($t)*);
+ }};
+ (@s; $o:ident: >i $($t:tt)*) => {{
+ $o.push_signal(Signal::StartIndent);
+ pi!(@s; $o: $($t)*);
+ }};
+ (@s; $o:ident: <i $($t:tt)*) => {{
+ $o.push_signal(Signal::FinishIndent);
+ pi!(@s; $o: $($t)*);
+ }};
+ (@s; $o:ident: {$expr:expr} $($t:tt)*) => {{
+ $o.extend($expr.print());
+ pi!(@s; $o: $($t)*);
+ }};
+ (@s; $o:ident: if ($e:expr)($($then:tt)*) $($t:tt)*) => {{
+ if $e {
+ pi!(@s; $o: $($then)*);
+ }
+ pi!(@s; $o: $($t)*);
+ }};
+ (@s; $o:ident: ifelse ($e:expr)($($then:tt)*)($($else:tt)*) $($t:tt)*) => {{
+ if $e {
+ pi!(@s; $o: $($then)*);
+ } else {
+ pi!(@s; $o: $($else)*);
+ }
+ pi!(@s; $o: $($t)*);
+ }};
+ (@s; $i:ident:) => {}
+}
+macro_rules! p {
+ (new: $($t:tt)*) => {
+ pi!(@i; $($t)*)
+ };
+ ($o:ident: $($t:tt)*) => {
+ pi!(@s; $o: $($t)*)
+ };
+}
+
+impl Printable for FieldName {
+ fn print(&self) -> PrintItems {
+ match self {
+ FieldName::Fixed(f) => {
+ p!(new: str(&f))
+ }
+ FieldName::Dyn(_) => todo!(),
+ }
+ }
+}
+
+impl Printable for Visibility {
+ fn print(&self) -> PrintItems {
+ match self {
+ Visibility::Normal => p!(new: str(":")),
+ Visibility::Hidden => p!(new: str("::")),
+ Visibility::Unhide => p!(new: str(":::")),
+ }
+ }
+}
+
+impl Printable for BinaryOpType {
+ fn print(&self) -> PrintItems {
+ let o = self.to_string();
+ p!(new: str(&o))
+ }
+}
+
+impl<T: Printable> Printable for Option<T> {
+ fn print(&self) -> PrintItems {
+ if let Some(v) = self {
+ v.print()
+ } else {
+ PrintItems::new()
+ }
+ }
+}
+
+impl Printable for Param {
+ fn print(&self) -> PrintItems {
+ p!(new:
+ str(&self.0)
+ if(self.1.is_some())(str(" = ") {self.1})
+ )
+ }
+}
+
+impl Printable for ParamsDesc {
+ fn print(&self) -> PrintItems {
+ let mut out = PrintItems::new();
+ for (i, item) in self.0.iter().enumerate() {
+ if i != 0 {
+ p!(out: str(", "));
+ }
+ out.extend(item.print());
+ }
+ out
+ }
+}
+
+impl Printable for ArgsDesc {
+ fn print(&self) -> PrintItems {
+ let mut out = PrintItems::new();
+ let mut first = Some(());
+ for u in self.unnamed.iter() {
+ if first.take().is_none() {
+ p!(out: str(", "));
+ }
+ p!(out: {u})
+ }
+ for (n, u) in self.named.iter() {
+ if first.take().is_none() {
+ p!(out: str(", "));
+ }
+ p!(out: str(&n) str(" = ") {u})
+ }
+
+ out
+ }
+}
+
+impl Printable for BindSpec {
+ fn print(&self) -> PrintItems {
+ p!(new: str(&self.name) if(self.params.is_some())(str("(") {self.params} str(")")) str(" = ") {self.value})
+ }
+}
+
+struct StrExpr<'s>(&'s str);
+
+impl<'s> Printable for StrExpr<'s> {
+ fn print(&self) -> PrintItems {
+ todo!()
+ }
+}
+
+impl Printable for ObjBody {
+ fn print(&self) -> PrintItems {
+ let mut pi = PrintItems::new();
+ p!(pi: str("{"));
+ match self {
+ ObjBody::MemberList(m) => {
+ if !m.is_empty() {
+ p!(pi: nl > i);
+ for m in m {
+ match m {
+ Member::Field(f) => {
+ p!(pi:
+ {f.name} {f.params}
+ if(f.plus)(str("+"))
+ {f.visibility} str(" ")
+ {f.value}
+ str(",") nl
+ );
+ }
+ Member::BindStmt(s) => {
+ p!(pi: str("local ") {s} str(",") nl)
+ }
+ Member::AssertStmt(a) => p!(pi: str("assert ") {a.0} if(a.1.is_some())(
+ str(" : ") {a.1}
+ ) str(",") nl),
+ }
+ }
+ p!(pi: <i);
+ } else {
+ p!(pi: str(" "))
+ }
+ }
+ ObjBody::ObjComp(_) => todo!(),
+ }
+ p!(pi: str("}"));
+ pi
+ }
+}
+
+impl Printable for Expr {
+ fn print(&self) -> PrintItems {
+ let mut pi = PrintItems::new();
+ match self {
+ Expr::Literal(l) => match l {
+ jrsonnet_parser::LiteralType::This => p!(pi: str("self")),
+ jrsonnet_parser::LiteralType::Super => p!(pi: str("super")),
+ jrsonnet_parser::LiteralType::Dollar => p!(pi: str("$")),
+ jrsonnet_parser::LiteralType::Null => p!(pi: str("null")),
+ jrsonnet_parser::LiteralType::True => p!(pi: str("true")),
+ jrsonnet_parser::LiteralType::False => p!(pi: str("false")),
+ },
+ Expr::Str(s) => {
+ p!(pi: str("\"") str(s) str("\""))
+ }
+ Expr::Num(n) => {
+ let n = n.to_string();
+ p!(pi: str(&n));
+ }
+ Expr::Var(v) => p!(pi: str(&v)),
+ Expr::Arr(a) => {
+ p!(pi: str("["));
+ for (i, v) in a.iter().enumerate() {
+ if i != 0 {
+ p!(pi: str(", "));
+ }
+ p!(pi: {v})
+ }
+ p!(pi: str("]"));
+ }
+ Expr::ArrComp(_, _) => todo!(),
+ Expr::Obj(o) => {
+ p!(pi: {o});
+ }
+ Expr::ObjExtend(a, b) => p!(pi: {a} str(" ") {b}),
+ Expr::Parened(v) => {
+ if let Expr::Parened(_) = &v.0 as &Expr {
+ p!(pi: {v})
+ } else {
+ p!(pi: str("(") {v} str(")"))
+ }
+ }
+ Expr::UnaryOp(_, _) => todo!(),
+ Expr::BinaryOp(a, o, b) => {
+ p!(pi:
+ {a} str(" ") if(!matches!(&b.0 as &Expr, Expr::Obj(_)))({o} str(" ")) {b}
+ )
+ }
+ Expr::AssertExpr(_, _) => todo!(),
+ Expr::LocalExpr(s, v) => {
+ p!(pi:
+ str("local") nl >i
+ );
+ for spec in s.iter() {
+ p!(pi: {spec} str(";") nl)
+ }
+ p!(pi:
+ <i
+ {v}
+ );
+ }
+ Expr::Import(i) => {
+ let v = i.to_str().unwrap();
+ p!(pi: str("import \"") str(&v) str("\""));
+ }
+ Expr::ImportStr(_) => todo!(),
+ Expr::ErrorStmt(_) => todo!(),
+ Expr::Apply(f, a, t) => p!(pi:
+ {f} str("(") {a} str(")") if(*t)(str("tailstrict"))
+ ),
+ Expr::Index(a, b) => p!(pi: {a} str("[") {b} str("]")),
+ Expr::Function(_, _) => todo!(),
+ Expr::Intrinsic(_) => todo!(),
+ Expr::IfElse {
+ cond,
+ cond_then,
+ cond_else,
+ } => p!(pi:
+ str("if ") {cond.0} str(" then") ifelse(cond_else.is_some())(
+ nl >i
+ {cond_then} nl
+ <i str("else") nl >i
+ {cond_else}
+ <i
+ )(str(" ") {cond_then})
+ ),
+ Expr::Slice(v, d) => {
+ p!(pi:
+ {v}
+ str("[") {d.start} str(":") {d.end}
+ if(d.step.is_some())(
+ str(":")
+ {d.step}
+ )
+ str("]")
+ )
+ }
+ }
+ pi
+ }
+}
+
+impl Printable for LocExpr {
+ fn print(&self) -> PrintItems {
+ self.0.print()
+ }
+}
+
+fn main() {
+ let parsed = jrsonnet_parser::parse(
+ r#"
+
+
+ # Edit me!
+ local b = import "b.libsonnet"; # comment
+ local a = import "a.libsonnet";
+
+ local f(x,y)=x+y;
+
+
+ local Template = {z: "foo"};
+
+ Template + {
+ local
+
+ h = 3,
+ assert self.a == 1
+
+ : "error",
+ "f": ((((((3)))))) ,
+ "g g":
+ f(4,2),
+ arr: [[
+ 1, 2,
+ ],
+ 3,
+ {
+ b: {
+ c: {
+ k: [16]
+ }
+ }
+ }
+ ],
+ m: a[1::],
+ m: b[::],
+ k: if a == b then
+
+
+ 2
+
+ else Template {}
+ }
+
+
+"#,
+ &ParserSettings {
+ file_name: PathBuf::from("example").into(),
+ },
+ )
+ .unwrap();
+
+ let o = dprint_core::formatting::format(
+ || {
+ let print_items = parsed.print();
+ print_items
+ },
+ PrintOptions {
+ indent_width: 2,
+ max_width: 100,
+ use_tabs: false,
+ new_line_text: "\n",
+ },
+ );
+ println!("{}", o);
+}
crates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -5,14 +5,13 @@
equals,
error::{Error::*, Result},
operator::evaluate_mod_op,
- parse_args, primitive_equals, push_frame, throw, with_state, ArrValue, Context, FuncVal,
+ primitive_equals, push_frame, throw, with_state, ArrValue, Context, FuncVal,
IndexableVal, Val,
};
use format::{format_arr, format_obj};
use gcmodule::Cc;
use jrsonnet_interner::IStr;
use jrsonnet_parser::{ArgsDesc, ExprLocation};
-use jrsonnet_types::ty;
use serde::Deserialize;
use serde_yaml::DeserializingQuirks;
use std::{
@@ -466,19 +465,19 @@
Ok(format!("{:x}", md5::compute(&str.as_bytes())))
}
-fn builtin_trace(context: Context, loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
- parse_args!(context, "trace", args, 2, [
- 0, str: ty!(string) => Val::Str;
- 1, rest: ty!(any);
- ], {
- eprint!("TRACE:");
- with_state(|s|{
- let locs = s.map_source_locations(&loc.0, &[loc.1]);
- eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);
- });
- eprintln!(" {}", str);
- Ok(rest)
- })
+#[jrsonnet_macros::builtin]
+fn builtin_trace(#[location] loc: &ExprLocation, str: IStr, rest: Any) -> Result<Any> {
+ eprint!("TRACE:");
+ with_state(|s| {
+ let locs = s.map_source_locations(&loc.0, &[loc.1]);
+ eprint!(
+ " {}:{}",
+ loc.0.file_name().unwrap().to_str().unwrap(),
+ locs[0].line
+ );
+ });
+ eprintln!(" {}", str);
+ Ok(rest) as Result<Any>
}
#[jrsonnet_macros::builtin]
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -16,8 +16,6 @@
pub enum Error {
#[error("intrinsic not found: {0}")]
IntrinsicNotFound(IStr),
- #[error("argument reordering in intrisics not supported yet")]
- IntrinsicArgumentReorderingIsNotSupportedYet,
#[error("operator {0} does not operate on type {1}")]
UnaryOperatorDoesNotOperateOnType(UnaryOpType, ValType),
crates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth1use crate::{2 error::Error::*, evaluate, evaluate_named, gc::TraceBox, throw, Context, FutureWrapper,3 GcHashMap, LazyVal, LazyValValue, Result, Val,4};5use gcmodule::Trace;6use jrsonnet_interner::IStr;7use jrsonnet_parser::{ArgsDesc, LocExpr, ParamsDesc};8use std::collections::HashMap;910const NO_DEFAULT_CONTEXT: &str =11 "no default context set for call with defined default parameter value";1213#[derive(Trace)]14struct EvaluateLazyVal {15 context: Context,16 expr: LocExpr,17}18impl LazyValValue for EvaluateLazyVal {19 fn get(self: Box<Self>) -> Result<Val> {20 evaluate(self.context, &self.expr)21 }22}2324/// Creates correct [context](Context) for function body evaluation returning error on invalid call.25///26/// ## Parameters27/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)28/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)29/// * `params`: function parameters' definition30/// * `args`: passed function arguments31/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily32pub fn parse_function_call(33 ctx: Context,34 body_ctx: Context,35 params: &ParamsDesc,36 args: &ArgsDesc,37 tailstrict: bool,38) -> Result<Context> {39 let mut passed_args = GcHashMap::with_capacity(params.len());40 if args.unnamed.len() > params.len() {41 throw!(TooManyArgsFunctionHas(params.len()))42 }4344 let mut filled_args = 0;4546 for (id, arg) in args.unnamed.iter().enumerate() {47 let name = params[id].0.clone();48 passed_args.insert(49 name,50 if tailstrict {51 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)52 } else {53 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {54 context: ctx.clone(),55 expr: arg.clone(),56 })))57 },58 );59 filled_args += 1;60 }6162 for (name, value) in args.named.iter() {63 // FIXME: O(n) for arg existence check64 if !params.iter().any(|p| &p.0 == name) {65 throw!(UnknownFunctionParameter((name as &str).to_owned()));66 }67 if passed_args68 .insert(69 name.clone(),70 if tailstrict {71 LazyVal::new_resolved(evaluate(ctx.clone(), value)?)72 } else {73 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {74 context: ctx.clone(),75 expr: value.clone(),76 })))77 },78 )79 .is_some()80 {81 throw!(BindingParameterASecondTime(name.clone()));82 }83 filled_args += 1;84 }8586 if filled_args < params.len() {87 // Some args are unset, but maybe we have defaults for them88 // Default values should be created in newly created context89 let future_context = FutureWrapper::<Context>::new();90 let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);9192 for param in params.iter().filter(|p| p.1.is_some()) {93 if passed_args.contains_key(¶m.0.clone()) {94 continue;95 }96 #[derive(Trace)]97 struct LazyNamedBinding {98 future_context: FutureWrapper<Context>,99 name: IStr,100 value: LocExpr,101 }102 impl LazyValValue for LazyNamedBinding {103 fn get(self: Box<Self>) -> Result<Val> {104 evaluate_named(self.future_context.unwrap(), &self.value, self.name)105 }106 }107 LazyVal::new(TraceBox(Box::new(LazyNamedBinding {108 future_context: future_context.clone(),109 name: param.0.clone(),110 value: param.1.clone().unwrap(),111 })));112113 defaults.insert(114 param.0.clone(),115 LazyVal::new(TraceBox(Box::new(LazyNamedBinding {116 future_context: future_context.clone(),117 name: param.0.clone(),118 value: param.1.clone().unwrap(),119 }))),120 );121 filled_args += 1;122 }123124 // Some args still wasn't filled125 if filled_args != params.len() {126 for param in params.iter().skip(args.unnamed.len()) {127 if !args.named.iter().any(|a| a.0 == param.0) {128 throw!(FunctionParameterNotBoundInCall(param.0.clone()));129 }130 }131 unreachable!();132 }133134 Ok(body_ctx135 .extend(passed_args, None, None, None)136 .extend_bound(defaults)137 .into_future(future_context))138 } else {139 let body_ctx = body_ctx.extend(passed_args, None, None, None);140 Ok(body_ctx)141 }142}143144#[derive(Clone, Copy)]145pub struct BuiltinParam {146 pub name: &'static str,147 pub has_default: bool,148}149150/// You shouldn't probally use this function, use jrsonnet_macros::builtin instead151///152/// ## Parameters153/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)154/// * `params`: function parameters' definition155/// * `args`: passed function arguments156/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily157pub fn parse_builtin_call<'k>(158 ctx: Context,159 params: &'static [BuiltinParam],160 args: &'k ArgsDesc,161 tailstrict: bool,162) -> Result<GcHashMap<&'k str, LazyVal>> {163 let mut passed_args = GcHashMap::with_capacity(params.len());164 if args.unnamed.len() > params.len() {165 throw!(TooManyArgsFunctionHas(params.len()))166 }167168 let mut filled_args = 0;169170 for (id, arg) in args.unnamed.iter().enumerate() {171 let name = params[id].name;172 passed_args.insert(173 name,174 if tailstrict {175 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)176 } else {177 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {178 context: ctx.clone(),179 expr: arg.clone(),180 })))181 },182 );183 filled_args += 1;184 }185186 for (name, value) in args.named.iter() {187 // FIXME: O(n) for arg existence check188 if !params.iter().any(|p| p.name == name as &str) {189 throw!(UnknownFunctionParameter((name as &str).to_owned()));190 }191 if passed_args192 .insert(193 name,194 if tailstrict {195 LazyVal::new_resolved(evaluate(ctx.clone(), value)?)196 } else {197 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {198 context: ctx.clone(),199 expr: value.clone(),200 })))201 },202 )203 .is_some()204 {205 throw!(BindingParameterASecondTime(name.clone()));206 }207 filled_args += 1;208 }209210 if filled_args < params.len() {211 for param in params.iter().filter(|p| p.has_default) {212 if passed_args.contains_key(¶m.name) {213 continue;214 }215 filled_args += 1;216 }217218 // Some args still wasn't filled219 if filled_args != params.len() {220 for param in params.iter().skip(args.unnamed.len()) {221 if !args.named.iter().any(|a| &a.0 as &str == param.name) {222 throw!(FunctionParameterNotBoundInCall(param.name.into()));223 }224 }225 unreachable!();226 }227 }228 Ok(passed_args)229}230231pub fn parse_function_call_map(232 ctx: Context,233 body_ctx: Option<Context>,234 params: &ParamsDesc,235 args: &HashMap<IStr, Val>,236 tailstrict: bool,237) -> Result<Context> {238 let mut out = GcHashMap::with_capacity(params.len());239 let mut positioned_args = vec![None; params.0.len()];240 for (name, val) in args.iter() {241 let idx = params242 .iter()243 .position(|p| *p.0 == **name)244 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;245246 if idx >= params.len() {247 throw!(TooManyArgsFunctionHas(params.len()));248 }249 if positioned_args[idx].is_some() {250 throw!(BindingParameterASecondTime(params[idx].0.clone()));251 }252 positioned_args[idx] = Some(val.clone());253 }254 // Fill defaults255 for (id, p) in params.iter().enumerate() {256 let val = if let Some(arg) = positioned_args[id].take() {257 LazyVal::new_resolved(arg)258 } else if let Some(default) = &p.1 {259 if tailstrict {260 LazyVal::new_resolved(evaluate(261 body_ctx.clone().expect(NO_DEFAULT_CONTEXT),262 default,263 )?)264 } else {265 let body_ctx = body_ctx.clone();266 let default = default.clone();267 #[derive(Trace)]268 struct EvaluateLazyVal {269 body_ctx: Option<Context>,270 default: LocExpr,271 }272 impl LazyValValue for EvaluateLazyVal {273 fn get(self: Box<Self>) -> Result<Val> {274 evaluate(275 self.body_ctx.clone().expect(NO_DEFAULT_CONTEXT),276 &self.default,277 )278 }279 }280 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal { body_ctx, default })))281 }282 } else {283 throw!(FunctionParameterNotBoundInCall(p.0.clone()));284 };285 out.insert(p.0.clone(), val);286 }287288 Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))289}290291pub fn place_args(body_ctx: Context, params: &ParamsDesc, args: &[Val]) -> Result<Context> {292 let mut out = GcHashMap::with_capacity(params.len());293 let mut positioned_args = vec![None; params.0.len()];294 for (id, arg) in args.iter().enumerate() {295 if id >= params.len() {296 throw!(TooManyArgsFunctionHas(params.len()));297 }298 positioned_args[id] = Some(arg);299 }300 // Fill defaults301 for (id, p) in params.iter().enumerate() {302 let val = if let Some(arg) = &positioned_args[id] {303 (*arg).clone()304 } else if let Some(default) = &p.1 {305 evaluate(body_ctx.clone(), default)?306 } else {307 throw!(FunctionParameterNotBoundInCall(p.0.clone()));308 };309 out.insert(p.0.clone(), LazyVal::new_resolved(val));310 }311312 Ok(body_ctx.extend(out, None, None, None))313}314315#[macro_export]316macro_rules! parse_args {317 ($ctx: expr, $fn_name: expr, $args: expr, $total_args: expr, [318 $($id: expr, $name: ident: $ty: expr $(=>$match: path)?);+ $(;)?319 ], $handler:block) => {{320 use $crate::{error::Error::*, throw, evaluate, push_description_frame, typed::CheckType};321322 let args = $args;323 if args.unnamed.len() + args.named.len() > $total_args {324 throw!(TooManyArgsFunctionHas($total_args));325 }326 $(327 if args.unnamed.len() + args.named.len() <= $id {328 throw!(FunctionParameterNotBoundInCall(stringify!($name).into()));329 }330 // Is named331 let $name = if $id >= $args.unnamed.len() {332 let named = &args.named[$id - $args.unnamed.len()];333 if &named.0 != stringify!($name) {334 throw!(IntrinsicArgumentReorderingIsNotSupportedYet);335 }336 &named.1337 } else {338 &$args.unnamed[$id]339 };340 let $name = push_description_frame(|| format!("evaluating builtin argument {}", stringify!($name)), || {341 let value = evaluate($ctx.clone(), &$name)?;342 $ty.check(&value)?;343 Ok(value)344 })?;345 $(346 let $name = if let $match(v) = $name {347 v348 } else {349 unreachable!();350 };351 )?352 )+353 ($handler as crate::Result<_>)354 }};355}crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-macros/src/lib.rs
+++ b/crates/jrsonnet-macros/src/lib.rs
@@ -1,6 +1,10 @@
use proc_macro2::Span;
use quote::quote;
-use syn::{parse_macro_input, FnArg, Ident, ItemFn, Pat};
+use syn::{parse_macro_input, FnArg, Ident, ItemFn, Pat, PatType};
+
+fn is_location_arg(t: &PatType) -> bool {
+ t.attrs.iter().any(|a| a.path.is_ident("location"))
+}
#[proc_macro_attribute]
pub fn builtin(
@@ -8,14 +12,11 @@
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
// syn::ItemFn::parse(input)
- let fun: ItemFn = parse_macro_input!(item);
+ let mut fun: ItemFn = parse_macro_input!(item);
- let inner_name = Ident::new("inner", Span::call_site());
- let mut inner_fun = fun.clone();
- inner_fun.sig.ident = inner_name.clone();
let result = match fun.sig.output {
syn::ReturnType::Default => panic!("builtin should return something"),
- syn::ReturnType::Type(_, ty) => ty,
+ syn::ReturnType::Type(_, ref ty) => ty.clone(),
};
let params = fun
@@ -26,6 +27,7 @@
FnArg::Receiver(_) => unreachable!(),
FnArg::Typed(t) => t,
})
+ .filter(|a| !is_location_arg(a))
.map(|t| {
let ident = match &t.pat as &Pat {
Pat::Ident(i) => i.ident.to_string(),
@@ -39,38 +41,53 @@
has_default: #optional,
}
}
- });
+ })
+ .collect::<Vec<_>>();
let args = fun
.sig
.inputs
- .iter()
+ .iter_mut()
.map(|i| match i {
FnArg::Receiver(_) => unreachable!(),
FnArg::Typed(t) => t,
})
.map(|t| {
- let ident = match &t.pat as &Pat {
- Pat::Ident(i) => i.ident.to_string(),
- _ => panic!("only idents supported yet"),
- };
- let ty = &t.ty;
- quote! {{
- let value = parsed.get(#ident).unwrap();
+ let count_before = t.attrs.len();
+ t.attrs.retain(|a| !a.path.is_ident("location"));
+ let count_after = t.attrs.len();
+ let is_location = count_before != count_after;
+ if is_location {
+ quote! {{
+ loc
+ }}
+ } else {
+ let ident = match &t.pat as &Pat {
+ Pat::Ident(i) => i.ident.to_string(),
+ _ => panic!("only idents supported yet"),
+ };
+ let ty = &t.ty;
+ quote! {{
+ let value = parsed.get(#ident).unwrap();
- jrsonnet_evaluator::push_description_frame(
- || format!("argument <{}> evaluation", #ident),
- || <#ty>::try_from(value.evaluate()?),
- )?
- }}
- });
+ jrsonnet_evaluator::push_description_frame(
+ || format!("argument <{}> evaluation", #ident),
+ || <#ty>::try_from(value.evaluate()?),
+ )?
+ }}
+ }
+ }).collect::<Vec<_>>();
+
+ let inner_name = Ident::new("inner", Span::call_site());
+ let mut inner_fun = fun.clone();
+ inner_fun.sig.ident = inner_name.clone();
let attrs = &fun.attrs;
let vis = &fun.vis;
let name = &fun.sig.ident;
(quote! {
#(#attrs)*
- #vis fn #name(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
+ #vis fn #name(context: Context, loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
#inner_fun
use jrsonnet_evaluator::function::BuiltinParam;
const PARAMS: &'static [BuiltinParam] = &[
crates/jrsonnet-types/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-types/src/lib.rs
+++ b/crates/jrsonnet-types/src/lib.rs
@@ -191,9 +191,9 @@
write!(f, "}}")?;
}
ComplexValType::Union(v) => write_union(f, true, v.iter())?,
- ComplexValType::UnionRef(v) => write_union(f, true, v.iter().map(|v| *v))?,
+ ComplexValType::UnionRef(v) => write_union(f, true, v.iter().copied())?,
ComplexValType::Sum(v) => write_union(f, false, v.iter())?,
- ComplexValType::SumRef(v) => write_union(f, false, v.iter().map(|v| *v))?,
+ ComplexValType::SumRef(v) => write_union(f, false, v.iter().copied())?,
};
Ok(())
}
flake.nixdiffbeforeafterboth--- a/flake.nix
+++ b/flake.nix
@@ -1,21 +1,59 @@
{
- description = "Rust jsonnet implementation";
-
- inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
- inputs.flake-utils.url = "github:numtide/flake-utils";
-
- outputs = { self, nixpkgs, flake-utils }:
+ description = "Dotfiles manager";
+ inputs = {
+ nixpkgs.url = "github:nixos/nixpkgs";
+ flake-utils.url = "github:numtide/flake-utils";
+ naersk.url = "github:nix-community/naersk";
+ rust-overlay.url = "github:oxalica/rust-overlay";
+ pre-commit-hooks.url = "github:cachix/pre-commit-hooks.nix";
+ };
+ outputs = { self, nixpkgs, flake-utils, rust-overlay, pre-commit-hooks, naersk }:
flake-utils.lib.eachDefaultSystem (system:
let
- pkgs = nixpkgs.legacyPackages.${system};
- jrsonnet = pkgs.rustPlatform.buildRustPackage rec {
- pname = "jrsonnet";
- version = "0.1.0";
- src = self;
- cargoSha256 = "sha256-cez8pJ/uwj+PHAPQwpSB4CKaxcP8Uvv8xguOrVXR2xE=";
+ pkgs = import nixpkgs
+ {
+ inherit system;
+ overlays = [ rust-overlay.overlay ];
+ };
+ rust = ((pkgs.rustChannelOf { date = "2021-11-11"; channel = "nightly"; }).default.override {
+ extensions = [ "rust-src" ];
+ });
+ naersk-lib = naersk.lib."${system}".override {
+ rustc = rust;
+ cargo = rust;
+ };
+ in
+ rec {
+ checks = {
+ pre-commit-check = pre-commit-hooks.lib.${system}.run {
+ src = ./.;
+ hooks = {
+ nixpkgs-fmt.enable = true;
+ };
+ };
+ };
+ defaultPackage = naersk-lib.buildPackage {
+ pname = "dotman";
+ root = ./.;
+ buildInputs = with pkgs; [
+ pkgs.sqlite
+ ];
};
- in {
- defaultPackage = jrsonnet;
- devShell = pkgs.mkShell {};
- });
+ devShell = pkgs.mkShell {
+ inherit (checks.pre-commit-check) shellHook;
+ nativeBuildInputs = with pkgs;[
+ pkgs.binutils
+ pkgs.pkgconfig
+ pkgs.clang
+ pkgs.x11
+ pkgs.alsaLib
+ pkgs.libudev
+ pkgs.sqlite
+ rust
+ cargo-edit
+ go-jsonnet
+ ];
+ };
+ }
+ );
}