git.delta.rocks / jrsonnet / refs/commits / be66091d566e

difftreelog

refactor merge trace to evaluator

Лач2020-07-19parent: #1820422.patch.diff
in: master

6 files changed

modifiedcrates/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" }
deletedcrates/jrsonnet-evaluator/src/trace.rsdiffbeforeafterboth

no changes

addedcrates/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
+				}
+			]
+		)
+	}
+}
addedcrates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-evaluator/src/trace/mod.rs
@@ -0,0 +1,217 @@
+mod location;
+
+use crate::{EvaluationState, LocError};
+pub use location::*;
+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::fmt::Write,
+		evaluation_state: &EvaluationState,
+		error: &LocError,
+	) -> Result<(), std::fmt::Error>;
+	// fn print_trace(
+	// 	&self,
+	// 	evaluation_state: &EvaluationState,
+	// 	error: &LocError,
+	// ) -> Result<(), std::fmt::Error> {
+	// 	self.write_trace(&mut std::fmt::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,
+	pub padding: usize,
+}
+
+impl TraceFormat for CompactFormat {
+	fn write_trace(
+		&self,
+		out: &mut dyn std::fmt::Write,
+		evaluation_state: &EvaluationState,
+		error: &LocError,
+	) -> Result<(), std::fmt::Error> {
+		writeln!(out, "{:?}", error.0)?;
+		let file_names = (error.1)
+			.0
+			.iter()
+			.map(|el| {
+				let resolved_path = self.resolver.resolve(&el.location.0);
+				// TODO: Process all trace elements first
+				let location = evaluation_state
+					.map_source_locations(&el.location.0, &[el.location.1, el.location.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,
+				"{:<p$}{:<w$}: {}",
+				"",
+				file,
+				el.desc,
+				p = self.padding,
+				w = align
+			)?;
+		}
+		Ok(())
+	}
+}
+
+pub struct JSFormat;
+impl TraceFormat for JSFormat {
+	fn write_trace(
+		&self,
+		out: &mut dyn std::fmt::Write,
+		evaluation_state: &EvaluationState,
+		error: &LocError,
+	) -> Result<(), std::fmt::Error> {
+		writeln!(out, "{:?}", error.0)?;
+		for (i, item) in (error.1).0.iter().enumerate() {
+			if i != 0 {
+				writeln!(out)?;
+			}
+			let desc = &item.desc;
+			let source = item.location.clone();
+			let start_end = evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);
+
+			write!(
+				out,
+				"    at {} ({}:{}:{})",
+				desc,
+				source.0.to_str().unwrap(),
+				start_end[0].line,
+				start_end[0].column,
+			)?;
+		}
+		Ok(())
+	}
+}
+
+/// rustc-like trace displaying
+#[cfg(feature = "explaining-traces")]
+pub struct ExplainingFormat {
+	pub resolver: PathResolver,
+}
+#[cfg(feature = "explaining-traces")]
+impl TraceFormat for ExplainingFormat {
+	fn write_trace(
+		&self,
+		out: &mut dyn std::fmt::Write,
+		evaluation_state: &EvaluationState,
+		error: &LocError,
+	) -> Result<(), std::fmt::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.desc;
+			let source = item.location.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(())
+	}
+}
deletedcrates/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"
deletedcrates/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(())
-	}
-}