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

difftreelog

fix(parser) desugar == to std.equals

Лач2020-06-04parent: #8c59188.patch.diff
in: master

4 files changed

modifiedcrates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/evaluate.rs
+++ b/crates/jsonnet-evaluator/src/evaluate.rs
@@ -5,9 +5,8 @@
 };
 use closure::closure;
 use jsonnet_parser::{
-	el, Arg, ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, FieldMember,
-	ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,
-	Visibility,
+	ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, FieldMember, ForSpecData,
+	IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType, Visibility,
 };
 use std::{
 	collections::{BTreeMap, HashMap},
@@ -102,55 +101,20 @@
 		match (evaluate(context.clone(), &a)?.unwrap_if_lazy()?, op, b) {
 			(Val::Bool(true), BinaryOpType::Or, _o) => Val::Bool(true),
 			(Val::Bool(false), BinaryOpType::And, _o) => Val::Bool(false),
-			(a, op, eb) => evaluate_binary_op_normal(
-				context.clone(),
-				&a,
-				op,
-				&evaluate(context, eb)?.unwrap_if_lazy()?,
-			)?,
+			(a, op, eb) => {
+				evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?.unwrap_if_lazy()?)?
+			}
 		},
 	)
 }
 
-pub fn evaluate_binary_op_normal(
-	context: Context,
-	a: &Val,
-	op: BinaryOpType,
-	b: &Val,
-) -> Result<Val> {
+pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {
 	Ok(match (a, op, b) {
 		(a, BinaryOpType::Add, b) => evaluate_add_op(a, b)?,
-
-		(Val::Str(v1), BinaryOpType::Eq, Val::Str(v2)) => Val::Bool(v1 == v2),
-		(Val::Str(v1), BinaryOpType::Ne, Val::Str(v2)) => Val::Bool(v1 != v2),
 
 		(Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize)),
-		(Val::Str(format), BinaryOpType::Mod, args) => evaluate(
-			context
-				.with_var("__tmp__format__".to_owned(), Val::Str(format.to_owned()))?
-				.with_var(
-					"__tmp__args__".to_owned(),
-					match args {
-						Val::Arr(v) => Val::Arr(v.clone()),
-						v => Val::Arr(vec![v.clone()]),
-					},
-				)?,
-			&el!(Expr::Apply(
-				el!(Expr::Index(
-					el!(Expr::Var("std".to_owned())),
-					el!(Expr::Str("format".to_owned()))
-				)),
-				ArgsDesc(vec![
-					Arg(None, el!(Expr::Var("__tmp__format__".to_owned()))),
-					Arg(None, el!(Expr::Var("__tmp__args__".to_owned())))
-				])
-			)),
-		)?,
 
 		// Bool X Bool
-		(Val::Bool(a), BinaryOpType::Eq, Val::Bool(b)) => Val::Bool(a == b),
-		(Val::Bool(a), BinaryOpType::Ne, Val::Bool(b)) => Val::Bool(a != b),
-
 		(Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),
 		(Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),
 
@@ -163,7 +127,6 @@
 		// Num X Num
 		(Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Num(v1 * v2),
 		(Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => Val::Num(v1 / v2),
-		(Val::Num(v1), BinaryOpType::Mod, Val::Num(v2)) => Val::Num(v1 % v2),
 
 		(Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::Num(v1 - v2),
 
@@ -171,9 +134,6 @@
 		(Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => Val::Bool(v1 > v2),
 		(Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => Val::Bool(v1 <= v2),
 		(Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => Val::Bool(v1 >= v2),
-
-		(Val::Num(v1), BinaryOpType::Eq, Val::Num(v2)) => Val::Bool((v1 - v2).abs() < f64::EPSILON),
-		(Val::Num(v1), BinaryOpType::Ne, Val::Num(v2)) => Val::Bool((v1 - v2).abs() > f64::EPSILON),
 
 		(Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {
 			Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)
@@ -191,28 +151,6 @@
 			Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)
 		}
 
-		// Arr X Arr
-		(Val::Arr(a), BinaryOpType::Eq, Val::Arr(b)) => {
-			if a.len() != b.len() {
-				Val::Bool(false)
-			} else {
-				for i in 0..a.len() {
-					if let Val::Bool(v) = evaluate_binary_op_normal(
-						context.clone(),
-						&a[i].clone().unwrap_if_lazy()?,
-						op,
-						&b[i].clone().unwrap_if_lazy()?,
-					)? {
-						if !v {
-							return Ok(Val::Bool(false));
-						}
-					} else {
-						unreachable!()
-					}
-				}
-				return Ok(Val::Bool(true));
-			}
-		}
 		_ => panic!("no rules for binary operation: {:?} {:?} {:?}", a, op, b),
 	})
 }
@@ -385,7 +323,7 @@
 		Index(value, index) => {
 			match (
 				evaluate(context.clone(), value)?.unwrap_if_lazy()?,
-				evaluate(context.clone(), index)?,
+				evaluate(context, index)?,
 			) {
 				(Val::Obj(v), Val::Str(s)) => {
 					if let Some(v) = v.get(&s)? {
@@ -506,9 +444,17 @@
 							panic!("bad objectFieldsEx call");
 						}
 					}
+					("std", "primitiveEquals") => {
+						assert_eq!(args.len(), 2);
+						let (a, b) = (
+							evaluate(context.clone(), &args[0].1)?,
+							evaluate(context, &args[1].1)?,
+						);
+						Val::Bool(a == b)
+					}
 					(ns, name) => panic!("Intristic not found: {}.{}", ns, name),
 				},
-				Val::Func(f) => push(locexpr.clone(), "function call".to_owned(), || {
+				Val::Func(f) => push(locexpr, "function call".to_owned(), || {
 					f.evaluate(
 						args.clone()
 							.into_iter()
@@ -528,10 +474,11 @@
 		}
 		Function(params, body) => evaluate_method(context, body, params.clone()),
 		AssertExpr(AssertStmt(value, msg), returned) => {
-			if push(value.clone(), "assertion condition".to_owned(), || {
+			let assertion_result = push(value.clone(), "assertion condition".to_owned(), || {
 				evaluate(context.clone(), &value)?
 					.try_cast_bool("assertion condition should be boolean")
-			})? {
+			})?;
+			if assertion_result {
 				push(
 					returned.clone(),
 					"assert 'return' branch".to_owned(),
@@ -555,9 +502,10 @@
 			cond_then,
 			cond_else,
 		} => {
-			if push(cond.0.clone(), "if condition".to_owned(), || {
+			let condition_result = push(cond.0.clone(), "if condition".to_owned(), || {
 				evaluate(context.clone(), &cond.0)?.try_cast_bool("if condition should be boolean")
-			})? {
+			})?;
+			if condition_result {
 				push(
 					cond_then.clone(),
 					"if condition 'then' branch".to_owned(),
modifiedcrates/jsonnet-parser/Cargo.tomldiffbeforeafterboth
--- a/crates/jsonnet-parser/Cargo.toml
+++ b/crates/jsonnet-parser/Cargo.toml
@@ -7,7 +7,7 @@
 [features]
 default = []
 # Trace peg token parsing
-trace = ["peg/trace"]
+# trace = ["peg/trace"]
 # TODO:
 # serialize = ["serde"]
 
modifiedcrates/jsonnet-parser/src/expr.rsdiffbeforeafterboth
before · crates/jsonnet-parser/src/expr.rs
1use serde::{Deserialize, Serialize};2use std::{fmt::Debug, rc::Rc};34#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]5pub enum FieldName {6	/// {fixed: 2}7	Fixed(String),8	/// {["dyn"+"amic"]: 3}9	Dyn(LocExpr),10}1112#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]13pub enum Visibility {14	/// :15	Normal,16	/// ::17	Hidden,18	/// :::19	Unhide,20}2122#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]23pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);2425#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]26pub struct FieldMember {27	pub name: FieldName,28	pub plus: bool,29	pub params: Option<ParamsDesc>,30	pub visibility: Visibility,31	pub value: LocExpr,32}3334#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]35pub enum Member {36	Field(FieldMember),37	BindStmt(BindSpec),38	AssertStmt(AssertStmt),39}4041#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]42pub enum UnaryOpType {43	Plus,44	Minus,45	BitNot,46	Not,47}4849#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]50pub enum BinaryOpType {51	Mul,52	Div,53	// Mod is desugared to stdlib54	// Mod,55	Add,56	Sub,5758	Lhs,59	Rhs,6061	Lt,62	Gt,63	Lte,64	Gte,6566	In,6768	Eq,69	Ne,7071	BitAnd,72	BitOr,73	BitXor,7475	And,76	Or,77}7879/// name, default value80#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]81pub struct Param(pub String, pub Option<LocExpr>);82/// Defined function parameters83#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]84pub struct ParamsDesc(pub Vec<Param>);85impl ParamsDesc {86	pub fn with_defaults(&self) -> Vec<Param> {87		self.0.iter().filter(|e| e.1.is_some()).cloned().collect()88	}89}9091#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]92pub struct Arg(pub Option<String>, pub LocExpr);93#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]94pub struct ArgsDesc(pub Vec<Arg>);9596#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]97pub struct BindSpec {98	pub name: String,99	pub params: Option<ParamsDesc>,100	pub value: LocExpr,101}102103#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]104pub struct IfSpecData(pub LocExpr);105#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]106pub struct ForSpecData(pub String, pub LocExpr);107108#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]109pub enum CompSpec {110	IfSpec(IfSpecData),111	ForSpec(ForSpecData),112}113114#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]115pub enum ObjBody {116	MemberList(Vec<Member>),117	ObjComp {118		pre_locals: Vec<BindSpec>,119		key: LocExpr,120		value: LocExpr,121		post_locals: Vec<BindSpec>,122		first: ForSpecData,123		rest: Vec<CompSpec>,124	},125}126127#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]128pub enum LiteralType {129	This,130	Super,131	Dollar,132	Null,133	True,134	False,135}136137#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]138pub struct SliceDesc {139	pub start: Option<LocExpr>,140	pub end: Option<LocExpr>,141	pub step: Option<LocExpr>,142}143144/// Syntax base145#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]146pub enum Expr {147	Literal(LiteralType),148149	/// String value: "hello"150	Str(String),151	/// Number: 1, 2.0, 2e+20152	Num(f64),153	/// Variable name: test154	Var(String),155156	/// Array of expressions: [1, 2, "Hello"]157	Arr(Vec<LocExpr>),158	/// Array comprehension:159	/// ```jsonnet160	///  ingredients: [161	///    { kind: kind, qty: 4 / 3 }162	///    for kind in [163	///      'Honey Syrup',164	///      'Lemon Juice',165	///      'Farmers Gin',166	///    ]167	///  ],168	/// ```169	ArrComp(LocExpr, Vec<CompSpec>),170171	/// Object: {a: 2}172	Obj(ObjBody),173	/// Object extension: var1 {b: 2}174	ObjExtend(LocExpr, ObjBody),175176	/// (obj)177	Parened(LocExpr),178179	/// Params in function definition180	/// hello, world, test = 2181	Params(ParamsDesc),182	/// Args in function call183	/// 2 + 2, 3, named = 6184	Args(ArgsDesc),185186	/// -2187	UnaryOp(UnaryOpType, LocExpr),188	/// 2 - 2189	BinaryOp(LocExpr, BinaryOpType, LocExpr),190	/// assert 2 == 2 : "Math is broken"191	AssertExpr(AssertStmt, LocExpr),192	/// local a = 2; { b: a }193	LocalExpr(Vec<BindSpec>, LocExpr),194195	/// a = 3196	Bind(BindSpec),197	/// import "hello"198	Import(String),199	/// importStr "file.txt"200	ImportStr(String),201	/// error "I'm broken"202	Error(LocExpr),203	/// a(b, c)204	Apply(LocExpr, ArgsDesc),205	///206	Select(LocExpr, String),207	/// a[b]208	Index(LocExpr, LocExpr),209	/// a[1::2]210	Slice(LocExpr, SliceDesc),211	/// function(x) x212	Function(ParamsDesc, LocExpr),213	/// if true == false then 1 else 2214	IfElse {215		cond: IfSpecData,216		cond_then: LocExpr,217		cond_else: Option<LocExpr>,218	},219	/// if 2 = 3220	IfSpec(IfSpecData),221	/// for elem in array222	ForSpec(ForSpecData),223}224225/// file, begin offset, end offset226#[derive(Clone, PartialEq, Serialize, Deserialize)]227pub struct ExprLocation(pub String, pub usize, pub usize);228impl Debug for ExprLocation {229	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {230		write!(f, "{}:{:?}-{:?}", self.0, self.1, self.2)231	}232}233234/// Holds AST expression and its location in source file+235#[derive(Clone, PartialEq, Serialize, Deserialize)]236pub struct LocExpr(pub Rc<Expr>, pub Option<Rc<ExprLocation>>);237impl Debug for LocExpr {238	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {239		write!(f, "{:?} from {:?}", self.0, self.1)240	}241}242243/// Creates LocExpr from Expr and ExprLocation components244#[macro_export]245macro_rules! loc_expr {246	($expr:expr, $need_loc:expr,($name:expr, $start:expr, $end:expr)) => {247		LocExpr(248			std::rc::Rc::new($expr),249			if $need_loc {250				Some(std::rc::Rc::new(ExprLocation(251					$name.to_owned(),252					$start,253					$end,254				)))255			} else {256				None257				},258			)259	};260}261262/// Creates LocExpr without location info263#[macro_export]264macro_rules! loc_expr_todo {265	($expr:expr) => {266		LocExpr(Rc::new($expr), None)267	};268}
after · crates/jsonnet-parser/src/expr.rs
1use serde::{Deserialize, Serialize};2use std::{fmt::Debug, rc::Rc};34#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]5pub enum FieldName {6	/// {fixed: 2}7	Fixed(String),8	/// {["dyn"+"amic"]: 3}9	Dyn(LocExpr),10}1112#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]13pub enum Visibility {14	/// :15	Normal,16	/// ::17	Hidden,18	/// :::19	Unhide,20}2122#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]23pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);2425#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]26pub struct FieldMember {27	pub name: FieldName,28	pub plus: bool,29	pub params: Option<ParamsDesc>,30	pub visibility: Visibility,31	pub value: LocExpr,32}3334#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]35pub enum Member {36	Field(FieldMember),37	BindStmt(BindSpec),38	AssertStmt(AssertStmt),39}4041#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]42pub enum UnaryOpType {43	Plus,44	Minus,45	BitNot,46	Not,47}4849#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]50pub enum BinaryOpType {51	Mul,52	Div,53	// Mod is desugared to std.mod54	// Mod,55	Add,56	Sub,5758	Lhs,59	Rhs,6061	Lt,62	Gt,63	Lte,64	Gte,6566	In,6768	// Eq/Ne is desugared to std.equals69	// Eq,70	// Ne,7172	BitAnd,73	BitOr,74	BitXor,7576	And,77	Or,78}7980/// name, default value81#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]82pub struct Param(pub String, pub Option<LocExpr>);83/// Defined function parameters84#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]85pub struct ParamsDesc(pub Vec<Param>);86impl ParamsDesc {87	pub fn with_defaults(&self) -> Vec<Param> {88		self.0.iter().filter(|e| e.1.is_some()).cloned().collect()89	}90}9192#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]93pub struct Arg(pub Option<String>, pub LocExpr);94#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]95pub struct ArgsDesc(pub Vec<Arg>);9697#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]98pub struct BindSpec {99	pub name: String,100	pub params: Option<ParamsDesc>,101	pub value: LocExpr,102}103104#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]105pub struct IfSpecData(pub LocExpr);106#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]107pub struct ForSpecData(pub String, pub LocExpr);108109#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]110pub enum CompSpec {111	IfSpec(IfSpecData),112	ForSpec(ForSpecData),113}114115#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]116pub enum ObjBody {117	MemberList(Vec<Member>),118	ObjComp {119		pre_locals: Vec<BindSpec>,120		key: LocExpr,121		value: LocExpr,122		post_locals: Vec<BindSpec>,123		first: ForSpecData,124		rest: Vec<CompSpec>,125	},126}127128#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]129pub enum LiteralType {130	This,131	Super,132	Dollar,133	Null,134	True,135	False,136}137138#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]139pub struct SliceDesc {140	pub start: Option<LocExpr>,141	pub end: Option<LocExpr>,142	pub step: Option<LocExpr>,143}144145/// Syntax base146#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]147pub enum Expr {148	Literal(LiteralType),149150	/// String value: "hello"151	Str(String),152	/// Number: 1, 2.0, 2e+20153	Num(f64),154	/// Variable name: test155	Var(String),156157	/// Array of expressions: [1, 2, "Hello"]158	Arr(Vec<LocExpr>),159	/// Array comprehension:160	/// ```jsonnet161	///  ingredients: [162	///    { kind: kind, qty: 4 / 3 }163	///    for kind in [164	///      'Honey Syrup',165	///      'Lemon Juice',166	///      'Farmers Gin',167	///    ]168	///  ],169	/// ```170	ArrComp(LocExpr, Vec<CompSpec>),171172	/// Object: {a: 2}173	Obj(ObjBody),174	/// Object extension: var1 {b: 2}175	ObjExtend(LocExpr, ObjBody),176177	/// (obj)178	Parened(LocExpr),179180	/// Params in function definition181	/// hello, world, test = 2182	Params(ParamsDesc),183	/// Args in function call184	/// 2 + 2, 3, named = 6185	Args(ArgsDesc),186187	/// -2188	UnaryOp(UnaryOpType, LocExpr),189	/// 2 - 2190	BinaryOp(LocExpr, BinaryOpType, LocExpr),191	/// assert 2 == 2 : "Math is broken"192	AssertExpr(AssertStmt, LocExpr),193	/// local a = 2; { b: a }194	LocalExpr(Vec<BindSpec>, LocExpr),195196	/// a = 3197	Bind(BindSpec),198	/// import "hello"199	Import(String),200	/// importStr "file.txt"201	ImportStr(String),202	/// error "I'm broken"203	Error(LocExpr),204	/// a(b, c)205	Apply(LocExpr, ArgsDesc),206	///207	Select(LocExpr, String),208	/// a[b]209	Index(LocExpr, LocExpr),210	/// a[1::2]211	Slice(LocExpr, SliceDesc),212	/// function(x) x213	Function(ParamsDesc, LocExpr),214	/// if true == false then 1 else 2215	IfElse {216		cond: IfSpecData,217		cond_then: LocExpr,218		cond_else: Option<LocExpr>,219	},220	/// if 2 = 3221	IfSpec(IfSpecData),222	/// for elem in array223	ForSpec(ForSpecData),224}225226/// file, begin offset, end offset227#[derive(Clone, PartialEq, Serialize, Deserialize)]228pub struct ExprLocation(pub String, pub usize, pub usize);229impl Debug for ExprLocation {230	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {231		write!(f, "{}:{:?}-{:?}", self.0, self.1, self.2)232	}233}234235/// Holds AST expression and its location in source file+236#[derive(Clone, PartialEq, Serialize, Deserialize)]237pub struct LocExpr(pub Rc<Expr>, pub Option<Rc<ExprLocation>>);238impl Debug for LocExpr {239	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {240		write!(f, "{:?} from {:?}", self.0, self.1)241	}242}243244/// Creates LocExpr from Expr and ExprLocation components245#[macro_export]246macro_rules! loc_expr {247	($expr:expr, $need_loc:expr,($name:expr, $start:expr, $end:expr)) => {248		LocExpr(249			std::rc::Rc::new($expr),250			if $need_loc {251				Some(std::rc::Rc::new(ExprLocation(252					$name.to_owned(),253					$start,254					$end,255				)))256			} else {257				None258				},259			)260	};261}262263/// Creates LocExpr without location info264#[macro_export]265macro_rules! loc_expr_todo {266	($expr:expr) => {267		LocExpr(Rc::new($expr), None)268	};269}
modifiedcrates/jsonnet-parser/src/lib.rsdiffbeforeafterboth
--- a/crates/jsonnet-parser/src/lib.rs
+++ b/crates/jsonnet-parser/src/lib.rs
@@ -230,8 +230,18 @@
 				--
 				a:(@) _ "&" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::BitAnd, b))}
 				--
-				a:(@) _ "==" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Eq, b))}
-				a:(@) _ "!=" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Ne, b))}
+				a:(@) _ "==" _ b:@ {loc_expr_todo!(Expr::Apply(
+					el!(Expr::Index(
+						el!(Expr::Var("std".to_owned())),
+						el!(Expr::Str("equals".to_owned()))
+					)), ArgsDesc(vec![Arg(None, a), Arg(None, b)])
+				))}
+				a:(@) _ "!=" _ b:@ {loc_expr_todo!(Expr::UnaryOp(UnaryOpType::Not, el!(Expr::Apply(
+					el!(Expr::Index(
+						el!(Expr::Var("std".to_owned())),
+						el!(Expr::Str("equals".to_owned()))
+					)), ArgsDesc(vec![Arg(None, a), Arg(None, b)])
+				))))}
 				--
 				a:(@) _ "<" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Lt, b))}
 				a:(@) _ ">" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Gt, b))}
@@ -385,25 +395,6 @@
 				el!(Expr::Num(2.0)),
 				BinaryOpType::Sub,
 				el!(Expr::Num(22.0))
-			))
-		);
-	}
-
-	#[test]
-	fn suffix_comparsion() {
-		use Expr::*;
-		assert_eq!(
-			parse!("std.type(a) == \"string\""),
-			el!(BinaryOp(
-				el!(Apply(
-					el!(Index(
-						el!(Var("std".to_owned())),
-						el!(Str("type".to_owned()))
-					)),
-					ArgsDesc(vec![Arg(None, el!(Var("a".to_owned())))])
-				)),
-				BinaryOpType::Eq,
-				el!(Str("string".to_owned()))
 			))
 		);
 	}
@@ -427,37 +418,6 @@
 				))]
 			)),
 		)
-	}
-
-	#[test]
-	fn array_comp_with_ifs() {
-		use Expr::*;
-		assert_eq!(
-			parse!("[k for k in std.objectFields(patch) if patch[k] == null]"),
-			el!(ArrComp(
-				el!(Var("k".to_owned())),
-				vec![
-					CompSpec::ForSpec(ForSpecData(
-						"k".to_owned(),
-						el!(Apply(
-							el!(Index(
-								el!(Var("std".to_owned())),
-								el!(Str("objectFields".to_owned()))
-							)),
-							ArgsDesc(vec![Arg(None, el!(Var("patch".to_owned())))])
-						))
-					)),
-					CompSpec::IfSpec(IfSpecData(el!(BinaryOp(
-						el!(Index(
-							el!(Var("patch".to_owned())),
-							el!(Var("k".to_owned()))
-						)),
-						BinaryOpType::Eq,
-						el!(Literal(LiteralType::Null))
-					))))
-				]
-			))
-		);
 	}
 
 	#[test]