From 0374cd028e161e8e7cc997df6c607c522edd0736 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Thu, 26 May 2022 18:05:31 +0000 Subject: [PATCH] refactor: virtual file handling --- --- 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" --- 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, 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, 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, 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 { --- a/crates/jrsonnet-parser/src/lib.rs +++ b/crates/jrsonnet-parser/src/lib.rs @@ -1,19 +1,18 @@ #![allow(clippy::redundant_closure_call)] -use std::{ - path::{Path, PathBuf}, - rc::Rc, -}; +use std::rc::Rc; use peg::parser; mod expr; pub use expr::*; pub use jrsonnet_interner::IStr; pub use peg; +mod source; mod unescape; +pub use source::Source; pub struct ParserSettings { - pub file_name: Rc, + pub file_name: Source, } macro_rules! expr_bin { @@ -232,7 +231,7 @@ pub rule var_expr(s: &ParserSettings) -> Expr = n:id() { expr::Expr::Var(n) } pub rule id_loc(s: &ParserSettings) -> LocExpr - = a:position!() n:id() b:position!() { LocExpr(Rc::new(expr::Expr::Str(n)), ExprLocation(s.file_name.clone(), a,b)) } + = a:position!() n:id() b:position!() { LocExpr(Rc::new(expr::Expr::Str(n)), ExprLocation(s.file_name.clone(), a as u32,b as u32)) } pub rule if_then_else_expr(s: &ParserSettings) -> Expr = cond:ifspec(s) _ keyword("then") _ cond_then:expr(s) cond_else:(_ keyword("else") _ e:expr(s) {e})? {Expr::IfElse{ cond, @@ -263,9 +262,9 @@ / array_expr(s) / array_comp_expr(s) - / keyword("importstr") _ path:string() {Expr::ImportStr(PathBuf::from(path))} - / keyword("importbin") _ path:string() {Expr::ImportBin(PathBuf::from(path))} - / keyword("import") _ path:string() {Expr::Import(PathBuf::from(path))} + / keyword("importstr") _ path:string() {Expr::ImportStr(path.into())} + / keyword("importbin") _ path:string() {Expr::ImportBin(path.into())} + / keyword("import") _ path:string() {Expr::Import(path.into())} / var_expr(s) / local_expr(s) @@ -299,7 +298,7 @@ use UnaryOpType::*; rule expr(s: &ParserSettings) -> LocExpr = precedence! { - start:position!() v:@ end:position!() { LocExpr(Rc::new(v), ExprLocation(s.file_name.clone(), start, end)) } + start:position!() v:@ end:position!() { LocExpr(Rc::new(v), ExprLocation(s.file_name.clone(), start as u32, end as u32)) } -- a:(@) _ binop(<"||">) _ b:@ {expr_bin!(a Or b)} -- @@ -357,7 +356,7 @@ let len = str.len(); LocExpr( Rc::new(Expr::Str(str)), - ExprLocation(settings.file_name.clone(), 0, len), + ExprLocation(settings.file_name.clone(), 0, len as u32), ) } @@ -368,14 +367,14 @@ use BinaryOpType::*; use super::{expr::*, parse}; - use crate::ParserSettings; + use crate::{source::Source, ParserSettings}; macro_rules! parse { ($s:expr) => { parse( $s, &ParserSettings { - file_name: PathBuf::from("test.jsonnet").into(), + file_name: Source::new(PathBuf::from("test.jsonnet")).unwrap(), }, ) .unwrap() @@ -386,7 +385,11 @@ ($expr:expr, $from:expr, $to:expr$(,)?) => { LocExpr( std::rc::Rc::new($expr), - ExprLocation(PathBuf::from("test.jsonnet").into(), $from, $to), + ExprLocation( + Source::new(PathBuf::from("test.jsonnet")).unwrap(), + $from, + $to, + ), ) }; } @@ -453,11 +456,15 @@ fn imports() { assert_eq!( parse!("import \"hello\""), - el!(Expr::Import(PathBuf::from("hello")), 0, 14), + el!(Expr::Import("hello".into()), 0, 14), ); assert_eq!( parse!("importstr \"garnish.txt\""), - el!(Expr::ImportStr(PathBuf::from("garnish.txt")), 0, 23) + el!(Expr::ImportStr("garnish.txt".into()), 0, 23) + ); + assert_eq!( + parse!("importbin \"garnish.bin\""), + el!(Expr::ImportBin("garnish.bin".into()), 0, 23) ); } @@ -720,7 +727,7 @@ fn add_location_info_to_all_sub_expressions() { use Expr::*; - let file_name: std::rc::Rc = PathBuf::from("test.jsonnet").into(); + let file_name = Source::new(PathBuf::from("test.jsonnet")).unwrap(); let expr = parse( "{} { local x = 1, x: x } + {}", &ParserSettings { @@ -759,12 +766,5 @@ 29 ), ); - } - // From source code - /* - #[bench] - fn bench_parse_peg(b: &mut Bencher) { - b.iter(|| parse!(jrsonnet_stdlib::STDLIB_STR)) } - */ } --- /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); +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 { + 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), + } + } +} -- gitstuff