git.delta.rocks / jrsonnet / refs/commits / ce2d8d503b4a

difftreelog

Merge pull request #19 from CertainLach/std-intrinsic

Yaroslav Bulyukin2020-10-20parents: #7e00c7d #0a096fe.patch.diff
in: master
Prevent changing meaning of std in desugared expressions

7 files changed

modifiedcrates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/builtin/mod.rs
1use crate::{2	equals,3	error::{Error::*, Result},4	evaluate, parse_args, primitive_equals, push, throw, with_state, Context, FuncVal, Val,5	ValType,6};7use format::{format_arr, format_obj};8use jrsonnet_parser::{ArgsDesc, ExprLocation};9use manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};10use std::{path::PathBuf, rc::Rc};1112pub mod stdlib;13pub use stdlib::*;1415pub mod format;16pub mod manifest;17pub mod sort;1819#[allow(clippy::cognitive_complexity)]20pub fn call_builtin(21	context: Context,22	loc: &Option<ExprLocation>,23	ns: &str,24	name: &str,25	args: &ArgsDesc,26) -> Result<Val> {27	Ok(match (ns, name as &str) {28		// arr/string/function29		("std", "length") => parse_args!(context, "std.length", args, 1, [30			0, x: [Val::Str|Val::Arr|Val::Obj], vec![ValType::Str, ValType::Arr, ValType::Obj];31		], {32			Ok(match x {33				Val::Str(n) => Val::Num(n.chars().count() as f64),34				Val::Arr(i) => Val::Num(i.len() as f64),35				Val::Obj(o) => Val::Num(36					o.fields_visibility()37						.into_iter()38						.filter(|(_k, v)| *v)39						.count() as f64,40				),41				_ => unreachable!(),42			})43		})?,44		// any45		("std", "type") => parse_args!(context, "std.type", args, 1, [46			0, x, vec![];47		], {48			Ok(Val::Str(x.value_type()?.name().into()))49		})?,50		// length, idx=>any51		("std", "makeArray") => parse_args!(context, "std.makeArray", args, 2, [52			0, sz: [Val::Num]!!Val::Num, vec![ValType::Num];53			1, func: [Val::Func]!!Val::Func, vec![ValType::Func];54		], {55			if sz < 0.0 {56				throw!(RuntimeError(format!("makeArray requires size >= 0, got {}", sz).into()));57			}58			let mut out = Vec::with_capacity(sz as usize);59			for i in 0..sz as usize {60				out.push(func.evaluate_values(61					Context::new(),62					&[Val::Num(i as f64)]63				)?)64			}65			Ok(Val::Arr(Rc::new(out)))66		})?,67		// string68		("std", "codepoint") => parse_args!(context, "std.codepoint", args, 1, [69			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];70		], {71			assert!(72				str.chars().count() == 1,73				"std.codepoint should receive single char string"74			);75			Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))76		})?,77		// object, includeHidden78		("std", "objectFieldsEx") => parse_args!(context, "std.objectFieldsEx",args, 2, [79			0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];80			1, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];81		], {82			let mut out = obj.fields_visibility()83				.into_iter()84				.filter(|(_k, v)| *v || inc_hidden)85				.map(|(k, _v)|k)86				.collect::<Vec<_>>();87			out.sort();88			Ok(Val::Arr(Rc::new(out.into_iter().map(Val::Str).collect())))89		})?,90		// object, field, includeHidden91		("std", "objectHasEx") => parse_args!(context, "std.objectHasEx", args, 3, [92			0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];93			1, f: [Val::Str]!!Val::Str, vec![ValType::Str];94			2, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];95		], {96			Ok(Val::Bool(97				obj.fields_visibility()98					.into_iter()99					.filter(|(_k, v)| *v || inc_hidden)100					.any(|(k, _v)| *k == *f),101			))102		})?,103		("std", "primitiveEquals") => parse_args!(context, "std.primitiveEquals", args, 2, [104			0, a, vec![];105			1, b, vec![];106		], {107			Ok(Val::Bool(primitive_equals(&a, &b)?))108		})?,109		// faster110		("std", "equals") => parse_args!(context, "std.equals", args, 2, [111			0, a, vec![];112			1, b, vec![];113		], {114			Ok(Val::Bool(equals(&a, &b)?))115		})?,116		("std", "modulo") => parse_args!(context, "std.modulo", args, 2, [117			0, a: [Val::Num]!!Val::Num, vec![ValType::Num];118			1, b: [Val::Num]!!Val::Num, vec![ValType::Num];119		], {120			Ok(Val::Num(a % b))121		})?,122		("std", "floor") => parse_args!(context, "std.floor", args, 1, [123			0, x: [Val::Num]!!Val::Num, vec![ValType::Num];124		], {125			Ok(Val::Num(x.floor()))126		})?,127		("std", "log") => parse_args!(context, "std.log", args, 2, [128			0, n: [Val::Num]!!Val::Num, vec![ValType::Num];129		], {130			Ok(Val::Num(n.ln()))131		})?,132		("std", "trace") => parse_args!(context, "std.trace", args, 2, [133			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];134			1, rest, vec![];135		], {136			eprint!("TRACE:");137			if let Some(loc) = loc {138				with_state(|s|{139					let locs = s.map_source_locations(&loc.0, &[loc.1]);140					eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);141				});142			}143			eprintln!(" {}", str);144			Ok(rest)145		})?,146		("std", "pow") => parse_args!(context, "std.modulo", args, 2, [147			0, x: [Val::Num]!!Val::Num, vec![ValType::Num];148			1, n: [Val::Num]!!Val::Num, vec![ValType::Num];149		], {150			Ok(Val::Num(x.powf(n)))151		})?,152		("std", "extVar") => parse_args!(context, "std.extVar", args, 1, [153			0, x: [Val::Str]!!Val::Str, vec![ValType::Str];154		], {155			Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or_else(156				|| UndefinedExternalVariable(x),157			)?)158		})?,159		("std", "native") => parse_args!(context, "std.native", args, 1, [160			0, x: [Val::Str]!!Val::Str, vec![ValType::Str];161		], {162			Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Rc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or_else(163				|| UndefinedExternalFunction(x),164			)?)165		})?,166		("std", "filter") => parse_args!(context, "std.filter", args, 2, [167			0, func: [Val::Func]!!Val::Func, vec![ValType::Func];168			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];169		], {170			Ok(Val::Arr(Rc::new(171				arr.iter()172					.cloned()173					.filter(|e| {174						func175							.evaluate_values(context.clone(), &[e.clone()])176							.unwrap()177							.try_cast_bool("filter predicate")178							.unwrap()179					})180					.collect(),181			)))182		})?,183		// faster184		("std", "foldl") => parse_args!(context, "std.foldl", args, 3, [185			0, func: [Val::Func]!!Val::Func, vec![ValType::Func];186			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];187			2, init, vec![];188		], {189			let mut acc = init;190			for i in arr.iter().cloned() {191				acc = func.evaluate_values(context.clone(), &[acc, i])?;192			}193			Ok(acc)194		})?,195		// faster196		("std", "foldr") => parse_args!(context, "std.foldr", args, 3, [197			0, func: [Val::Func]!!Val::Func, vec![ValType::Func];198			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];199			2, init, vec![];200		], {201			let mut acc = init;202			for i in arr.iter().rev().cloned() {203				acc = func.evaluate_values(context.clone(), &[acc, i])?;204			}205			Ok(acc)206		})?,207		// faster208		#[allow(non_snake_case)]209		("std", "sortImpl") => parse_args!(context, "std.sort", args, 2, [210			0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];211			1, keyF: [Val::Func]!!Val::Func, vec![ValType::Func];212		], {213			if arr.len() <= 1 {214				return Ok(Val::Arr(arr))215			}216			Ok(Val::Arr(sort::sort(context, arr, &keyF)?))217		})?,218		// faster219		("std", "format") => parse_args!(context, "std.format", args, 2, [220			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];221			1, vals, vec![]222		], {223			push(&Some(ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)), ||format!("std.format of {}", str), ||{224				Ok(match vals {225					Val::Arr(vals) => Val::Str(format_arr(&str, &vals)?.into()),226					Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),227					o => Val::Str(format_arr(&str, &[o])?.into()),228				})229			})230		})?,231		// faster232		("std", "range") => parse_args!(context, "std.range", args, 2, [233			0, from: [Val::Num]!!Val::Num, vec![ValType::Num];234			1, to: [Val::Num]!!Val::Num, vec![ValType::Num];235		], {236			let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));237			for i in from as usize..=to as usize {238				out.push(Val::Num(i as f64));239			}240			Ok(Val::Arr(Rc::new(out)))241		})?,242		("std", "char") => parse_args!(context, "std.char", args, 1, [243			0, n: [Val::Num]!!Val::Num, vec![ValType::Num];244		], {245			let mut out = String::new();246			out.push(std::char::from_u32(n as u32).ok_or_else(||247				InvalidUnicodeCodepointGot(n as u32)248			)?);249			Ok(Val::Str(out.into()))250		})?,251		("std", "encodeUTF8") => parse_args!(context, "std.encodeUtf8", args, 1, [252			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];253		], {254			Ok(Val::Arr(Rc::new(str.bytes().map(|b| Val::Num(b as f64)).collect())))255		})?,256		("std", "md5") => parse_args!(context, "std.md5", args, 1, [257			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];258		], {259			Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))260		})?,261		// faster262		("std", "base64") => parse_args!(context, "std.base64", args, 1, [263			0, input: [Val::Str | Val::Arr], vec![ValType::Arr, ValType::Str];264		], {265			Ok(Val::Str(match input {266				Val::Str(s) => {267					base64::encode(s.bytes().collect::<Vec<_>>()).into()268				},269				Val::Arr(a) => {270					base64::encode(a.iter().map(|v| {271						Ok(v.clone().try_cast_num("base64 array")? as u8)272					}).collect::<Result<Vec<_>>>()?).into()273				},274				_ => unreachable!()275			}))276		})?,277		// faster278		("std", "join") => parse_args!(context, "std.join", args, 2, [279			0, sep: [Val::Str|Val::Arr], vec![ValType::Str, ValType::Arr];280			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];281		], {282			Ok(match sep {283				Val::Arr(joiner_items) => {284					let mut out = Vec::new();285286					let mut first = true;287					for item in arr.iter().cloned() {288						if let Val::Arr(items) = item.unwrap_if_lazy()? {289							if !first {290								out.reserve(joiner_items.len());291								out.extend(joiner_items.iter().cloned());292							}293							first = false;294							out.reserve(items.len());295							out.extend(items.iter().cloned());296						} else {297							throw!(RuntimeError("in std.join all items should be arrays".into()));298						}299					}300301					Val::Arr(Rc::new(out))302				},303				Val::Str(sep) => {304					let mut out = String::new();305306					let mut first = true;307					for item in arr.iter().cloned() {308						if let Val::Str(item) = item.unwrap_if_lazy()? {309							if !first {310								out += &sep;311							}312							first = false;313							out += &item;314						} else {315							throw!(RuntimeError("in std.join all items should be strings".into()));316						}317					}318319					Val::Str(out.into())320				},321				_ => unreachable!()322			})323		})?,324		// Faster325		("std", "escapeStringJson") => parse_args!(context, "std.escapeStringJson", args, 1, [326			0, str_: [Val::Str]!!Val::Str, vec![ValType::Str];327		], {328			Ok(Val::Str(escape_string_json(&str_).into()))329		})?,330		// Faster331		("std", "manifestJsonEx") => parse_args!(context, "std.manifestJsonEx", args, 2, [332			0, value, vec![];333			1, indent: [Val::Str]!!Val::Str, vec![ValType::Str];334		], {335			Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {336				padding: &indent,337				mtype: ManifestType::Std,338			})?.into()))339		})?,340		// Faster341		("std", "reverse") => parse_args!(context, "std.reverse", args, 1, [342			0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];343		], {344			let mut marr = arr;345			Rc::make_mut(&mut marr).reverse();346			Ok(Val::Arr(marr))347		})?,348		("std", "id") => parse_args!(context, "std.id", args, 1, [349			0, v, vec![];350		], {351			Ok(v)352		})?,353		(ns, name) => throw!(IntrinsicNotFound(ns.into(), name.into())),354	})355}
after · crates/jrsonnet-evaluator/src/builtin/mod.rs
1use crate::{2	equals,3	error::{Error::*, Result},4	evaluate, parse_args, primitive_equals, push, throw, with_state, Context, FuncVal, Val,5	ValType,6};7use format::{format_arr, format_obj};8use jrsonnet_parser::{ArgsDesc, ExprLocation};9use manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};10use std::{path::PathBuf, rc::Rc};1112pub mod stdlib;13pub use stdlib::*;1415pub mod format;16pub mod manifest;17pub mod sort;1819#[allow(clippy::cognitive_complexity)]20pub fn call_builtin(21	context: Context,22	loc: &Option<ExprLocation>,23	name: &str,24	args: &ArgsDesc,25) -> Result<Val> {26	Ok(match name as &str {27		// arr/string/function28		"length" => parse_args!(context, "std.length", args, 1, [29			0, x: [Val::Str|Val::Arr|Val::Obj], vec![ValType::Str, ValType::Arr, ValType::Obj];30		], {31			Ok(match x {32				Val::Str(n) => Val::Num(n.chars().count() as f64),33				Val::Arr(i) => Val::Num(i.len() as f64),34				Val::Obj(o) => Val::Num(35					o.fields_visibility()36						.into_iter()37						.filter(|(_k, v)| *v)38						.count() as f64,39				),40				_ => unreachable!(),41			})42		})?,43		// any44		"type" => parse_args!(context, "std.type", args, 1, [45			0, x, vec![];46		], {47			Ok(Val::Str(x.value_type()?.name().into()))48		})?,49		// length, idx=>any50		"makeArray" => parse_args!(context, "std.makeArray", args, 2, [51			0, sz: [Val::Num]!!Val::Num, vec![ValType::Num];52			1, func: [Val::Func]!!Val::Func, vec![ValType::Func];53		], {54			if sz < 0.0 {55				throw!(RuntimeError(format!("makeArray requires size >= 0, got {}", sz).into()));56			}57			let mut out = Vec::with_capacity(sz as usize);58			for i in 0..sz as usize {59				out.push(func.evaluate_values(60					Context::new(),61					&[Val::Num(i as f64)]62				)?)63			}64			Ok(Val::Arr(Rc::new(out)))65		})?,66		// string67		"codepoint" => parse_args!(context, "std.codepoint", args, 1, [68			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];69		], {70			assert!(71				str.chars().count() == 1,72				"std.codepoint should receive single char string"73			);74			Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))75		})?,76		// object, includeHidden77		"objectFieldsEx" => parse_args!(context, "std.objectFieldsEx",args, 2, [78			0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];79			1, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];80		], {81			let mut out = obj.fields_visibility()82				.into_iter()83				.filter(|(_k, v)| *v || inc_hidden)84				.map(|(k, _v)|k)85				.collect::<Vec<_>>();86			out.sort();87			Ok(Val::Arr(Rc::new(out.into_iter().map(Val::Str).collect())))88		})?,89		// object, field, includeHidden90		"objectHasEx" => parse_args!(context, "std.objectHasEx", args, 3, [91			0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];92			1, f: [Val::Str]!!Val::Str, vec![ValType::Str];93			2, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];94		], {95			Ok(Val::Bool(96				obj.fields_visibility()97					.into_iter()98					.filter(|(_k, v)| *v || inc_hidden)99					.any(|(k, _v)| *k == *f),100			))101		})?,102		"primitiveEquals" => parse_args!(context, "std.primitiveEquals", args, 2, [103			0, a, vec![];104			1, b, vec![];105		], {106			Ok(Val::Bool(primitive_equals(&a, &b)?))107		})?,108		// faster109		"equals" => parse_args!(context, "std.equals", args, 2, [110			0, a, vec![];111			1, b, vec![];112		], {113			Ok(Val::Bool(equals(&a, &b)?))114		})?,115		"modulo" => parse_args!(context, "std.modulo", args, 2, [116			0, a: [Val::Num]!!Val::Num, vec![ValType::Num];117			1, b: [Val::Num]!!Val::Num, vec![ValType::Num];118		], {119			Ok(Val::Num(a % b))120		})?,121		"floor" => parse_args!(context, "std.floor", args, 1, [122			0, x: [Val::Num]!!Val::Num, vec![ValType::Num];123		], {124			Ok(Val::Num(x.floor()))125		})?,126		"log" => parse_args!(context, "std.log", args, 2, [127			0, n: [Val::Num]!!Val::Num, vec![ValType::Num];128		], {129			Ok(Val::Num(n.ln()))130		})?,131		"trace" => parse_args!(context, "std.trace", args, 2, [132			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];133			1, rest, vec![];134		], {135			eprint!("TRACE:");136			if let Some(loc) = loc {137				with_state(|s|{138					let locs = s.map_source_locations(&loc.0, &[loc.1]);139					eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);140				});141			}142			eprintln!(" {}", str);143			Ok(rest)144		})?,145		"pow" => parse_args!(context, "std.modulo", args, 2, [146			0, x: [Val::Num]!!Val::Num, vec![ValType::Num];147			1, n: [Val::Num]!!Val::Num, vec![ValType::Num];148		], {149			Ok(Val::Num(x.powf(n)))150		})?,151		"extVar" => parse_args!(context, "std.extVar", args, 1, [152			0, x: [Val::Str]!!Val::Str, vec![ValType::Str];153		], {154			Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or_else(155				|| UndefinedExternalVariable(x),156			)?)157		})?,158		"native" => parse_args!(context, "std.native", args, 1, [159			0, x: [Val::Str]!!Val::Str, vec![ValType::Str];160		], {161			Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Rc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or_else(162				|| UndefinedExternalFunction(x),163			)?)164		})?,165		"filter" => parse_args!(context, "std.filter", args, 2, [166			0, func: [Val::Func]!!Val::Func, vec![ValType::Func];167			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];168		], {169			Ok(Val::Arr(Rc::new(170				arr.iter()171					.cloned()172					.filter(|e| {173						func174							.evaluate_values(context.clone(), &[e.clone()])175							.unwrap()176							.try_cast_bool("filter predicate")177							.unwrap()178					})179					.collect(),180			)))181		})?,182		// faster183		"foldl" => parse_args!(context, "std.foldl", args, 3, [184			0, func: [Val::Func]!!Val::Func, vec![ValType::Func];185			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];186			2, init, vec![];187		], {188			let mut acc = init;189			for i in arr.iter().cloned() {190				acc = func.evaluate_values(context.clone(), &[acc, i])?;191			}192			Ok(acc)193		})?,194		// faster195		"foldr" => parse_args!(context, "std.foldr", args, 3, [196			0, func: [Val::Func]!!Val::Func, vec![ValType::Func];197			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];198			2, init, vec![];199		], {200			let mut acc = init;201			for i in arr.iter().rev().cloned() {202				acc = func.evaluate_values(context.clone(), &[acc, i])?;203			}204			Ok(acc)205		})?,206		// faster207		#[allow(non_snake_case)]208		"sortImpl" => parse_args!(context, "std.sort", args, 2, [209			0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];210			1, keyF: [Val::Func]!!Val::Func, vec![ValType::Func];211		], {212			if arr.len() <= 1 {213				return Ok(Val::Arr(arr))214			}215			Ok(Val::Arr(sort::sort(context, arr, &keyF)?))216		})?,217		// faster218		"format" => parse_args!(context, "std.format", args, 2, [219			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];220			1, vals, vec![]221		], {222			push(&Some(ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)), ||format!("std.format of {}", str), ||{223				Ok(match vals {224					Val::Arr(vals) => Val::Str(format_arr(&str, &vals)?.into()),225					Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),226					o => Val::Str(format_arr(&str, &[o])?.into()),227				})228			})229		})?,230		// faster231		"range" => parse_args!(context, "std.range", args, 2, [232			0, from: [Val::Num]!!Val::Num, vec![ValType::Num];233			1, to: [Val::Num]!!Val::Num, vec![ValType::Num];234		], {235			let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));236			for i in from as usize..=to as usize {237				out.push(Val::Num(i as f64));238			}239			Ok(Val::Arr(Rc::new(out)))240		})?,241		"char" => parse_args!(context, "std.char", args, 1, [242			0, n: [Val::Num]!!Val::Num, vec![ValType::Num];243		], {244			let mut out = String::new();245			out.push(std::char::from_u32(n as u32).ok_or_else(||246				InvalidUnicodeCodepointGot(n as u32)247			)?);248			Ok(Val::Str(out.into()))249		})?,250		"encodeUTF8" => parse_args!(context, "std.encodeUtf8", args, 1, [251			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];252		], {253			Ok(Val::Arr(Rc::new(str.bytes().map(|b| Val::Num(b as f64)).collect())))254		})?,255		"md5" => parse_args!(context, "std.md5", args, 1, [256			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];257		], {258			Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))259		})?,260		// faster261		"base64" => parse_args!(context, "std.base64", args, 1, [262			0, input: [Val::Str | Val::Arr], vec![ValType::Arr, ValType::Str];263		], {264			Ok(Val::Str(match input {265				Val::Str(s) => {266					base64::encode(s.bytes().collect::<Vec<_>>()).into()267				},268				Val::Arr(a) => {269					base64::encode(a.iter().map(|v| {270						Ok(v.clone().try_cast_num("base64 array")? as u8)271					}).collect::<Result<Vec<_>>>()?).into()272				},273				_ => unreachable!()274			}))275		})?,276		// faster277		"join" => parse_args!(context, "std.join", args, 2, [278			0, sep: [Val::Str|Val::Arr], vec![ValType::Str, ValType::Arr];279			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];280		], {281			Ok(match sep {282				Val::Arr(joiner_items) => {283					let mut out = Vec::new();284285					let mut first = true;286					for item in arr.iter().cloned() {287						if let Val::Arr(items) = item.unwrap_if_lazy()? {288							if !first {289								out.reserve(joiner_items.len());290								out.extend(joiner_items.iter().cloned());291							}292							first = false;293							out.reserve(items.len());294							out.extend(items.iter().cloned());295						} else {296							throw!(RuntimeError("in std.join all items should be arrays".into()));297						}298					}299300					Val::Arr(Rc::new(out))301				},302				Val::Str(sep) => {303					let mut out = String::new();304305					let mut first = true;306					for item in arr.iter().cloned() {307						if let Val::Str(item) = item.unwrap_if_lazy()? {308							if !first {309								out += &sep;310							}311							first = false;312							out += &item;313						} else {314							throw!(RuntimeError("in std.join all items should be strings".into()));315						}316					}317318					Val::Str(out.into())319				},320				_ => unreachable!()321			})322		})?,323		// Faster324		"escapeStringJson" => parse_args!(context, "std.escapeStringJson", args, 1, [325			0, str_: [Val::Str]!!Val::Str, vec![ValType::Str];326		], {327			Ok(Val::Str(escape_string_json(&str_).into()))328		})?,329		// Faster330		"manifestJsonEx" => parse_args!(context, "std.manifestJsonEx", args, 2, [331			0, value, vec![];332			1, indent: [Val::Str]!!Val::Str, vec![ValType::Str];333		], {334			Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {335				padding: &indent,336				mtype: ManifestType::Std,337			})?.into()))338		})?,339		// Faster340		"reverse" => parse_args!(context, "std.reverse", args, 1, [341			0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];342		], {343			let mut marr = arr;344			Rc::make_mut(&mut marr).reverse();345			Ok(Val::Arr(marr))346		})?,347		"id" => parse_args!(context, "std.id", args, 1, [348			0, v, vec![];349		], {350			Ok(v)351		})?,352		name => throw!(IntrinsicNotFound(name.into())),353	})354}
modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -8,8 +8,8 @@
 
 #[derive(Error, Debug, Clone)]
 pub enum Error {
-	#[error("intrinsic not found: {0}.{1}")]
-	IntrinsicNotFound(Rc<str>, Rc<str>),
+	#[error("intrinsic not found: {0}")]
+	IntrinsicNotFound(Rc<str>),
 	#[error("argument reordering in intrisics not supported yet")]
 	IntrinsicArgumentReorderingIsNotSupportedYet,
 
modifiedcrates/jrsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate.rs
@@ -466,10 +466,8 @@
 						|| {
 							if let Some(v) = v.get(s.clone())? {
 								Ok(v.unwrap_if_lazy()?)
-							} else if let Some(Val::Str(n)) =
-								v.get("__intrinsic_namespace__".into())?
-							{
-								Ok(Val::Func(Rc::new(FuncVal::Intrinsic(n, s))))
+							} else if v.get("__intrinsic_namespace__".into())?.is_some() {
+								Ok(Val::Func(Rc::new(FuncVal::Intrinsic(s))))
 							} else {
 								throw!(NoSuchField(s))
 							}
@@ -558,6 +556,7 @@
 		Function(params, body) => {
 			evaluate_method(context, "anonymous".into(), params.clone(), body.clone())
 		}
+		Intrinsic(name) => Val::Func(Rc::new(FuncVal::Intrinsic(name.clone()))),
 		AssertExpr(AssertStmt(value, msg), returned) => {
 			let assertion_result = push(
 				&value.1,
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -893,4 +893,12 @@
 		)?;
 		Ok(())
 	}
+
+	#[test]
+	fn constant_intrinsic() -> crate::error::Result<()> {
+		assert_eval!(
+			"local std2 = std; local std = std2 { primitiveEquals(a, b):: false }; 1 == 1"
+		);
+		Ok(())
+	}
 }
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -76,7 +76,7 @@
 	/// Plain function implemented in jsonnet
 	Normal(FuncDesc),
 	/// Standard library function
-	Intrinsic(Rc<str>, Rc<str>),
+	Intrinsic(Rc<str>),
 	/// Library functions implemented in native
 	NativeExt(Rc<str>, Rc<NativeCallback>),
 }
