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
--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -20,13 +20,12 @@
 pub fn call_builtin(
 	context: Context,
 	loc: &Option<ExprLocation>,
-	ns: &str,
 	name: &str,
 	args: &ArgsDesc,
 ) -> Result<Val> {
-	Ok(match (ns, name as &str) {
+	Ok(match name as &str {
 		// arr/string/function
-		("std", "length") => parse_args!(context, "std.length", args, 1, [
+		"length" => parse_args!(context, "std.length", args, 1, [
 			0, x: [Val::Str|Val::Arr|Val::Obj], vec![ValType::Str, ValType::Arr, ValType::Obj];
 		], {
 			Ok(match x {
@@ -42,13 +41,13 @@
 			})
 		})?,
 		// any
-		("std", "type") => parse_args!(context, "std.type", args, 1, [
+		"type" => parse_args!(context, "std.type", args, 1, [
 			0, x, vec![];
 		], {
 			Ok(Val::Str(x.value_type()?.name().into()))
 		})?,
 		// length, idx=>any
-		("std", "makeArray") => parse_args!(context, "std.makeArray", args, 2, [
+		"makeArray" => parse_args!(context, "std.makeArray", args, 2, [
 			0, sz: [Val::Num]!!Val::Num, vec![ValType::Num];
 			1, func: [Val::Func]!!Val::Func, vec![ValType::Func];
 		], {
@@ -65,7 +64,7 @@
 			Ok(Val::Arr(Rc::new(out)))
 		})?,
 		// string
-		("std", "codepoint") => parse_args!(context, "std.codepoint", args, 1, [
+		"codepoint" => parse_args!(context, "std.codepoint", args, 1, [
 			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
 		], {
 			assert!(
@@ -75,7 +74,7 @@
 			Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))
 		})?,
 		// object, includeHidden
-		("std", "objectFieldsEx") => parse_args!(context, "std.objectFieldsEx",args, 2, [
+		"objectFieldsEx" => parse_args!(context, "std.objectFieldsEx",args, 2, [
 			0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];
 			1, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];
 		], {
@@ -88,7 +87,7 @@
 			Ok(Val::Arr(Rc::new(out.into_iter().map(Val::Str).collect())))
 		})?,
 		// object, field, includeHidden
-		("std", "objectHasEx") => parse_args!(context, "std.objectHasEx", args, 3, [
+		"objectHasEx" => parse_args!(context, "std.objectHasEx", args, 3, [
 			0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];
 			1, f: [Val::Str]!!Val::Str, vec![ValType::Str];
 			2, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];
@@ -100,36 +99,36 @@
 					.any(|(k, _v)| *k == *f),
 			))
 		})?,
-		("std", "primitiveEquals") => parse_args!(context, "std.primitiveEquals", args, 2, [
+		"primitiveEquals" => parse_args!(context, "std.primitiveEquals", args, 2, [
 			0, a, vec![];
 			1, b, vec![];
 		], {
 			Ok(Val::Bool(primitive_equals(&a, &b)?))
 		})?,
 		// faster
-		("std", "equals") => parse_args!(context, "std.equals", args, 2, [
+		"equals" => parse_args!(context, "std.equals", args, 2, [
 			0, a, vec![];
 			1, b, vec![];
 		], {
 			Ok(Val::Bool(equals(&a, &b)?))
 		})?,
-		("std", "modulo") => parse_args!(context, "std.modulo", args, 2, [
+		"modulo" => parse_args!(context, "std.modulo", args, 2, [
 			0, a: [Val::Num]!!Val::Num, vec![ValType::Num];
 			1, b: [Val::Num]!!Val::Num, vec![ValType::Num];
 		], {
 			Ok(Val::Num(a % b))
 		})?,
-		("std", "floor") => parse_args!(context, "std.floor", args, 1, [
+		"floor" => parse_args!(context, "std.floor", args, 1, [
 			0, x: [Val::Num]!!Val::Num, vec![ValType::Num];
 		], {
 			Ok(Val::Num(x.floor()))
 		})?,
-		("std", "log") => parse_args!(context, "std.log", args, 2, [
+		"log" => parse_args!(context, "std.log", args, 2, [
 			0, n: [Val::Num]!!Val::Num, vec![ValType::Num];
 		], {
 			Ok(Val::Num(n.ln()))
 		})?,
-		("std", "trace") => parse_args!(context, "std.trace", args, 2, [
+		"trace" => parse_args!(context, "std.trace", args, 2, [
 			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
 			1, rest, vec![];
 		], {
@@ -143,27 +142,27 @@
 			eprintln!(" {}", str);
 			Ok(rest)
 		})?,
-		("std", "pow") => parse_args!(context, "std.modulo", args, 2, [
+		"pow" => parse_args!(context, "std.modulo", args, 2, [
 			0, x: [Val::Num]!!Val::Num, vec![ValType::Num];
 			1, n: [Val::Num]!!Val::Num, vec![ValType::Num];
 		], {
 			Ok(Val::Num(x.powf(n)))
 		})?,
-		("std", "extVar") => parse_args!(context, "std.extVar", args, 1, [
+		"extVar" => parse_args!(context, "std.extVar", args, 1, [
 			0, x: [Val::Str]!!Val::Str, vec![ValType::Str];
 		], {
 			Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or_else(
 				|| UndefinedExternalVariable(x),
 			)?)
 		})?,
-		("std", "native") => parse_args!(context, "std.native", args, 1, [
+		"native" => parse_args!(context, "std.native", args, 1, [
 			0, x: [Val::Str]!!Val::Str, vec![ValType::Str];
 		], {
 			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(
 				|| UndefinedExternalFunction(x),
 			)?)
 		})?,
-		("std", "filter") => parse_args!(context, "std.filter", args, 2, [
+		"filter" => parse_args!(context, "std.filter", args, 2, [
 			0, func: [Val::Func]!!Val::Func, vec![ValType::Func];
 			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
 		], {
@@ -181,7 +180,7 @@
 			)))
 		})?,
 		// faster
-		("std", "foldl") => parse_args!(context, "std.foldl", args, 3, [
+		"foldl" => parse_args!(context, "std.foldl", args, 3, [
 			0, func: [Val::Func]!!Val::Func, vec![ValType::Func];
 			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
 			2, init, vec![];
@@ -193,7 +192,7 @@
 			Ok(acc)
 		})?,
 		// faster
-		("std", "foldr") => parse_args!(context, "std.foldr", args, 3, [
+		"foldr" => parse_args!(context, "std.foldr", args, 3, [
 			0, func: [Val::Func]!!Val::Func, vec![ValType::Func];
 			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
 			2, init, vec![];
@@ -206,7 +205,7 @@
 		})?,
 		// faster
 		#[allow(non_snake_case)]
-		("std", "sortImpl") => parse_args!(context, "std.sort", args, 2, [
+		"sortImpl" => parse_args!(context, "std.sort", args, 2, [
 			0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
 			1, keyF: [Val::Func]!!Val::Func, vec![ValType::Func];
 		], {
@@ -216,7 +215,7 @@
 			Ok(Val::Arr(sort::sort(context, arr, &keyF)?))
 		})?,
 		// faster
-		("std", "format") => parse_args!(context, "std.format", args, 2, [
+		"format" => parse_args!(context, "std.format", args, 2, [
 			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
 			1, vals, vec![]
 		], {
@@ -229,7 +228,7 @@
 			})
 		})?,
 		// faster
-		("std", "range") => parse_args!(context, "std.range", args, 2, [
+		"range" => parse_args!(context, "std.range", args, 2, [
 			0, from: [Val::Num]!!Val::Num, vec![ValType::Num];
 			1, to: [Val::Num]!!Val::Num, vec![ValType::Num];
 		], {
@@ -239,7 +238,7 @@
 			}
 			Ok(Val::Arr(Rc::new(out)))
 		})?,
-		("std", "char") => parse_args!(context, "std.char", args, 1, [
+		"char" => parse_args!(context, "std.char", args, 1, [
 			0, n: [Val::Num]!!Val::Num, vec![ValType::Num];
 		], {
 			let mut out = String::new();
@@ -248,18 +247,18 @@
 			)?);
 			Ok(Val::Str(out.into()))
 		})?,
-		("std", "encodeUTF8") => parse_args!(context, "std.encodeUtf8", args, 1, [
+		"encodeUTF8" => parse_args!(context, "std.encodeUtf8", args, 1, [
 			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
 		], {
 			Ok(Val::Arr(Rc::new(str.bytes().map(|b| Val::Num(b as f64)).collect())))
 		})?,
-		("std", "md5") => parse_args!(context, "std.md5", args, 1, [
+		"md5" => parse_args!(context, "std.md5", args, 1, [
 			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
 		], {
 			Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))
 		})?,
 		// faster
-		("std", "base64") => parse_args!(context, "std.base64", args, 1, [
+		"base64" => parse_args!(context, "std.base64", args, 1, [
 			0, input: [Val::Str | Val::Arr], vec![ValType::Arr, ValType::Str];
 		], {
 			Ok(Val::Str(match input {
@@ -275,7 +274,7 @@
 			}))
 		})?,
 		// faster
-		("std", "join") => parse_args!(context, "std.join", args, 2, [
+		"join" => parse_args!(context, "std.join", args, 2, [
 			0, sep: [Val::Str|Val::Arr], vec![ValType::Str, ValType::Arr];
 			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
 		], {
@@ -322,13 +321,13 @@
 			})
 		})?,
 		// Faster
-		("std", "escapeStringJson") => parse_args!(context, "std.escapeStringJson", args, 1, [
+		"escapeStringJson" => parse_args!(context, "std.escapeStringJson", args, 1, [
 			0, str_: [Val::Str]!!Val::Str, vec![ValType::Str];
 		], {
 			Ok(Val::Str(escape_string_json(&str_).into()))
 		})?,
 		// Faster
-		("std", "manifestJsonEx") => parse_args!(context, "std.manifestJsonEx", args, 2, [
+		"manifestJsonEx" => parse_args!(context, "std.manifestJsonEx", args, 2, [
 			0, value, vec![];
 			1, indent: [Val::Str]!!Val::Str, vec![ValType::Str];
 		], {
@@ -338,18 +337,18 @@
 			})?.into()))
 		})?,
 		// Faster
-		("std", "reverse") => parse_args!(context, "std.reverse", args, 1, [
+		"reverse" => parse_args!(context, "std.reverse", args, 1, [
 			0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
 		], {
 			let mut marr = arr;
 			Rc::make_mut(&mut marr).reverse();
 			Ok(Val::Arr(marr))
 		})?,
-		("std", "id") => parse_args!(context, "std.id", args, 1, [
+		"id" => parse_args!(context, "std.id", args, 1, [
 			0, v, vec![];
 		], {
 			Ok(v)
 		})?,
-		(ns, name) => throw!(IntrinsicNotFound(ns.into(), name.into())),
+		name => throw!(IntrinsicNotFound(name.into())),
 	})
 }
modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/error.rs
1use crate::{2	builtin::{format::FormatError, sort::SortError},3	ValType,4};5use jrsonnet_parser::{BinaryOpType, ExprLocation, UnaryOpType};6use std::{path::PathBuf, rc::Rc};7use thiserror::Error;89#[derive(Error, Debug, Clone)]10pub enum Error {11	#[error("intrinsic not found: {0}.{1}")]12	IntrinsicNotFound(Rc<str>, Rc<str>),13	#[error("argument reordering in intrisics not supported yet")]14	IntrinsicArgumentReorderingIsNotSupportedYet,1516	#[error("operator {0} does not operate on type {1}")]17	UnaryOperatorDoesNotOperateOnType(UnaryOpType, ValType),18	#[error("binary operation {1} {0} {2} is not implemented")]19	BinaryOperatorDoesNotOperateOnValues(BinaryOpType, ValType, ValType),2021	#[error("no top level object in this context")]22	NoTopLevelObjectFound,23	#[error("self is only usable inside objects")]24	CantUseSelfOutsideOfObject,25	#[error("super is only usable inside objects")]26	CantUseSuperOutsideOfObject,2728	#[error("for loop can only iterate over arrays")]29	InComprehensionCanOnlyIterateOverArray,3031	#[error("array out of bounds: {0} is not within [0,{1})")]32	ArrayBoundsError(usize, usize),3334	#[error("assert failed: {0}")]35	AssertionFailed(Rc<str>),3637	#[error("variable is not defined: {0}")]38	VariableIsNotDefined(Rc<str>),39	#[error("type mismatch: expected {}, got {2} {0}", .1.iter().map(|e| format!("{}", e)).collect::<Vec<_>>().join(", "))]40	TypeMismatch(&'static str, Vec<ValType>, ValType),41	#[error("no such field: {0}")]42	NoSuchField(Rc<str>),4344	#[error("only functions can be called, got {0}")]45	OnlyFunctionsCanBeCalledGot(ValType),46	#[error("parameter {0} is not defined")]47	UnknownFunctionParameter(String),48	#[error("argument {0} is already bound")]49	BindingParameterASecondTime(Rc<str>),50	#[error("too many args, function has {0}")]51	TooManyArgsFunctionHas(usize),52	#[error("founction argument is not passed: {0}")]53	FunctionParameterNotBoundInCall(Rc<str>),5455	#[error("external variable is not defined: {0}")]56	UndefinedExternalVariable(Rc<str>),57	#[error("native is not defined: {0}")]58	UndefinedExternalFunction(Rc<str>),5960	#[error("field name should be string, got {0}")]61	FieldMustBeStringGot(ValType),6263	#[error("attempted to index array with string {0}")]64	AttemptedIndexAnArrayWithString(Rc<str>),65	#[error("{0} index type should be {1}, got {2}")]66	ValueIndexMustBeTypeGot(ValType, ValType, ValType),67	#[error("cant index into {0}")]68	CantIndexInto(ValType),6970	#[error("super can't be used standalone")]71	StandaloneSuper,7273	#[error("can't resolve {1} from {0}")]74	ImportFileNotFound(PathBuf, PathBuf),75	#[error("resolved file not found: {0}")]76	ResolvedFileNotFound(PathBuf),77	#[error("imported file is not valid utf-8: {0:?}")]78	ImportBadFileUtf8(PathBuf),79	#[error("tried to import {1} from {0}, but imports is not supported")]80	ImportNotSupported(PathBuf, PathBuf),81	#[error("syntax error")]82	ImportSyntaxError {83		path: Rc<PathBuf>,84		source_code: Rc<str>,85		error: Box<jrsonnet_parser::ParseError>,86	},8788	#[error("runtime error: {0}")]89	RuntimeError(Rc<str>),90	#[error("stack overflow, try to reduce recursion, or set --max-stack to bigger value")]91	StackOverflow,92	#[error("tried to index by fractional value")]93	FractionalIndex,94	#[error("attempted to divide by zero")]95	DivisionByZero,9697	#[error("string manifest output is not an string")]98	StringManifestOutputIsNotAString,99	#[error("stream manifest output is not an array")]100	StreamManifestOutputIsNotAArray,101	#[error("multi manifest output is not an object")]102	MultiManifestOutputIsNotAObject,103104	#[error("cant recurse stream manifest")]105	StreamManifestOutputCannotBeRecursed,106	#[error("stream manifest output cannot consist of raw strings")]107	StreamManifestCannotNestString,108109	#[error("{0}")]110	ImportCallbackError(String),111	#[error("invalid unicode codepoint: {0}")]112	InvalidUnicodeCodepointGot(u32),113114	#[error("format error: {0}")]115	Format(#[from] FormatError),116	#[error("sort error: {0}")]117	Sort(#[from] SortError),118}119impl From<Error> for LocError {120	fn from(e: Error) -> Self {121		Self::new(e)122	}123}124125#[derive(Clone, Debug)]126pub struct StackTraceElement {127	pub location: ExprLocation,128	pub desc: String,129}130#[derive(Debug, Clone)]131pub struct StackTrace(pub Vec<StackTraceElement>);132133#[derive(Debug, Clone)]134pub struct LocError(Box<(Error, StackTrace)>);135impl LocError {136	pub fn new(e: Error) -> Self {137		Self(Box::new((e, StackTrace(vec![]))))138	}139140	pub const fn error(&self) -> &Error {141		&(self.0).0142	}143	pub const fn trace(&self) -> &StackTrace {144		&(self.0).1145	}146	pub fn trace_mut(&mut self) -> &mut StackTrace {147		&mut (self.0).1148	}149}150151pub type Result<V> = std::result::Result<V, LocError>;152153#[macro_export]154macro_rules! throw {155	($e: expr) => {156		return Err($e.into());157	};158}
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)))),