git.delta.rocks / jrsonnet / refs/commits / 64fb3950ee00

difftreelog

fix(evaluator) utf-8 support

Лач2020-05-31parent: #26c294d.patch.diff
in: master

3 files changed

modifiedcrates/jsonnet-evaluator/src/ctx.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/ctx.rs
+++ b/crates/jsonnet-evaluator/src/ctx.rs
@@ -1,4 +1,4 @@
-use crate::{future_wrapper, rc_fn_helper, LazyBinding, ObjValue, LazyVal, Val};
+use crate::{future_wrapper, rc_fn_helper, LazyBinding, LazyVal, ObjValue};
 use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
 
 rc_fn_helper!(
@@ -51,13 +51,9 @@
 	}
 
 	pub fn binding(&self, name: &str) -> LazyVal {
-		self.0
-			.bindings
-			.get(name)
-			.cloned()
-			.unwrap_or_else(|| {
-				panic!("can't find {} in {:?}", name, self);
-			})
+		self.0.bindings.get(name).cloned().unwrap_or_else(|| {
+			panic!("can't find {} in {:?}", name, self);
+		})
 	}
 	pub fn into_future(self, ctx: FutureContext) -> Context {
 		{
modifiedcrates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/evaluate.rs
+++ b/crates/jsonnet-evaluator/src/evaluate.rs
@@ -1,7 +1,7 @@
 use crate::{
 	binding, bool_val, context_creator, function_default, function_rhs, future_wrapper,
-	lazy_binding, lazy_val, Binding, Context, ContextCreator, FuncDesc, LazyBinding, ObjMember,
-	ObjValue, Val,
+	lazy_binding, lazy_val, Context, ContextCreator, FuncDesc, LazyBinding, ObjMember, ObjValue,
+	Val,
 };
 use closure::closure;
 use jsonnet_parser::{
@@ -284,8 +284,7 @@
 					.unwrap_or_else(|| panic!("out of bounds"))
 					.clone(),
 				(Val::Str(s), Val::Num(n)) => {
-					// FIXME: Only works for ASCII
-					Val::Str(String::from_utf8(vec![s.as_bytes()[n as usize]]).unwrap())
+					Val::Str(s.chars().skip(n as usize - 1).take(1).collect())
 				}
 				(v, i) => todo!("not implemented: {:?}[{:?}]", v, i.unwrap_if_lazy()),
 			}
@@ -320,7 +319,7 @@
 						assert_eq!(args.len(), 1);
 						let expr = &args.get(0).unwrap().1;
 						match evaluate(context, expr) {
-							Val::Str(n) => Val::Num(n.len() as f64),
+							Val::Str(n) => Val::Num(n.chars().count() as f64),
 							Val::Arr(i) => Val::Num(i.len() as f64),
 							v => panic!("can't get length of {:?}", v),
 						}
@@ -349,8 +348,11 @@
 					("std", "codepoint") => {
 						assert_eq!(args.len(), 1);
 						if let Val::Str(s) = evaluate(context.clone(), &args[0].1) {
-							// FIXME: this is not a codepoint
-							Val::Num(s.as_bytes()[0] as f64)
+							assert!(
+								s.chars().count() == 1,
+								"std.codepoint should receive single char string"
+							);
+							Val::Num(s.chars().take(1).next().unwrap() as u32 as f64)
 						} else {
 							panic!("bad codepoint call");
 						}
modifiedcrates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth
before · crates/jsonnet-evaluator/src/lib.rs
1#![feature(box_syntax, box_patterns)]2#![feature(type_alias_impl_trait)]3#![feature(debug_non_exhaustive)]4#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]5mod ctx;6mod dynamic;7mod evaluate;8mod obj;9mod val;1011pub use ctx::*;12pub use dynamic::*;13pub use evaluate::*;14use jsonnet_parser::*;15pub use obj::*;16pub use val::*;1718rc_fn_helper!(19	Binding,20	binding,21	dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Val22);23rc_fn_helper!(24	LazyBinding,25	lazy_binding,26	dyn Fn(Option<ObjValue>, Option<ObjValue>) -> LazyVal27);28rc_fn_helper!(FunctionRhs, function_rhs, dyn Fn(Context) -> Val);29rc_fn_helper!(30	FunctionDefault,31	function_default,32	dyn Fn(Context, Expr) -> Val33);3435#[cfg(test)]36pub mod tests {37	use super::{evaluate, Context, Val};38	use jsonnet_parser::*;3940	macro_rules! eval {41		($str: expr) => {42			evaluate(Context::new(), &parse($str).unwrap())43		};44	}4546	macro_rules! eval_stdlib {47		($str: expr) => {{48			let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";49			evaluate(Context::new(), &parse(&(std + $str)).unwrap())50			}};51	}5253	macro_rules! assert_eval {54		($str: expr) => {55			assert_eq!(56				evaluate(Context::new(), &parse($str).unwrap()),57				Val::Literal(LiteralType::True)58				)59		};60	}61	macro_rules! assert_json {62		($str: expr, $out: expr) => {63			assert_eq!(64				format!("{}", evaluate(Context::new(), &parse($str).unwrap())),65				$out66				)67		};68	}69	macro_rules! assert_json_stdlib {70		($str: expr, $out: expr) => {71			assert_eq!(72				format!("{}", eval_stdlib!($str)),73				$out74				)75		};76	}77	macro_rules! assert_eval_neg {78		($str: expr) => {79			assert_eq!(80				evaluate(Context::new(), &parse($str).unwrap()),81				Val::Literal(LiteralType::False)82				)83		};84	}8586	/// Sanity checking, before trusting to another tests87	#[test]88	fn equality_operator() {89		assert_eval!("2 == 2");90		assert_eval_neg!("2 != 2");91		assert_eval!("2 != 3");92		assert_eval_neg!("2 == 3");93		assert_eval!("'Hello' == 'Hello'");94		assert_eval_neg!("'Hello' != 'Hello'");95		assert_eval!("'Hello' != 'World'");96		assert_eval_neg!("'Hello' == 'World'");97	}9899	#[test]100	fn math_evaluation() {101		assert_eval!("2 + 2 * 2 == 6");102		assert_eval!("3 + (2 + 2 * 2) == 9");103	}104105	#[test]106	fn string_concat() {107		assert_eval!("'Hello' + 'World' == 'HelloWorld'");108		assert_eval!("'Hello' * 3 == 'HelloHelloHello'");109		assert_eval!("'Hello' + 'World' * 3 == 'HelloWorldWorldWorld'");110	}111112	#[test]113	fn local() {114		assert_eval!("local a = 2; local b = 3; a + b == 5");115		assert_eval!("local a = 1, b = a + 1; a + b == 3");116		assert_eval!("local a = 1; local a = 2; a == 2");117	}118119	#[test]120	fn object_lazyness() {121		assert_json!("local a = {a:error 'test'}; {}", r#"{}"#);122	}123124	#[test]125	fn object_inheritance() {126		assert_json!("{a: self.b} + {b:3}", r#"{"a":3,"b":3}"#);127	}128129	#[test]130	fn test_object() {131		assert_json!("{a:2}", r#"{"a":2}"#);132		assert_json!("{a:2+2}", r#"{"a":4}"#);133		assert_json!("{a:2}+{b:2}", r#"{"a":2,"b":2}"#);134		assert_json!("{b:3}+{b:2}", r#"{"b":2}"#);135		assert_json!("{b:3}+{b+:2}", r#"{"b":5}"#);136		assert_json!("local test='a'; {[test]:2}", r#"{"a":2}"#);137		assert_json!(138			r#"139				{140					name: "Alice",141					welcome: "Hello " + self.name + "!",142				}143			"#,144			r#"{"name":"Alice","welcome":"Hello Alice!"}"#145		);146		assert_json!(147			r#"148				{149					name: "Alice",150					welcome: "Hello " + self.name + "!",151				} + {152					name: "Bob"153				}154			"#,155			r#"{"name":"Bob","welcome":"Hello Bob!"}"#156		);157	}158159	#[test]160	fn functions() {161		assert_json!(r#"local a = function(b, c = 2) b + c; a(2)"#, "4");162		assert_json!(163			r#"local a = function(b, c = "Dear") b + c + d, d = "World"; a("Hello")"#,164			r#""HelloDearWorld""#165		);166	}167168	#[test]169	fn local_methods() {170		assert_json!(r#"local a(b, c = 2) = b + c; a(2)"#, "4");171		assert_json!(172			r#"local a(b, c = "Dear") = b + c + d, d = "World"; a("Hello")"#,173			r#""HelloDearWorld""#174		);175	}176177	#[test]178	fn object_locals() {179		assert_json!(r#"{local a = 3, b: a}"#, r#"{"b":3}"#);180		assert_json!(r#"{local a = 3, local c = a, b: c}"#, r#"{"b":3}"#);181		assert_json!(182			r#"{local a = function (b) {[b]:4}, test: a("test")}"#,183			r#"{"test":{"test":4}}"#184		);185	}186187	#[test]188	fn direct_self() {189		println!(190			"{:#?}",191			eval!(192				r#"193					{194						local me = self,195						a: 3,196						b(): me.a,197					}198				"#199			)200		);201	}202203	#[test]204	fn indirect_self() {205		// `self` assigned to `me` was lost when being206		// referenced from field207		eval_stdlib!(208			r#"{209				local me = self,210				a: 3,211				b: me.a,212			}.b"#213		);214	}215216	// We can't trust other tests (And official jsonnet testsuite), if assert is not working correctly217	#[test]218	fn std_assert_ok() {219		eval_stdlib!("std.assertEqual(4.5 << 2, 16)");220	}221222	#[test]223	#[should_panic]224	fn std_assert_failure() {225		eval_stdlib!("std.assertEqual(4.5 << 2, 15)");226	}227228	#[test]229	fn string_is_string() {230		assert_eq!(231			eval_stdlib!("local arr = 'hello'; (!std.isArray(arr)) && (!std.isString(arr))"),232			Val::Literal(LiteralType::False)233		);234	}235236	#[test]237	fn base64_works() {238		assert_json_stdlib!(r#"std.base64("test")"#, r#""dGVzdA==""#);239	}240}
after · crates/jsonnet-evaluator/src/lib.rs
1#![feature(box_syntax, box_patterns)]2#![feature(type_alias_impl_trait)]3#![feature(debug_non_exhaustive)]4#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]5mod ctx;6mod dynamic;7mod evaluate;8mod obj;9mod val;1011pub use ctx::*;12pub use dynamic::*;13pub use evaluate::*;14use jsonnet_parser::*;15pub use obj::*;16pub use val::*;1718rc_fn_helper!(19	Binding,20	binding,21	dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Val22);23rc_fn_helper!(24	LazyBinding,25	lazy_binding,26	dyn Fn(Option<ObjValue>, Option<ObjValue>) -> LazyVal27);28rc_fn_helper!(FunctionRhs, function_rhs, dyn Fn(Context) -> Val);29rc_fn_helper!(30	FunctionDefault,31	function_default,32	dyn Fn(Context, Expr) -> Val33);3435#[cfg(test)]36pub mod tests {37	use super::{evaluate, Context, Val};38	use jsonnet_parser::*;3940	macro_rules! eval {41		($str: expr) => {42			evaluate(Context::new(), &parse($str).unwrap())43		};44	}4546	macro_rules! eval_stdlib {47		($str: expr) => {{48			let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";49			evaluate(Context::new(), &parse(&(std + $str)).unwrap())50			}};51	}5253	macro_rules! assert_eval {54		($str: expr) => {55			assert_eq!(56				evaluate(Context::new(), &parse($str).unwrap()),57				Val::Literal(LiteralType::True)58				)59		};60	}61	macro_rules! assert_json {62		($str: expr, $out: expr) => {63			assert_eq!(64				format!("{}", evaluate(Context::new(), &parse($str).unwrap())),65				$out66				)67		};68	}69	macro_rules! assert_json_stdlib {70		($str: expr, $out: expr) => {71			assert_eq!(format!("{}", eval_stdlib!($str)), $out)72		};73	}74	macro_rules! assert_eval_neg {75		($str: expr) => {76			assert_eq!(77				evaluate(Context::new(), &parse($str).unwrap()),78				Val::Literal(LiteralType::False)79				)80		};81	}8283	/// Sanity checking, before trusting to another tests84	#[test]85	fn equality_operator() {86		assert_eval!("2 == 2");87		assert_eval_neg!("2 != 2");88		assert_eval!("2 != 3");89		assert_eval_neg!("2 == 3");90		assert_eval!("'Hello' == 'Hello'");91		assert_eval_neg!("'Hello' != 'Hello'");92		assert_eval!("'Hello' != 'World'");93		assert_eval_neg!("'Hello' == 'World'");94	}9596	#[test]97	fn math_evaluation() {98		assert_eval!("2 + 2 * 2 == 6");99		assert_eval!("3 + (2 + 2 * 2) == 9");100	}101102	#[test]103	fn string_concat() {104		assert_eval!("'Hello' + 'World' == 'HelloWorld'");105		assert_eval!("'Hello' * 3 == 'HelloHelloHello'");106		assert_eval!("'Hello' + 'World' * 3 == 'HelloWorldWorldWorld'");107	}108109	#[test]110	fn local() {111		assert_eval!("local a = 2; local b = 3; a + b == 5");112		assert_eval!("local a = 1, b = a + 1; a + b == 3");113		assert_eval!("local a = 1; local a = 2; a == 2");114	}115116	#[test]117	fn object_lazyness() {118		assert_json!("local a = {a:error 'test'}; {}", r#"{}"#);119	}120121	#[test]122	fn object_inheritance() {123		assert_json!("{a: self.b} + {b:3}", r#"{"a":3,"b":3}"#);124	}125126	#[test]127	fn test_object() {128		assert_json!("{a:2}", r#"{"a":2}"#);129		assert_json!("{a:2+2}", r#"{"a":4}"#);130		assert_json!("{a:2}+{b:2}", r#"{"a":2,"b":2}"#);131		assert_json!("{b:3}+{b:2}", r#"{"b":2}"#);132		assert_json!("{b:3}+{b+:2}", r#"{"b":5}"#);133		assert_json!("local test='a'; {[test]:2}", r#"{"a":2}"#);134		assert_json!(135			r#"136				{137					name: "Alice",138					welcome: "Hello " + self.name + "!",139				}140			"#,141			r#"{"name":"Alice","welcome":"Hello Alice!"}"#142		);143		assert_json!(144			r#"145				{146					name: "Alice",147					welcome: "Hello " + self.name + "!",148				} + {149					name: "Bob"150				}151			"#,152			r#"{"name":"Bob","welcome":"Hello Bob!"}"#153		);154	}155156	#[test]157	fn functions() {158		assert_json!(r#"local a = function(b, c = 2) b + c; a(2)"#, "4");159		assert_json!(160			r#"local a = function(b, c = "Dear") b + c + d, d = "World"; a("Hello")"#,161			r#""HelloDearWorld""#162		);163	}164165	#[test]166	fn local_methods() {167		assert_json!(r#"local a(b, c = 2) = b + c; a(2)"#, "4");168		assert_json!(169			r#"local a(b, c = "Dear") = b + c + d, d = "World"; a("Hello")"#,170			r#""HelloDearWorld""#171		);172	}173174	#[test]175	fn object_locals() {176		assert_json!(r#"{local a = 3, b: a}"#, r#"{"b":3}"#);177		assert_json!(r#"{local a = 3, local c = a, b: c}"#, r#"{"b":3}"#);178		assert_json!(179			r#"{local a = function (b) {[b]:4}, test: a("test")}"#,180			r#"{"test":{"test":4}}"#181		);182	}183184	#[test]185	fn direct_self() {186		println!(187			"{:#?}",188			eval!(189				r#"190					{191						local me = self,192						a: 3,193						b(): me.a,194					}195				"#196			)197		);198	}199200	#[test]201	fn indirect_self() {202		// `self` assigned to `me` was lost when being203		// referenced from field204		eval_stdlib!(205			r#"{206				local me = self,207				a: 3,208				b: me.a,209			}.b"#210		);211	}212213	// We can't trust other tests (And official jsonnet testsuite), if assert is not working correctly214	#[test]215	fn std_assert_ok() {216		eval_stdlib!("std.assertEqual(4.5 << 2, 16)");217	}218219	#[test]220	#[should_panic]221	fn std_assert_failure() {222		eval_stdlib!("std.assertEqual(4.5 << 2, 15)");223	}224225	#[test]226	fn string_is_string() {227		assert_eq!(228			eval_stdlib!("local arr = 'hello'; (!std.isArray(arr)) && (!std.isString(arr))"),229			Val::Literal(LiteralType::False)230		);231	}232233	#[test]234	fn base64_works() {235		assert_json_stdlib!(r#"std.base64("test")"#, r#""dGVzdA==""#);236	}237238	#[test]239	fn utf8_chars() {240		assert_json_stdlib!(241			r#"local c="😎";{c:std.codepoint(c),l:std.length(c)}"#,242			r#"{"c":128526,"l":1}"#243		)244	}245}