difftreelog
fix(evaluator) utf-8 support
in: master
3 files changed
crates/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 {
{
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.rsdiffbeforeafterboth1#![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}