difftreelog
fix(evaluator) utf-8 support
in: master
3 files changed
crates/jsonnet-evaluator/src/ctx.rsdiffbeforeafterboth1use 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}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}crates/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");
}
crates/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}"#
+ )
+ }
}