difftreelog
Merge pull request #19 from CertainLach/std-intrinsic
in: master
Prevent changing meaning of std in desugared expressions
7 files changed
crates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth20pub fn call_builtin(20pub fn call_builtin(21 context: Context,21 context: Context,22 loc: &Option<ExprLocation>,22 loc: &Option<ExprLocation>,23 ns: &str,24 name: &str,23 name: &str,25 args: &ArgsDesc,24 args: &ArgsDesc,26) -> Result<Val> {25) -> Result<Val> {27 Ok(match (ns, name as &str) {26 Ok(match name as &str {28 // arr/string/function27 // arr/string/function29 ("std", "length") => parse_args!(context, "std.length", args, 1, [28 "length" => parse_args!(context, "std.length", args, 1, [30 0, x: [Val::Str|Val::Arr|Val::Obj], vec![ValType::Str, ValType::Arr, ValType::Obj];29 0, x: [Val::Str|Val::Arr|Val::Obj], vec![ValType::Str, ValType::Arr, ValType::Obj];31 ], {30 ], {32 Ok(match x {31 Ok(match x {42 })41 })43 })?,42 })?,44 // any43 // any45 ("std", "type") => parse_args!(context, "std.type", args, 1, [44 "type" => parse_args!(context, "std.type", args, 1, [46 0, x, vec![];45 0, x, vec![];47 ], {46 ], {48 Ok(Val::Str(x.value_type()?.name().into()))47 Ok(Val::Str(x.value_type()?.name().into()))49 })?,48 })?,50 // length, idx=>any49 // length, idx=>any51 ("std", "makeArray") => parse_args!(context, "std.makeArray", args, 2, [50 "makeArray" => parse_args!(context, "std.makeArray", args, 2, [52 0, sz: [Val::Num]!!Val::Num, vec![ValType::Num];51 0, sz: [Val::Num]!!Val::Num, vec![ValType::Num];53 1, func: [Val::Func]!!Val::Func, vec![ValType::Func];52 1, func: [Val::Func]!!Val::Func, vec![ValType::Func];54 ], {53 ], {65 Ok(Val::Arr(Rc::new(out)))64 Ok(Val::Arr(Rc::new(out)))66 })?,65 })?,67 // string66 // string68 ("std", "codepoint") => parse_args!(context, "std.codepoint", args, 1, [67 "codepoint" => parse_args!(context, "std.codepoint", args, 1, [69 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];68 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];70 ], {69 ], {71 assert!(70 assert!(75 Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))74 Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))76 })?,75 })?,77 // object, includeHidden76 // object, includeHidden78 ("std", "objectFieldsEx") => parse_args!(context, "std.objectFieldsEx",args, 2, [77 "objectFieldsEx" => parse_args!(context, "std.objectFieldsEx",args, 2, [79 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];78 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];80 1, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];79 1, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];81 ], {80 ], {88 Ok(Val::Arr(Rc::new(out.into_iter().map(Val::Str).collect())))87 Ok(Val::Arr(Rc::new(out.into_iter().map(Val::Str).collect())))89 })?,88 })?,90 // object, field, includeHidden89 // object, field, includeHidden91 ("std", "objectHasEx") => parse_args!(context, "std.objectHasEx", args, 3, [90 "objectHasEx" => parse_args!(context, "std.objectHasEx", args, 3, [92 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];91 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];93 1, f: [Val::Str]!!Val::Str, vec![ValType::Str];92 1, f: [Val::Str]!!Val::Str, vec![ValType::Str];94 2, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];93 2, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];100 .any(|(k, _v)| *k == *f),99 .any(|(k, _v)| *k == *f),101 ))100 ))102 })?,101 })?,103 ("std", "primitiveEquals") => parse_args!(context, "std.primitiveEquals", args, 2, [102 "primitiveEquals" => parse_args!(context, "std.primitiveEquals", args, 2, [104 0, a, vec![];103 0, a, vec![];105 1, b, vec![];104 1, b, vec![];106 ], {105 ], {107 Ok(Val::Bool(primitive_equals(&a, &b)?))106 Ok(Val::Bool(primitive_equals(&a, &b)?))108 })?,107 })?,109 // faster108 // faster110 ("std", "equals") => parse_args!(context, "std.equals", args, 2, [109 "equals" => parse_args!(context, "std.equals", args, 2, [111 0, a, vec![];110 0, a, vec![];112 1, b, vec![];111 1, b, vec![];113 ], {112 ], {114 Ok(Val::Bool(equals(&a, &b)?))113 Ok(Val::Bool(equals(&a, &b)?))115 })?,114 })?,116 ("std", "modulo") => parse_args!(context, "std.modulo", args, 2, [115 "modulo" => parse_args!(context, "std.modulo", args, 2, [117 0, a: [Val::Num]!!Val::Num, vec![ValType::Num];116 0, a: [Val::Num]!!Val::Num, vec![ValType::Num];118 1, b: [Val::Num]!!Val::Num, vec![ValType::Num];117 1, b: [Val::Num]!!Val::Num, vec![ValType::Num];119 ], {118 ], {120 Ok(Val::Num(a % b))119 Ok(Val::Num(a % b))121 })?,120 })?,122 ("std", "floor") => parse_args!(context, "std.floor", args, 1, [121 "floor" => parse_args!(context, "std.floor", args, 1, [123 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];122 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];124 ], {123 ], {125 Ok(Val::Num(x.floor()))124 Ok(Val::Num(x.floor()))126 })?,125 })?,127 ("std", "log") => parse_args!(context, "std.log", args, 2, [126 "log" => parse_args!(context, "std.log", args, 2, [128 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];127 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];129 ], {128 ], {130 Ok(Val::Num(n.ln()))129 Ok(Val::Num(n.ln()))131 })?,130 })?,132 ("std", "trace") => parse_args!(context, "std.trace", args, 2, [131 "trace" => parse_args!(context, "std.trace", args, 2, [133 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];132 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];134 1, rest, vec![];133 1, rest, vec![];135 ], {134 ], {143 eprintln!(" {}", str);142 eprintln!(" {}", str);144 Ok(rest)143 Ok(rest)145 })?,144 })?,146 ("std", "pow") => parse_args!(context, "std.modulo", args, 2, [145 "pow" => parse_args!(context, "std.modulo", args, 2, [147 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];146 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];148 1, n: [Val::Num]!!Val::Num, vec![ValType::Num];147 1, n: [Val::Num]!!Val::Num, vec![ValType::Num];149 ], {148 ], {150 Ok(Val::Num(x.powf(n)))149 Ok(Val::Num(x.powf(n)))151 })?,150 })?,152 ("std", "extVar") => parse_args!(context, "std.extVar", args, 1, [151 "extVar" => parse_args!(context, "std.extVar", args, 1, [153 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];152 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];154 ], {153 ], {155 Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or_else(154 Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or_else(156 || UndefinedExternalVariable(x),155 || UndefinedExternalVariable(x),157 )?)156 )?)158 })?,157 })?,159 ("std", "native") => parse_args!(context, "std.native", args, 1, [158 "native" => parse_args!(context, "std.native", args, 1, [160 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];159 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];161 ], {160 ], {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(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(163 || UndefinedExternalFunction(x),162 || UndefinedExternalFunction(x),164 )?)163 )?)165 })?,164 })?,166 ("std", "filter") => parse_args!(context, "std.filter", args, 2, [165 "filter" => parse_args!(context, "std.filter", args, 2, [167 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];166 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];168 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];167 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];169 ], {168 ], {181 )))180 )))182 })?,181 })?,183 // faster182 // faster184 ("std", "foldl") => parse_args!(context, "std.foldl", args, 3, [183 "foldl" => parse_args!(context, "std.foldl", args, 3, [185 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];184 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];186 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];185 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];187 2, init, vec![];186 2, init, vec![];193 Ok(acc)192 Ok(acc)194 })?,193 })?,195 // faster194 // faster196 ("std", "foldr") => parse_args!(context, "std.foldr", args, 3, [195 "foldr" => parse_args!(context, "std.foldr", args, 3, [197 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];196 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];198 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];197 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];199 2, init, vec![];198 2, init, vec![];206 })?,205 })?,207 // faster206 // faster208 #[allow(non_snake_case)]207 #[allow(non_snake_case)]209 ("std", "sortImpl") => parse_args!(context, "std.sort", args, 2, [208 "sortImpl" => parse_args!(context, "std.sort", args, 2, [210 0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];209 0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];211 1, keyF: [Val::Func]!!Val::Func, vec![ValType::Func];210 1, keyF: [Val::Func]!!Val::Func, vec![ValType::Func];212 ], {211 ], {216 Ok(Val::Arr(sort::sort(context, arr, &keyF)?))215 Ok(Val::Arr(sort::sort(context, arr, &keyF)?))217 })?,216 })?,218 // faster217 // faster219 ("std", "format") => parse_args!(context, "std.format", args, 2, [218 "format" => parse_args!(context, "std.format", args, 2, [220 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];219 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];221 1, vals, vec![]220 1, vals, vec![]222 ], {221 ], {229 })228 })230 })?,229 })?,231 // faster230 // faster232 ("std", "range") => parse_args!(context, "std.range", args, 2, [231 "range" => parse_args!(context, "std.range", args, 2, [233 0, from: [Val::Num]!!Val::Num, vec![ValType::Num];232 0, from: [Val::Num]!!Val::Num, vec![ValType::Num];234 1, to: [Val::Num]!!Val::Num, vec![ValType::Num];233 1, to: [Val::Num]!!Val::Num, vec![ValType::Num];235 ], {234 ], {239 }238 }240 Ok(Val::Arr(Rc::new(out)))239 Ok(Val::Arr(Rc::new(out)))241 })?,240 })?,242 ("std", "char") => parse_args!(context, "std.char", args, 1, [241 "char" => parse_args!(context, "std.char", args, 1, [243 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];242 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];244 ], {243 ], {245 let mut out = String::new();244 let mut out = String::new();248 )?);247 )?);249 Ok(Val::Str(out.into()))248 Ok(Val::Str(out.into()))250 })?,249 })?,251 ("std", "encodeUTF8") => parse_args!(context, "std.encodeUtf8", args, 1, [250 "encodeUTF8" => parse_args!(context, "std.encodeUtf8", args, 1, [252 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];251 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];253 ], {252 ], {254 Ok(Val::Arr(Rc::new(str.bytes().map(|b| Val::Num(b as f64)).collect())))253 Ok(Val::Arr(Rc::new(str.bytes().map(|b| Val::Num(b as f64)).collect())))255 })?,254 })?,256 ("std", "md5") => parse_args!(context, "std.md5", args, 1, [255 "md5" => parse_args!(context, "std.md5", args, 1, [257 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];256 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];258 ], {257 ], {259 Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))258 Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))260 })?,259 })?,261 // faster260 // faster262 ("std", "base64") => parse_args!(context, "std.base64", args, 1, [261 "base64" => parse_args!(context, "std.base64", args, 1, [263 0, input: [Val::Str | Val::Arr], vec![ValType::Arr, ValType::Str];262 0, input: [Val::Str | Val::Arr], vec![ValType::Arr, ValType::Str];264 ], {263 ], {265 Ok(Val::Str(match input {264 Ok(Val::Str(match input {275 }))274 }))276 })?,275 })?,277 // faster276 // faster278 ("std", "join") => parse_args!(context, "std.join", args, 2, [277 "join" => parse_args!(context, "std.join", args, 2, [279 0, sep: [Val::Str|Val::Arr], vec![ValType::Str, ValType::Arr];278 0, sep: [Val::Str|Val::Arr], vec![ValType::Str, ValType::Arr];280 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];279 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];281 ], {280 ], {322 })321 })323 })?,322 })?,324 // Faster323 // Faster325 ("std", "escapeStringJson") => parse_args!(context, "std.escapeStringJson", args, 1, [324 "escapeStringJson" => parse_args!(context, "std.escapeStringJson", args, 1, [326 0, str_: [Val::Str]!!Val::Str, vec![ValType::Str];325 0, str_: [Val::Str]!!Val::Str, vec![ValType::Str];327 ], {326 ], {328 Ok(Val::Str(escape_string_json(&str_).into()))327 Ok(Val::Str(escape_string_json(&str_).into()))329 })?,328 })?,330 // Faster329 // Faster331 ("std", "manifestJsonEx") => parse_args!(context, "std.manifestJsonEx", args, 2, [330 "manifestJsonEx" => parse_args!(context, "std.manifestJsonEx", args, 2, [332 0, value, vec![];331 0, value, vec![];333 1, indent: [Val::Str]!!Val::Str, vec![ValType::Str];332 1, indent: [Val::Str]!!Val::Str, vec![ValType::Str];334 ], {333 ], {338 })?.into()))337 })?.into()))339 })?,338 })?,340 // Faster339 // Faster341 ("std", "reverse") => parse_args!(context, "std.reverse", args, 1, [340 "reverse" => parse_args!(context, "std.reverse", args, 1, [342 0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];341 0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];343 ], {342 ], {344 let mut marr = arr;343 let mut marr = arr;345 Rc::make_mut(&mut marr).reverse();344 Rc::make_mut(&mut marr).reverse();346 Ok(Val::Arr(marr))345 Ok(Val::Arr(marr))347 })?,346 })?,348 ("std", "id") => parse_args!(context, "std.id", args, 1, [347 "id" => parse_args!(context, "std.id", args, 1, [349 0, v, vec![];348 0, v, vec![];350 ], {349 ], {351 Ok(v)350 Ok(v)352 })?,351 })?,353 (ns, name) => throw!(IntrinsicNotFound(ns.into(), name.into())),352 name => throw!(IntrinsicNotFound(name.into())),354 })353 })355}354}356355crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth889#[derive(Error, Debug, Clone)]9#[derive(Error, Debug, Clone)]10pub enum Error {10pub enum Error {11 #[error("intrinsic not found: {0}.{1}")]11 #[error("intrinsic not found: {0}")]12 IntrinsicNotFound(Rc<str>, Rc<str>),12 IntrinsicNotFound(Rc<str>),13 #[error("argument reordering in intrisics not supported yet")]13 #[error("argument reordering in intrisics not supported yet")]14 IntrinsicArgumentReorderingIsNotSupportedYet,14 IntrinsicArgumentReorderingIsNotSupportedYet,1515crates/jrsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth466 || {466 || {467 if let Some(v) = v.get(s.clone())? {467 if let Some(v) = v.get(s.clone())? {468 Ok(v.unwrap_if_lazy()?)468 Ok(v.unwrap_if_lazy()?)469 } else if let Some(Val::Str(n)) =469 } else if v.get("__intrinsic_namespace__".into())?.is_some() {470 v.get("__intrinsic_namespace__".into())?471 {472 Ok(Val::Func(Rc::new(FuncVal::Intrinsic(n, s))))470 Ok(Val::Func(Rc::new(FuncVal::Intrinsic(s))))473 } else {471 } else {474 throw!(NoSuchField(s))472 throw!(NoSuchField(s))475 }473 }558 Function(params, body) => {556 Function(params, body) => {559 evaluate_method(context, "anonymous".into(), params.clone(), body.clone())557 evaluate_method(context, "anonymous".into(), params.clone(), body.clone())560 }558 }559 Intrinsic(name) => Val::Func(Rc::new(FuncVal::Intrinsic(name.clone()))),561 AssertExpr(AssertStmt(value, msg), returned) => {560 AssertExpr(AssertStmt(value, msg), returned) => {562 let assertion_result = push(561 let assertion_result = push(563 &value.1,562 &value.1,crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth894 Ok(())894 Ok(())895 }895 }896897 #[test]898 fn constant_intrinsic() -> crate::error::Result<()> {899 assert_eval!(900 "local std2 = std; local std = std2 { primitiveEquals(a, b):: false }; 1 == 1"901 );902 Ok(())903 }896}904}897905crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth76 /// Plain function implemented in jsonnet76 /// Plain function implemented in jsonnet77 Normal(FuncDesc),77 Normal(FuncDesc),78 /// Standard library function78 /// Standard library function79 Intrinsic(Rc<str>, Rc<str>),79 Intrinsic(Rc<str>),80 /// Library functions implemented in native80 /// Library functions implemented in native81 NativeExt(Rc<str>, Rc<NativeCallback>),81 NativeExt(Rc<str>, Rc<NativeCallback>),82}82}85 fn eq(&self, other: &Self) -> bool {85 fn eq(&self, other: &Self) -> bool {86 match (self, other) {86 match (self, other) {87 (Self::Normal(a), Self::Normal(b)) => a == b,87 (Self::Normal(a), Self::Normal(b)) => a == b,88 (Self::Intrinsic(ans, an), Self::Intrinsic(bns, bn)) => ans == bns && an == bn,88 (Self::Intrinsic(an), Self::Intrinsic(bn)) => an == bn,89 (Self::NativeExt(an, _), Self::NativeExt(bn, _)) => an == bn,89 (Self::NativeExt(an, _), Self::NativeExt(bn, _)) => an == bn,90 (..) => false,90 (..) => false,91 }91 }92 }92 }93}93}94impl FuncVal {94impl FuncVal {95 pub fn is_ident(&self) -> bool {95 pub fn is_ident(&self) -> bool {96 matches!(&self, Self::Intrinsic(ns, n) if ns as &str == "std" && n as &str == "id")96 matches!(&self, Self::Intrinsic(n) if n as &str == "id")97 }97 }98 pub fn name(&self) -> Rc<str> {98 pub fn name(&self) -> Rc<str> {99 match self {99 match self {100 Self::Normal(normal) => normal.name.clone(),100 Self::Normal(normal) => normal.name.clone(),101 Self::Intrinsic(ns, name) => format!("intrinsic.{}.{}", ns, name).into(),101 Self::Intrinsic(name) => format!("std.{}", name).into(),102 Self::NativeExt(n, _) => format!("native.{}", n).into(),102 Self::NativeExt(n, _) => format!("native.{}", n).into(),103 }103 }104 }104 }120 )?;120 )?;121 evaluate(ctx, &func.body)121 evaluate(ctx, &func.body)122 }122 }123 Self::Intrinsic(ns, name) => call_builtin(call_ctx, loc, ns, name, args),123 Self::Intrinsic(name) => call_builtin(call_ctx, loc, name, args),124 Self::NativeExt(_name, handler) => {124 Self::NativeExt(_name, handler) => {125 let args = parse_function_call(call_ctx, None, &handler.params, args, true)?;125 let args = parse_function_call(call_ctx, None, &handler.params, args, true)?;126 let mut out_args = Vec::with_capacity(handler.params.len());126 let mut out_args = Vec::with_capacity(handler.params.len());149 )?;149 )?;150 evaluate(ctx, &func.body)150 evaluate(ctx, &func.body)151 }151 }152 Self::Intrinsic(_, _) => todo!(),152 Self::Intrinsic(_) => todo!(),153 Self::NativeExt(_, _) => todo!(),153 Self::NativeExt(_, _) => todo!(),154 }154 }155 }155 }160 let ctx = place_args(call_ctx, Some(func.ctx.clone()), &func.params, args)?;160 let ctx = place_args(call_ctx, Some(func.ctx.clone()), &func.params, args)?;161 evaluate(ctx, &func.body)161 evaluate(ctx, &func.body)162 }162 }163 Self::Intrinsic(_, _) => todo!(),163 Self::Intrinsic(_) => todo!(),164 Self::NativeExt(_, _) => todo!(),164 Self::NativeExt(_, _) => todo!(),165 }165 }166 }166 }crates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth311 Index(LocExpr, LocExpr),311 Index(LocExpr, LocExpr),312 /// function(x) x312 /// function(x) x313 Function(ParamsDesc, LocExpr),313 Function(ParamsDesc, LocExpr),314 /// std.primitiveEquals315 Intrinsic(Rc<str>),314 /// if true == false then 1 else 2316 /// if true == false then 1 else 2315 IfElse {317 IfElse {316 cond: IfSpecData,318 cond: IfSpecData,crates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth221 a:(@) _ "&" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::BitAnd, b))}221 a:(@) _ "&" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::BitAnd, b))}222 --222 --223 a:(@) _ "==" _ b:@ {loc_expr_todo!(Expr::Apply(223 a:(@) _ "==" _ b:@ {loc_expr_todo!(Expr::Apply(224 el!(Expr::Index(224 el!(Expr::Intrinsic("equals".into())),225 el!(Expr::Var("std".into())),226 el!(Expr::Str("equals".into()))227 )),228 ArgsDesc(vec![Arg(None, a), Arg(None, b)]),225 ArgsDesc(vec![Arg(None, a), Arg(None, b)]),229 true226 true230 ))}227 ))}231 a:(@) _ "!=" _ b:@ {loc_expr_todo!(Expr::UnaryOp(UnaryOpType::Not, el!(Expr::Apply(228 a:(@) _ "!=" _ b:@ {loc_expr_todo!(Expr::UnaryOp(UnaryOpType::Not, el!(Expr::Apply(232 el!(Expr::Index(229 el!(Expr::Intrinsic("equals".into())),233 el!(Expr::Var("std".into())),234 el!(Expr::Str("equals".into()))235 )),236 ArgsDesc(vec![Arg(None, a), Arg(None, b)]),230 ArgsDesc(vec![Arg(None, a), Arg(None, b)]),237 true231 true242 a:(@) _ "<=" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Lte, b))}236 a:(@) _ "<=" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Lte, b))}243 a:(@) _ ">=" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Gte, b))}237 a:(@) _ ">=" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Gte, b))}244 a:(@) _ keyword("in") _ b:@ {loc_expr_todo!(Expr::Apply(238 a:(@) _ keyword("in") _ b:@ {loc_expr_todo!(Expr::Apply(245 el!(Expr::Index(239 el!(Expr::Intrinsic("objectHasEx".into())), ArgsDesc(vec![Arg(None, b), Arg(None, a), Arg(None, el!(Expr::Literal(LiteralType::True)))]),246 el!(Expr::Var("std".into())),247 el!(Expr::Str("objectHasEx".into()))248 )), ArgsDesc(vec![Arg(None, b), Arg(None, a), Arg(None, el!(Expr::Literal(LiteralType::True)))]),249 true240 true250 ))}241 ))}258 a:(@) _ "*" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Mul, b))}249 a:(@) _ "*" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Mul, b))}259 a:(@) _ "/" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Div, b))}250 a:(@) _ "/" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Div, b))}260 a:(@) _ "%" _ b:@ {loc_expr_todo!(Expr::Apply(251 a:(@) _ "%" _ b:@ {loc_expr_todo!(Expr::Apply(261 el!(Expr::Index(252 el!(Expr::Intrinsic("mod".into())), ArgsDesc(vec![Arg(None, a), Arg(None, b)]),262 el!(Expr::Var("std".into())),263 el!(Expr::Str("mod".into()))264 )), ArgsDesc(vec![Arg(None, a), Arg(None, b)]),265 false253 false266 ))}254 ))}270 "~" _ b:@ { loc_expr_todo!(Expr::UnaryOp(UnaryOpType::BitNot, b)) }258 "~" _ b:@ { loc_expr_todo!(Expr::UnaryOp(UnaryOpType::BitNot, b)) }271 --259 --272 a:(@) _ "[" _ s:slice_desc(s) _ "]" {loc_expr_todo!(Expr::Apply(260 a:(@) _ "[" _ s:slice_desc(s) _ "]" {loc_expr_todo!(Expr::Apply(273 el!(Expr::Index(261 el!(Expr::Intrinsic("slice".into())),274 el!(Expr::Var("std".into())),275 el!(Expr::Str("slice".into())),276 )),277 ArgsDesc(vec![262 ArgsDesc(vec![278 Arg(None, a),263 Arg(None, a),