git.delta.rocks / jrsonnet / refs/commits / 393dcbd419fd

difftreelog

feat(macro) pass call location to builtins

Yaroslav Bolyukin2021-12-26parent: #a5471f2.patch.diff
in: master

8 files changed

addedcmds/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" }
addedcmds/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);
+}
modifiedcrates/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]
modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/error.rs
1use crate::{2	builtin::{format::FormatError, sort::SortError},3	typed::TypeLocError,4};5use gcmodule::Trace;6use jrsonnet_interner::IStr;7use jrsonnet_parser::{BinaryOpType, ExprLocation, UnaryOpType};8use jrsonnet_types::ValType;9use std::{10	path::{Path, PathBuf},11	rc::Rc,12};13use thiserror::Error;1415#[derive(Error, Debug, Clone, Trace)]16pub enum Error {17	#[error("intrinsic not found: {0}")]18	IntrinsicNotFound(IStr),19	#[error("argument reordering in intrisics not supported yet")]20	IntrinsicArgumentReorderingIsNotSupportedYet,2122	#[error("operator {0} does not operate on type {1}")]23	UnaryOperatorDoesNotOperateOnType(UnaryOpType, ValType),24	#[error("binary operation {1} {0} {2} is not implemented")]25	BinaryOperatorDoesNotOperateOnValues(BinaryOpType, ValType, ValType),2627	#[error("no top level object in this context")]28	NoTopLevelObjectFound,29	#[error("self is only usable inside objects")]30	CantUseSelfOutsideOfObject,31	#[error("no super found")]32	NoSuperFound,3334	#[error("for loop can only iterate over arrays")]35	InComprehensionCanOnlyIterateOverArray,3637	#[error("array out of bounds: {0} is not within [0,{1})")]38	ArrayBoundsError(usize, usize),3940	#[error("assert failed: {0}")]41	AssertionFailed(IStr),4243	#[error("variable is not defined: {0}")]44	VariableIsNotDefined(IStr),45	#[error("type mismatch: expected {}, got {2} {0}", .1.iter().map(|e| format!("{}", e)).collect::<Vec<_>>().join(", "))]46	TypeMismatch(&'static str, Vec<ValType>, ValType),47	#[error("no such field: {0}")]48	NoSuchField(IStr),4950	#[error("only functions can be called, got {0}")]51	OnlyFunctionsCanBeCalledGot(ValType),52	#[error("parameter {0} is not defined")]53	UnknownFunctionParameter(String),54	#[error("argument {0} is already bound")]55	BindingParameterASecondTime(IStr),56	#[error("too many args, function has {0}")]57	TooManyArgsFunctionHas(usize),58	#[error("function argument is not passed: {0}")]59	FunctionParameterNotBoundInCall(IStr),6061	#[error("external variable is not defined: {0}")]62	UndefinedExternalVariable(IStr),63	#[error("native is not defined: {0}")]64	UndefinedExternalFunction(IStr),6566	#[error("field name should be string, got {0}")]67	FieldMustBeStringGot(ValType),6869	#[error("attempted to index array with string {0}")]70	AttemptedIndexAnArrayWithString(IStr),71	#[error("{0} index type should be {1}, got {2}")]72	ValueIndexMustBeTypeGot(ValType, ValType, ValType),73	#[error("cant index into {0}")]74	CantIndexInto(ValType),75	#[error("{0} is not indexable")]76	ValueIsNotIndexable(ValType),7778	#[error("super can't be used standalone")]79	StandaloneSuper,8081	#[error("can't resolve {1} from {0}")]82	ImportFileNotFound(PathBuf, PathBuf),83	#[error("resolved file not found: {0}")]84	ResolvedFileNotFound(PathBuf),85	#[error("imported file is not valid utf-8: {0:?}")]86	ImportBadFileUtf8(PathBuf),87	#[error("tried to import {1} from {0}, but imports is not supported")]88	ImportNotSupported(PathBuf, PathBuf),89	#[error(90		"syntax error: expected {}, got {:?}",91		.error.expected,92		.source_code.chars().nth(error.location.offset).map(|c| c.to_string()).unwrap_or_else(|| "EOF".into())93	)]94	ImportSyntaxError {95		#[skip_trace]96		path: Rc<Path>,97		source_code: IStr,98		#[skip_trace]99		error: Box<jrsonnet_parser::ParseError>,100	},101102	#[error("runtime error: {0}")]103	RuntimeError(IStr),104	#[error("stack overflow, try to reduce recursion, or set --max-stack to bigger value")]105	StackOverflow,106	#[error("infinite recursion detected")]107	RecursiveLazyValueEvaluation,108	#[error("tried to index by fractional value")]109	FractionalIndex,110	#[error("attempted to divide by zero")]111	DivisionByZero,112113	#[error("string manifest output is not an string")]114	StringManifestOutputIsNotAString,115	#[error("stream manifest output is not an array")]116	StreamManifestOutputIsNotAArray,117	#[error("multi manifest output is not an object")]118	MultiManifestOutputIsNotAObject,119120	#[error("cant recurse stream manifest")]121	StreamManifestOutputCannotBeRecursed,122	#[error("stream manifest output cannot consist of raw strings")]123	StreamManifestCannotNestString,124125	#[error("{0}")]126	ImportCallbackError(String),127	#[error("invalid unicode codepoint: {0}")]128	InvalidUnicodeCodepointGot(u32),129130	#[error("format error: {0}")]131	Format(#[from] FormatError),132	#[error("type error: {0}")]133	TypeError(TypeLocError),134	#[error("sort error: {0}")]135	Sort(#[from] SortError),136137	#[cfg(feature = "anyhow-error")]138	#[error(transparent)]139	Other(Rc<anyhow::Error>),140}141142#[cfg(feature = "anyhow-error")]143impl From<anyhow::Error> for LocError {144	fn from(e: anyhow::Error) -> Self {145		Self::new(Error::Other(Rc::new(e)))146	}147}148149impl From<Error> for LocError {150	fn from(e: Error) -> Self {151		Self::new(e)152	}153}154155#[derive(Clone, Debug, Trace)]156pub struct StackTraceElement {157	pub location: Option<ExprLocation>,158	pub desc: String,159}160#[derive(Debug, Clone, Trace)]161pub struct StackTrace(pub Vec<StackTraceElement>);162163#[derive(Debug, Clone, Trace)]164pub struct LocError(Box<(Error, StackTrace)>);165impl LocError {166	pub fn new(e: Error) -> Self {167		Self(Box::new((e, StackTrace(vec![]))))168	}169170	pub const fn error(&self) -> &Error {171		&(self.0).0172	}173	pub fn error_mut(&mut self) -> &mut Error {174		&mut (self.0).0175	}176	pub const fn trace(&self) -> &StackTrace {177		&(self.0).1178	}179	pub fn trace_mut(&mut self) -> &mut StackTrace {180		&mut (self.0).1181	}182}183184pub type Result<V> = std::result::Result<V, LocError>;185186#[macro_export]187macro_rules! throw {188	($e: expr) => {189		return Err($e.into())190	};191}
after · crates/jrsonnet-evaluator/src/error.rs
1use crate::{2	builtin::{format::FormatError, sort::SortError},3	typed::TypeLocError,4};5use gcmodule::Trace;6use jrsonnet_interner::IStr;7use jrsonnet_parser::{BinaryOpType, ExprLocation, UnaryOpType};8use jrsonnet_types::ValType;9use std::{10	path::{Path, PathBuf},11	rc::Rc,12};13use thiserror::Error;1415#[derive(Error, Debug, Clone, Trace)]16pub enum Error {17	#[error("intrinsic not found: {0}")]18	IntrinsicNotFound(IStr),1920	#[error("operator {0} does not operate on type {1}")]21	UnaryOperatorDoesNotOperateOnType(UnaryOpType, ValType),22	#[error("binary operation {1} {0} {2} is not implemented")]23	BinaryOperatorDoesNotOperateOnValues(BinaryOpType, ValType, ValType),2425	#[error("no top level object in this context")]26	NoTopLevelObjectFound,27	#[error("self is only usable inside objects")]28	CantUseSelfOutsideOfObject,29	#[error("no super found")]30	NoSuperFound,3132	#[error("for loop can only iterate over arrays")]33	InComprehensionCanOnlyIterateOverArray,3435	#[error("array out of bounds: {0} is not within [0,{1})")]36	ArrayBoundsError(usize, usize),3738	#[error("assert failed: {0}")]39	AssertionFailed(IStr),4041	#[error("variable is not defined: {0}")]42	VariableIsNotDefined(IStr),43	#[error("type mismatch: expected {}, got {2} {0}", .1.iter().map(|e| format!("{}", e)).collect::<Vec<_>>().join(", "))]44	TypeMismatch(&'static str, Vec<ValType>, ValType),45	#[error("no such field: {0}")]46	NoSuchField(IStr),4748	#[error("only functions can be called, got {0}")]49	OnlyFunctionsCanBeCalledGot(ValType),50	#[error("parameter {0} is not defined")]51	UnknownFunctionParameter(String),52	#[error("argument {0} is already bound")]53	BindingParameterASecondTime(IStr),54	#[error("too many args, function has {0}")]55	TooManyArgsFunctionHas(usize),56	#[error("function argument is not passed: {0}")]57	FunctionParameterNotBoundInCall(IStr),5859	#[error("external variable is not defined: {0}")]60	UndefinedExternalVariable(IStr),61	#[error("native is not defined: {0}")]62	UndefinedExternalFunction(IStr),6364	#[error("field name should be string, got {0}")]65	FieldMustBeStringGot(ValType),6667	#[error("attempted to index array with string {0}")]68	AttemptedIndexAnArrayWithString(IStr),69	#[error("{0} index type should be {1}, got {2}")]70	ValueIndexMustBeTypeGot(ValType, ValType, ValType),71	#[error("cant index into {0}")]72	CantIndexInto(ValType),73	#[error("{0} is not indexable")]74	ValueIsNotIndexable(ValType),7576	#[error("super can't be used standalone")]77	StandaloneSuper,7879	#[error("can't resolve {1} from {0}")]80	ImportFileNotFound(PathBuf, PathBuf),81	#[error("resolved file not found: {0}")]82	ResolvedFileNotFound(PathBuf),83	#[error("imported file is not valid utf-8: {0:?}")]84	ImportBadFileUtf8(PathBuf),85	#[error("tried to import {1} from {0}, but imports is not supported")]86	ImportNotSupported(PathBuf, PathBuf),87	#[error(88		"syntax error: expected {}, got {:?}",89		.error.expected,90		.source_code.chars().nth(error.location.offset).map(|c| c.to_string()).unwrap_or_else(|| "EOF".into())91	)]92	ImportSyntaxError {93		#[skip_trace]94		path: Rc<Path>,95		source_code: IStr,96		#[skip_trace]97		error: Box<jrsonnet_parser::ParseError>,98	},99100	#[error("runtime error: {0}")]101	RuntimeError(IStr),102	#[error("stack overflow, try to reduce recursion, or set --max-stack to bigger value")]103	StackOverflow,104	#[error("infinite recursion detected")]105	RecursiveLazyValueEvaluation,106	#[error("tried to index by fractional value")]107	FractionalIndex,108	#[error("attempted to divide by zero")]109	DivisionByZero,110111	#[error("string manifest output is not an string")]112	StringManifestOutputIsNotAString,113	#[error("stream manifest output is not an array")]114	StreamManifestOutputIsNotAArray,115	#[error("multi manifest output is not an object")]116	MultiManifestOutputIsNotAObject,117118	#[error("cant recurse stream manifest")]119	StreamManifestOutputCannotBeRecursed,120	#[error("stream manifest output cannot consist of raw strings")]121	StreamManifestCannotNestString,122123	#[error("{0}")]124	ImportCallbackError(String),125	#[error("invalid unicode codepoint: {0}")]126	InvalidUnicodeCodepointGot(u32),127128	#[error("format error: {0}")]129	Format(#[from] FormatError),130	#[error("type error: {0}")]131	TypeError(TypeLocError),132	#[error("sort error: {0}")]133	Sort(#[from] SortError),134135	#[cfg(feature = "anyhow-error")]136	#[error(transparent)]137	Other(Rc<anyhow::Error>),138}139140#[cfg(feature = "anyhow-error")]141impl From<anyhow::Error> for LocError {142	fn from(e: anyhow::Error) -> Self {143		Self::new(Error::Other(Rc::new(e)))144	}145}146147impl From<Error> for LocError {148	fn from(e: Error) -> Self {149		Self::new(e)150	}151}152153#[derive(Clone, Debug, Trace)]154pub struct StackTraceElement {155	pub location: Option<ExprLocation>,156	pub desc: String,157}158#[derive(Debug, Clone, Trace)]159pub struct StackTrace(pub Vec<StackTraceElement>);160161#[derive(Debug, Clone, Trace)]162pub struct LocError(Box<(Error, StackTrace)>);163impl LocError {164	pub fn new(e: Error) -> Self {165		Self(Box::new((e, StackTrace(vec![]))))166	}167168	pub const fn error(&self) -> &Error {169		&(self.0).0170	}171	pub fn error_mut(&mut self) -> &mut Error {172		&mut (self.0).0173	}174	pub const fn trace(&self) -> &StackTrace {175		&(self.0).1176	}177	pub fn trace_mut(&mut self) -> &mut StackTrace {178		&mut (self.0).1179	}180}181182pub type Result<V> = std::result::Result<V, LocError>;183184#[macro_export]185macro_rules! throw {186	($e: expr) => {187		return Err($e.into())188	};189}
modifiedcrates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function.rs
+++ b/crates/jrsonnet-evaluator/src/function.rs
@@ -311,45 +311,3 @@
 
 	Ok(body_ctx.extend(out, None, None, None))
 }
