difftreelog
refactor merge trace to evaluator
in: master
6 files changed
crates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -9,7 +9,7 @@
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
-default = ["serialized-stdlib", "faster"]
+default = ["serialized-stdlib", "faster", "explaining-traces"]
# Serializes standard library AST instead of parsing them every run
serialized-stdlib = ["serde", "bincode", "jrsonnet-parser/deserialize"]
# Same as above, but with generated code instead of serde. Reduces memory usage, but increases binary size and compilation time
@@ -17,17 +17,32 @@
# Replace some standard library functions with faster implementations (I.e manifestJsonEx)
# Library works fine without this feature, but requires more memory and time for std function calls
faster = []
+# Rustc-like trace visualization
+explaining-traces = ["annotate-snippets"]
[dependencies]
jrsonnet-parser = { path = "../jrsonnet-parser", version = "1.0.0" }
+jrsonnet-stdlib = { path = "../jrsonnet-stdlib", version = "1.0.0" }
+pathdiff = "0.2.0"
+
closure = "0.3.0"
-jrsonnet-stdlib = { path = "../jrsonnet-stdlib", version = "1.0.0" }
indexmap = "1.4.0"
+
md5 = "0.7.0"
base64 = "0.12.3"
-serde = { version = "1.0.114", optional = true }
-bincode = { version = "1.3.1", optional = true }
+# Serialized stdlib
+[dependencies.serde]
+version = "1.0.114"
+optional = true
+[dependencies.bincode]
+version = "1.3.1"
+optional = true
+
+# Explaining traces
+[dependencies.annotate-snippets]
+version = "0.9.0"
+optional = true
[build-dependencies]
jrsonnet-parser = { path = "../jrsonnet-parser", features = ["dump", "serialize", "deserialize"], version = "1.0.0" }
crates/jrsonnet-evaluator/src/trace.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/trace.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-#[derive(Clone, PartialEq, Debug)]
-pub struct CodeLocation {
- pub line: usize,
- pub column: usize,
-
- pub line_start_offset: usize,
- pub line_end_offset: usize,
-}
-
-pub fn offset_to_location(file: &str, offsets: &[usize]) -> Vec<CodeLocation> {
- if offsets.is_empty() {
- return vec![];
- }
- let mut line = 1;
- let mut column = 1;
- let max_offset = *offsets.iter().max().unwrap();
-
- let mut offset_map = offsets
- .iter()
- .enumerate()
- .map(|(pos, offset)| (*offset, pos))
- .collect::<Vec<_>>();
- offset_map.sort_by_key(|v| v.0);
- offset_map.reverse();
-
- let mut out = vec![
- CodeLocation {
- column: 0,
- line: 0,
- line_start_offset: 0,
- line_end_offset: 0
- };
- offsets.len()
- ];
- let mut with_no_known_line_ending = vec![];
- let mut this_line_offset = 0;
- for (pos, ch) in file.chars().enumerate() {
- column += 1;
- match offset_map.last() {
- Some(x) if x.0 == pos => {
- let out_idx = x.1;
- with_no_known_line_ending.push(out_idx);
- out[out_idx].line = line;
- out[out_idx].column = column;
- out[out_idx].line_start_offset = this_line_offset;
- offset_map.pop();
- }
- _ => {}
- }
- if ch == '\n' {
- line += 1;
- column = 1;
-
- for idx in with_no_known_line_ending.drain(..) {
- out[idx].line_end_offset = pos;
- }
- this_line_offset = pos + 1;
-
- if pos == max_offset + 1 {
- break;
- }
- }
- }
- let file_end = file.chars().count();
- for idx in with_no_known_line_ending {
- out[idx].line_end_offset = file_end;
- }
-
- out
-}
-
-#[cfg(test)]
-pub mod tests {
- use super::{offset_to_location, CodeLocation};
-
- #[test]
- fn test() {
- assert_eq!(
- offset_to_location(
- "hello world\n_______________________________________________________",
- &[0, 14]
- ),
- vec![
- CodeLocation {
- line: 1,
- column: 1,
- line_start_offset: 0,
- line_end_offset: 11
- },
- CodeLocation {
- line: 2,
- column: 3,
- line_start_offset: 11,
- line_end_offset: 67
- }
- ]
- )
- }
-}
crates/jrsonnet-evaluator/src/trace/location.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-evaluator/src/trace/location.rs
@@ -0,0 +1,99 @@
+#[derive(Clone, PartialEq, Debug)]
+pub struct CodeLocation {
+ pub line: usize,
+ pub column: usize,
+
+ pub line_start_offset: usize,
+ pub line_end_offset: usize,
+}
+
+pub fn offset_to_location(file: &str, offsets: &[usize]) -> Vec<CodeLocation> {
+ if offsets.is_empty() {
+ return vec![];
+ }
+ let mut line = 1;
+ let mut column = 1;
+ let max_offset = *offsets.iter().max().unwrap();
+
+ let mut offset_map = offsets
+ .iter()
+ .enumerate()
+ .map(|(pos, offset)| (*offset, pos))
+ .collect::<Vec<_>>();
+ offset_map.sort_by_key(|v| v.0);
+ offset_map.reverse();
+
+ let mut out = vec![
+ CodeLocation {
+ column: 0,
+ line: 0,
+ line_start_offset: 0,
+ line_end_offset: 0
+ };
+ offsets.len()
+ ];
+ let mut with_no_known_line_ending = vec![];
+ let mut this_line_offset = 0;
+ for (pos, ch) in file.chars().enumerate() {
+ column += 1;
+ match offset_map.last() {
+ Some(x) if x.0 == pos => {
+ let out_idx = x.1;
+ with_no_known_line_ending.push(out_idx);
+ out[out_idx].line = line;
+ out[out_idx].column = column;
+ out[out_idx].line_start_offset = this_line_offset;
+ offset_map.pop();
+ }
+ _ => {}
+ }
+ if ch == '\n' {
+ line += 1;
+ column = 1;
+
+ for idx in with_no_known_line_ending.drain(..) {
+ out[idx].line_end_offset = pos;
+ }
+ this_line_offset = pos + 1;
+
+ if pos == max_offset + 1 {
+ break;
+ }
+ }
+ }
+ let file_end = file.chars().count();
+ for idx in with_no_known_line_ending {
+ out[idx].line_end_offset = file_end;
+ }
+
+ out
+}
+
+#[cfg(test)]
+pub mod tests {
+ use super::{offset_to_location, CodeLocation};
+
+ #[test]
+ fn test() {
+ assert_eq!(
+ offset_to_location(
+ "hello world\n_______________________________________________________",
+ &[0, 14]
+ ),
+ vec![
+ CodeLocation {
+ line: 1,
+ column: 1,
+ line_start_offset: 0,
+ line_end_offset: 11
+ },
+ CodeLocation {
+ line: 2,
+ column: 3,
+ line_start_offset: 11,
+ line_end_offset: 67
+ }
+ ]
+ )
+ }
+}
crates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth1mod location;23use crate::{EvaluationState, LocError};4pub use location::*;5use std::path::PathBuf;67/// How paths should be displayed8pub enum PathResolver {9 /// Only filename will be shown10 FileName,11 /// Absolute path of file12 Absolute,13 /// Relative path from base directory14 Relative(PathBuf),15}1617impl PathResolver {18 pub fn resolve(&self, from: &PathBuf) -> String {19 match self {20 PathResolver::FileName => from.file_name().unwrap().to_string_lossy().into_owned(),21 PathResolver::Absolute => from.to_string_lossy().into_owned(),22 PathResolver::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 trace to string pretty-printing36pub 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 - 1,69 start.line,70 end.column71 )?;72 }73 Ok(())74}7576/// vanilla jsonnet like 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 writeln!(out, "{:?}", error.0)?;90 let file_names = (error.1)91 .092 .iter()93 .map(|el| {94 let resolved_path = self.resolver.resolve(&el.location.0);95 // TODO: Process all trace elements first96 let location = evaluation_state97 .map_source_locations(&el.location.0, &[el.location.1, el.location.2]);98 (resolved_path, location)99 })100 .map(|(mut n, location)| {101 use std::fmt::Write;102 write!(n, ":").unwrap();103 print_code_location(&mut n, &location[0], &location[1]).unwrap();104 n105 })106 .collect::<Vec<_>>();107 let align = file_names.iter().map(|e| e.len()).max().unwrap_or(0);108 for (i, (el, file)) in (error.1).0.iter().zip(file_names).enumerate() {109 if i != 0 {110 writeln!(out)?;111 }112 write!(113 out,114 "{:<p$}{:<w$}: {}",115 "",116 file,117 el.desc,118 p = self.padding,119 w = align120 )?;121 }122 Ok(())123 }124}125126pub struct JSFormat;127impl TraceFormat for JSFormat {128 fn write_trace(129 &self,130 out: &mut dyn std::fmt::Write,131 evaluation_state: &EvaluationState,132 error: &LocError,133 ) -> Result<(), std::fmt::Error> {134 writeln!(out, "{:?}", error.0)?;135 for (i, item) in (error.1).0.iter().enumerate() {136 if i != 0 {137 writeln!(out)?;138 }139 let desc = &item.desc;140 let source = item.location.clone();141 let start_end = evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);142143 write!(144 out,145 " at {} ({}:{}:{})",146 desc,147 source.0.to_str().unwrap(),148 start_end[0].line,149 start_end[0].column,150 )?;151 }152 Ok(())153 }154}155156/// rustc-like trace displaying157#[cfg(feature = "explaining-traces")]158pub struct ExplainingFormat {159 pub resolver: PathResolver,160}161#[cfg(feature = "explaining-traces")]162impl TraceFormat for ExplainingFormat {163 fn write_trace(164 &self,165 out: &mut dyn std::fmt::Write,166 evaluation_state: &EvaluationState,167 error: &LocError,168 ) -> Result<(), std::fmt::Error> {169 use annotate_snippets::{170 display_list::{DisplayList, FormatOptions},171 snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},172 };173 writeln!(out, "{:?}", error.0)?;174 let trace = &error.1;175 for item in trace.0.iter() {176 let desc = &item.desc;177 let source = item.location.clone();178 let start_end = evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);179180 let source_fragment: String = evaluation_state181 .get_source(&source.0)182 .unwrap()183 .chars()184 .skip(start_end[0].line_start_offset)185 .take(start_end[1].line_end_offset - start_end[0].line_start_offset)186 .collect();187188 let origin = self.resolver.resolve(&source.0);189 let snippet = Snippet {190 opt: FormatOptions {191 color: true,192 ..Default::default()193 },194 title: None,195 footer: vec![],196 slices: vec![Slice {197 source: &source_fragment,198 line_start: start_end[0].line,199 origin: Some(&origin),200 fold: false,201 annotations: vec![SourceAnnotation {202 label: desc,203 annotation_type: AnnotationType::Error,204 range: (205 source.1 - start_end[0].line_start_offset,206 source.2 - start_end[0].line_start_offset,207 ),208 }],209 }],210 };211212 let dl = DisplayList::from(snippet);213 writeln!(out, "{}", dl)?;214 }215 Ok(())216 }217}crates/jrsonnet-trace/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-trace/Cargo.toml
+++ /dev/null
@@ -1,13 +0,0 @@
-[package]
-name = "jrsonnet-trace"
-version = "1.0.0"
-authors = ["Лач <iam@lach.pw>"]
-edition = "2018"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-jrsonnet-evaluator = { path = "../jrsonnet-evaluator", version = "1.0.0" }
-jrsonnet-parser = { path = "../jrsonnet-parser", version = "1.0.0" }
-pathdiff = "0.2.0"
-annotate-snippets = "0.9.0"
crates/jrsonnet-trace/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-trace/src/lib.rs
+++ /dev/null
@@ -1,173 +0,0 @@
-use jrsonnet_evaluator::{trace::CodeLocation, EvaluationState, LocError};
-use std::path::PathBuf;
-
-/// How paths should be displayed
-pub enum PathResolver {
- /// Only filename will be shown
- FileName,
- /// Absolute path of file
- Absolute,
- /// Relative path from base directory
- Relative(PathBuf),
-}
-
-impl PathResolver {
- pub fn resolve(&self, from: &PathBuf) -> String {
- match self {
- PathResolver::FileName => from.file_name().unwrap().to_string_lossy().into_owned(),
- PathResolver::Absolute => from.to_string_lossy().into_owned(),
- PathResolver::Relative(base) => {
- if from.is_relative() {
- return from.to_string_lossy().into_owned();
- }
- pathdiff::diff_paths(from, base)
- .unwrap()
- .to_string_lossy()
- .into_owned()
- }
- }
- }
-}
-
-/// Implements trace to string pretty-printing
-pub trait TraceFormat {
- fn write_trace(
- &self,
- out: &mut dyn std::io::Write,
- evaluation_state: &EvaluationState,
- error: &LocError,
- ) -> Result<(), std::io::Error>;
- fn print_trace(
- &self,
- evaluation_state: &EvaluationState,
- error: &LocError,
- ) -> Result<(), std::io::Error> {
- self.write_trace(&mut std::io::stdout(), evaluation_state, error)
- }
-}
-
-fn print_code_location(
- out: &mut impl std::fmt::Write,
- start: &CodeLocation,
- end: &CodeLocation,
-) -> Result<(), std::fmt::Error> {
- if start.line == end.line {
- if start.column == end.column {
- write!(out, "{}:{}", start.line, end.column - 1)?;
- } else {
- write!(out, "{}:{}-{}", start.line, start.column - 1, end.column)?;
- }
- } else {
- write!(
- out,
- "{}:{}-{}:{}",
- start.line,
- end.column - 1,
- start.line,
- end.column
- )?;
- }
- Ok(())
-}
-
-/// vanilla jsonnet like formatting
-pub struct CompactFormat {
- pub resolver: PathResolver,
-}
-
-impl TraceFormat for CompactFormat {
- fn write_trace(
- &self,
- out: &mut dyn std::io::Write,
- evaluation_state: &EvaluationState,
- error: &LocError,
- ) -> Result<(), std::io::Error> {
- writeln!(out, "{:?}", error.0)?;
- let file_names = (error.1)
- .0
- .iter()
- .map(|el| {
- let resolved_path = self.resolver.resolve(&(el.0).0);
- // TODO: Process all trace elements first
- let location =
- evaluation_state.map_source_locations(&(el.0).0, &[(el.0).1, (el.0).2]);
- (resolved_path, location)
- })
- .map(|(mut n, location)| {
- use std::fmt::Write;
- write!(n, ":").unwrap();
- print_code_location(&mut n, &location[0], &location[1]).unwrap();
- n
- })
- .collect::<Vec<_>>();
- let align = file_names.iter().map(|e| e.len()).max().unwrap_or(0);
- for (i, (el, file)) in (error.1).0.iter().zip(file_names).enumerate() {
- if i != 0 {
- writeln!(out)?;
- }
- write!(out, "{:<w$}: {}", file, el.1, w = align)?;
- }
- Ok(())
- }
-}
-
-/// rustc-like trace displaying
-pub struct ExplainingFormat {
- pub resolver: PathResolver,
-}
-impl TraceFormat for ExplainingFormat {
- fn write_trace(
- &self,
- out: &mut dyn std::io::Write,
- evaluation_state: &EvaluationState,
- error: &LocError,
- ) -> Result<(), std::io::Error> {
- use annotate_snippets::{
- display_list::{DisplayList, FormatOptions},
- snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},
- };
- writeln!(out, "{:?}", error.0)?;
- let trace = &error.1;
- for item in trace.0.iter() {
- let desc = &item.1;
- let source = item.0.clone();
- let start_end = evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);
-
- let source_fragment: String = evaluation_state
- .get_source(&source.0)
- .unwrap()
- .chars()
- .skip(start_end[0].line_start_offset)
- .take(start_end[1].line_end_offset - start_end[0].line_start_offset)
- .collect();
-
- let origin = self.resolver.resolve(&source.0);
- let snippet = Snippet {
- opt: FormatOptions {
- color: true,
- ..Default::default()
- },
- title: None,
- footer: vec![],
- slices: vec![Slice {
- source: &source_fragment,
- line_start: start_end[0].line,
- origin: Some(&origin),
- fold: false,
- annotations: vec![SourceAnnotation {
- label: desc,
- annotation_type: AnnotationType::Error,
- range: (
- source.1 - start_end[0].line_start_offset,
- source.2 - start_end[0].line_start_offset,
- ),
- }],
- }],
- };
-
- let dl = DisplayList::from(snippet);
- writeln!(out, "{}", dl)?;
- }
- Ok(())
- }
-}