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
before · crates/jsonnet-evaluator/src/ctx.rs
1use crate::{future_wrapper, rc_fn_helper, LazyBinding, ObjValue, LazyVal, Val};2use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};34rc_fn_helper!(5	ContextCreator,6	context_creator,7	dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Context8);910future_wrapper!(Context, FutureContext);1112#[derive(Debug)]13struct ContextInternals {14	dollar: Option<ObjValue>,15	this: Option<ObjValue>,16	super_obj: Option<ObjValue>,17	bindings: Rc<HashMap<String, LazyVal>>,18}19pub struct Context(Rc<ContextInternals>);20impl Debug for Context {21	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {22		f.debug_struct("Context")23			.field("this", &self.0.this.clone().map(|e| Rc::as_ptr(&e.0)))24			.finish()25	}26}27impl Context {28	pub fn new_future() -> FutureContext {29		FutureContext(Rc::new(RefCell::new(None)))30	}3132	pub fn dollar(&self) -> &Option<ObjValue> {33		&self.0.dollar34	}3536	pub fn this(&self) -> &Option<ObjValue> {37		&self.0.this38	}3940	pub fn super_obj(&self) -> &Option<ObjValue> {41		&self.0.super_obj42	}4344	pub fn new() -> Context {45		Context(Rc::new(ContextInternals {46			dollar: None,47			this: None,48			super_obj: None,49			bindings: Rc::new(HashMap::new()),50		}))51	}5253	pub fn binding(&self, name: &str) -> LazyVal {54		self.055			.bindings56			.get(name)57			.cloned()58			.unwrap_or_else(|| {59				panic!("can't find {} in {:?}", name, self);60			})61	}62	pub fn into_future(self, ctx: FutureContext) -> Context {63		{64			ctx.0.borrow_mut().replace(self);65		}66		ctx.unwrap()67	}6869	pub fn extend(70		&self,71		new_bindings: HashMap<String, LazyBinding>,72		new_dollar: Option<ObjValue>,73		new_this: Option<ObjValue>,74		new_super_obj: Option<ObjValue>,75	) -> Context {76		let dollar = new_dollar.or_else(|| self.0.dollar.clone());77		let this = new_this.or_else(|| self.0.this.clone());78		let super_obj = new_super_obj.or_else(|| self.0.super_obj.clone());79		let bindings = if new_bindings.is_empty() {80			self.0.bindings.clone()81		} else {82			let mut new = HashMap::new(); // = self.0.bindings.clone();83			for (k, v) in self.0.bindings.iter() {84				new.insert(k.clone(), v.clone());85			}86			for (k, v) in new_bindings.into_iter() {87				new.insert(k, v.0(this.clone(), super_obj.clone()));88			}89			Rc::new(new)90		};91		Context(Rc::new(ContextInternals {92			dollar,93			this,94			super_obj,95			bindings,96		}))97	}98}99100impl Default for Context {101	fn default() -> Self {102		Self::new()103	}104}105106impl PartialEq for Context {107	fn eq(&self, other: &Self) -> bool {108		Rc::ptr_eq(&self.0, &other.0)109	}110}111112impl Clone for Context {113	fn clone(&self) -> Self {114		Context(self.0.clone())115	}116}
after · crates/jsonnet-evaluator/src/ctx.rs
1use crate::{future_wrapper, rc_fn_helper, LazyBinding, LazyVal, ObjValue};2use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};34rc_fn_helper!(5	ContextCreator,6	context_creator,7	dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Context8);910future_wrapper!(Context, FutureContext);1112#[derive(Debug)]13struct ContextInternals {14	dollar: Option<ObjValue>,15	this: Option<ObjValue>,16	super_obj: Option<ObjValue>,17	bindings: Rc<HashMap<String, LazyVal>>,18}19pub struct Context(Rc<ContextInternals>);20impl Debug for Context {21	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {22		f.debug_struct("Context")23			.field("this", &self.0.this.clone().map(|e| Rc::as_ptr(&e.0)))24			.finish()25	}26}27impl Context {28	pub fn new_future() -> FutureContext {29		FutureContext(Rc::new(RefCell::new(None)))30	}3132	pub fn dollar(&self) -> &Option<ObjValue> {33		&self.0.dollar34	}3536	pub fn this(&self) -> &Option<ObjValue> {37		&self.0.this38	}3940	pub fn super_obj(&self) -> &Option<ObjValue> {41		&self.0.super_obj42	}4344	pub fn new() -> Context {45		Context(Rc::new(ContextInternals {46			dollar: None,47			this: None,48			super_obj: None,49			bindings: Rc::new(HashMap::new()),50		}))51	}5253	pub fn binding(&self, name: &str) -> LazyVal {54		self.0.bindings.get(name).cloned().unwrap_or_else(|| {55			panic!("can't find {} in {:?}", name, self);56		})57	}58	pub fn into_future(self, ctx: FutureContext) -> Context {59		{60			ctx.0.borrow_mut().replace(self);61		}62		ctx.unwrap()63	}6465	pub fn extend(66		&self,67		new_bindings: HashMap<String, LazyBinding>,68		new_dollar: Option<ObjValue>,69		new_this: Option<ObjValue>,70		new_super_obj: Option<ObjValue>,71	) -> Context {72		let dollar = new_dollar.or_else(|| self.0.dollar.clone());73		let this = new_this.or_else(|| self.0.this.clone());74		let super_obj = new_super_obj.or_else(|| self.0.super_obj.clone());75		let bindings = if new_bindings.is_empty() {76			self.0.bindings.clone()77		} else {78			let mut new = HashMap::new(); // = self.0.bindings.clone();79			for (k, v) in self.0.bindings.iter() {80				new.insert(k.clone(), v.clone());81			}82			for (k, v) in new_bindings.into_iter() {83				new.insert(k, v.0(this.clone(), super_obj.clone()));84			}85			Rc::new(new)86		};87		Context(Rc::new(ContextInternals {88			dollar,89			this,90			super_obj,91			bindings,92		}))93	}94}9596impl Default for Context {97	fn default() -> Self {98		Self::new()99	}100}101102impl PartialEq for Context {103	fn eq(&self, other: &Self) -> bool {104		Rc::ptr_eq(&self.0, &other.0)105	}106}107108impl Clone for Context {109	fn clone(&self) -> Self {110		Context(self.0.clone())111	}112}
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
--- a/crates/jsonnet-evaluator/src/lib.rs
+++ b/crates/jsonnet-evaluator/src/lib.rs
@@ -68,10 +68,7 @@
 	}
 	macro_rules! assert_json_stdlib {
 		($str: expr, $out: expr) => {
-			assert_eq!(
-				format!("{}", eval_stdlib!($str)),
-				$out
-				)
+			assert_eq!(format!("{}", eval_stdlib!($str)), $out)
 		};
 	}
 	macro_rules! assert_eval_neg {
@@ -237,4 +234,12 @@
 	fn base64_works() {
 		assert_json_stdlib!(r#"std.base64("test")"#, r#""dGVzdA==""#);
 	}
+
+	#[test]
+	fn utf8_chars() {
+		assert_json_stdlib!(
+			r#"local c="😎";{c:std.codepoint(c),l:std.length(c)}"#,
+			r#"{"c":128526,"l":1}"#
+		)
+	}
 }