git.delta.rocks / jrsonnet / refs/commits / 0831da3ed8d9

difftreelog

perf reimplement AST codegen

Yaroslav Bolyukin2022-08-07parent: #b305ce3.patch.diff
in: master

10 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -275,6 +275,7 @@
  "jrsonnet-gcmodule",
  "rustc-hash",
  "serde",
+ "structdump",
 ]
 
 [[package]]
@@ -292,10 +293,10 @@
 dependencies = [
  "jrsonnet-gcmodule",
  "jrsonnet-interner",
- "jrsonnet-stdlib",
  "peg",
  "serde",
  "static_assertions",
+ "structdump",
 ]
 
 [[package]]
@@ -312,6 +313,7 @@
  "serde",
  "serde_json",
  "serde_yaml_with_quirks",
+ "structdump",
 ]
 
 [[package]]
@@ -576,6 +578,28 @@
 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 
 [[package]]
+name = "structdump"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0570327507bf281d8a6e6b0d4c082b12cb6bcee27efce755aa5efacd44076c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "structdump-derive",
+]
+
+[[package]]
+name = "structdump-derive"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29cc0b59cfa11f1bceda09a9a7e37e6a6c3138575fd24ade8aa9af6d09aedf28"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
 name = "syn"
 version = "1.0.96"
 source = "registry+https://github.com/rust-lang/crates.io-index"
modifiedCargo.tomldiffbeforeafterboth
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,6 +8,6 @@
 opt-level = 3
 lto = "fat"
 codegen-units = 1
-debug = 0
+# debug = 0
 panic = "abort"
-strip = true
+# strip = true
modifiedcrates/jrsonnet-interner/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-interner/Cargo.toml
+++ b/crates/jrsonnet-interner/Cargo.toml
@@ -7,12 +7,19 @@
 edition = "2021"
 
 [features]
-default = ["serde"]
+default = []
+# Implement value serialization using structdump
+structdump = ["dep:structdump"]
+# Implement value serialization using serde
+#
+# Warning: serialized values won't be deduplicated
 serde = ["dep:serde"]
 
 [dependencies]
 jrsonnet-gcmodule = { version = "0.3.4" }
 
 serde = { version = "1.0", optional = true }
+structdump = { version = "0.2.0", optional = true }
+
 rustc-hash = "1.1"
 hashbrown = { version = "0.12.1", features = ["inline-more"] }
modifiedcrates/jrsonnet-interner/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-interner/src/lib.rs
+++ b/crates/jrsonnet-interner/src/lib.rs
@@ -205,6 +205,7 @@
 	}
 }
 
+#[cfg(feature = "serde")]
 impl serde::Serialize for IStr {
 	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 	where
@@ -214,6 +215,7 @@
 	}
 }
 
+#[cfg(feature = "serde")]
 impl<'de> serde::Deserialize<'de> for IStr {
 	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
 	where
@@ -224,6 +226,24 @@
 	}
 }
 
