git.delta.rocks / jrsonnet / refs/commits / 0374cd028e16

difftreelog

refactor virtual file handling

Yaroslav Bolyukin2022-05-26parent: #c11576e.patch.diff
in: master

4 files changed

modifiedcrates/jrsonnet-parser/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-parser/Cargo.toml
+++ b/crates/jrsonnet-parser/Cargo.toml
@@ -11,6 +11,7 @@
 
 [dependencies]
 jrsonnet-interner = { path = "../jrsonnet-interner", version = "0.4.2" }
+static_assertions = "1.1.0"
 
 peg = "0.8.0"
 
modifiedcrates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth
--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -1,7 +1,6 @@
 use std::{
-	fmt::{Debug, Display},
+	fmt::{self, Debug, Display},
 	ops::Deref,
-	path::{Path, PathBuf},
 	rc::Rc,
 };
 
@@ -10,6 +9,8 @@
 #[cfg(feature = "serde")]
 use serde::{Deserialize, Serialize};
 
+use crate::source::Source;
+
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[derive(Debug, PartialEq, Trace)]
 pub enum FieldName {
@@ -68,7 +69,7 @@
 }
 
 impl Display for UnaryOpType {
-	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 		use UnaryOpType::*;
 		write!(
 			f,
@@ -118,7 +119,7 @@
 }
 
 impl Display for BinaryOpType {
-	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 		use BinaryOpType::*;
 		write!(
 			f,
@@ -326,11 +327,11 @@
 	LocalExpr(Vec<BindSpec>, LocExpr),
 
 	/// import "hello"
-	Import(PathBuf),
+	Import(IStr),
 	/// importStr "file.txt"
-	ImportStr(PathBuf),
+	ImportStr(IStr),
 	/// importBin "file.txt"
-	ImportBin(PathBuf),
+	ImportBin(IStr),
 	/// error "I'm broken"
 	ErrorStmt(LocExpr),
 	/// a(b, c)
@@ -358,15 +359,19 @@
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[derive(Clone, PartialEq, Trace)]
 #[skip_trace]
-pub struct ExprLocation(pub Rc<Path>, pub usize, pub usize);
+#[repr(C)]
+pub struct ExprLocation(pub Source, pub u32, pub u32);
 impl ExprLocation {
 	pub fn belongs_to(&self, other: &ExprLocation) -> bool {
 		other.0 == self.0 && other.1 <= self.1 && other.2 >= self.2
 	}
 }
 
+#[cfg(target_pointer_width = "64")]
+static_assertions::assert_eq_size!(ExprLocation, [u8; 16]);
+
 impl Debug for ExprLocation {
-	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 		write!(f, "{:?}:{:?}-{:?}", self.0, self.1, self.2)
 	}
 }
@@ -376,8 +381,11 @@
 #[derive(Clone, PartialEq, Trace)]
 pub struct LocExpr(pub Rc<Expr>, pub ExprLocation);
 
+#[cfg(target_pointer_width = "64")]
+static_assertions::assert_eq_size!(LocExpr, [u8; 24]);
+
 impl Debug for LocExpr {
-	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 		if f.alternate() {
 			write!(f, "{:#?}", self.0)?;
 		} else {
modifiedcrates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth
1#![allow(clippy::redundant_closure_call)]1#![allow(clippy::redundant_closure_call)]
22
3use std::{3use std::rc::Rc;
4 path::{Path, PathBuf},
5 rc::Rc,
6};
74
8use peg::parser;5use peg::parser;
9mod expr;6mod expr;
10pub use expr::*;7pub use expr::*;
11pub use jrsonnet_interner::IStr;8pub use jrsonnet_interner::IStr;
12pub use peg;9pub use peg;
10mod source;
13mod unescape;11mod unescape;
12pub use source::Source;
1413
15pub struct ParserSettings {14pub struct ParserSettings {
16 pub file_name: Rc<Path>,15 pub file_name: Source,
17}16}
1817
19macro_rules! expr_bin {18macro_rules! expr_bin {
232 pub rule var_expr(s: &ParserSettings) -> Expr231 pub rule var_expr(s: &ParserSettings) -> Expr
233 = n:id() { expr::Expr::Var(n) }232 = n:id() { expr::Expr::Var(n) }
234 pub rule id_loc(s: &ParserSettings) -> LocExpr233 pub rule id_loc(s: &ParserSettings) -> LocExpr
235 = a:position!() n:id() b:position!() { LocExpr(Rc::new(expr::Expr::Str(n)), ExprLocation(s.file_name.clone(), a,b)) }234 = a:position!() n:id() b:position!() { LocExpr(Rc::new(expr::Expr::Str(n)), ExprLocation(s.file_name.clone(), a as u32,b as u32)) }
236 pub rule if_then_else_expr(s: &ParserSettings) -> Expr235 pub rule if_then_else_expr(s: &ParserSettings) -> Expr
237 = cond:ifspec(s) _ keyword("then") _ cond_then:expr(s) cond_else:(_ keyword("else") _ e:expr(s) {e})? {Expr::IfElse{236 = cond:ifspec(s) _ keyword("then") _ cond_then:expr(s) cond_else:(_ keyword("else") _ e:expr(s) {e})? {Expr::IfElse{
238 cond,237 cond,
263 / array_expr(s)262 / array_expr(s)
264 / array_comp_expr(s)263 / array_comp_expr(s)
265264
266 / keyword("importstr") _ path:string() {Expr::ImportStr(PathBuf::from(path))}265 / keyword("importstr") _ path:string() {Expr::ImportStr(path.into())}
267 / keyword("importbin") _ path:string() {Expr::ImportBin(PathBuf::from(path))}266 / keyword("importbin") _ path:string() {Expr::ImportBin(path.into())}
268 / keyword("import") _ path:string() {Expr::Import(PathBuf::from(path))}267 / keyword("import") _ path:string() {Expr::Import(path.into())}
269268
270 / var_expr(s)269 / var_expr(s)
271 / local_expr(s)270 / local_expr(s)
299 use UnaryOpType::*;298 use UnaryOpType::*;
300 rule expr(s: &ParserSettings) -> LocExpr299 rule expr(s: &ParserSettings) -> LocExpr
301 = precedence! {300 = precedence! {
302 start:position!() v:@ end:position!() { LocExpr(Rc::new(v), ExprLocation(s.file_name.clone(), start, end)) }301 start:position!() v:@ end:position!() { LocExpr(Rc::new(v), ExprLocation(s.file_name.clone(), start as u32, end as u32)) }
303 --302 --
304 a:(@) _ binop(<"||">) _ b:@ {expr_bin!(a Or b)}303 a:(@) _ binop(<"||">) _ b:@ {expr_bin!(a Or b)}
305 --304 --
357 let len = str.len();356 let len = str.len();
358 LocExpr(357 LocExpr(
359 Rc::new(Expr::Str(str)),358 Rc::new(Expr::Str(str)),
360 ExprLocation(settings.file_name.clone(), 0, len),359 ExprLocation(settings.file_name.clone(), 0, len as u32),
361 )360 )
362}361}
363362
368 use BinaryOpType::*;367 use BinaryOpType::*;
369368
370 use super::{expr::*, parse};369 use super::{expr::*, parse};
371 use crate::ParserSettings;370 use crate::{source::Source, ParserSettings};
372371
373 macro_rules! parse {372 macro_rules! parse {
374 ($s:expr) => {373 ($s:expr) => {
375 parse(374 parse(
376 $s,375 $s,
377 &ParserSettings {376 &ParserSettings {
378 file_name: PathBuf::from("test.jsonnet").into(),377 file_name: Source::new(PathBuf::from("test.jsonnet")).unwrap(),
379 },378 },
380 )379 )
381 .unwrap()380 .unwrap()
386 ($expr:expr, $from:expr, $to:expr$(,)?) => {385 ($expr:expr, $from:expr, $to:expr$(,)?) => {
387 LocExpr(386 LocExpr(
388 std::rc::Rc::new($expr),387 std::rc::Rc::new($expr),
389 ExprLocation(PathBuf::from("test.jsonnet").into(), $from, $to),388 ExprLocation(
389 Source::new(PathBuf::from("test.jsonnet")).unwrap(),
390 $from,
391 $to,
392 ),
390 )393 )
391 };394 };
453 fn imports() {456 fn imports() {
454 assert_eq!(457 assert_eq!(
455 parse!("import \"hello\""),458 parse!("import \"hello\""),
456 el!(Expr::Import(PathBuf::from("hello")), 0, 14),459 el!(Expr::Import("hello".into()), 0, 14),
457 );460 );
458 assert_eq!(461 assert_eq!(
459 parse!("importstr \"garnish.txt\""),462 parse!("importstr \"garnish.txt\""),
460 el!(Expr::ImportStr(PathBuf::from("garnish.txt")), 0, 23)463 el!(Expr::ImportStr("garnish.txt".into()), 0, 23)
461 );464 );
465 assert_eq!(
466 parse!("importbin \"garnish.bin\""),
467 el!(Expr::ImportBin("garnish.bin".into()), 0, 23)
468 );
462 }469 }
463470
464 #[test]471 #[test]
720 fn add_location_info_to_all_sub_expressions() {727 fn add_location_info_to_all_sub_expressions() {
721 use Expr::*;728 use Expr::*;
722729
723 let file_name: std::rc::Rc<std::path::Path> = PathBuf::from("test.jsonnet").into();730 let file_name = Source::new(PathBuf::from("test.jsonnet")).unwrap();
724 let expr = parse(731 let expr = parse(
725 "{} { local x = 1, x: x } + {}",732 "{} { local x = 1, x: x } + {}",
726 &ParserSettings {733 &ParserSettings {
760 ),767 ),
761 );768 );
762 }769 }
763 // From source code
764 /*
765 #[bench]
766 fn bench_parse_peg(b: &mut Bencher) {
767 b.iter(|| parse!(jrsonnet_stdlib::STDLIB_STR))
768 }
769 */
770}770}
771771
addedcrates/jrsonnet-parser/src/source.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-parser/src/source.rs
@@ -0,0 +1,93 @@
+use std::{
+	borrow::Cow,
+	fmt,
+	path::{Component, Path, PathBuf},
+	rc::Rc,
+};
+
+use gcmodule::{Trace, Tracer};
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Serialize};
+
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[derive(PartialEq, Eq, Debug, Hash)]
+enum Inner {
+	Real(PathBuf),
+	Virtual(Cow<'static, str>),
+}
+
+/// Either real file, or virtual
+/// Hash of FileName always have same value as raw Path, to make it possible to use with raw_entry_mut
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct Source(Rc<Inner>);
+static_assertions::assert_eq_size!(Source, *const ());
+
+impl Trace for Source {
+	fn trace(&self, _tracer: &mut Tracer) {}
+
+	fn is_type_tracked() -> bool {
+		false
+	}
+}
+
+impl Source {
+	/// Fails when path contains inner /../ or /./ references, or not absolute
+	pub fn new(path: PathBuf) -> Option<Self> {
+		if !path.is_absolute()
+			|| path
+				.components()
+				.any(|c| matches!(c, Component::CurDir | Component::ParentDir))
+		{
+			return None;
+		}
+		Some(Self(Rc::new(Inner::Real(path))))
+	}
+
+	pub fn new_virtual(n: Cow<'static, str>) -> Self {
+		Self(Rc::new(Inner::Virtual(n)))
+	}
+
+	pub fn short_display(&self) -> ShortDisplay {
+		ShortDisplay(self.clone())
+	}
+	pub fn full_path(&self) -> String {
+		match self.inner() {
+			Inner::Real(r) => r.display().to_string(),
+			Inner::Virtual(v) => v.to_string(),
+		}
+	}
+
+	/// Returns None if file is virtual
+	pub fn path(&self) -> Option<&Path> {
+		match self.inner() {
+			Inner::Real(r) => Some(r),
+			Inner::Virtual(_) => None,
+		}
+	}
+	pub fn repr(&self) -> Result<&Path, &str> {
+		match self.inner() {
+			Inner::Real(r) => Ok(r),
+			Inner::Virtual(v) => Err(v.as_ref()),
+		}
+	}
+
+	fn inner(&self) -> &Inner {
+		&self.0 as &Inner
+	}
+}
+pub struct ShortDisplay(Source);
+impl fmt::Display for ShortDisplay {
+	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+		match &self.0 .0 as &Inner {
+			Inner::Real(r) => {
+				write!(
+					f,
+					"{}",
+					r.file_name().expect("path is valid").to_string_lossy()
+				)
+			}
+			Inner::Virtual(n) => write!(f, "{}", n),
+		}
+	}
+}