difftreelog
feat std.parseYaml intrinsic
in: master
6 files changed
crates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -7,11 +7,9 @@
edition = "2018"
[features]
-default = ["serialized-stdlib", "explaining-traces", "serde-json"]
+default = ["serialized-stdlib", "explaining-traces"]
# Serializes standard library AST instead of parsing them every run
-serialized-stdlib = ["serde", "bincode", "jrsonnet-parser/deserialize"]
-# Allow to convert Val into serde_json::Value and backwards
-serde-json = ["serde", "serde_json"]
+serialized-stdlib = ["bincode", "jrsonnet-parser/deserialize"]
# Rustc-like trace visualization
explaining-traces = ["annotate-snippets"]
# Allows library authors to throw custom errors
@@ -34,21 +32,17 @@
thiserror = "1.0"
gcmodule = { git = "https://github.com/CertainLach/gcmodule", branch = "jrsonnet" }
+serde = "1.0"
+serde_json = "1.0"
+serde_yaml = { git = "https://github.com/CertainLach/serde-yaml", branch = "feature/old-octals-quirk" }
+
[dependencies.anyhow]
version = "1.0"
optional = true
# Serialized stdlib
-[dependencies.serde]
-version = "1.0"
-optional = true
[dependencies.bincode]
version = "1.3.1"
-optional = true
-
-# Serde json
-[dependencies.serde_json]
-version = "1.0"
optional = true
# Explaining traces
crates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -3,15 +3,17 @@
equals,
error::{Error::*, Result},
operator::evaluate_mod_op,
- parse_args, primitive_equals, push_frame, throw, with_state, ArrValue, Context,
- EvaluationState, FuncVal, IndexableVal, LazyVal, Val,
+ parse_args, primitive_equals, push_frame, throw, with_state, ArrValue, Context, FuncVal,
+ IndexableVal, LazyVal, Val,
};
use format::{format_arr, format_obj};
use gcmodule::Cc;
use jrsonnet_interner::IStr;
use jrsonnet_parser::{ArgsDesc, ExprLocation};
use jrsonnet_types::ty;
-use std::{collections::HashMap, path::PathBuf, rc::Rc};
+use serde::Deserialize;
+use serde_yaml::DeserializingQuirks;
+use std::{collections::HashMap, convert::TryFrom, path::PathBuf, rc::Rc};
pub mod stdlib;
pub use stdlib::*;
@@ -128,6 +130,7 @@
("strReplace".into(), builtin_str_replace),
("splitLimit".into(), builtin_splitlimit),
("parseJson".into(), builtin_parse_json),
+ ("parseYaml".into(), builtin_parse_yaml),
("asciiUpper".into(), builtin_ascii_upper),
("asciiLower".into(), builtin_ascii_lower),
("member".into(), builtin_member),
@@ -210,9 +213,30 @@
parse_args!(context, "parseJson", args, 1, [
0, s: ty!(string) => Val::Str;
], {
- let state = EvaluationState::default();
- let path = PathBuf::from("std.parseJson").into();
- state.evaluate_snippet_raw(path ,s)
+ let value: serde_json::Value = serde_json::from_str(&s).map_err(|e| RuntimeError(format!("failed to parse json: {}", e).into()))?;
+ Ok(Val::try_from(&value)?)
+ })
+}
+
+fn builtin_parse_yaml(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
+ parse_args!(context, "parseYaml", args, 1, [
+ 0, s: ty!(string) => Val::Str;
+ ], {
+ let value = serde_yaml::Deserializer::from_str_with_quirks(&s, DeserializingQuirks { old_octals: true });
+ let mut out = vec![];
+ for item in value {
+ let value = serde_json::Value::deserialize(item)
+ .map_err(|e| RuntimeError(format!("failed to parse yaml: {}", e).into()))?;
+ let val = Val::try_from(&value)?;
+ out.push(val);
+ }
+ if out.is_empty() {
+ Ok(Val::Null)
+ } else if out.len() == 1 {
+ Ok(out.into_iter().next().unwrap())
+ } else {
+ Ok(Val::Arr(out.into()))
+ }
})
}
crates/jrsonnet-evaluator/src/integrations/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/integrations/mod.rs
+++ b/crates/jrsonnet-evaluator/src/integrations/mod.rs
@@ -1,2 +1 @@
-#[cfg(feature = "serde-json")]
pub mod serde;
crates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/integrations/serde.rs
+++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs
@@ -15,7 +15,7 @@
Val::Num(n) => Self::Number(if n.fract() <= f64::EPSILON {
(*n as i64).into()
} else {
- Number::from_f64(*n).expect("to json number")
+ Number::from_f64(*n).expect("jsonnet numbers can't be infinite or NaN")
}),
Val::Arr(a) => {
let mut out = Vec::with_capacity(a.len());
@@ -29,7 +29,9 @@
for key in o.fields() {
out.insert(
(&key as &str).into(),
- (&o.get(key)?.expect("field exists")).try_into()?,
+ (&o.get(key)?
+ .expect("key is present in fields, so value should exist"))
+ .try_into()?,
);
}
Self::Object(out)
@@ -39,27 +41,30 @@
}
}
-impl From<&Value> for Val {
- fn from(v: &Value) -> Self {
- match v {
+impl TryFrom<&Value> for Val {
+ type Error = LocError;
+ fn try_from(v: &Value) -> Result<Self> {
+ Ok(match v {
Value::Null => Self::Null,
Value::Bool(v) => Self::Bool(*v),
- Value::Number(n) => Self::Num(n.as_f64().expect("as f64")),
+ Value::Number(n) => Self::Num(n.as_f64().ok_or_else(|| {
+ RuntimeError(format!("json number can't be represented as jsonnet: {}", n).into())
+ })?),
Value::String(s) => Self::Str((s as &str).into()),
Value::Array(a) => {
let mut out: Vec<Self> = Vec::with_capacity(a.len());
for v in a {
- out.push(v.into());
+ out.push(v.try_into()?);
}
Self::Arr(out.into())
}
Value::Object(o) => {
let mut builder = ObjValueBuilder::with_capacity(o.len());
for (k, v) in o {
- builder.member((k as &str).into()).value(v.into());
+ builder.member((k as &str).into()).value(v.try_into()?);
}
Self::Obj(builder.build())
}
- }
+ })
}
}
crates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth1mod location;23use crate::{error::Error, EvaluationState, LocError};4pub use location::*;5use std::path::{Path, PathBuf};67/// The way paths should be displayed8pub enum PathResolver {9 /// Only filename10 FileName,11 /// Absolute path12 Absolute,13 /// Path relative to base directory14 Relative(PathBuf),15}1617impl PathResolver {18 pub fn resolve(&self, from: &Path) -> String {19 match self {20 Self::FileName => from.file_name().unwrap().to_string_lossy().into_owned(),21 Self::Absolute => from.to_string_lossy().into_owned(),22 Self::Relative(base) => {23 if from.is_relative() {24 return from.to_string_lossy().into_owned();25 }26 pathdiff::diff_paths(from, base)27 .unwrap()28 .to_string_lossy()29 .into_owned()30 }31 }32 }33}3435/// Implements pretty-printing of traces36pub trait TraceFormat {37 fn write_trace(38 &self,39 out: &mut dyn std::fmt::Write,40 evaluation_state: &EvaluationState,41 error: &LocError,42 ) -> Result<(), std::fmt::Error>;43 // fn print_trace(44 // &self,45 // evaluation_state: &EvaluationState,46 // error: &LocError,47 // ) -> Result<(), std::fmt::Error> {48 // self.write_trace(&mut std::fmt::stdout(), evaluation_state, error)49 // }50}5152fn print_code_location(53 out: &mut impl std::fmt::Write,54 start: &CodeLocation,55 end: &CodeLocation,56) -> Result<(), std::fmt::Error> {57 if start.line == end.line {58 if start.column == end.column {59 write!(out, "{}:{}", start.line, end.column - 1)?;60 } else {61 write!(out, "{}:{}-{}", start.line, start.column - 1, end.column)?;62 }63 } else {64 write!(65 out,66 "{}:{}-{}:{}",67 start.line,68 end.column.saturating_sub(1),69 start.line,70 end.column71 )?;72 }73 Ok(())74}7576/// vanilla-like jsonnet formatting77pub struct CompactFormat {78 pub resolver: PathResolver,79 pub padding: usize,80}8182impl TraceFormat for CompactFormat {83 fn write_trace(84 &self,85 out: &mut dyn std::fmt::Write,86 evaluation_state: &EvaluationState,87 error: &LocError,88 ) -> Result<(), std::fmt::Error> {89 write!(out, "{}", error.error())?;90 if let Error::ImportSyntaxError {91 path,92 source_code,93 error,94 } = error.error()95 {96 writeln!(out)?;97 use std::fmt::Write;98 let mut n = self.resolver.resolve(path);99 let mut offset = error.location.offset;100 let is_eof = if offset >= source_code.len() {101 offset = source_code.len() - 1;102 true103 } else {104 false105 };106 let mut location = offset_to_location(source_code, &[offset])107 .into_iter()108 .next()109 .unwrap();110 if is_eof {111 location.column += 1;112 }113114 write!(n, ":").unwrap();115 print_code_location(&mut n, &location, &location).unwrap();116 write!(out, "{:<p$}{}", "", n, p = self.padding,)?;117 }118 let file_names = error119 .trace()120 .0121 .iter()122 .map(|el| &el.location)123 .map(|location| {124 use std::fmt::Write;125 if let Some(location) = location {126 let mut resolved_path = self.resolver.resolve(&location.0);127 // TODO: Process all trace elements first128 let location = evaluation_state129 .map_source_locations(&location.0, &[location.1, location.2]);130 write!(resolved_path, ":").unwrap();131 print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();132 write!(resolved_path, ":").unwrap();133 Some(resolved_path)134 } else {135 None136 }137 })138 .collect::<Vec<_>>();139 let align = file_names140 .iter()141 .flatten()142 .map(|e| e.len())143 .max()144 .unwrap_or(0);145 for (el, file) in error.trace().0.iter().zip(file_names) {146 writeln!(out)?;147 if let Some(file) = file {148 write!(149 out,150 "{:<p$}{:<w$} {}",151 "",152 file,153 el.desc,154 p = self.padding,155 w = align156 )?;157 } else {158 write!(out, "{:<p$}{}", "", el.desc, p = self.padding,)?;159 }160 }161 Ok(())162 }163}164165pub struct JsFormat;166impl TraceFormat for JsFormat {167 fn write_trace(168 &self,169 out: &mut dyn std::fmt::Write,170 evaluation_state: &EvaluationState,171 error: &LocError,172 ) -> Result<(), std::fmt::Error> {173 write!(out, "{}", error.error())?;174 for item in error.trace().0.iter() {175 writeln!(out)?;176 let desc = &item.desc;177 if let Some(source) = &item.location {178 let start_end =179 evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);180181 write!(182 out,183 " at {} ({}:{}:{})",184 desc,185 source.0.to_str().unwrap(),186 start_end[0].line,187 start_end[0].column,188 )?;189 } else {190 write!(out, " during {}", desc)?;191 }192 }193 Ok(())194 }195}196197/// rustc-like trace displaying198#[cfg(feature = "explaining-traces")]199pub struct ExplainingFormat {200 pub resolver: PathResolver,201}202#[cfg(feature = "explaining-traces")]203impl TraceFormat for ExplainingFormat {204 fn write_trace(205 &self,206 out: &mut dyn std::fmt::Write,207 evaluation_state: &EvaluationState,208 error: &LocError,209 ) -> Result<(), std::fmt::Error> {210 write!(out, "{}", error.error())?;211 if let Error::ImportSyntaxError {212 path,213 source_code,214 error,215 } = error.error()216 {217 writeln!(out)?;218 let offset = error.location.offset;219 let location = offset_to_location(source_code, &[offset])220 .into_iter()221 .next()222 .unwrap();223 let mut end_location = location.clone();224 end_location.offset += 1;225226 self.print_snippet(227 out,228 source_code,229 path,230 &location,231 &end_location,232 "syntax error",233 )?;234 }235 let trace = &error.trace();236 for item in trace.0.iter() {237 writeln!(out)?;238 let desc = &item.desc;239 if let Some(source) = &item.location {240 let start_end =241 evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);242 self.print_snippet(243 out,244 &evaluation_state.get_source(&source.0).unwrap(),245 &source.0,246 &start_end[0],247 &start_end[1],248 desc,249 )?;250 } else {251 write!(out, "{}", desc)?;252 }253 }254 Ok(())255 }256}257258impl ExplainingFormat {259 fn print_snippet(260 &self,261 out: &mut dyn std::fmt::Write,262 source: &str,263 origin: &Path,264 start: &CodeLocation,265 end: &CodeLocation,266 desc: &str,267 ) -> Result<(), std::fmt::Error> {268 use annotate_snippets::{269 display_list::{DisplayList, FormatOptions},270 snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},271 };272273 let source_fragment: String = source274 .chars()275 .skip(start.line_start_offset)276 .take(end.line_end_offset - end.line_start_offset)277 .collect();278279 let origin = self.resolver.resolve(origin);280 let snippet = Snippet {281 opt: FormatOptions {282 color: true,283 ..Default::default()284 },285 title: None,286 footer: vec![],287 slices: vec![Slice {288 source: &source_fragment,289 line_start: start.line,290 origin: Some(&origin),291 fold: false,292 annotations: vec![SourceAnnotation {293 label: desc,294 annotation_type: AnnotationType::Error,295 range: (296 start.offset - start.line_start_offset,297 (end.offset - start.line_start_offset).min(source_fragment.len()),298 ),299 }],300 }],301 };302303 let dl = DisplayList::from(snippet);304 write!(out, "{}", dl)?;305306 Ok(())307 }308}1mod location;23use crate::{error::Error, EvaluationState, LocError};4pub use location::*;5use std::path::{Path, PathBuf};67/// The way paths should be displayed8pub enum PathResolver {9 /// Only filename10 FileName,11 /// Absolute path12 Absolute,13 /// Path relative to base directory14 Relative(PathBuf),15}1617impl PathResolver {18 pub fn resolve(&self, from: &Path) -> String {19 match self {20 Self::FileName => from.file_name().unwrap().to_string_lossy().into_owned(),21 Self::Absolute => from.to_string_lossy().into_owned(),22 Self::Relative(base) => {23 if from.is_relative() {24 return from.to_string_lossy().into_owned();25 }26 pathdiff::diff_paths(from, base)27 .unwrap()28 .to_string_lossy()29 .into_owned()30 }31 }32 }33}3435/// Implements pretty-printing of traces36pub trait TraceFormat {37 fn write_trace(38 &self,39 out: &mut dyn std::fmt::Write,40 evaluation_state: &EvaluationState,41 error: &LocError,42 ) -> Result<(), std::fmt::Error>;43 // fn print_trace(44 // &self,45 // evaluation_state: &EvaluationState,46 // error: &LocError,47 // ) -> Result<(), std::fmt::Error> {48 // self.write_trace(&mut std::fmt::stdout(), evaluation_state, error)49 // }50}5152fn print_code_location(53 out: &mut impl std::fmt::Write,54 start: &CodeLocation,55 end: &CodeLocation,56) -> Result<(), std::fmt::Error> {57 if start.line == end.line {58 if start.column == end.column {59 write!(out, "{}:{}", start.line, end.column - 1)?;60 } else {61 write!(out, "{}:{}-{}", start.line, start.column - 1, end.column)?;62 }63 } else {64 write!(65 out,66 "{}:{}-{}:{}",67 start.line,68 end.column.saturating_sub(1),69 start.line,70 end.column71 )?;72 }73 Ok(())74}7576/// vanilla-like jsonnet formatting77pub struct CompactFormat {78 pub resolver: PathResolver,79 pub padding: usize,80}8182impl TraceFormat for CompactFormat {83 fn write_trace(84 &self,85 out: &mut dyn std::fmt::Write,86 evaluation_state: &EvaluationState,87 error: &LocError,88 ) -> Result<(), std::fmt::Error> {89 write!(out, "{}", error.error())?;90 if let Error::ImportSyntaxError {91 path,92 source_code,93 error,94 } = error.error()95 {96 writeln!(out)?;97 use std::fmt::Write;98 let mut n = self.resolver.resolve(path);99 let mut offset = error.location.offset;100 let is_eof = if offset >= source_code.len() {101 offset = source_code.len().saturating_sub(1);102 true103 } else {104 false105 };106 let mut location = offset_to_location(source_code, &[offset])107 .into_iter()108 .next()109 .unwrap();110 if is_eof {111 location.column += 1;112 }113114 write!(n, ":").unwrap();115 print_code_location(&mut n, &location, &location).unwrap();116 write!(out, "{:<p$}{}", "", n, p = self.padding,)?;117 }118 let file_names = error119 .trace()120 .0121 .iter()122 .map(|el| &el.location)123 .map(|location| {124 use std::fmt::Write;125 if let Some(location) = location {126 let mut resolved_path = self.resolver.resolve(&location.0);127 // TODO: Process all trace elements first128 let location = evaluation_state129 .map_source_locations(&location.0, &[location.1, location.2]);130 write!(resolved_path, ":").unwrap();131 print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();132 write!(resolved_path, ":").unwrap();133 Some(resolved_path)134 } else {135 None136 }137 })138 .collect::<Vec<_>>();139 let align = file_names140 .iter()141 .flatten()142 .map(|e| e.len())143 .max()144 .unwrap_or(0);145 for (el, file) in error.trace().0.iter().zip(file_names) {146 writeln!(out)?;147 if let Some(file) = file {148 write!(149 out,150 "{:<p$}{:<w$} {}",151 "",152 file,153 el.desc,154 p = self.padding,155 w = align156 )?;157 } else {158 write!(out, "{:<p$}{}", "", el.desc, p = self.padding,)?;159 }160 }161 Ok(())162 }163}164165pub struct JsFormat;166impl TraceFormat for JsFormat {167 fn write_trace(168 &self,169 out: &mut dyn std::fmt::Write,170 evaluation_state: &EvaluationState,171 error: &LocError,172 ) -> Result<(), std::fmt::Error> {173 write!(out, "{}", error.error())?;174 for item in error.trace().0.iter() {175 writeln!(out)?;176 let desc = &item.desc;177 if let Some(source) = &item.location {178 let start_end =179 evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);180181 write!(182 out,183 " at {} ({}:{}:{})",184 desc,185 source.0.to_str().unwrap(),186 start_end[0].line,187 start_end[0].column,188 )?;189 } else {190 write!(out, " during {}", desc)?;191 }192 }193 Ok(())194 }195}196197/// rustc-like trace displaying198#[cfg(feature = "explaining-traces")]199pub struct ExplainingFormat {200 pub resolver: PathResolver,201}202#[cfg(feature = "explaining-traces")]203impl TraceFormat for ExplainingFormat {204 fn write_trace(205 &self,206 out: &mut dyn std::fmt::Write,207 evaluation_state: &EvaluationState,208 error: &LocError,209 ) -> Result<(), std::fmt::Error> {210 write!(out, "{}", error.error())?;211 if let Error::ImportSyntaxError {212 path,213 source_code,214 error,215 } = error.error()216 {217 writeln!(out)?;218 let offset = error.location.offset;219 let location = offset_to_location(source_code, &[offset])220 .into_iter()221 .next()222 .unwrap();223 let mut end_location = location.clone();224 end_location.offset += 1;225226 self.print_snippet(227 out,228 source_code,229 path,230 &location,231 &end_location,232 "syntax error",233 )?;234 }235 let trace = &error.trace();236 for item in trace.0.iter() {237 writeln!(out)?;238 let desc = &item.desc;239 if let Some(source) = &item.location {240 let start_end =241 evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);242 self.print_snippet(243 out,244 &evaluation_state.get_source(&source.0).unwrap(),245 &source.0,246 &start_end[0],247 &start_end[1],248 desc,249 )?;250 } else {251 write!(out, "{}", desc)?;252 }253 }254 Ok(())255 }256}257258impl ExplainingFormat {259 fn print_snippet(260 &self,261 out: &mut dyn std::fmt::Write,262 source: &str,263 origin: &Path,264 start: &CodeLocation,265 end: &CodeLocation,266 desc: &str,267 ) -> Result<(), std::fmt::Error> {268 use annotate_snippets::{269 display_list::{DisplayList, FormatOptions},270 snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},271 };272273 let source_fragment: String = source274 .chars()275 .skip(start.line_start_offset)276 .take(end.line_end_offset - end.line_start_offset)277 .collect();278279 let origin = self.resolver.resolve(origin);280 let snippet = Snippet {281 opt: FormatOptions {282 color: true,283 ..Default::default()284 },285 title: None,286 footer: vec![],287 slices: vec![Slice {288 source: &source_fragment,289 line_start: start.line,290 origin: Some(&origin),291 fold: false,292 annotations: vec![SourceAnnotation {293 label: desc,294 annotation_type: AnnotationType::Error,295 range: (296 start.offset - start.line_start_offset,297 (end.offset - start.line_start_offset).min(source_fragment.len()),298 ),299 }],300 }],301 };302303 let dl = DisplayList::from(snippet);304 write!(out, "{}", dl)?;305306 Ok(())307 }308}crates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/std.jsonnet
+++ b/crates/jrsonnet-stdlib/src/std.jsonnet
@@ -23,6 +23,7 @@
trace:: $intrinsic(trace),
id:: $intrinsic(id),
parseJson:: $intrinsic(parseJson),
+ parseYaml:: $intrinsic(parseYaml),
log:: $intrinsic(log),
pow:: $intrinsic(pow),