@@ -85,7 +85,7 @@
 	fn eq(&self, other: &Self) -> bool {
 		match (self, other) {
 			(Self::Normal(a), Self::Normal(b)) => a == b,
-			(Self::Intrinsic(ans, an), Self::Intrinsic(bns, bn)) => ans == bns && an == bn,
+			(Self::Intrinsic(an), Self::Intrinsic(bn)) => an == bn,
 			(Self::NativeExt(an, _), Self::NativeExt(bn, _)) => an == bn,
 			(..) => false,
 		}
@@ -93,12 +93,12 @@
 }
 impl FuncVal {
 	pub fn is_ident(&self) -> bool {
-		matches!(&self, Self::Intrinsic(ns, n) if ns as &str == "std" && n as &str == "id")
+		matches!(&self, Self::Intrinsic(n) if n as &str == "id")
 	}
 	pub fn name(&self) -> Rc<str> {
 		match self {
 			Self::Normal(normal) => normal.name.clone(),
-			Self::Intrinsic(ns, name) => format!("intrinsic.{}.{}", ns, name).into(),
+			Self::Intrinsic(name) => format!("std.{}", name).into(),
 			Self::NativeExt(n, _) => format!("native.{}", n).into(),
 		}
 	}
@@ -120,7 +120,7 @@
 				)?;
 				evaluate(ctx, &func.body)
 			}