+#[cfg(feature = "structdump")]
+impl structdump::Codegen for IStr {
+	fn gen_code(
+		&self,
+		res: &mut structdump::CodegenResult,
+		_unique: bool,
+	) -> structdump::TokenStream {
+		let s: &str = self;
+		res.add_code(
+			structdump::quote! {
+				structdump_import::IStr::from(#s)
+			},
+			Some(structdump::quote![structdump_import::IStr]),
+			false,
+		)
+	}
+}
+
 thread_local! {
 	static POOL: RefCell<HashMap<Inner, (), BuildHasherDefault<FxHasher>>> = RefCell::new(HashMap::with_capacity_and_hasher(200, BuildHasherDefault::default()));
 }
modifiedcrates/jrsonnet-parser/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-parser/Cargo.toml
+++ b/crates/jrsonnet-parser/Cargo.toml
@@ -7,7 +7,23 @@
 edition = "2021"
 
 [features]
+default = []
 exp-destruct = []
+# Implement serialization of AST using structdump
+#
+# Structdump generates code, which exactly replicated passed AST
+# Contrary to serde, has no code bloat problem, and is recommended
+#
+# The only limitation is serialized form is only useable if built from build script
+structdump = ["dep:structdump", "jrsonnet-interner/structdump"]
+# Implement serialization of AST using serde
+#
+# Warning: as serde doesn't deduplicate strings, `Source` struct will bloat
+# output binary with repeating source code. To resolve this issue, you should either
+# override serialization of this struct using custom `Serializer`/`Deserializer`,
+# not rely on Source, and fill its `source_code` with empty value, or use `structdump`
+# instead
+serde = ["dep:serde"]
 
 [dependencies]
 jrsonnet-interner = { path = "../jrsonnet-interner", version = "0.4.2" }
@@ -18,6 +34,4 @@
 peg = "0.8.0"
 
 serde = { version = "1.0", features = ["derive", "rc"], optional = true }
-
-[dev-dependencies]
-jrsonnet-stdlib = { path = "../jrsonnet-stdlib", version = "0.4.2" }
+structdump = { version = "0.2.0", features = ["derive"], optional = true }
modifiedcrates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth
before · crates/jrsonnet-parser/src/expr.rs
1use std::{2	fmt::{self, Debug, Display},3	ops::Deref,4	rc::Rc,5};67use jrsonnet_gcmodule::Trace;8use jrsonnet_interner::IStr;9#[cfg(feature = "serde")]10use serde::{Deserialize, Serialize};1112use crate::source::Source;1314#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]15#[derive(Debug, PartialEq, Trace)]16pub enum FieldName {17	/// {fixed: 2}18	Fixed(IStr),19	/// {["dyn"+"amic"]: 3}20	Dyn(LocExpr),21}2223#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]24#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]25pub enum Visibility {26	/// :27	Normal,28	/// ::29	Hidden,30	/// :::31	Unhide,32}3334impl Visibility {35	pub fn is_visible(&self) -> bool {36		matches!(self, Self::Normal | Self::Unhide)37	}38}3940#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]41#[derive(Clone, Debug, PartialEq, Trace)]42pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);4344#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]45#[derive(Debug, PartialEq, Trace)]46pub struct FieldMember {47	pub name: FieldName,48	pub plus: bool,49	pub params: Option<ParamsDesc>,50	pub visibility: Visibility,51	pub value: LocExpr,52}5354#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]55#[derive(Debug, PartialEq, Trace)]56pub enum Member {57	Field(FieldMember),58	BindStmt(BindSpec),59	AssertStmt(AssertStmt),60}6162#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]63#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]64pub enum UnaryOpType {65	Plus,66	Minus,67	BitNot,68	Not,69}7071impl Display for UnaryOpType {72	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {73		use UnaryOpType::*;74		write!(75			f,76			"{}",77			match self {78				Plus => "+",79				Minus => "-",80				BitNot => "~",81				Not => "!",82			}83		)84	}85}8687#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]88#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]89pub enum BinaryOpType {90	Mul,91	Div,9293	/// Implemented as intrinsic, put here for completeness94	Mod,9596	Add,97	Sub,9899	Lhs,100	Rhs,101102	Lt,103	Gt,104	Lte,105	Gte,106107	BitAnd,108	BitOr,109	BitXor,110111	Eq,112	Neq,113114	And,115	Or,116117	// Equialent to std.objectHasEx(a, b, true)118	In,119}120121impl Display for BinaryOpType {122	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {123		use BinaryOpType::*;124		write!(125			f,126			"{}",127			match self {128				Mul => "*",129				Div => "/",130				Mod => "%",131				Add => "+",132				Sub => "-",133				Lhs => "<<",134				Rhs => ">>",135				Lt => "<",136				Gt => ">",137				Lte => "<=",138				Gte => ">=",139				BitAnd => "&",140				BitOr => "|",141				BitXor => "^",142				Eq => "==",143				Neq => "!=",144				And => "&&",145				Or => "||",146				In => "in",147			}148		)149	}150}151152/// name, default value153#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]154#[derive(Debug, PartialEq, Trace)]155pub struct Param(pub Destruct, pub Option<LocExpr>);156157/// Defined function parameters158#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]159#[derive(Debug, Clone, PartialEq, Trace)]160pub struct ParamsDesc(pub Rc<Vec<Param>>);161162impl Deref for ParamsDesc {163	type Target = Vec<Param>;164	fn deref(&self) -> &Self::Target {165		&self.0166	}167}168169#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]170#[derive(Debug, PartialEq, Trace)]171pub struct ArgsDesc {172	pub unnamed: Vec<LocExpr>,173	pub named: Vec<(IStr, LocExpr)>,174}175impl ArgsDesc {176	pub fn new(unnamed: Vec<LocExpr>, named: Vec<(IStr, LocExpr)>) -> Self {177		Self { unnamed, named }178	}179}180181#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]182#[derive(Debug, Clone, PartialEq, Eq, Trace)]183pub enum DestructRest {184	/// ...rest185	Keep(IStr),186	/// ...187	Drop,188}189190#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]191#[derive(Debug, Clone, PartialEq, Trace)]192pub enum Destruct {193	Full(IStr),194	#[cfg(feature = "exp-destruct")]195	Skip,196	#[cfg(feature = "exp-destruct")]197	Array {198		start: Vec<Destruct>,199		rest: Option<DestructRest>,200		end: Vec<Destruct>,201	},202	#[cfg(feature = "exp-destruct")]203	Object {204		fields: Vec<(IStr, Option<Destruct>, Option<LocExpr>)>,205		rest: Option<DestructRest>,206	},207}208impl Destruct {209	/// Name of destructure, used for function parameter names210	pub fn name(&self) -> Option<IStr> {211		match self {212			Self::Full(name) => Some(name.clone()),213			#[cfg(feature = "exp-destruct")]214			_ => None,215		}216	}217}218219#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]220#[derive(Debug, Clone, PartialEq, Trace)]221pub enum BindSpec {222	Field {223		into: Destruct,224		value: LocExpr,225	},226	Function {227		name: IStr,228		params: ParamsDesc,229		value: LocExpr,230	},231}232233#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]234#[derive(Debug, PartialEq, Trace)]235pub struct IfSpecData(pub LocExpr);236237#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]238#[derive(Debug, PartialEq, Trace)]239pub struct ForSpecData(pub IStr, pub LocExpr);240241#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]242#[derive(Debug, PartialEq, Trace)]243pub enum CompSpec {244	IfSpec(IfSpecData),245	ForSpec(ForSpecData),246}247248#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]249#[derive(Debug, PartialEq, Trace)]250pub struct ObjComp {251	pub pre_locals: Vec<BindSpec>,252	pub key: LocExpr,253	pub plus: bool,254	pub value: LocExpr,255	pub post_locals: Vec<BindSpec>,256	pub compspecs: Vec<CompSpec>,257}258259#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]260#[derive(Debug, PartialEq, Trace)]261pub enum ObjBody {262	MemberList(Vec<Member>),263	ObjComp(ObjComp),264}265266#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]267#[derive(Debug, PartialEq, Eq, Clone, Copy, Trace)]268pub enum LiteralType {269	This,270	Super,271	Dollar,272	Null,273	True,274	False,275}276277#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]278#[derive(Debug, PartialEq, Trace)]279pub struct SliceDesc {280	pub start: Option<LocExpr>,281	pub end: Option<LocExpr>,282	pub step: Option<LocExpr>,283}284285/// Syntax base286#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]287#[derive(Debug, PartialEq, Trace)]288pub enum Expr {289	Literal(LiteralType),290291	/// String value: "hello"292	Str(IStr),293	/// Number: 1, 2.0, 2e+20294	Num(f64),295	/// Variable name: test296	Var(IStr),297298	/// Array of expressions: [1, 2, "Hello"]299	Arr(Vec<LocExpr>),300	/// Array comprehension:301	/// ```jsonnet302	///  ingredients: [303	///    { kind: kind, qty: 4 / 3 }304	///    for kind in [305	///      'Honey Syrup',306	///      'Lemon Juice',307	///      'Farmers Gin',308	///    ]309	///  ],310	/// ```311	ArrComp(LocExpr, Vec<CompSpec>),312313	/// Object: {a: 2}314	Obj(ObjBody),315	/// Object extension: var1 {b: 2}316	ObjExtend(LocExpr, ObjBody),317318	/// (obj)319	Parened(LocExpr),320321	/// -2322	UnaryOp(UnaryOpType, LocExpr),323	/// 2 - 2324	BinaryOp(LocExpr, BinaryOpType, LocExpr),325	/// assert 2 == 2 : "Math is broken"326	AssertExpr(AssertStmt, LocExpr),327	/// local a = 2; { b: a }328	LocalExpr(Vec<BindSpec>, LocExpr),329330	/// import "hello"331	Import(IStr),332	/// importStr "file.txt"333	ImportStr(IStr),334	/// importBin "file.txt"335	ImportBin(IStr),336	/// error "I'm broken"337	ErrorStmt(LocExpr),338	/// a(b, c)339	Apply(LocExpr, ArgsDesc, bool),340	/// a[b]341	Index(LocExpr, LocExpr),342	/// function(x) x343	Function(ParamsDesc, LocExpr),344	/// if true == false then 1 else 2345	IfElse {346		cond: IfSpecData,347		cond_then: LocExpr,348		cond_else: Option<LocExpr>,349	},350	Slice(LocExpr, SliceDesc),351}352353/// file, begin offset, end offset354#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]355#[derive(Clone, PartialEq, Eq, Trace)]356#[trace(skip)]357#[repr(C)]358pub struct ExprLocation(pub Source, pub u32, pub u32);359impl ExprLocation {360	pub fn belongs_to(&self, other: &ExprLocation) -> bool {361		other.0 == self.0 && other.1 <= self.1 && other.2 >= self.2362	}363}364365#[cfg(target_pointer_width = "64")]366static_assertions::assert_eq_size!(ExprLocation, [u8; 16]);367368impl Debug for ExprLocation {369	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {370		write!(f, "{:?}:{:?}-{:?}", self.0, self.1, self.2)371	}372}373374/// Holds AST expression and its location in source file375#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]376#[derive(Clone, PartialEq, Trace)]377pub struct LocExpr(pub Rc<Expr>, pub ExprLocation);378379#[cfg(target_pointer_width = "64")]380static_assertions::assert_eq_size!(LocExpr, [u8; 24]);381382impl Debug for LocExpr {383	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {384		if f.alternate() {385			write!(f, "{:#?}", self.0)?;386		} else {387			write!(f, "{:?}", self.0)?;388		}389		write!(f, " from {:?}", self.1)?;390		Ok(())391	}392}
after · crates/jrsonnet-parser/src/expr.rs
1use std::{2	fmt::{self, Debug, Display},3	ops::Deref,4	rc::Rc,5};67use jrsonnet_gcmodule::Trace;8use jrsonnet_interner::IStr;9#[cfg(feature = "serde")]10use serde::{Deserialize, Serialize};11#[cfg(feature = "structdump")]12use structdump::Codegen;1314use crate::source::Source;1516#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]17#[cfg_attr(feature = "structdump", derive(Codegen))]18#[derive(Debug, PartialEq, Trace)]19pub enum FieldName {20	/// {fixed: 2}21	Fixed(IStr),22	/// {["dyn"+"amic"]: 3}23	Dyn(LocExpr),24}2526#[cfg_attr(feature = "structdump", derive(Codegen))]27#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]28#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]29pub enum Visibility {30	/// :31	Normal,32	/// ::33	Hidden,34	/// :::35	Unhide,36}3738impl Visibility {39	pub fn is_visible(&self) -> bool {40		matches!(self, Self::Normal | Self::Unhide)41	}42}4344#[cfg_attr(feature = "structdump", derive(Codegen))]45#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]46#[derive(Clone, Debug, PartialEq, Trace)]47pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);4849#[cfg_attr(feature = "structdump", derive(Codegen))]50#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]51#[derive(Debug, PartialEq, Trace)]52pub struct FieldMember {53	pub name: FieldName,54	pub plus: bool,55	pub params: Option<ParamsDesc>,56	pub visibility: Visibility,57	pub value: LocExpr,58}5960#[cfg_attr(feature = "structdump", derive(Codegen))]61#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]62#[derive(Debug, PartialEq, Trace)]63pub enum Member {64	Field(FieldMember),65	BindStmt(BindSpec),66	AssertStmt(AssertStmt),67}6869#[cfg_attr(feature = "structdump", derive(Codegen))]70#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]71#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]72pub enum UnaryOpType {73	Plus,74	Minus,75	BitNot,76	Not,77}7879impl Display for UnaryOpType {80	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {81		use UnaryOpType::*;82		write!(83			f,84			"{}",85			match self {86				Plus => "+",87				Minus => "-",88				BitNot => "~",89				Not => "!",90			}91		)92	}93}9495#[cfg_attr(feature = "structdump", derive(Codegen))]96#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]97#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]98pub enum BinaryOpType {99	Mul,100	Div,101102	/// Implemented as intrinsic, put here for completeness103	Mod,104105	Add,106	Sub,107108	Lhs,109	Rhs,110111	Lt,112	Gt,113	Lte,114	Gte,115116	BitAnd,117	BitOr,118	BitXor,119120	Eq,121	Neq,122123	And,124	Or,125126	// Equialent to std.objectHasEx(a, b, true)127	In,128}129130impl Display for BinaryOpType {131	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {132		use BinaryOpType::*;133		write!(134			f,135			"{}",136			match self {137				Mul => "*",138				Div => "/",139				Mod => "%",140				Add => "+",141				Sub => "-",142				Lhs => "<<",143				Rhs => ">>",144				Lt => "<",145				Gt => ">",146				Lte => "<=",147				Gte => ">=",148				BitAnd => "&",149				BitOr => "|",150				BitXor => "^",151				Eq => "==",152				Neq => "!=",153				And => "&&",154				Or => "||",155				In => "in",156			}157		)158	}159}160161/// name, default value162#[cfg_attr(feature = "structdump", derive(Codegen))]163#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]164#[derive(Debug, PartialEq, Trace)]165pub struct Param(pub Destruct, pub Option<LocExpr>);166167/// Defined function parameters168#[cfg_attr(feature = "structdump", derive(Codegen))]169#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]170#[derive(Debug, Clone, PartialEq, Trace)]171pub struct ParamsDesc(pub Rc<Vec<Param>>);172173impl Deref for ParamsDesc {174	type Target = Vec<Param>;175	fn deref(&self) -> &Self::Target {176		&self.0177	}178}179180#[cfg_attr(feature = "structdump", derive(Codegen))]181#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]182#[derive(Debug, PartialEq, Trace)]183pub struct ArgsDesc {184	pub unnamed: Vec<LocExpr>,185	pub named: Vec<(IStr, LocExpr)>,186}187impl ArgsDesc {188	pub fn new(unnamed: Vec<LocExpr>, named: Vec<(IStr, LocExpr)>) -> Self {189		Self { unnamed, named }190	}191}192193#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]194#[derive(Debug, Clone, PartialEq, Eq, Trace)]195pub enum DestructRest {196	/// ...rest197	Keep(IStr),198	/// ...199	Drop,200}201202#[cfg_attr(feature = "structdump", derive(Codegen))]203#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]204#[derive(Debug, Clone, PartialEq, Trace)]205pub enum Destruct {206	Full(IStr),207	#[cfg(feature = "exp-destruct")]208	Skip,209	#[cfg(feature = "exp-destruct")]210	Array {211		start: Vec<Destruct>,212		rest: Option<DestructRest>,213		end: Vec<Destruct>,214	},215	#[cfg(feature = "exp-destruct")]216	Object {217		fields: Vec<(IStr, Option<Destruct>, Option<LocExpr>)>,218		rest: Option<DestructRest>,219	},220}221impl Destruct {222	/// Name of destructure, used for function parameter names223	pub fn name(&self) -> Option<IStr> {224		match self {225			Self::Full(name) => Some(name.clone()),226			#[cfg(feature = "exp-destruct")]227			_ => None,228		}229	}230}231232#[cfg_attr(feature = "structdump", derive(Codegen))]233#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]234#[derive(Debug, Clone, PartialEq, Trace)]235pub enum BindSpec {236	Field {237		into: Destruct,238		value: LocExpr,239	},240	Function {241		name: IStr,242		params: ParamsDesc,243		value: LocExpr,244	},245}246247#[cfg_attr(feature = "structdump", derive(Codegen))]248#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]249#[derive(Debug, PartialEq, Trace)]250pub struct IfSpecData(pub LocExpr);251252#[cfg_attr(feature = "structdump", derive(Codegen))]253#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]254#[derive(Debug, PartialEq, Trace)]255pub struct ForSpecData(pub IStr, pub LocExpr);256257#[cfg_attr(feature = "structdump", derive(Codegen))]258#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]259#[derive(Debug, PartialEq, Trace)]260pub enum CompSpec {261	IfSpec(IfSpecData),262	ForSpec(ForSpecData),263}264265#[cfg_attr(feature = "structdump", derive(Codegen))]266#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]267#[derive(Debug, PartialEq, Trace)]268pub struct ObjComp {269	pub pre_locals: Vec<BindSpec>,270	pub key: LocExpr,271	pub plus: bool,272	pub value: LocExpr,273	pub post_locals: Vec<BindSpec>,274	pub compspecs: Vec<CompSpec>,275}276277#[cfg_attr(feature = "structdump", derive(Codegen))]278#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]279#[derive(Debug, PartialEq, Trace)]280pub enum ObjBody {281	MemberList(Vec<Member>),282	ObjComp(ObjComp),283}284285#[cfg_attr(feature = "structdump", derive(Codegen))]286#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]287#[derive(Debug, PartialEq, Eq, Clone, Copy, Trace)]288pub enum LiteralType {289	This,290	Super,291	Dollar,292	Null,293	True,294	False,295}296297#[cfg_attr(feature = "structdump", derive(Codegen))]298#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]299#[derive(Debug, PartialEq, Trace)]300pub struct SliceDesc {301	pub start: Option<LocExpr>,302	pub end: Option<LocExpr>,303	pub step: Option<LocExpr>,304}305306/// Syntax base307#[cfg_attr(feature = "structdump", derive(Codegen))]308#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]309#[derive(Debug, PartialEq, Trace)]310pub enum Expr {311	Literal(LiteralType),312313	/// String value: "hello"314	Str(IStr),315	/// Number: 1, 2.0, 2e+20316	Num(f64),317	/// Variable name: test318	Var(IStr),319320	/// Array of expressions: [1, 2, "Hello"]321	Arr(Vec<LocExpr>),322	/// Array comprehension:323	/// ```jsonnet324	///  ingredients: [325	///    { kind: kind, qty: 4 / 3 }326	///    for kind in [327	///      'Honey Syrup',328	///      'Lemon Juice',329	///      'Farmers Gin',330	///    ]331	///  ],332	/// ```333	ArrComp(LocExpr, Vec<CompSpec>),334335	/// Object: {a: 2}336	Obj(ObjBody),337	/// Object extension: var1 {b: 2}338	ObjExtend(LocExpr, ObjBody),339340	/// (obj)341	Parened(LocExpr),342343	/// -2344	UnaryOp(UnaryOpType, LocExpr),345	/// 2 - 2346	BinaryOp(LocExpr, BinaryOpType, LocExpr),347	/// assert 2 == 2 : "Math is broken"348	AssertExpr(AssertStmt, LocExpr),349	/// local a = 2; { b: a }350	LocalExpr(Vec<BindSpec>, LocExpr),351352	/// import "hello"353	Import(IStr),354	/// importStr "file.txt"355	ImportStr(IStr),356	/// importBin "file.txt"357	ImportBin(IStr),358	/// error "I'm broken"359	ErrorStmt(LocExpr),360	/// a(b, c)361	Apply(LocExpr, ArgsDesc, bool),362	/// a[b]363	Index(LocExpr, LocExpr),364	/// function(x) x365	Function(ParamsDesc, LocExpr),366	/// if true == false then 1 else 2367	IfElse {368		cond: IfSpecData,369		cond_then: LocExpr,370		cond_else: Option<LocExpr>,371	},372	Slice(LocExpr, SliceDesc),373}374375/// file, begin offset, end offset376#[cfg_attr(feature = "structdump", derive(Codegen))]377#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]378#[derive(Clone, PartialEq, Eq, Trace)]379#[trace(skip)]380#[repr(C)]381pub struct ExprLocation(pub Source, pub u32, pub u32);382impl ExprLocation {383	pub fn belongs_to(&self, other: &ExprLocation) -> bool {384		other.0 == self.0 && other.1 <= self.1 && other.2 >= self.2385	}386}387388#[cfg(target_pointer_width = "64")]389static_assertions::assert_eq_size!(ExprLocation, [u8; 16]);390391impl Debug for ExprLocation {392	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {393		write!(f, "{:?}:{:?}-{:?}", self.0, self.1, self.2)394	}395}396397/// Holds AST expression and its location in source file398#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]399#[cfg_attr(feature = "structdump", derive(Codegen))]400#[derive(Clone, PartialEq, Trace)]401pub struct LocExpr(pub Rc<Expr>, pub ExprLocation);402403#[cfg(target_pointer_width = "64")]404static_assertions::assert_eq_size!(LocExpr, [u8; 24]);405406impl Debug for LocExpr {407	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {408		if f.alternate() {409			write!(f, "{:#?}", self.0)?;410		} else {411			write!(f, "{:?}", self.0)?;412		}413		write!(f, " from {:?}", self.1)?;414		Ok(())415	}416}
modifiedcrates/jrsonnet-parser/src/source.rsdiffbeforeafterboth
--- a/crates/jrsonnet-parser/src/source.rs
+++ b/crates/jrsonnet-parser/src/source.rs
@@ -9,9 +9,12 @@
 use jrsonnet_interner::IStr;
 #[cfg(feature = "serde")]
 use serde::{Deserialize, Serialize};
+#[cfg(feature = "structdump")]
+use structdump::Codegen;
 
 use crate::location::{location_to_offset, offset_to_location, CodeLocation};
 
+#[cfg_attr(feature = "structdump", derive(Codegen))]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[derive(PartialEq, Eq, Debug, Hash, Clone)]
 pub enum SourcePath {
@@ -39,9 +42,10 @@
 
 /// 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 = "structdump", derive(Codegen))]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[derive(Clone, PartialEq, Eq, Debug)]
-pub struct Source(Rc<(SourcePath, IStr)>);
+pub struct Source(pub Rc<(SourcePath, IStr)>);
 static_assertions::assert_eq_size!(Source, *const ());
 
 impl Trace for Source {
modifiedcrates/jrsonnet-stdlib/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/Cargo.toml
+++ b/crates/jrsonnet-stdlib/Cargo.toml
@@ -7,9 +7,10 @@
 edition = "2021"
 
 [features]
-default = []
-# Serializes standard library AST, and deserialize on start, instead of parsing it every run from text
-serialized-stdlib = ["bincode", "jrsonnet-parser/serde"]
+default = ["codegenerated-stdlib"]
+# Speed-up initialization by generating code for parsed stdlib, instead
+# of invoking parser for it
+codegenerated-stdlib = ["jrsonnet-parser/structdump"]
 # Enables legacy `std.thisFile` support, at the cost of worse caching
 legacy-this-file = []
 # Add order preservation flag to some functions
@@ -45,8 +46,5 @@
 serde_yaml_with_quirks = "0.8.24"
 
 [build-dependencies]
-jrsonnet-parser = { path = "../jrsonnet-parser", version = "0.4.2", features = [
-    "serde",
-] }
-serde = "1.0"
-bincode = "1.3"
+jrsonnet-parser = { path = "../jrsonnet-parser", version = "0.4.2" }
+structdump = { version = "0.2.0", features = ["derive"] }
modifiedcrates/jrsonnet-stdlib/build.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/build.rs
+++ b/crates/jrsonnet-stdlib/build.rs
@@ -1,7 +1,7 @@
 use std::{borrow::Cow, env, fs::File, io::Write, path::Path};
 
-use bincode::serialize;
 use jrsonnet_parser::{parse, ParserSettings, Source};
+use structdump::CodegenResult;
 
 fn main() {
 	let parsed = parse(
@@ -15,10 +15,15 @@
 	)
 	.expect("parse");
 
+	let mut out = CodegenResult::default();
+
+	let v = out.codegen(&parsed, true);
+
 	{
 		let out_dir = env::var("OUT_DIR").unwrap();
-		let dest_path = Path::new(&out_dir).join("stdlib.bincode");
+		let dest_path = Path::new(&out_dir).join("stdlib.rs");
 		let mut f = File::create(&dest_path).unwrap();
-		f.write_all(&serialize(&parsed).unwrap()).unwrap();
+		f.write_all(v.to_string().replace(';', ";\n").as_bytes())
+			.unwrap();
 	}
 }
modifiedcrates/jrsonnet-stdlib/src/expr.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/expr.rs
+++ b/crates/jrsonnet-stdlib/src/expr.rs
@@ -1,22 +1,103 @@
-use std::borrow::Cow;
+use jrsonnet_parser::LocExpr;
 
-use jrsonnet_parser::{LocExpr, ParserSettings, Source};
+mod structdump_import {
+	pub(super) use std::{borrow::Cow, rc::Rc};
 
-pub const STDLIB_STR: &str = include_str!("./std.jsonnet");
+	pub(super) use jrsonnet_parser::*;
+	pub(super) use vec;
+	pub(super) use Option;
+}
 
 pub fn stdlib_expr() -> LocExpr {
 	#[cfg(feature = "serialized-stdlib")]
 	{
+		use bincode::{BincodeRead, DefaultOptions, Options};
+		use serde::{Deserialize, Deserializer};
+
+		struct LocDeserializer<R, O: Options> {
+			source: Source,
+			wrapped: bincode::Deserializer<R, O>,
+		}
+		macro_rules! delegate {
+			($(fn $name:ident($($arg:ident: $ty:ty),*))+) => {$(
+				fn $name<V>(mut self $(, $arg: $ty)*, visitor: V) -> Result<V::Value, Self::Error>
+				where V: serde::de::Visitor<'de>,
+				{
+					self.wrapped.$name($($arg,)* visitor)
+				}
+			)+};
+		}
+		impl<'de, R, O> Deserializer<'de> for LocDeserializer<R, O>
+		where
+			R: BincodeRead<'de>,
+			O: Options,
+		{
+			type Error = <&'de mut bincode::Deserializer<R, O> as Deserializer<'de>>::Error;
+
+			delegate! {
+				fn deserialize_any()
+				fn deserialize_bool()
+				fn deserialize_u16()
+				fn deserialize_u32()
+				fn deserialize_u64()
+				fn deserialize_i16()
+				fn deserialize_i32()
+				fn deserialize_i64()
+				fn deserialize_f32()
+				fn deserialize_f64()
+				fn deserialize_u128()
+				fn deserialize_i128()
+				fn deserialize_u8()
+				fn deserialize_i8()
+				fn deserialize_unit()
+				fn deserialize_char()
+				fn deserialize_str()
+				fn deserialize_string()
+				fn deserialize_bytes()
+				fn deserialize_byte_buf()
+				fn deserialize_enum(name: &'static str, variants: &'static [&'static str])
+				fn deserialize_tuple(len: usize)
+				fn deserialize_option()
+				fn deserialize_seq()
+				fn deserialize_map()
+				fn deserialize_struct(name: &'static str, fields: &'static [&'static str])
+				fn deserialize_identifier()
+				fn deserialize_newtype_struct(name: &'static str)
+				fn deserialize_unit_struct(name: &'static str)
+				fn deserialize_tuple_struct(name: &'static str, len: usize)
+				fn deserialize_ignored_any()
+			}
+
+			fn is_human_readable(&self) -> bool {
+				false
+			}
+		}
+
+		// In build.rs, Source object is populated with empty values, deserializer wrapper loads correct values on deserialize
+		let mut deserializer = bincode::Deserializer::from_slice(
+			include_bytes!(concat!(env!("OUT_DIR"), "/stdlib.bincode")),
+			DefaultOptions::new()
+				.with_fixint_encoding()
+				.allow_trailing_bytes(),
+		);
+
 		// Should not panic, stdlib.bincode is generated in build.rs
-		return bincode::deserialize(include_bytes!(concat!(env!("OUT_DIR"), "/stdlib.bincode")))
-			.unwrap();
+		LocExpr::deserialize(&mut deserializer).unwrap()
 	}
 
-	jrsonnet_parser::parse(
-		STDLIB_STR,
-		&ParserSettings {
-			file_name: Source::new_virtual(Cow::Borrowed("<std>"), STDLIB_STR.into()),
-		},
-	)
-	.unwrap()
+	#[cfg(feature = "codegenerated-stdlib")]
+	{
+		include!(concat!(env!("OUT_DIR"), "/stdlib.rs"))
+	}
+
+	#[cfg(not(feature = "codegenerated-stdlib"))]
+	{
+		jrsonnet_parser::parse(
+			STDLIB_STR,
+			&ParserSettings {
+				file_name: Source::new_virtual(Cow::Borrowed("<std>"), STDLIB_STR.into()),
+			},
+		)
+		.unwrap()
+	}
 }