-
-#[macro_export]
-macro_rules! parse_args {
-	($ctx: expr, $fn_name: expr, $args: expr, $total_args: expr, [
-		$($id: expr, $name: ident: $ty: expr $(=>$match: path)?);+ $(;)?
-	], $handler:block) => {{
-		use $crate::{error::Error::*, throw, evaluate, push_description_frame, typed::CheckType};
-
-		let args = $args;
-		if args.unnamed.len() + args.named.len() > $total_args {
-			throw!(TooManyArgsFunctionHas($total_args));
-		}
-		$(
-			if args.unnamed.len() + args.named.len() <= $id {
-				throw!(FunctionParameterNotBoundInCall(stringify!($name).into()));
-			}
-			// Is named
-			let $name = if $id >= $args.unnamed.len() {
-				let named = &args.named[$id - $args.unnamed.len()];
-				if &named.0 != stringify!($name) {
-					throw!(IntrinsicArgumentReorderingIsNotSupportedYet);
-				}
-				&named.1
-			} else {
-				&$args.unnamed[$id]
-			};
-			let $name = push_description_frame(|| format!("evaluating builtin argument {}", stringify!($name)), || {
-				let value = evaluate($ctx.clone(), &$name)?;
-				$ty.check(&value)?;
-				Ok(value)
-			})?;
-			$(
-				let $name = if let $match(v) = $name {
-					v
-				} else {
-					unreachable!();
-				};
-			)?
-		)+
-		($handler as crate::Result<_>)
-	}};
-}
modifiedcrates/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] = &[
modifiedcrates/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(())
 	}
modifiedflake.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
+          ];
+        };
+      }
+    );
 }