-			Self::Intrinsic(ns, name) => call_builtin(call_ctx, loc, ns, name, args),
+			Self::Intrinsic(name) => call_builtin(call_ctx, loc, name, args),
 			Self::NativeExt(_name, handler) => {
 				let args = parse_function_call(call_ctx, None, &handler.params, args, true)?;
 				let mut out_args = Vec::with_capacity(handler.params.len());
@@ -149,7 +149,7 @@
 				)?;
 				evaluate(ctx, &func.body)
 			}
-			Self::Intrinsic(_, _) => todo!(),
+			Self::Intrinsic(_) => todo!(),
 			Self::NativeExt(_, _) => todo!(),
 		}
 	}
@@ -160,7 +160,7 @@
 				let ctx = place_args(call_ctx, Some(func.ctx.clone()), &func.params, args)?;
 				evaluate(ctx, &func.body)
 			}
-			Self::Intrinsic(_, _) => todo!(),
+			Self::Intrinsic(_) => todo!(),
 			Self::NativeExt(_, _) => todo!(),
 		}
 	}
modifiedcrates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth
--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -311,6 +311,8 @@
 	Index(LocExpr, LocExpr),
 	/// function(x) x
 	Function(ParamsDesc, LocExpr),
+	/// std.primitiveEquals
+	Intrinsic(Rc<str>),
 	/// if true == false then 1 else 2
 	IfElse {
 		cond: IfSpecData,
modifiedcrates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-parser/src/lib.rs
+++ b/crates/jrsonnet-parser/src/lib.rs
@@ -221,18 +221,12 @@
 				a:(@) _ "&" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::BitAnd, b))}
 				--
 				a:(@) _ "==" _ b:@ {loc_expr_todo!(Expr::Apply(
-					el!(Expr::Index(
-						el!(Expr::Var("std".into())),
-						el!(Expr::Str("equals".into()))
-					)),
+					el!(Expr::Intrinsic("equals".into())),
 					ArgsDesc(vec![Arg(None, a), Arg(None, b)]),
 					true
 				))}
 				a:(@) _ "!=" _ b:@ {loc_expr_todo!(Expr::UnaryOp(UnaryOpType::Not, el!(Expr::Apply(
-					el!(Expr::Index(
-						el!(Expr::Var("std".into())),
-						el!(Expr::Str("equals".into()))
-					)),
+					el!(Expr::Intrinsic("equals".into())),
 					ArgsDesc(vec![Arg(None, a), Arg(None, b)]),
 					true
 				))))}
@@ -242,10 +236,7 @@
 				a:(@) _ "<=" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Lte, b))}
 				a:(@) _ ">=" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Gte, b))}
 				a:(@) _ keyword("in") _ b:@ {loc_expr_todo!(Expr::Apply(
-					el!(Expr::Index(
-						el!(Expr::Var("std".into())),
-						el!(Expr::Str("objectHasEx".into()))
-					)), ArgsDesc(vec![Arg(None, b), Arg(None, a), Arg(None, el!(Expr::Literal(LiteralType::True)))]),
+					el!(Expr::Intrinsic("objectHasEx".into())), ArgsDesc(vec![Arg(None, b), Arg(None, a), Arg(None, el!(Expr::Literal(LiteralType::True)))]),
 					true
 				))}
 				--
@@ -258,10 +249,7 @@
 				a:(@) _ "*" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Mul, b))}
 				a:(@) _ "/" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Div, b))}
 				a:(@) _ "%" _ b:@ {loc_expr_todo!(Expr::Apply(
-					el!(Expr::Index(
-						el!(Expr::Var("std".into())),
-						el!(Expr::Str("mod".into()))
-					)), ArgsDesc(vec![Arg(None, a), Arg(None, b)]),
+					el!(Expr::Intrinsic("mod".into())), ArgsDesc(vec![Arg(None, a), Arg(None, b)]),
 					false
 				))}
 				--
@@ -270,10 +258,7 @@
 						"~" _ b:@ { loc_expr_todo!(Expr::UnaryOp(UnaryOpType::BitNot, b)) }
 				--
 				a:(@) _ "[" _ s:slice_desc(s) _ "]" {loc_expr_todo!(Expr::Apply(
-					el!(Expr::Index(
-						el!(Expr::Var("std".into())),
-						el!(Expr::Str("slice".into())),
-					)),
+					el!(Expr::Intrinsic("slice".into())),
 					ArgsDesc(vec![
 						Arg(None, a),
 						Arg(None, s.start.unwrap_or_else(||el!(Expr::Literal(LiteralType::Null)))),