difftreelog
refactor switch gc to gcmodule
in: master
39 files changed
.github/workflows/release.ymldiffbeforeafterboth--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -21,7 +21,7 @@
args: --all
cargo-release:
- if: startsWith(github.ref, 'refs/tags/')
+ if: startsWith(github.ref, 'refs/tags/') && !endsWith(github.ref, '-test')
needs: [test]
runs-on: ubuntu-latest
steps:
Cargo.tomldiffbeforeafterboth--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,11 +1,6 @@
[workspace]
members = [
- "crates/jrsonnet-interner",
- "crates/jrsonnet-parser",
- "crates/jrsonnet-evaluator",
- "crates/jrsonnet-stdlib",
- "crates/jrsonnet-cli",
- "crates/jrsonnet-types",
+ "crates/*",
"bindings/jsonnet",
"cmds/jrsonnet",
]
bindings/jsonnet/Cargo.tomldiffbeforeafterboth--- a/bindings/jsonnet/Cargo.toml
+++ b/bindings/jsonnet/Cargo.toml
@@ -10,7 +10,7 @@
[dependencies]
jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "0.4.2" }
jrsonnet-parser = { path = "../../crates/jrsonnet-parser", version = "0.4.2" }
-jrsonnet-gc = { version = "0.4.2", features = ["derive"] }
+gcmodule = { git = "https://github.com/CertainLach/gcmodule", branch = "jrsonnet" }
[lib]
crate-type = ["cdylib"]
bindings/jsonnet/src/native.rsdiffbeforeafterboth--- a/bindings/jsonnet/src/native.rs
+++ b/bindings/jsonnet/src/native.rs
@@ -1,9 +1,10 @@
+use gcmodule::Cc;
use jrsonnet_evaluator::{
error::{Error, LocError},
+ gc::TraceBox,
native::{NativeCallback, NativeCallbackHandler},
EvaluationState, Val,
};
-use jrsonnet_gc::{unsafe_empty_trace, Finalize, Gc, Trace};
use jrsonnet_parser::{Param, ParamsDesc};
use std::{
ffi::{c_void, CStr},
@@ -18,16 +19,15 @@
success: *mut c_int,
) -> *mut Val;
+#[derive(gcmodule::Trace)]
struct JsonnetNativeCallbackHandler {
+ #[skip_trace]
ctx: *const c_void,
+ #[skip_trace]
cb: JsonnetNativeCallback,
-}
-impl Finalize for JsonnetNativeCallbackHandler {}
-unsafe impl Trace for JsonnetNativeCallbackHandler {
- unsafe_empty_trace!();
}
impl NativeCallbackHandler for JsonnetNativeCallbackHandler {
- fn call(&self, _from: Option<Rc<Path>>, args: &[Val]) -> Result<Val, LocError> {
+ fn call(&self, _from: Rc<Path>, args: &[Val]) -> Result<Val, LocError> {
let mut n_args = Vec::new();
for a in args {
n_args.push(Some(Box::new(a.clone())));
@@ -74,9 +74,9 @@
vm.add_native(
name,
- Gc::new(NativeCallback::new(
+ Cc::new(NativeCallback::new(
params,
- Box::new(JsonnetNativeCallbackHandler { ctx, cb }),
+ TraceBox(Box::new(JsonnetNativeCallbackHandler { ctx, cb })),
)),
)
}
bindings/jsonnet/src/val_make.rsdiffbeforeafterboth--- a/bindings/jsonnet/src/val_make.rs
+++ b/bindings/jsonnet/src/val_make.rs
@@ -1,7 +1,7 @@
//! Create values in VM
+use gcmodule::Cc;
use jrsonnet_evaluator::{ArrValue, EvaluationState, ObjValue, Val};
-use jrsonnet_gc::Gc;
use std::{
ffi::CStr,
os::raw::{c_char, c_double, c_int},
@@ -38,7 +38,7 @@
#[no_mangle]
pub extern "C" fn jsonnet_json_make_array(_vm: &EvaluationState) -> *mut Val {
- Box::into_raw(Box::new(Val::Arr(ArrValue::Eager(Gc::new(Vec::new())))))
+ Box::into_raw(Box::new(Val::Arr(ArrValue::Eager(Cc::new(Vec::new())))))
}
#[no_mangle]
bindings/jsonnet/src/val_modify.rsdiffbeforeafterboth--- a/bindings/jsonnet/src/val_modify.rs
+++ b/bindings/jsonnet/src/val_modify.rs
@@ -2,8 +2,8 @@
//! Only tested with variables, which haven't altered by code before appearing here
//! In jrsonnet every value is immutable, and this code is probally broken
+use gcmodule::Cc;
use jrsonnet_evaluator::{ArrValue, EvaluationState, LazyBinding, LazyVal, ObjMember, Val};
-use jrsonnet_gc::Gc;
use jrsonnet_parser::Visibility;
use std::{ffi::CStr, os::raw::c_char};
@@ -23,7 +23,7 @@
new.push(item);
}
new.push(LazyVal::new_resolved(val.clone()));
- *arr = Val::Arr(ArrValue::Lazy(Gc::new(new)));
+ *arr = Val::Arr(ArrValue::Lazy(Cc::new(new)));
}
_ => panic!("should receive array"),
}
cmds/jrsonnet/Cargo.tomldiffbeforeafterboth--- a/cmds/jrsonnet/Cargo.toml
+++ b/cmds/jrsonnet/Cargo.toml
@@ -17,6 +17,7 @@
jrsonnet-cli = { path = "../../crates/jrsonnet-cli", version = "0.4.2" }
mimallocator = { version = "0.1.3", optional = true }
thiserror = "1.0"
+gcmodule = { git = "https://github.com/CertainLach/gcmodule", branch = "jrsonnet" }
[dependencies.clap]
git = "https://github.com/clap-rs/clap"
cmds/jrsonnet/src/main.rsdiffbeforeafterboth--- a/cmds/jrsonnet/src/main.rs
+++ b/cmds/jrsonnet/src/main.rs
@@ -136,7 +136,7 @@
let val = if opts.input.exec {
state.evaluate_snippet_raw(
- PathBuf::from("args").into(),
+ PathBuf::from("<cmdline>").into(),
(&opts.input.input as &str).into(),
)?
} else if opts.input.input == "-" {
crates/jrsonnet-cli/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-cli/Cargo.toml
+++ b/crates/jrsonnet-cli/Cargo.toml
@@ -12,11 +12,7 @@
"explaining-traces",
] }
jrsonnet-parser = { path = "../../crates/jrsonnet-parser", version = "0.4.2" }
-jrsonnet-gc = { version = "0.4.2", features = [
- "derive",
- "unstable-config",
- "unstable-stats",
-] }
+gcmodule = { git = "https://github.com/CertainLach/gcmodule", branch = "jrsonnet" }
[dependencies.clap]
git = "https://github.com/clap-rs/clap"
crates/jrsonnet-cli/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-cli/src/lib.rs
+++ b/crates/jrsonnet-cli/src/lib.rs
@@ -103,12 +103,6 @@
#[derive(Clap)]
#[clap(help_heading = "GARBAGE COLLECTION")]
pub struct GcOpts {
- /// Min bytes allocated to start garbage collection
- #[clap(long, default_value = "20000000")]
- gc_initial_threshold: usize,
- /// How much heap should grow after unsuccessful garbage collection
- #[clap(long)]
- gc_used_space_ratio: Option<f64>,
/// Do not skip gc on exit
#[clap(long)]
gc_collect_on_exit: bool,
@@ -122,32 +116,28 @@
gc_collect_before_printing_stats: bool,
}
impl GcOpts {
+ pub fn configure_global(&self) {
+ if !self.gc_collect_on_exit {
+ gcmodule::set_thread_collect_on_drop(false)
+ }
+ }
pub fn stats_printer(&self) -> Option<GcStatsPrinter> {
- self.gc_print_stats
- .then(|| GcStatsPrinter(self.gc_collect_before_printing_stats))
+ self.gc_print_stats.then(|| GcStatsPrinter {
+ collect_before_printing_stats: self.gc_collect_before_printing_stats,
+ })
}
- pub fn configure_global(&self) {
- jrsonnet_gc::configure(|config| {
- config.leak_on_drop = !self.gc_collect_on_exit;
- config.threshold = self.gc_initial_threshold;
- if let Some(used_space_ratio) = self.gc_used_space_ratio {
- config.used_space_ratio = used_space_ratio;
- }
- });
- }
}
-pub struct GcStatsPrinter(bool);
+
+pub struct GcStatsPrinter {
+ collect_before_printing_stats: bool,
+}
impl Drop for GcStatsPrinter {
fn drop(&mut self) {
- if self.0 {
- jrsonnet_gc::force_collect()
+ eprintln!("=== GC STATS ===");
+ if self.collect_before_printing_stats {
+ let collected = gcmodule::collect_thread_cycles();
+ eprintln!("Collected: {}", collected);
}
- eprintln!("=== GC STATS ===");
- jrsonnet_gc::configure(|c| {
- eprintln!("Final threshold: {:?}", c.threshold);
- });
- let stats = jrsonnet_gc::stats();
- eprintln!("Collections performed: {}", stats.collections_performed);
- eprintln!("Bytes still allocated: {}", stats.bytes_allocated);
+ eprintln!("Tracked: {}", gcmodule::count_thread_tracked())
}
}
crates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -32,7 +32,7 @@
rustc-hash = "1.1.0"
thiserror = "1.0"
-jrsonnet-gc = { version = "0.4.2", features = ["derive"] }
+gcmodule = { git = "https://github.com/CertainLach/gcmodule", branch = "jrsonnet" }
[dependencies.anyhow]
version = "1.0"
@@ -53,7 +53,7 @@
# Explaining traces
[dependencies.annotate-snippets]
-version = "0.9.0"
+version = "0.9.1"
features = ["color"]
optional = true
crates/jrsonnet-evaluator/build.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/build.rs
+++ b/crates/jrsonnet-evaluator/build.rs
@@ -13,7 +13,6 @@
STDLIB_STR,
&ParserSettings {
file_name: PathBuf::from("std.jsonnet").into(),
- loc_data: true,
},
)
.expect("parse");
crates/jrsonnet-evaluator/src/builtin/format.rsdiffbeforeafterboth1//! faster std.format impl2#![allow(clippy::too_many_arguments)]34use crate::{error::Error::*, throw, LocError, ObjValue, Result, Val};5use jrsonnet_gc::Trace;6use jrsonnet_interner::IStr;7use jrsonnet_types::ValType;8use thiserror::Error;910#[derive(Debug, Clone, Error, Trace)]11#[trivially_drop]12pub enum FormatError {13 #[error("truncated format code")]14 TruncatedFormatCode,15 #[error("unrecognized conversion type: {0}")]16 UnrecognizedConversionType(char),1718 #[error("not enough values")]19 NotEnoughValues,2021 #[error("cannot use * width with object")]22 CannotUseStarWidthWithObject,23 #[error("mapping keys required")]24 MappingKeysRequired,25 #[error("no such format field: {0}")]26 NoSuchFormatField(IStr),27}2829impl From<FormatError> for LocError {30 fn from(e: FormatError) -> Self {31 Self::new(Format(e))32 }33}3435use FormatError::*;3637type ParseResult<'t, T> = std::result::Result<(T, &'t str), FormatError>;3839pub fn try_parse_mapping_key(str: &str) -> ParseResult<&str> {40 if str.is_empty() {41 return Err(TruncatedFormatCode);42 }43 let bytes = str.as_bytes();44 if bytes[0] == b'(' {45 let mut i = 1;46 while i < bytes.len() {47 if bytes[i] == b')' {48 return Ok((&str[1..i as usize], &str[i as usize + 1..]));49 }50 i += 1;51 }52 Err(TruncatedFormatCode)53 } else {54 Ok(("", str))55 }56}5758#[cfg(test)]59pub mod tests_key {60 use super::*;6162 #[test]63 fn parse_key() {64 assert_eq!(65 try_parse_mapping_key("(hello ) world").unwrap(),66 ("hello ", " world")67 );68 assert_eq!(try_parse_mapping_key("() world").unwrap(), ("", " world"));69 assert_eq!(try_parse_mapping_key(" world").unwrap(), ("", " world"));70 assert_eq!(71 try_parse_mapping_key(" () world").unwrap(),72 ("", " () world")73 );74 }7576 #[test]77 #[should_panic]78 fn parse_key_missing_start() {79 try_parse_mapping_key("").unwrap();80 }8182 #[test]83 #[should_panic]84 fn parse_key_missing_end() {85 try_parse_mapping_key("( ").unwrap();86 }87}8889#[derive(Default, Debug)]90pub struct CFlags {91 pub alt: bool,92 pub zero: bool,93 pub left: bool,94 pub blank: bool,95 pub sign: bool,96}9798pub fn try_parse_cflags(str: &str) -> ParseResult<CFlags> {99 if str.is_empty() {100 return Err(TruncatedFormatCode);101 }102 let bytes = str.as_bytes();103 let mut i = 0;104 let mut out = CFlags::default();105 loop {106 if bytes.len() == i {107 return Err(TruncatedFormatCode);108 }109 match bytes[i] {110 b'#' => out.alt = true,111 b'0' => out.zero = true,112 b'-' => out.left = true,113 b' ' => out.blank = true,114 b'+' => out.sign = true,115 _ => break,116 }117 i += 1;118 }119 Ok((out, &str[i..]))120}121122#[derive(Debug, PartialEq)]123pub enum Width {124 Star,125 Fixed(usize),126}127pub fn try_parse_field_width(str: &str) -> ParseResult<Width> {128 if str.is_empty() {129 return Err(TruncatedFormatCode);130 }131 let bytes = str.as_bytes();132 if bytes[0] == b'*' {133 return Ok((Width::Star, &str[1..]));134 }135 let mut out: usize = 0;136 let mut digits = 0;137 while let Some(digit) = (bytes[digits] as char).to_digit(10) {138 out *= 10;139 out += digit as usize;140 digits += 1;141 if digits == bytes.len() {142 return Err(TruncatedFormatCode);143 }144 }145 Ok((Width::Fixed(out), &str[digits..]))146}147148pub fn try_parse_precision(str: &str) -> ParseResult<Option<Width>> {149 if str.is_empty() {150 return Err(TruncatedFormatCode);151 }152 let bytes = str.as_bytes();153 if bytes[0] == b'.' {154 try_parse_field_width(&str[1..]).map(|(r, s)| (Some(r), s))155 } else {156 Ok((None, str))157 }158}159160// Only skips161pub fn try_parse_length_modifier(str: &str) -> ParseResult<()> {162 if str.is_empty() {163 return Err(TruncatedFormatCode);164 }165 let bytes = str.as_bytes();166 let mut idx = 0;167 while bytes[idx] == b'h' || bytes[idx] == b'l' || bytes[idx] == b'L' {168 idx += 1;169 if bytes.len() == idx {170 return Err(TruncatedFormatCode);171 }172 }173 Ok(((), &str[idx..]))174}175176#[derive(Debug, PartialEq)]177pub enum ConvTypeV {178 Decimal,179 Octal,180 Hexadecimal,181 Scientific,182 Float,183 Shorter,184 Char,185 String,186 Percent,187}188pub struct ConvType {189 v: ConvTypeV,190 caps: bool,191}192193pub fn parse_conversion_type(str: &str) -> ParseResult<ConvType> {194 if str.is_empty() {195 return Err(TruncatedFormatCode);196 }197198 let code = str.as_bytes()[0];199 let v: (ConvTypeV, bool) = match code {200 b'd' | b'i' | b'u' => (ConvTypeV::Decimal, false),201 b'o' => (ConvTypeV::Octal, false),202 b'x' => (ConvTypeV::Hexadecimal, false),203 b'X' => (ConvTypeV::Hexadecimal, true),204 b'e' => (ConvTypeV::Scientific, false),205 b'E' => (ConvTypeV::Scientific, true),206 b'f' => (ConvTypeV::Float, false),207 b'F' => (ConvTypeV::Float, true),208 b'g' => (ConvTypeV::Shorter, false),209 b'G' => (ConvTypeV::Shorter, true),210 b'c' => (ConvTypeV::Char, false),211 b's' => (ConvTypeV::String, false),212 b'%' => (ConvTypeV::Percent, false),213 c => return Err(UnrecognizedConversionType(c as char)),214 };215216 Ok((ConvType { v: v.0, caps: v.1 }, &str[1..]))217}218219#[derive(Debug)]220pub struct Code<'s> {221 mkey: &'s str,222 cflags: CFlags,223 width: Width,224 precision: Option<Width>,225 convtype: ConvTypeV,226 caps: bool,227}228pub fn parse_code(str: &str) -> ParseResult<Code> {229 if str.is_empty() {230 return Err(TruncatedFormatCode);231 }232 let (mkey, str) = try_parse_mapping_key(str)?;233 let (cflags, str) = try_parse_cflags(str)?;234 let (width, str) = try_parse_field_width(str)?;235 let (precision, str) = try_parse_precision(str)?;236 let (_, str) = try_parse_length_modifier(str)?;237 let (convtype, str) = parse_conversion_type(str)?;238239 Ok((240 Code {241 mkey,242 cflags,243 width,244 precision,245 convtype: convtype.v,246 caps: convtype.caps,247 },248 str,249 ))250}251252#[derive(Debug)]253pub enum Element<'s> {254 String(&'s str),255 Code(Code<'s>),256}257pub fn parse_codes(mut str: &str) -> Result<Vec<Element>> {258 let mut bytes = str.as_bytes();259 let mut out = vec![];260 let mut offset = 0;261262 loop {263 while offset != bytes.len() && bytes[offset] != b'%' {264 offset += 1;265 }266 if offset != 0 {267 out.push(Element::String(&str[0..offset]));268 }269 if offset == bytes.len() {270 return Ok(out);271 }272 str = &str[offset + 1..];273 let (code, nstr) = parse_code(str)?;274 str = nstr;275 bytes = str.as_bytes();276 offset = 0;277278 out.push(Element::Code(code))279 }280}281282const NUMBERS: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz";283284#[inline]285pub fn render_integer(286 out: &mut String,287 iv: i64,288 padding: usize,289 precision: usize,290 blank: bool,291 sign: bool,292 radix: i64,293 prefix: &str,294 caps: bool,295) {296 // Digit char indexes in reverse order, i.e297 // for radix = 16 and n = 12f: [15, 2, 1]298 let digits = if iv == 0 {299 vec![0u8]300 } else {301 let mut v = iv.abs();302 let mut nums = Vec::with_capacity(1);303 while v > 0 {304 nums.push((v % radix) as u8);305 v /= radix;306 }307 nums308 };309 let neg = iv < 0;310 let zp = padding.saturating_sub(if neg || blank || sign { 1 } else { 0 });311 let zp2 = zp312 .max(precision)313 .saturating_sub(prefix.len() + digits.len());314315 if neg {316 out.push('-')317 } else if sign {318 out.push('+');319 } else if blank {320 out.push(' ');321 }322323 out.reserve(zp2);324 for _ in 0..zp2 {325 out.push('0');326 }327 out.push_str(prefix);328329 for digit in digits.into_iter().rev() {330 let ch = NUMBERS[digit as usize] as char;331 out.push(if caps { ch.to_ascii_uppercase() } else { ch });332 }333}334335pub fn render_decimal(336 out: &mut String,337 iv: i64,338 padding: usize,339 precision: usize,340 blank: bool,341 sign: bool,342) {343 render_integer(out, iv, padding, precision, blank, sign, 10, "", false)344}345pub fn render_octal(346 out: &mut String,347 iv: i64,348 padding: usize,349 precision: usize,350 alt: bool,351 blank: bool,352 sign: bool,353) {354 render_integer(355 out,356 iv,357 padding,358 precision,359 blank,360 sign,361 8,362 if alt && iv != 0 { "0" } else { "" },363 false,364 )365}366pub fn render_hexadecimal(367 out: &mut String,368 iv: i64,369 padding: usize,370 precision: usize,371 alt: bool,372 blank: bool,373 sign: bool,374 caps: bool,375) {376 render_integer(377 out,378 iv,379 padding,380 precision,381 blank,382 sign,383 16,384 match (alt, caps) {385 (true, true) => "0X",386 (true, false) => "0x",387 (false, _) => "",388 },389 caps,390 )391}392393pub fn render_float(394 out: &mut String,395 n: f64,396 mut padding: usize,397 precision: usize,398 blank: bool,399 sign: bool,400 ensure_pt: bool,401 trailing: bool,402) {403 let dot_size = if precision == 0 && !ensure_pt { 0 } else { 1 };404 padding = padding.saturating_sub(dot_size + precision);405 render_decimal(out, n.floor() as i64, padding, 0, blank, sign);406 if precision == 0 {407 if ensure_pt {408 out.push('.');409 }410 return;411 }412 let frac = n413 .fract()414 .mul_add(10.0_f64.powf(precision as f64), 0.5)415 .floor();416 if trailing || frac > 0.0 {417 out.push('.');418 let mut frac_str = String::new();419 render_decimal(&mut frac_str, frac as i64, precision, 0, false, false);420 let mut trim = frac_str.len();421 if !trailing {422 for b in frac_str.as_bytes().iter().rev() {423 if *b == b'0' {424 trim -= 1;425 }426 }427 }428 out.push_str(&frac_str[..trim]);429 } else if ensure_pt {430 out.push('.');431 }432}433434pub fn render_float_sci(435 out: &mut String,436 n: f64,437 mut padding: usize,438 precision: usize,439 blank: bool,440 sign: bool,441 ensure_pt: bool,442 trailing: bool,443 caps: bool,444) {445 let exponent = n.log10().floor();446 let mantissa = if exponent as i16 == -324 {447 n * 10.0 / 10.0_f64.powf(exponent + 1.0)448 } else {449 n / 10.0_f64.powf(exponent)450 };451 let mut exponent_str = String::new();452 render_decimal(&mut exponent_str, exponent as i64, 3, 0, false, true);453454 // +1 for e455 padding = padding.saturating_sub(exponent_str.len() + 1);456457 render_float(458 out, mantissa, padding, precision, blank, sign, ensure_pt, trailing,459 );460 out.push(if caps { 'E' } else { 'e' });461 out.push_str(&exponent_str);462}463464pub fn format_code(465 out: &mut String,466 value: &Val,467 code: &Code,468 width: usize,469 precision: Option<usize>,470) -> Result<()> {471 let clfags = &code.cflags;472 let (fpprec, iprec) = match precision {473 Some(v) => (v, v),474 None => (6, 0),475 };476 let padding = if clfags.zero && !clfags.left {477 width478 } else {479 0480 };481482 // TODO: If left padded, can optimize by writing directly to out483 let mut tmp_out = String::new();484485 match code.convtype {486 ConvTypeV::String => tmp_out.push_str(&value.clone().to_string()?),487 ConvTypeV::Decimal => {488 let value = value.clone().try_cast_num("%d/%u/%i requires number")?;489 render_decimal(490 &mut tmp_out,491 value as i64,492 padding,493 iprec,494 clfags.blank,495 clfags.sign,496 );497 }498 ConvTypeV::Octal => {499 let value = value.clone().try_cast_num("%o requires number")?;500 render_octal(501 &mut tmp_out,502 value as i64,503 padding,504 iprec,505 clfags.alt,506 clfags.blank,507 clfags.sign,508 );509 }510 ConvTypeV::Hexadecimal => {511 let value = value.clone().try_cast_num("%x/%X requires number")?;512 render_hexadecimal(513 &mut tmp_out,514 value as i64,515 padding,516 iprec,517 clfags.alt,518 clfags.blank,519 clfags.sign,520 code.caps,521 );522 }523 ConvTypeV::Scientific => {524 let value = value.clone().try_cast_num("%e/%E requires number")?;525 render_float_sci(526 &mut tmp_out,527 value,528 padding,529 fpprec,530 clfags.blank,531 clfags.sign,532 clfags.alt,533 true,534 code.caps,535 );536 }537 ConvTypeV::Float => {538 let value = value.clone().try_cast_num("%e/%E requires number")?;539 render_float(540 &mut tmp_out,541 value,542 padding,543 fpprec,544 clfags.blank,545 clfags.sign,546 clfags.alt,547 true,548 );549 }550 ConvTypeV::Shorter => {551 let value = value.clone().try_cast_num("%g/%G requires number")?;552 let exponent = value.log10().floor();553 if exponent < -4.0 || exponent >= fpprec as f64 {554 render_float_sci(555 &mut tmp_out,556 value,557 padding,558 fpprec - 1,559 clfags.blank,560 clfags.sign,561 clfags.alt,562 clfags.alt,563 code.caps,564 );565 } else {566 let digits_before_pt = 1.max(exponent as usize + 1);567 render_float(568 &mut tmp_out,569 value,570 padding,571 fpprec - digits_before_pt,572 clfags.blank,573 clfags.sign,574 clfags.alt,575 clfags.alt,576 );577 }578 }579 ConvTypeV::Char => match value.clone() {580 Val::Num(n) => tmp_out.push(581 std::char::from_u32(n as u32)582 .ok_or_else(|| InvalidUnicodeCodepointGot(n as u32))?,583 ),584 Val::Str(s) => {585 if s.chars().count() != 1 {586 throw!(RuntimeError(587 format!("%c expected 1 char string, got {}", s.chars().count()).into(),588 ));589 }590 tmp_out.push_str(&s);591 }592 _ => {593 throw!(TypeMismatch(594 "%c requires number/string",595 vec![ValType::Num, ValType::Str],596 value.value_type(),597 ));598 }599 },600 ConvTypeV::Percent => tmp_out.push('%'),601 };602603 let padding = width.saturating_sub(tmp_out.len());604605 if !clfags.left {606 for _ in 0..padding {607 out.push(' ');608 }609 }610 out.push_str(&tmp_out);611 if clfags.left {612 for _ in 0..padding {613 out.push(' ');614 }615 }616617 Ok(())618}619620pub fn format_arr(str: &str, mut values: &[Val]) -> Result<String> {621 let codes = parse_codes(str)?;622 let mut out = String::new();623624 for code in codes {625 match code {626 Element::String(s) => {627 out.push_str(s);628 }629 Element::Code(c) => {630 let width = match c.width {631 Width::Star => {632 if values.is_empty() {633 throw!(NotEnoughValues);634 }635 let value = &values[0];636 values = &values[1..];637 value.clone().try_cast_num("field width")? as usize638 }639 Width::Fixed(n) => n,640 };641 let precision = match c.precision {642 Some(Width::Star) => {643 if values.is_empty() {644 throw!(NotEnoughValues);645 }646 let value = &values[0];647 values = &values[1..];648 Some(value.clone().try_cast_num("field precision")? as usize)649 }650 Some(Width::Fixed(n)) => Some(n),651 None => None,652 };653654 // %% should not consume a value655 let value = if c.convtype == ConvTypeV::Percent {656 &Val::Null657 } else {658 if values.is_empty() {659 throw!(NotEnoughValues);660 }661 let value = &values[0];662 values = &values[1..];663 value664 };665666 format_code(&mut out, value, &c, width, precision)?;667 }668 }669 }670671 Ok(out)672}673674pub fn format_obj(str: &str, values: &ObjValue) -> Result<String> {675 let codes = parse_codes(str)?;676 let mut out = String::new();677678 for code in codes {679 match code {680 Element::String(s) => {681 out.push_str(s);682 }683 Element::Code(c) => {684 // TODO: Operate on ref685 let f: IStr = c.mkey.into();686 let width = match c.width {687 Width::Star => {688 throw!(CannotUseStarWidthWithObject);689 }690 Width::Fixed(n) => n,691 };692 let precision = match c.precision {693 Some(Width::Star) => {694 throw!(CannotUseStarWidthWithObject);695 }696 Some(Width::Fixed(n)) => Some(n),697 None => None,698 };699700 let value = if c.convtype == ConvTypeV::Percent {701 Val::Null702 } else {703 if f.is_empty() {704 throw!(MappingKeysRequired);705 }706 if let Some(v) = values.get(f.clone())? {707 v708 } else {709 throw!(NoSuchFormatField(f));710 }711 };712713 format_code(&mut out, &value, &c, width, precision)?;714 }715 }716 }717718 Ok(out)719}720721#[cfg(test)]722pub mod test_format {723 use super::*;724725 #[test]726 fn parse() {727 assert_eq!(728 parse_codes(729 "How much error budget is left looking at our %.3f%% availability gurantees?"730 )731 .unwrap()732 .len(),733 4734 );735 }736737 #[test]738 fn octals() {739 assert_eq!(format_arr("%#o", &[Val::Num(8.0)]).unwrap(), "010");740 assert_eq!(format_arr("%#4o", &[Val::Num(8.0)]).unwrap(), " 010");741 assert_eq!(format_arr("%4o", &[Val::Num(8.0)]).unwrap(), " 10");742 assert_eq!(format_arr("%04o", &[Val::Num(8.0)]).unwrap(), "0010");743 assert_eq!(format_arr("%+4o", &[Val::Num(8.0)]).unwrap(), " +10");744 assert_eq!(format_arr("%+04o", &[Val::Num(8.0)]).unwrap(), "+010");745 assert_eq!(format_arr("%-4o", &[Val::Num(8.0)]).unwrap(), "10 ");746 assert_eq!(format_arr("%+-4o", &[Val::Num(8.0)]).unwrap(), "+10 ");747 assert_eq!(format_arr("%+-04o", &[Val::Num(8.0)]).unwrap(), "+10 ");748 }749750 #[test]751 fn percent_doesnt_consumes_values() {752 assert_eq!(753 format_arr(754 "How much error budget is left looking at our %.3f%% availability gurantees?",755 &[Val::Num(4.0)]756 )757 .unwrap(),758 "How much error budget is left looking at our 4.000% availability gurantees?"759 );760 }761}1//! faster std.format impl2#![allow(clippy::too_many_arguments)]34use crate::{error::Error::*, throw, LocError, ObjValue, Result, Val};5use gcmodule::Trace;6use jrsonnet_interner::IStr;7use jrsonnet_types::ValType;8use thiserror::Error;910#[derive(Debug, Clone, Error, Trace)]11pub enum FormatError {12 #[error("truncated format code")]13 TruncatedFormatCode,14 #[error("unrecognized conversion type: {0}")]15 UnrecognizedConversionType(char),1617 #[error("not enough values")]18 NotEnoughValues,1920 #[error("cannot use * width with object")]21 CannotUseStarWidthWithObject,22 #[error("mapping keys required")]23 MappingKeysRequired,24 #[error("no such format field: {0}")]25 NoSuchFormatField(IStr),26}2728impl From<FormatError> for LocError {29 fn from(e: FormatError) -> Self {30 Self::new(Format(e))31 }32}3334use FormatError::*;3536type ParseResult<'t, T> = std::result::Result<(T, &'t str), FormatError>;3738pub fn try_parse_mapping_key(str: &str) -> ParseResult<&str> {39 if str.is_empty() {40 return Err(TruncatedFormatCode);41 }42 let bytes = str.as_bytes();43 if bytes[0] == b'(' {44 let mut i = 1;45 while i < bytes.len() {46 if bytes[i] == b')' {47 return Ok((&str[1..i as usize], &str[i as usize + 1..]));48 }49 i += 1;50 }51 Err(TruncatedFormatCode)52 } else {53 Ok(("", str))54 }55}5657#[cfg(test)]58pub mod tests_key {59 use super::*;6061 #[test]62 fn parse_key() {63 assert_eq!(64 try_parse_mapping_key("(hello ) world").unwrap(),65 ("hello ", " world")66 );67 assert_eq!(try_parse_mapping_key("() world").unwrap(), ("", " world"));68 assert_eq!(try_parse_mapping_key(" world").unwrap(), ("", " world"));69 assert_eq!(70 try_parse_mapping_key(" () world").unwrap(),71 ("", " () world")72 );73 }7475 #[test]76 #[should_panic]77 fn parse_key_missing_start() {78 try_parse_mapping_key("").unwrap();79 }8081 #[test]82 #[should_panic]83 fn parse_key_missing_end() {84 try_parse_mapping_key("( ").unwrap();85 }86}8788#[derive(Default, Debug)]89pub struct CFlags {90 pub alt: bool,91 pub zero: bool,92 pub left: bool,93 pub blank: bool,94 pub sign: bool,95}9697pub fn try_parse_cflags(str: &str) -> ParseResult<CFlags> {98 if str.is_empty() {99 return Err(TruncatedFormatCode);100 }101 let bytes = str.as_bytes();102 let mut i = 0;103 let mut out = CFlags::default();104 loop {105 if bytes.len() == i {106 return Err(TruncatedFormatCode);107 }108 match bytes[i] {109 b'#' => out.alt = true,110 b'0' => out.zero = true,111 b'-' => out.left = true,112 b' ' => out.blank = true,113 b'+' => out.sign = true,114 _ => break,115 }116 i += 1;117 }118 Ok((out, &str[i..]))119}120121#[derive(Debug, PartialEq)]122pub enum Width {123 Star,124 Fixed(usize),125}126pub fn try_parse_field_width(str: &str) -> ParseResult<Width> {127 if str.is_empty() {128 return Err(TruncatedFormatCode);129 }130 let bytes = str.as_bytes();131 if bytes[0] == b'*' {132 return Ok((Width::Star, &str[1..]));133 }134 let mut out: usize = 0;135 let mut digits = 0;136 while let Some(digit) = (bytes[digits] as char).to_digit(10) {137 out *= 10;138 out += digit as usize;139 digits += 1;140 if digits == bytes.len() {141 return Err(TruncatedFormatCode);142 }143 }144 Ok((Width::Fixed(out), &str[digits..]))145}146147pub fn try_parse_precision(str: &str) -> ParseResult<Option<Width>> {148 if str.is_empty() {149 return Err(TruncatedFormatCode);150 }151 let bytes = str.as_bytes();152 if bytes[0] == b'.' {153 try_parse_field_width(&str[1..]).map(|(r, s)| (Some(r), s))154 } else {155 Ok((None, str))156 }157}158159// Only skips160pub fn try_parse_length_modifier(str: &str) -> ParseResult<()> {161 if str.is_empty() {162 return Err(TruncatedFormatCode);163 }164 let bytes = str.as_bytes();165 let mut idx = 0;166 while bytes[idx] == b'h' || bytes[idx] == b'l' || bytes[idx] == b'L' {167 idx += 1;168 if bytes.len() == idx {169 return Err(TruncatedFormatCode);170 }171 }172 Ok(((), &str[idx..]))173}174175#[derive(Debug, PartialEq)]176pub enum ConvTypeV {177 Decimal,178 Octal,179 Hexadecimal,180 Scientific,181 Float,182 Shorter,183 Char,184 String,185 Percent,186}187pub struct ConvType {188 v: ConvTypeV,189 caps: bool,190}191192pub fn parse_conversion_type(str: &str) -> ParseResult<ConvType> {193 if str.is_empty() {194 return Err(TruncatedFormatCode);195 }196197 let code = str.as_bytes()[0];198 let v: (ConvTypeV, bool) = match code {199 b'd' | b'i' | b'u' => (ConvTypeV::Decimal, false),200 b'o' => (ConvTypeV::Octal, false),201 b'x' => (ConvTypeV::Hexadecimal, false),202 b'X' => (ConvTypeV::Hexadecimal, true),203 b'e' => (ConvTypeV::Scientific, false),204 b'E' => (ConvTypeV::Scientific, true),205 b'f' => (ConvTypeV::Float, false),206 b'F' => (ConvTypeV::Float, true),207 b'g' => (ConvTypeV::Shorter, false),208 b'G' => (ConvTypeV::Shorter, true),209 b'c' => (ConvTypeV::Char, false),210 b's' => (ConvTypeV::String, false),211 b'%' => (ConvTypeV::Percent, false),212 c => return Err(UnrecognizedConversionType(c as char)),213 };214215 Ok((ConvType { v: v.0, caps: v.1 }, &str[1..]))216}217218#[derive(Debug)]219pub struct Code<'s> {220 mkey: &'s str,221 cflags: CFlags,222 width: Width,223 precision: Option<Width>,224 convtype: ConvTypeV,225 caps: bool,226}227pub fn parse_code(str: &str) -> ParseResult<Code> {228 if str.is_empty() {229 return Err(TruncatedFormatCode);230 }231 let (mkey, str) = try_parse_mapping_key(str)?;232 let (cflags, str) = try_parse_cflags(str)?;233 let (width, str) = try_parse_field_width(str)?;234 let (precision, str) = try_parse_precision(str)?;235 let (_, str) = try_parse_length_modifier(str)?;236 let (convtype, str) = parse_conversion_type(str)?;237238 Ok((239 Code {240 mkey,241 cflags,242 width,243 precision,244 convtype: convtype.v,245 caps: convtype.caps,246 },247 str,248 ))249}250251#[derive(Debug)]252pub enum Element<'s> {253 String(&'s str),254 Code(Code<'s>),255}256pub fn parse_codes(mut str: &str) -> Result<Vec<Element>> {257 let mut bytes = str.as_bytes();258 let mut out = vec![];259 let mut offset = 0;260261 loop {262 while offset != bytes.len() && bytes[offset] != b'%' {263 offset += 1;264 }265 if offset != 0 {266 out.push(Element::String(&str[0..offset]));267 }268 if offset == bytes.len() {269 return Ok(out);270 }271 str = &str[offset + 1..];272 let (code, nstr) = parse_code(str)?;273 str = nstr;274 bytes = str.as_bytes();275 offset = 0;276277 out.push(Element::Code(code))278 }279}280281const NUMBERS: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz";282283#[inline]284pub fn render_integer(285 out: &mut String,286 iv: i64,287 padding: usize,288 precision: usize,289 blank: bool,290 sign: bool,291 radix: i64,292 prefix: &str,293 caps: bool,294) {295 // Digit char indexes in reverse order, i.e296 // for radix = 16 and n = 12f: [15, 2, 1]297 let digits = if iv == 0 {298 vec![0u8]299 } else {300 let mut v = iv.abs();301 let mut nums = Vec::with_capacity(1);302 while v > 0 {303 nums.push((v % radix) as u8);304 v /= radix;305 }306 nums307 };308 let neg = iv < 0;309 let zp = padding.saturating_sub(if neg || blank || sign { 1 } else { 0 });310 let zp2 = zp311 .max(precision)312 .saturating_sub(prefix.len() + digits.len());313314 if neg {315 out.push('-')316 } else if sign {317 out.push('+');318 } else if blank {319 out.push(' ');320 }321322 out.reserve(zp2);323 for _ in 0..zp2 {324 out.push('0');325 }326 out.push_str(prefix);327328 for digit in digits.into_iter().rev() {329 let ch = NUMBERS[digit as usize] as char;330 out.push(if caps { ch.to_ascii_uppercase() } else { ch });331 }332}333334pub fn render_decimal(335 out: &mut String,336 iv: i64,337 padding: usize,338 precision: usize,339 blank: bool,340 sign: bool,341) {342 render_integer(out, iv, padding, precision, blank, sign, 10, "", false)343}344pub fn render_octal(345 out: &mut String,346 iv: i64,347 padding: usize,348 precision: usize,349 alt: bool,350 blank: bool,351 sign: bool,352) {353 render_integer(354 out,355 iv,356 padding,357 precision,358 blank,359 sign,360 8,361 if alt && iv != 0 { "0" } else { "" },362 false,363 )364}365pub fn render_hexadecimal(366 out: &mut String,367 iv: i64,368 padding: usize,369 precision: usize,370 alt: bool,371 blank: bool,372 sign: bool,373 caps: bool,374) {375 render_integer(376 out,377 iv,378 padding,379 precision,380 blank,381 sign,382 16,383 match (alt, caps) {384 (true, true) => "0X",385 (true, false) => "0x",386 (false, _) => "",387 },388 caps,389 )390}391392pub fn render_float(393 out: &mut String,394 n: f64,395 mut padding: usize,396 precision: usize,397 blank: bool,398 sign: bool,399 ensure_pt: bool,400 trailing: bool,401) {402 let dot_size = if precision == 0 && !ensure_pt { 0 } else { 1 };403 padding = padding.saturating_sub(dot_size + precision);404 render_decimal(out, n.floor() as i64, padding, 0, blank, sign);405 if precision == 0 {406 if ensure_pt {407 out.push('.');408 }409 return;410 }411 let frac = n412 .fract()413 .mul_add(10.0_f64.powf(precision as f64), 0.5)414 .floor();415 if trailing || frac > 0.0 {416 out.push('.');417 let mut frac_str = String::new();418 render_decimal(&mut frac_str, frac as i64, precision, 0, false, false);419 let mut trim = frac_str.len();420 if !trailing {421 for b in frac_str.as_bytes().iter().rev() {422 if *b == b'0' {423 trim -= 1;424 }425 }426 }427 out.push_str(&frac_str[..trim]);428 } else if ensure_pt {429 out.push('.');430 }431}432433pub fn render_float_sci(434 out: &mut String,435 n: f64,436 mut padding: usize,437 precision: usize,438 blank: bool,439 sign: bool,440 ensure_pt: bool,441 trailing: bool,442 caps: bool,443) {444 let exponent = n.log10().floor();445 let mantissa = if exponent as i16 == -324 {446 n * 10.0 / 10.0_f64.powf(exponent + 1.0)447 } else {448 n / 10.0_f64.powf(exponent)449 };450 let mut exponent_str = String::new();451 render_decimal(&mut exponent_str, exponent as i64, 3, 0, false, true);452453 // +1 for e454 padding = padding.saturating_sub(exponent_str.len() + 1);455456 render_float(457 out, mantissa, padding, precision, blank, sign, ensure_pt, trailing,458 );459 out.push(if caps { 'E' } else { 'e' });460 out.push_str(&exponent_str);461}462463pub fn format_code(464 out: &mut String,465 value: &Val,466 code: &Code,467 width: usize,468 precision: Option<usize>,469) -> Result<()> {470 let clfags = &code.cflags;471 let (fpprec, iprec) = match precision {472 Some(v) => (v, v),473 None => (6, 0),474 };475 let padding = if clfags.zero && !clfags.left {476 width477 } else {478 0479 };480481 // TODO: If left padded, can optimize by writing directly to out482 let mut tmp_out = String::new();483484 match code.convtype {485 ConvTypeV::String => tmp_out.push_str(&value.clone().to_string()?),486 ConvTypeV::Decimal => {487 let value = value.clone().try_cast_num("%d/%u/%i requires number")?;488 render_decimal(489 &mut tmp_out,490 value as i64,491 padding,492 iprec,493 clfags.blank,494 clfags.sign,495 );496 }497 ConvTypeV::Octal => {498 let value = value.clone().try_cast_num("%o requires number")?;499 render_octal(500 &mut tmp_out,501 value as i64,502 padding,503 iprec,504 clfags.alt,505 clfags.blank,506 clfags.sign,507 );508 }509 ConvTypeV::Hexadecimal => {510 let value = value.clone().try_cast_num("%x/%X requires number")?;511 render_hexadecimal(512 &mut tmp_out,513 value as i64,514 padding,515 iprec,516 clfags.alt,517 clfags.blank,518 clfags.sign,519 code.caps,520 );521 }522 ConvTypeV::Scientific => {523 let value = value.clone().try_cast_num("%e/%E requires number")?;524 render_float_sci(525 &mut tmp_out,526 value,527 padding,528 fpprec,529 clfags.blank,530 clfags.sign,531 clfags.alt,532 true,533 code.caps,534 );535 }536 ConvTypeV::Float => {537 let value = value.clone().try_cast_num("%e/%E requires number")?;538 render_float(539 &mut tmp_out,540 value,541 padding,542 fpprec,543 clfags.blank,544 clfags.sign,545 clfags.alt,546 true,547 );548 }549 ConvTypeV::Shorter => {550 let value = value.clone().try_cast_num("%g/%G requires number")?;551 let exponent = value.log10().floor();552 if exponent < -4.0 || exponent >= fpprec as f64 {553 render_float_sci(554 &mut tmp_out,555 value,556 padding,557 fpprec - 1,558 clfags.blank,559 clfags.sign,560 clfags.alt,561 clfags.alt,562 code.caps,563 );564 } else {565 let digits_before_pt = 1.max(exponent as usize + 1);566 render_float(567 &mut tmp_out,568 value,569 padding,570 fpprec - digits_before_pt,571 clfags.blank,572 clfags.sign,573 clfags.alt,574 clfags.alt,575 );576 }577 }578 ConvTypeV::Char => match value.clone() {579 Val::Num(n) => tmp_out.push(580 std::char::from_u32(n as u32)581 .ok_or_else(|| InvalidUnicodeCodepointGot(n as u32))?,582 ),583 Val::Str(s) => {584 if s.chars().count() != 1 {585 throw!(RuntimeError(586 format!("%c expected 1 char string, got {}", s.chars().count()).into(),587 ));588 }589 tmp_out.push_str(&s);590 }591 _ => {592 throw!(TypeMismatch(593 "%c requires number/string",594 vec![ValType::Num, ValType::Str],595 value.value_type(),596 ));597 }598 },599 ConvTypeV::Percent => tmp_out.push('%'),600 };601602 let padding = width.saturating_sub(tmp_out.len());603604 if !clfags.left {605 for _ in 0..padding {606 out.push(' ');607 }608 }609 out.push_str(&tmp_out);610 if clfags.left {611 for _ in 0..padding {612 out.push(' ');613 }614 }615616 Ok(())617}618619pub fn format_arr(str: &str, mut values: &[Val]) -> Result<String> {620 let codes = parse_codes(str)?;621 let mut out = String::new();622623 for code in codes {624 match code {625 Element::String(s) => {626 out.push_str(s);627 }628 Element::Code(c) => {629 let width = match c.width {630 Width::Star => {631 if values.is_empty() {632 throw!(NotEnoughValues);633 }634 let value = &values[0];635 values = &values[1..];636 value.clone().try_cast_num("field width")? as usize637 }638 Width::Fixed(n) => n,639 };640 let precision = match c.precision {641 Some(Width::Star) => {642 if values.is_empty() {643 throw!(NotEnoughValues);644 }645 let value = &values[0];646 values = &values[1..];647 Some(value.clone().try_cast_num("field precision")? as usize)648 }649 Some(Width::Fixed(n)) => Some(n),650 None => None,651 };652653 // %% should not consume a value654 let value = if c.convtype == ConvTypeV::Percent {655 &Val::Null656 } else {657 if values.is_empty() {658 throw!(NotEnoughValues);659 }660 let value = &values[0];661 values = &values[1..];662 value663 };664665 format_code(&mut out, value, &c, width, precision)?;666 }667 }668 }669670 Ok(out)671}672673pub fn format_obj(str: &str, values: &ObjValue) -> Result<String> {674 let codes = parse_codes(str)?;675 let mut out = String::new();676677 for code in codes {678 match code {679 Element::String(s) => {680 out.push_str(s);681 }682 Element::Code(c) => {683 // TODO: Operate on ref684 let f: IStr = c.mkey.into();685 let width = match c.width {686 Width::Star => {687 throw!(CannotUseStarWidthWithObject);688 }689 Width::Fixed(n) => n,690 };691 let precision = match c.precision {692 Some(Width::Star) => {693 throw!(CannotUseStarWidthWithObject);694 }695 Some(Width::Fixed(n)) => Some(n),696 None => None,697 };698699 let value = if c.convtype == ConvTypeV::Percent {700 Val::Null701 } else {702 if f.is_empty() {703 throw!(MappingKeysRequired);704 }705 if let Some(v) = values.get(f.clone())? {706 v707 } else {708 throw!(NoSuchFormatField(f));709 }710 };711712 format_code(&mut out, &value, &c, width, precision)?;713 }714 }715 }716717 Ok(out)718}719720#[cfg(test)]721pub mod test_format {722 use super::*;723724 #[test]725 fn parse() {726 assert_eq!(727 parse_codes(728 "How much error budget is left looking at our %.3f%% availability gurantees?"729 )730 .unwrap()731 .len(),732 4733 );734 }735736 #[test]737 fn octals() {738 assert_eq!(format_arr("%#o", &[Val::Num(8.0)]).unwrap(), "010");739 assert_eq!(format_arr("%#4o", &[Val::Num(8.0)]).unwrap(), " 010");740 assert_eq!(format_arr("%4o", &[Val::Num(8.0)]).unwrap(), " 10");741 assert_eq!(format_arr("%04o", &[Val::Num(8.0)]).unwrap(), "0010");742 assert_eq!(format_arr("%+4o", &[Val::Num(8.0)]).unwrap(), " +10");743 assert_eq!(format_arr("%+04o", &[Val::Num(8.0)]).unwrap(), "+010");744 assert_eq!(format_arr("%-4o", &[Val::Num(8.0)]).unwrap(), "10 ");745 assert_eq!(format_arr("%+-4o", &[Val::Num(8.0)]).unwrap(), "+10 ");746 assert_eq!(format_arr("%+-04o", &[Val::Num(8.0)]).unwrap(), "+10 ");747 }748749 #[test]750 fn percent_doesnt_consumes_values() {751 assert_eq!(752 format_arr(753 "How much error budget is left looking at our %.3f%% availability gurantees?",754 &[Val::Num(4.0)]755 )756 .unwrap(),757 "How much error budget is left looking at our 4.000% availability gurantees?"758 );759 }760}crates/jrsonnet-evaluator/src/builtin/manifest.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/manifest.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/manifest.rs
@@ -1,6 +1,6 @@
use crate::error::Error::*;
use crate::error::Result;
-use crate::push_frame;
+use crate::push_description_frame;
use crate::{throw, Val};
#[derive(PartialEq, Clone, Copy)]
@@ -103,8 +103,7 @@
buf.push_str(cur_padding);
escape_string_json_buf(&field, buf);
buf.push_str(": ");
- push_frame(
- None,
+ push_description_frame(
|| format!("field <{}> manifestification", field.clone()),
|| {
let value = obj.get(field.clone())?.unwrap();
crates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -7,7 +7,7 @@
EvaluationState, FuncVal, IndexableVal, LazyVal, Val,
};
use format::{format_arr, format_obj};
-use jrsonnet_gc::Gc;
+use gcmodule::Cc;
use jrsonnet_interner::IStr;
use jrsonnet_parser::{ArgsDesc, ExprLocation};
use jrsonnet_types::ty;
@@ -24,7 +24,7 @@
pub fn std_format(str: IStr, vals: Val) -> Result<Val> {
push_frame(
- Some(&ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)),
+ &ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0),
|| format!("std.format of {}", str),
|| {
Ok(match vals {
@@ -68,7 +68,7 @@
}
}
-type Builtin = fn(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val>;
+type Builtin = fn(context: Context, loc: &ExprLocation, args: &ArgsDesc) -> Result<Val>;
type BuiltinsType = HashMap<Box<str>, Builtin>;
@@ -136,7 +136,7 @@
};
}
-fn builtin_length(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_length(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "length", args, 1, [
0, x: ty!((string | object | array));
], {
@@ -154,7 +154,7 @@
})
}
-fn builtin_type(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_type(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "type", args, 1, [
0, x: ty!(any);
], {
@@ -162,11 +162,7 @@
})
}
-fn builtin_make_array(
- context: Context,
- _loc: Option<&ExprLocation>,
- args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_make_array(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "makeArray", args, 2, [
0, sz: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;
1, func: ty!(function) => Val::Func;
@@ -182,11 +178,7 @@
})
}
-fn builtin_codepoint(
- context: Context,
- _loc: Option<&ExprLocation>,
- args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_codepoint(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "codepoint", args, 1, [
0, str: ty!(char) => Val::Str;
], {
@@ -194,11 +186,7 @@
})
}
-fn builtin_object_fields_ex(
- context: Context,
- _loc: Option<&ExprLocation>,
- args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_object_fields_ex(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "objectFieldsEx", args, 2, [
0, obj: ty!(object) => Val::Obj;
1, inc_hidden: ty!(boolean) => Val::Bool;
@@ -208,11 +196,7 @@
})
}
-fn builtin_object_has_ex(
- context: Context,
- _loc: Option<&ExprLocation>,
- args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_object_has_ex(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "objectHasEx", args, 3, [
0, obj: ty!(object) => Val::Obj;
1, f: ty!(string) => Val::Str;
@@ -222,11 +206,7 @@
})
}
-fn builtin_parse_json(
- context: Context,
- _loc: Option<&ExprLocation>,
- args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_parse_json(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "parseJson", args, 1, [
0, s: ty!(string) => Val::Str;
], {
@@ -236,7 +216,7 @@
})
}
-fn builtin_slice(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_slice(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "slice", args, 4, [
0, indexable: ty!((string | array));
1, index: ty!((number | null));
@@ -252,7 +232,7 @@
})
}
-fn builtin_substr(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_substr(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "substr", args, 3, [
0, str: ty!(string) => Val::Str;
1, from: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;
@@ -263,11 +243,7 @@
})
}
-fn builtin_primitive_equals(
- context: Context,
- _loc: Option<&ExprLocation>,
- args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_primitive_equals(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "primitiveEquals", args, 2, [
0, a: ty!(any);
1, b: ty!(any);
@@ -276,7 +252,7 @@
})
}
-fn builtin_equals(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_equals(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "equals", args, 2, [
0, a: ty!(any);
1, b: ty!(any);
@@ -285,7 +261,7 @@
})
}
-fn builtin_modulo(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_modulo(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "modulo", args, 2, [
0, a: ty!(number) => Val::Num;
1, b: ty!(number) => Val::Num;
@@ -294,7 +270,7 @@
})
}
-fn builtin_mod(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_mod(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "mod", args, 2, [
0, a: ty!((number | string));
1, b: ty!(any);
@@ -303,7 +279,7 @@
})
}
-fn builtin_floor(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_floor(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "floor", args, 1, [
0, x: ty!(number) => Val::Num;
], {
@@ -311,7 +287,7 @@
})
}
-fn builtin_ceil(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_ceil(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "ceil", args, 1, [
0, x: ty!(number) => Val::Num;
], {
@@ -319,7 +295,7 @@
})
}
-fn builtin_log(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_log(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "log", args, 1, [
0, n: ty!(number) => Val::Num;
], {
@@ -327,7 +303,7 @@
})
}
-fn builtin_pow(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_pow(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "pow", args, 2, [
0, x: ty!(number) => Val::Num;
1, n: ty!(number) => Val::Num;
@@ -336,7 +312,7 @@
})
}
-fn builtin_sqrt(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_sqrt(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "sqrt", args, 1, [
0, x: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;
], {
@@ -344,7 +320,7 @@
})
}
-fn builtin_sin(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_sin(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "sin", args, 1, [
0, x: ty!(number) => Val::Num;
], {
@@ -352,7 +328,7 @@
})
}
-fn builtin_cos(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_cos(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "cos", args, 1, [
0, x: ty!(number) => Val::Num;
], {
@@ -360,7 +336,7 @@
})
}
-fn builtin_tan(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_tan(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "tan", args, 1, [
0, x: ty!(number) => Val::Num;
], {
@@ -368,7 +344,7 @@
})
}
-fn builtin_asin(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_asin(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "asin", args, 1, [
0, x: ty!(number) => Val::Num;
], {
@@ -376,7 +352,7 @@
})
}
-fn builtin_acos(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_acos(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "acos", args, 1, [
0, x: ty!(number) => Val::Num;
], {
@@ -384,7 +360,7 @@
})
}
-fn builtin_atan(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_atan(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "atan", args, 1, [
0, x: ty!(number) => Val::Num;
], {
@@ -392,7 +368,7 @@
})
}
-fn builtin_exp(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_exp(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "exp", args, 1, [
0, x: ty!(number) => Val::Num;
], {
@@ -411,7 +387,7 @@
}
}
-fn builtin_mantissa(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_mantissa(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "mantissa", args, 1, [
0, x: ty!(number) => Val::Num;
], {
@@ -419,7 +395,7 @@
})
}
-fn builtin_exponent(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_exponent(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "exponent", args, 1, [
0, x: ty!(number) => Val::Num;
], {
@@ -427,7 +403,7 @@
})
}
-fn builtin_ext_var(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_ext_var(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "extVar", args, 1, [
0, x: ty!(string) => Val::Str;
], {
@@ -435,15 +411,15 @@
})
}
-fn builtin_native(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_native(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "native", args, 1, [
0, x: ty!(string) => Val::Str;
], {
- Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Gc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or(UndefinedExternalFunction(x))?)
+ Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Cc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or(UndefinedExternalFunction(x))?)
})
}
-fn builtin_filter(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_filter(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "filter", args, 2, [
0, func: ty!(function) => Val::Func;
1, arr: ty!(array) => Val::Arr;
@@ -454,7 +430,7 @@
})
}
-fn builtin_map(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_map(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "map", args, 2, [
0, func: ty!(function) => Val::Func;
1, arr: ty!(array) => Val::Arr;
@@ -464,7 +440,7 @@
})
}
-fn builtin_flatmap(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_flatmap(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "flatMap", args, 2, [
0, func: ty!(function) => Val::Func;
1, arr: ty!((array | string));
@@ -498,7 +474,7 @@
})
}
-fn builtin_foldl(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_foldl(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "foldl", args, 3, [
0, func: ty!(function) => Val::Func;
1, arr: ty!(array) => Val::Arr;
@@ -512,7 +488,7 @@
})
}
-fn builtin_foldr(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_foldr(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "foldr", args, 3, [
0, func: ty!(function) => Val::Func;
1, arr: ty!(array) => Val::Arr;
@@ -527,11 +503,7 @@
}
#[allow(non_snake_case)]
-fn builtin_sort_impl(
- context: Context,
- _loc: Option<&ExprLocation>,
- args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_sort_impl(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "sort", args, 2, [
0, arr: ty!(array) => Val::Arr;
1, keyF: ty!(function) => Val::Func;
@@ -543,7 +515,7 @@
})
}
-fn builtin_format(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_format(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "format", args, 2, [
0, str: ty!(string) => Val::Str;
1, vals: ty!(any)
@@ -552,7 +524,7 @@
})
}
-fn builtin_range(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_range(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "range", args, 2, [
0, from: ty!(number) => Val::Num;
1, to: ty!(number) => Val::Num;
@@ -568,7 +540,7 @@
})
}
-fn builtin_char(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_char(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "char", args, 1, [
0, n: ty!(number) => Val::Num;
], {
@@ -580,11 +552,7 @@
})
}
-fn builtin_encode_utf8(
- context: Context,
- _loc: Option<&ExprLocation>,
- args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_encode_utf8(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "encodeUTF8", args, 1, [
0, str: ty!(string) => Val::Str;
], {
@@ -592,11 +560,7 @@
})
}
-fn builtin_decode_utf8(
- context: Context,
- _loc: Option<&ExprLocation>,
- args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_decode_utf8(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "decodeUTF8", args, 1, [
0, arr: ty!((Array<ubyte>)) => Val::Arr;
], {
@@ -609,7 +573,7 @@
})
}
-fn builtin_md5(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_md5(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "md5", args, 1, [
0, str: ty!(string) => Val::Str;
], {
@@ -617,24 +581,22 @@
})
}
-fn builtin_trace(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_trace(context: Context, loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "trace", args, 2, [
0, str: ty!(string) => Val::Str;
1, rest: ty!(any);
], {
eprint!("TRACE:");
- if let Some(loc) = loc {
with_state(|s|{
let locs = s.map_source_locations(&loc.0, &[loc.1]);
eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);
});
- }
eprintln!(" {}", str);
Ok(rest)
})
}
-fn builtin_base64(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_base64(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "base64", args, 1, [
0, input: ty!((string | (Array<number>)));
], {
@@ -654,7 +616,7 @@
fn builtin_base64_decode_bytes(
context: Context,
- _loc: Option<&ExprLocation>,
+ _loc: &ExprLocation,
args: &ArgsDesc,
) -> Result<Val> {
parse_args!(context, "base64DecodeBytes", args, 1, [
@@ -669,11 +631,7 @@
})
}
-fn builtin_base64_decode(
- context: Context,
- _loc: Option<&ExprLocation>,
- args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_base64_decode(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "base64Decode", args, 1, [
0, input: ty!(string) => Val::Str;
], {
@@ -685,7 +643,7 @@
})
}
-fn builtin_join(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_join(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "join", args, 2, [
0, sep: ty!((string | array));
1, arr: ty!(array) => Val::Arr;
@@ -744,7 +702,7 @@
fn builtin_escape_string_json(
context: Context,
- _loc: Option<&ExprLocation>,
+ _loc: &ExprLocation,
args: &ArgsDesc,
) -> Result<Val> {
parse_args!(context, "escapeStringJson", args, 1, [
@@ -754,11 +712,7 @@
})
}
-fn builtin_manifest_json_ex(
- context: Context,
- _loc: Option<&ExprLocation>,
- args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_manifest_json_ex(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "manifestJsonEx", args, 2, [
0, value: ty!(any);
1, indent: ty!(string) => Val::Str;
@@ -772,7 +726,7 @@
fn builtin_manifest_yaml_doc(
context: Context,
- _loc: Option<&ExprLocation>,
+ _loc: &ExprLocation,
args: &ArgsDesc,
) -> Result<Val> {
parse_args!(context, "manifestYamlDoc", args, 2, [
@@ -786,7 +740,7 @@
})
}
-fn builtin_reverse(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_reverse(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "reverse", args, 1, [
0, value: ty!(array) => Val::Arr;
], {
@@ -794,7 +748,7 @@
})
}
-fn builtin_id(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_id(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "id", args, 1, [
0, v: ty!(any);
], {
@@ -802,11 +756,7 @@
})
}
-fn builtin_str_replace(
- context: Context,
- _loc: Option<&ExprLocation>,
- args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_str_replace(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "strReplace", args, 3, [
0, str: ty!(string) => Val::Str;
1, from: ty!(string) => Val::Str;
@@ -816,11 +766,7 @@
})
}
-fn builtin_splitlimit(
- context: Context,
- _loc: Option<&ExprLocation>,
- args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_splitlimit(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "splitLimit", args, 3, [
0, str: ty!(string) => Val::Str;
1, c: ty!(char) => Val::Str;
@@ -839,11 +785,7 @@
})
}
-fn builtin_ascii_upper(
- context: Context,
- _loc: Option<&ExprLocation>,
- args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_ascii_upper(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "asciiUpper", args, 1, [
0, str: ty!(string) => Val::Str;
], {
@@ -851,11 +793,7 @@
})
}
-fn builtin_ascii_lower(
- context: Context,
- _loc: Option<&ExprLocation>,
- args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_ascii_lower(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "asciiLower", args, 1, [
0, str: ty!(string) => Val::Str;
], {
@@ -863,7 +801,7 @@
})
}
-fn builtin_member(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_member(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "member", args, 2, [
0, arr: ty!((array | string));
1, x: ty!(any);
@@ -887,7 +825,7 @@
})
}
-fn builtin_count(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_count(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "count", args, 2, [
0, arr: ty!(array) => Val::Arr;
1, x: ty!(any);
@@ -905,7 +843,7 @@
pub fn call_builtin(
context: Context,
- loc: Option<&ExprLocation>,
+ loc: &ExprLocation,
name: &str,
args: &ArgsDesc,
) -> Result<Val> {
crates/jrsonnet-evaluator/src/builtin/sort.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/sort.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/sort.rs
@@ -2,9 +2,9 @@
error::{Error, LocError, Result},
throw, Context, FuncVal, Val,
};
-use jrsonnet_gc::{Finalize, Gc, Trace};
+use gcmodule::{Cc, Trace};
-#[derive(Debug, Clone, thiserror::Error, Trace, Finalize)]
+#[derive(Debug, Clone, thiserror::Error, Trace)]
pub enum SortError {
#[error("sort key should be string or number")]
SortKeyShouldBeStringOrNumber,
@@ -59,7 +59,7 @@
Ok(sort_type)
}
-pub fn sort(ctx: Context, values: Gc<Vec<Val>>, key_getter: &FuncVal) -> Result<Gc<Vec<Val>>> {
+pub fn sort(ctx: Context, values: Cc<Vec<Val>>, key_getter: &FuncVal) -> Result<Cc<Vec<Val>>> {
if values.len() <= 1 {
return Ok(values);
}
@@ -77,7 +77,7 @@
}),
SortKeyType::Unknown => unreachable!(),
};
- Ok(Gc::new(mvalues))
+ Ok(Cc::new(mvalues))
} else {
let mut vk = Vec::with_capacity(values.len());
for value in values.iter() {
@@ -98,6 +98,6 @@
}),
SortKeyType::Unknown => unreachable!(),
};
- Ok(Gc::new(vk.into_iter().map(|v| v.0).collect()))
+ Ok(Cc::new(vk.into_iter().map(|v| v.0).collect()))
}
}
crates/jrsonnet-evaluator/src/builtin/stdlib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/stdlib.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/stdlib.rs
@@ -15,7 +15,6 @@
jrsonnet_parser::parse(
jrsonnet_stdlib::STDLIB_STR,
&ParserSettings {
- loc_data: true,
file_name: PathBuf::from("std.jsonnet").into(),
},
)
crates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/ctx.rs
+++ b/crates/jrsonnet-evaluator/src/ctx.rs
@@ -1,16 +1,15 @@
+use crate::cc_ptr_eq;
+use crate::gc::GcHashMap;
use crate::{
error::Error::*, map::LayeredHashMap, FutureWrapper, LazyBinding, LazyVal, ObjValue, Result,
Val,
};
-use jrsonnet_gc::{Gc, Trace};
+use gcmodule::{Cc, Trace};
use jrsonnet_interner::IStr;
-use rustc_hash::FxHashMap;
use std::fmt::Debug;
-use std::hash::BuildHasherDefault;
#[derive(Clone, Trace)]
-#[trivially_drop]
-pub struct ContextCreator(pub Context, pub FutureWrapper<FxHashMap<IStr, LazyBinding>>);
+pub struct ContextCreator(pub Context, pub FutureWrapper<GcHashMap<IStr, LazyBinding>>);
impl ContextCreator {
pub fn create(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<Context> {
self.0.clone().extend_unbound(
@@ -23,7 +22,6 @@
}
#[derive(Trace)]
-#[trivially_drop]
struct ContextInternals {
dollar: Option<ObjValue>,
this: Option<ObjValue>,
@@ -37,8 +35,7 @@
}
#[derive(Debug, Clone, Trace)]
-#[trivially_drop]
-pub struct Context(Gc<ContextInternals>);
+pub struct Context(Cc<ContextInternals>);
impl Context {
pub fn new_future() -> FutureWrapper<Self> {
FutureWrapper::new()
@@ -57,7 +54,7 @@
}
pub fn new() -> Self {
- Self(Gc::new(ContextInternals {
+ Self(Cc::new(ContextInternals {
dollar: None,
this: None,
super_obj: None,
@@ -84,19 +81,18 @@
}
pub fn with_var(self, name: IStr, value: Val) -> Self {
- let mut new_bindings =
- FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default());
+ let mut new_bindings = GcHashMap::with_capacity(1);
new_bindings.insert(name, LazyVal::new_resolved(value));
self.extend(new_bindings, None, None, None)
}
pub fn with_this_super(self, new_this: ObjValue, new_super_obj: Option<ObjValue>) -> Self {
- self.extend(FxHashMap::default(), None, Some(new_this), new_super_obj)
+ self.extend(GcHashMap::new(), None, Some(new_this), new_super_obj)
}
pub fn extend(
self,
- new_bindings: FxHashMap<IStr, LazyVal>,
+ new_bindings: GcHashMap<IStr, LazyVal>,
new_dollar: Option<ObjValue>,
new_this: Option<ObjValue>,
new_super_obj: Option<ObjValue>,
@@ -110,30 +106,29 @@
} else {
ctx.bindings.clone().extend(new_bindings)
};
- Self(Gc::new(ContextInternals {
+ Self(Cc::new(ContextInternals {
dollar,
this,
super_obj,
bindings,
}))
}
- pub fn extend_bound(self, new_bindings: FxHashMap<IStr, LazyVal>) -> Self {
+ pub fn extend_bound(self, new_bindings: GcHashMap<IStr, LazyVal>) -> Self {
let new_this = self.0.this.clone();
let new_super_obj = self.0.super_obj.clone();
self.extend(new_bindings, None, new_this, new_super_obj)
}
pub fn extend_unbound(
self,
- new_bindings: FxHashMap<IStr, LazyBinding>,
+ new_bindings: GcHashMap<IStr, LazyBinding>,
new_dollar: Option<ObjValue>,
new_this: Option<ObjValue>,
new_super_obj: Option<ObjValue>,
) -> Result<Self> {
let this = new_this.or_else(|| self.0.this.clone());
let super_obj = new_super_obj.or_else(|| self.0.super_obj.clone());
- let mut new =
- FxHashMap::with_capacity_and_hasher(new_bindings.len(), BuildHasherDefault::default());
- for (k, v) in new_bindings.into_iter() {
+ let mut new = GcHashMap::with_capacity(new_bindings.len());
+ for (k, v) in new_bindings.0.into_iter() {
new.insert(k, v.evaluate(this.clone(), super_obj.clone())?);
}
Ok(self.extend(new, new_dollar, this, super_obj))
@@ -152,6 +147,6 @@
impl PartialEq for Context {
fn eq(&self, other: &Self) -> bool {
- Gc::ptr_eq(&self.0, &other.0)
+ cc_ptr_eq(&self.0, &other.0)
}
}
crates/jrsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/dynamic.rs
+++ b/crates/jrsonnet-evaluator/src/dynamic.rs
@@ -1,11 +1,12 @@
-use jrsonnet_gc::{Gc, GcCell, Trace};
+use std::cell::RefCell;
+use gcmodule::{Cc, Trace};
+
#[derive(Clone, Trace)]
-#[trivially_drop]
-pub struct FutureWrapper<V: Trace + 'static>(pub Gc<GcCell<Option<V>>>);
+pub struct FutureWrapper<V: Trace + 'static>(pub Cc<RefCell<Option<V>>>);
impl<T: Trace + 'static> FutureWrapper<T> {
pub fn new() -> Self {
- Self(Gc::new(GcCell::new(None)))
+ Self(Cc::new(RefCell::new(None)))
}
pub fn fill(self, value: T) {
assert!(self.0.borrow().is_none(), "wrapper is filled already");
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -1,5 +1,8 @@
-use crate::{Val, builtin::{format::FormatError, sort::SortError}, typed::TypeLocError};
-use jrsonnet_gc::Trace;
+use crate::{
+ builtin::{format::FormatError, sort::SortError},
+ typed::TypeLocError,
+};
+use gcmodule::Trace;
use jrsonnet_interner::IStr;
use jrsonnet_parser::{BinaryOpType, ExprLocation, UnaryOpType};
use jrsonnet_types::ValType;
@@ -10,7 +13,6 @@
use thiserror::Error;
#[derive(Error, Debug, Clone, Trace)]
-#[trivially_drop]
pub enum Error {
#[error("intrinsic not found: {0}")]
IntrinsicNotFound(IStr),
@@ -85,14 +87,15 @@
#[error("tried to import {1} from {0}, but imports is not supported")]
ImportNotSupported(PathBuf, PathBuf),
#[error(
- "syntax error, expected one of {}, got {:?}",
+ "syntax error: expected {}, got {:?}",
.error.expected,
.source_code.chars().nth(error.location.offset).map(|c| c.to_string()).unwrap_or_else(|| "EOF".into())
)]
ImportSyntaxError {
+ #[skip_trace]
path: Rc<Path>,
source_code: IStr,
- #[unsafe_ignore_trace]
+ #[skip_trace]
error: Box<jrsonnet_parser::ParseError>,
},
@@ -150,17 +153,14 @@
}
#[derive(Clone, Debug, Trace)]
-#[trivially_drop]
pub struct StackTraceElement {
pub location: Option<ExprLocation>,
pub desc: String,
}
#[derive(Debug, Clone, Trace)]
-#[trivially_drop]
pub struct StackTrace(pub Vec<StackTraceElement>);
#[derive(Debug, Clone, Trace)]
-#[trivially_drop]
pub struct LocError(Box<(Error, StackTrace)>);
impl LocError {
pub fn new(e: Error) -> Self {
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -2,19 +2,18 @@
builtin::std_slice,
error::Error::*,
evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},
+ gc::TraceBox,
push_frame, throw, with_state, ArrValue, Bindable, Context, ContextCreator, FuncDesc, FuncVal,
- FutureWrapper, LazyBinding, LazyVal, LazyValValue, ObjValue, ObjValueBuilder, ObjectAssertion,
- Result, Val,
+ FutureWrapper, GcHashMap, LazyBinding, LazyVal, LazyValValue, ObjValue,
+ ObjValueBuilder, ObjectAssertion, Result, Val,
};
-use jrsonnet_gc::{Gc, Trace};
+use gcmodule::{Cc, Trace};
use jrsonnet_interner::IStr;
use jrsonnet_parser::{
ArgsDesc, AssertStmt, BindSpec, CompSpec, Expr, ExprLocation, FieldMember, ForSpecData,
IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc,
};
use jrsonnet_types::ValType;
-use rustc_hash::{FxHashMap, FxHasher};
-use std::{collections::HashMap, hash::BuildHasherDefault};
pub mod operator;
pub fn evaluate_binding_in_future(
@@ -26,7 +25,6 @@
let params = params.clone();
#[derive(Trace)]
- #[trivially_drop]
struct LazyMethodBinding {
context_creator: FutureWrapper<Context>,
name: IStr,
@@ -44,15 +42,14 @@
}
}
- LazyVal::new(Box::new(LazyMethodBinding {
+ LazyVal::new(TraceBox(Box::new(LazyMethodBinding {
context_creator,
name: b.name.clone(),
params,
value: b.value.clone(),
- }))
+ })))
} else {
#[derive(Trace)]
- #[trivially_drop]
struct LazyNamedBinding {
context_creator: FutureWrapper<Context>,
name: IStr,
@@ -63,11 +60,11 @@
evaluate_named(self.context_creator.unwrap(), &self.value, self.name)
}
}
- LazyVal::new(Box::new(LazyNamedBinding {
+ LazyVal::new(TraceBox(Box::new(LazyNamedBinding {
context_creator,
name: b.name.clone(),
value: b.value,
- }))
+ })))
}
}
@@ -77,7 +74,6 @@
let params = params.clone();
#[derive(Trace)]
- #[trivially_drop]
struct BindableMethodLazyVal {
this: Option<ObjValue>,
super_obj: Option<ObjValue>,
@@ -99,7 +95,6 @@
}
#[derive(Trace)]
- #[trivially_drop]
struct BindableMethod {
context_creator: ContextCreator,
name: IStr,
@@ -108,30 +103,31 @@
}
impl Bindable for BindableMethod {
fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {
- Ok(LazyVal::new(Box::new(BindableMethodLazyVal {
- this,
- super_obj,
+ Ok(LazyVal::new(TraceBox(Box::new(
+ BindableMethodLazyVal {
+ this,
+ super_obj,
- context_creator: self.context_creator.clone(),
- name: self.name.clone(),
- params: self.params.clone(),
- value: self.value.clone(),
- })))
+ context_creator: self.context_creator.clone(),
+ name: self.name.clone(),
+ params: self.params.clone(),
+ value: self.value.clone(),
+ },
+ ))))
}
}
(
b.name.clone(),
- LazyBinding::Bindable(Gc::new(Box::new(BindableMethod {
+ LazyBinding::Bindable(Cc::new(TraceBox(Box::new(BindableMethod {
context_creator,
name: b.name.clone(),
params,
value: b.value.clone(),
- }))),
+ })))),
)
} else {
#[derive(Trace)]
- #[trivially_drop]
struct BindableNamedLazyVal {
this: Option<ObjValue>,
super_obj: Option<ObjValue>,
@@ -151,7 +147,6 @@
}
#[derive(Trace)]
- #[trivially_drop]
struct BindableNamed {
context_creator: ContextCreator,
name: IStr,
@@ -159,30 +154,32 @@
}
impl Bindable for BindableNamed {
fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {
- Ok(LazyVal::new(Box::new(BindableNamedLazyVal {
- this,
- super_obj,
+ Ok(LazyVal::new(TraceBox(Box::new(
+ BindableNamedLazyVal {
+ this,
+ super_obj,
- context_creator: self.context_creator.clone(),
- name: self.name.clone(),
- value: self.value.clone(),
- })))
+ context_creator: self.context_creator.clone(),
+ name: self.name.clone(),
+ value: self.value.clone(),
+ },
+ ))))
}
}
(
b.name.clone(),
- LazyBinding::Bindable(Gc::new(Box::new(BindableNamed {
+ LazyBinding::Bindable(Cc::new(TraceBox(Box::new(BindableNamed {
context_creator,
name: b.name.clone(),
value: b.value.clone(),
- }))),
+ })))),
)
}
}
pub fn evaluate_method(ctx: Context, name: IStr, params: ParamsDesc, body: LocExpr) -> Val {
- Val::Func(Gc::new(FuncVal::Normal(FuncDesc {
+ Val::Func(Cc::new(FuncVal::Normal(FuncDesc {
name,
ctx,
params,
@@ -240,8 +237,7 @@
let future_this = FutureWrapper::new();
let context_creator = ContextCreator(context.clone(), new_bindings.clone());
{
- let mut bindings: FxHashMap<IStr, LazyBinding> =
- FxHashMap::with_capacity_and_hasher(members.len(), BuildHasherDefault::default());
+ let mut bindings: GcHashMap<IStr, LazyBinding> = GcHashMap::with_capacity(members.len());
for (n, b) in members
.iter()
.filter_map(|m| match m {
@@ -272,7 +268,6 @@
let name = name.unwrap();
#[derive(Trace)]
- #[trivially_drop]
struct ObjMemberBinding {
context_creator: ContextCreator,
value: LocExpr,
@@ -296,11 +291,11 @@
.with_add(*plus)
.with_visibility(*visibility)
.with_location(value.1.clone())
- .bindable(Box::new(ObjMemberBinding {
+ .bindable(TraceBox(Box::new(ObjMemberBinding {
context_creator: context_creator.clone(),
value: value.clone(),
name,
- }));
+ })));
}
Member::Field(FieldMember {
name,
@@ -314,7 +309,6 @@
}
let name = name.unwrap();
#[derive(Trace)]
- #[trivially_drop]
struct ObjMemberBinding {
context_creator: ContextCreator,
value: LocExpr,
@@ -339,17 +333,16 @@
.member(name.clone())
.hide()
.with_location(value.1.clone())
- .bindable(Box::new(ObjMemberBinding {
+ .bindable(TraceBox(Box::new(ObjMemberBinding {
context_creator: context_creator.clone(),
value: value.clone(),
params: params.clone(),
name,
- }));
+ })));
}
Member::BindStmt(_) => {}
Member::AssertStmt(stmt) => {
#[derive(Trace)]
- #[trivially_drop]
struct ObjectAssert {
context_creator: ContextCreator,
assert: AssertStmt,
@@ -364,10 +357,10 @@
evaluate_assert(ctx, &self.assert)
}
}
- builder.assert(Box::new(ObjectAssert {
+ builder.assert(TraceBox(Box::new(ObjectAssert {
context_creator: context_creator.clone(),
assert: stmt.clone(),
- }));
+ })));
}
}
}
@@ -385,11 +378,8 @@
evaluate_comp(context.clone(), &obj.compspecs, &mut |ctx| {
let new_bindings = FutureWrapper::new();
let context_creator = ContextCreator(context.clone(), new_bindings.clone());
- let mut bindings: FxHashMap<IStr, LazyBinding> =
- FxHashMap::with_capacity_and_hasher(
- obj.pre_locals.len() + obj.post_locals.len(),
- BuildHasherDefault::default(),
- );
+ let mut bindings: GcHashMap<IStr, LazyBinding> =
+ GcHashMap::with_capacity(obj.pre_locals.len() + obj.post_locals.len());
for (n, b) in obj
.pre_locals
.iter()
@@ -406,7 +396,6 @@
Val::Null => {}
Val::Str(n) => {
#[derive(Trace)]
- #[trivially_drop]
struct ObjCompBinding {
context: Context,
value: LocExpr,
@@ -418,12 +407,9 @@
_super_obj: Option<ObjValue>,
) -> Result<LazyVal> {
Ok(LazyVal::new_resolved(evaluate(
- self.context.clone().extend(
- FxHashMap::default(),
- None,
- this,
- None,
- ),
+ self.context
+ .clone()
+ .extend(GcHashMap::new(), None, this, None),
&self.value,
)?))
}
@@ -432,10 +418,10 @@
.member(n)
.with_location(obj.value.1.clone())
.with_add(obj.plus)
- .bindable(Box::new(ObjCompBinding {
+ .bindable(TraceBox(Box::new(ObjCompBinding {
context: ctx,
value: obj.value.clone(),
- }));
+ })));
}
v => throw!(FieldMustBeStringGot(v.value_type())),
}
@@ -454,7 +440,7 @@
context: Context,
value: &LocExpr,
args: &ArgsDesc,
- loc: Option<&ExprLocation>,
+ loc: &ExprLocation,
tailstrict: bool,
) -> Result<Val> {
let value = evaluate(context.clone(), value)?;
@@ -475,7 +461,7 @@
let value = &assertion.0;
let msg = &assertion.1;
let assertion_result = push_frame(
- value.1.as_ref(),
+ &value.1,
|| "assertion condition".to_owned(),
|| {
evaluate(context.clone(), value)?
@@ -484,7 +470,7 @@
)?;
if !assertion_result {
push_frame(
- value.1.as_ref(),
+ &value.1,
|| "assertion failure".to_owned(),
|| {
if let Some(msg) = msg {
@@ -534,8 +520,8 @@
BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,
UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,
Var(name) => push_frame(
- loc.as_ref(),
- || format!("variable <{}>", name),
+ loc,
+ || format!("variable <{}> access", name),
|| context.binding(name.clone())?.evaluate(),
)?,
Index(value, index) => {
@@ -543,7 +529,7 @@
(Val::Obj(v), Val::Str(s)) => {
let sn = s.clone();
push_frame(
- loc.as_ref(),
+ loc,
|| format!("field <{}> access", sn),
|| {
if let Some(v) = v.get(s.clone())? {
@@ -591,10 +577,8 @@
}
}
LocalExpr(bindings, returned) => {
- let mut new_bindings: FxHashMap<IStr, LazyVal> = HashMap::with_capacity_and_hasher(
- bindings.len(),
- BuildHasherDefault::<FxHasher>::default(),
- );
+ let mut new_bindings: GcHashMap<IStr, LazyVal> =
+ GcHashMap::with_capacity(bindings.len());
let future_context = Context::new_future();
for b in bindings {
new_bindings.insert(
@@ -612,7 +596,6 @@
for item in items {
// TODO: Implement ArrValue::Lazy with same context for every element?
#[derive(Trace)]
- #[trivially_drop]
struct ArrayElement {
context: Context,
item: LocExpr,
@@ -622,10 +605,10 @@
evaluate(self.context, &self.item)
}
}
- out.push(LazyVal::new(Box::new(ArrayElement {
+ out.push(LazyVal::new(TraceBox(Box::new(ArrayElement {
context: context.clone(),
item: item.clone(),
- })));
+ }))));
}
Val::Arr(out.into())
}
@@ -635,26 +618,24 @@
out.push(evaluate(ctx, expr)?);
Ok(())
})?;
- Val::Arr(ArrValue::Eager(Gc::new(out)))
+ Val::Arr(ArrValue::Eager(Cc::new(out)))
}
Obj(body) => Val::Obj(evaluate_object(context, body)?),
ObjExtend(s, t) => evaluate_add_op(
&evaluate(context.clone(), s)?,
&Val::Obj(evaluate_object(context, t)?),
)?,
- Apply(value, args, tailstrict) => {
- evaluate_apply(context, value, args, loc.as_ref(), *tailstrict)?
- }
+ Apply(value, args, tailstrict) => evaluate_apply(context, value, args, loc, *tailstrict)?,
Function(params, body) => {
evaluate_method(context, "anonymous".into(), params.clone(), body.clone())
}
- Intrinsic(name) => Val::Func(Gc::new(FuncVal::Intrinsic(name.clone()))),
+ Intrinsic(name) => Val::Func(Cc::new(FuncVal::Intrinsic(name.clone()))),
AssertExpr(assert, returned) => {
evaluate_assert(context.clone(), assert)?;
evaluate(context, returned)?
}
ErrorStmt(e) => push_frame(
- loc.as_ref(),
+ loc,
|| "error statement".to_owned(),
|| {
throw!(RuntimeError(
@@ -668,7 +649,7 @@
cond_else,
} => {
if push_frame(
- loc.as_ref(),
+ loc,
|| "if condition".to_owned(),
|| evaluate(context.clone(), &cond.0)?.try_cast_bool("in if condition"),
)? {
@@ -703,23 +684,17 @@
std_slice(indexable.into_indexable()?, start, end, step)?
}
Import(path) => {
- let tmp = loc
- .clone()
- .expect("imports cannot be used without loc_data")
- .0;
+ let tmp = loc.clone().0;
let mut import_location = tmp.to_path_buf();
import_location.pop();
push_frame(
- loc.as_ref(),
+ loc,
|| format!("import {:?}", path),
|| with_state(|s| s.import_file(&import_location, path)),
)?
}
ImportStr(path) => {
- let tmp = loc
- .clone()
- .expect("imports cannot be used without loc_data")
- .0;
+ let tmp = loc.clone().0;
let mut import_location = tmp.to_path_buf();
import_location.pop();
Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)
crates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function.rs
+++ b/crates/jrsonnet-evaluator/src/function.rs
@@ -1,18 +1,13 @@
-use crate::{
- error::Error::*, evaluate, evaluate_named, throw, Context, FutureWrapper, LazyVal,
- LazyValValue, Result, Val,
-};
-use jrsonnet_gc::Trace;
+use crate::{Context, FutureWrapper, GcHashMap, LazyVal, LazyValValue, Result, Val, error::Error::*, evaluate, evaluate_named, gc::TraceBox, throw};
+use gcmodule::Trace;
use jrsonnet_interner::IStr;
use jrsonnet_parser::{ArgsDesc, LocExpr, ParamsDesc};
-use rustc_hash::FxHashMap;
-use std::{collections::HashMap, hash::BuildHasherDefault};
+use std::collections::HashMap;
const NO_DEFAULT_CONTEXT: &str =
"no default context set for call with defined default parameter value";
#[derive(Trace)]
-#[trivially_drop]
struct EvaluateLazyVal {
context: Context,
expr: LocExpr,
@@ -38,8 +33,7 @@
args: &ArgsDesc,
tailstrict: bool,
) -> Result<Context> {
- let mut passed_args =
- HashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());
+ let mut passed_args = GcHashMap::with_capacity(params.len());
if args.unnamed.len() > params.len() {
throw!(TooManyArgsFunctionHas(params.len()))
}
@@ -53,10 +47,10 @@
if tailstrict {
LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)
} else {
- LazyVal::new(Box::new(EvaluateLazyVal {
+ LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {
context: ctx.clone(),
expr: arg.clone(),
- }))
+ })))
},
);
filled_args += 1;
@@ -73,10 +67,10 @@
if tailstrict {
LazyVal::new_resolved(evaluate(ctx.clone(), value)?)
} else {
- LazyVal::new(Box::new(EvaluateLazyVal {
+ LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {
context: ctx.clone(),
expr: value.clone(),
- }))
+ })))
},
)
.is_some()
@@ -90,17 +84,13 @@
// Some args are unset, but maybe we have defaults for them
// Default values should be created in newly created context
let future_context = FutureWrapper::<Context>::new();
- let mut defaults = HashMap::with_capacity_and_hasher(
- params.len() - filled_args,
- BuildHasherDefault::default(),
- );
+ let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);
for param in params.iter().filter(|p| p.1.is_some()) {
if passed_args.contains_key(¶m.0.clone()) {
continue;
}
#[derive(Trace)]
- #[trivially_drop]
struct LazyNamedBinding {
future_context: FutureWrapper<Context>,
name: IStr,
@@ -111,19 +101,19 @@
evaluate_named(self.future_context.unwrap(), &self.value, self.name)
}
}
- LazyVal::new(Box::new(LazyNamedBinding {
+ LazyVal::new(TraceBox(Box::new(LazyNamedBinding {
future_context: future_context.clone(),
name: param.0.clone(),
value: param.1.clone().unwrap(),
- }));
+ })));
defaults.insert(
param.0.clone(),
- LazyVal::new(Box::new(LazyNamedBinding {
+ LazyVal::new(TraceBox(Box::new(LazyNamedBinding {
future_context: future_context.clone(),
name: param.0.clone(),
value: param.1.clone().unwrap(),
- })),
+ }))),
);
filled_args += 1;
}
@@ -155,7 +145,7 @@
args: &HashMap<IStr, Val>,
tailstrict: bool,
) -> Result<Context> {
- let mut out = FxHashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());
+ let mut out = GcHashMap::with_capacity(params.len());
let mut positioned_args = vec![None; params.0.len()];
for (name, val) in args.iter() {
let idx = params
@@ -185,7 +175,6 @@
let body_ctx = body_ctx.clone();
let default = default.clone();
#[derive(Trace)]
- #[trivially_drop]
struct EvaluateLazyVal {
body_ctx: Option<Context>,
default: LocExpr,
@@ -198,7 +187,10 @@
)
}
}
- LazyVal::new(Box::new(EvaluateLazyVal { body_ctx, default }))
+ LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {
+ body_ctx,
+ default,
+ })))
}
} else {
throw!(FunctionParameterNotBoundInCall(p.0.clone()));
@@ -215,7 +207,7 @@
params: &ParamsDesc,
args: &[Val],
) -> Result<Context> {
- let mut out = FxHashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());
+ let mut out = GcHashMap::with_capacity(params.len());
let mut positioned_args = vec![None; params.0.len()];
for (id, arg) in args.iter().enumerate() {
if id >= params.len() {
@@ -243,7 +235,7 @@
($ctx: expr, $fn_name: expr, $args: expr, $total_args: expr, [
$($id: expr, $name: ident: $ty: expr $(=>$match: path)?);+ $(;)?
], $handler:block) => {{
- use $crate::{error::Error::*, throw, evaluate, push_frame, typed::CheckType};
+ use $crate::{error::Error::*, throw, evaluate, push_description_frame, typed::CheckType};
let args = $args;
if args.unnamed.len() + args.named.len() > $total_args {
@@ -263,7 +255,7 @@
} else {
&$args.unnamed[$id]
};
- let $name = push_frame(None, || format!("evaluating argument"), || {
+ let $name = push_description_frame(|| format!("evaluating builtin argument {}", stringify!($name)), || {
let value = evaluate($ctx.clone(), &$name)?;
$ty.check(&value)?;
Ok(value)
crates/jrsonnet-evaluator/src/gc.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-evaluator/src/gc.rs
@@ -0,0 +1,141 @@
+/// Macros to help deal with Gc
+use std::{
+ borrow::{Borrow, BorrowMut},
+ hash::BuildHasherDefault,
+ ops::{Deref, DerefMut},
+};
+
+use gcmodule::{Trace, Tracer};
+use rustc_hash::{FxHashMap, FxHashSet};
+
+/// Replacement for box, which assumes that the underlying type is [`Trace`]
+#[derive(Debug, Clone)]
+pub struct TraceBox<T: ?Sized>(pub Box<T>);
+
+impl<T: ?Sized + Trace> Trace for TraceBox<T> {
+ fn trace(&self, tracer: &mut Tracer) {
+ self.0.trace(tracer)
+ }
+
+ fn is_type_tracked() -> bool {
+ return true;
+ }
+}
+
+// TODO: Replace with CoerceUnsized
+impl<T: ?Sized> From<Box<T>> for TraceBox<T> {
+ fn from(inner: Box<T>) -> Self {
+ Self(inner)
+ }
+}
+
+impl<T: ?Sized> Deref for TraceBox<T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+impl<T: Trace + ?Sized> DerefMut for TraceBox<T> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
+impl<T: ?Sized> Borrow<T> for TraceBox<T> {
+ fn borrow(&self) -> &T {
+ &*self.0
+ }
+}
+
+impl<T: ?Sized> BorrowMut<T> for TraceBox<T> {
+ fn borrow_mut(&mut self) -> &mut T {
+ &mut *self.0
+ }
+}
+
+impl<T: ?Sized> AsRef<T> for TraceBox<T> {
+ fn as_ref(&self) -> &T {
+ &*self.0
+ }
+}
+
+impl<T: ?Sized> AsMut<T> for TraceBox<T> {
+ fn as_mut(&mut self) -> &mut T {
+ &mut *self.0
+ }
+}
+
+#[derive(Clone)]
+pub struct GcHashSet<V>(pub FxHashSet<V>);
+impl<V> GcHashSet<V> {
+ pub fn new() -> Self {
+ Self(Default::default())
+ }
+ pub fn with_capacity(capacity: usize) -> Self {
+ Self(FxHashSet::with_capacity_and_hasher(
+ capacity,
+ BuildHasherDefault::default(),
+ ))
+ }
+}
+impl<V> Trace for GcHashSet<V>
+where
+ V: Trace,
+{
+ fn trace(&self, tracer: &mut gcmodule::Tracer) {
+ for v in &self.0 {
+ v.trace(tracer);
+ }
+ }
+}
+impl<V> Deref for GcHashSet<V> {
+ type Target = FxHashSet<V>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+impl<V> DerefMut for GcHashSet<V> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
+#[derive(Clone)]
+pub struct GcHashMap<K, V>(pub FxHashMap<K, V>);
+impl<K, V> GcHashMap<K, V> {
+ pub fn new() -> Self {
+ Self(Default::default())
+ }
+ pub fn with_capacity(capacity: usize) -> Self {
+ Self(FxHashMap::with_capacity_and_hasher(
+ capacity,
+ BuildHasherDefault::default(),
+ ))
+ }
+}
+impl<K, V> Trace for GcHashMap<K, V>
+where
+ K: Trace,
+ V: Trace,
+{
+ fn trace(&self, tracer: &mut gcmodule::Tracer) {
+ for (k, v) in &self.0 {
+ k.trace(tracer);
+ v.trace(tracer);
+ }
+ }
+}
+impl<K, V> Deref for GcHashMap<K, V> {
+ type Target = FxHashMap<K, V>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+impl<K, V> DerefMut for GcHashMap<K, V> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -25,30 +25,31 @@
use error::{Error::*, LocError, Result, StackTraceElement};
pub use evaluate::*;
pub use function::parse_function_call;
+use gc::{GcHashMap, TraceBox};
+use gcmodule::{Cc, Trace};
pub use import::*;
-use jrsonnet_gc::{Finalize, Gc, Trace};
pub use jrsonnet_interner::IStr;
use jrsonnet_parser::*;
use native::NativeCallback;
pub use obj::*;
-use rustc_hash::FxHashMap;
use std::{
cell::{Ref, RefCell, RefMut},
collections::HashMap,
fmt::Debug,
- hash::BuildHasherDefault,
path::{Path, PathBuf},
rc::Rc,
};
use trace::{location_to_offset, offset_to_location, CodeLocation, CompactFormat, TraceFormat};
pub use val::*;
+pub mod gc;
-pub trait Bindable: Trace {
+pub trait Bindable: Trace + 'static {
fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal>;
}
-#[derive(Trace, Finalize, Clone)]
+
+#[derive(Clone, Trace)]
pub enum LazyBinding {
- Bindable(Gc<Box<dyn Bindable>>),
+ Bindable(Cc<TraceBox<dyn Bindable>>),
Bound(LazyVal),
}
@@ -74,7 +75,7 @@
/// Used for s`td.extVar`
pub ext_vars: HashMap<IStr, Val>,
/// Used for ext.native
- pub ext_natives: HashMap<IStr, Gc<NativeCallback>>,
+ pub ext_natives: HashMap<IStr, Cc<NativeCallback>>,
/// TLA vars
pub tla_vars: HashMap<IStr, Val>,
/// Global variables are inserted in default context
@@ -172,19 +173,27 @@
EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap()))
}
pub(crate) fn push_frame<T>(
- e: Option<&ExprLocation>,
+ e: &ExprLocation,
frame_desc: impl FnOnce() -> String,
f: impl FnOnce() -> Result<T>,
) -> Result<T> {
with_state(|s| s.push(e, frame_desc, f))
}
+#[allow(dead_code)]
pub(crate) fn push_val_frame(
- e: Option<&ExprLocation>,
+ e: &ExprLocation,
frame_desc: impl FnOnce() -> String,
f: impl FnOnce() -> Result<Val>,
) -> Result<Val> {
- with_state(|s| s.push(e, frame_desc, f))
+ with_state(|s| s.push_val(e, frame_desc, f))
+}
+#[allow(dead_code)]
+pub(crate) fn push_description_frame<T>(
+ frame_desc: impl FnOnce() -> String,
+ f: impl FnOnce() -> Result<T>,
+) -> Result<T> {
+ with_state(|s| s.push_description(frame_desc, f))
}
/// Maintains stack trace and import resolution
@@ -201,7 +210,6 @@
&source_code,
&ParserSettings {
file_name: path.clone(),
- loc_data: true,
},
)
.map_err(|error| ImportSyntaxError {
@@ -246,7 +254,7 @@
ro_map.get(name).map(|value| value.source_code.clone())
}
pub fn map_source_locations(&self, file: &Path, locs: &[usize]) -> Vec<CodeLocation> {
- offset_to_location(&self.get_source(file).unwrap(), locs)
+ offset_to_location(&self.get_source(file).unwrap_or("".into()), locs)
}
pub fn map_from_source_location(
&self,
@@ -322,8 +330,7 @@
/// Creates context with all passed global variables
pub fn create_default_context(&self) -> Context {
let globals = &self.settings().globals;
- let mut new_bindings: FxHashMap<IStr, LazyVal> =
- FxHashMap::with_capacity_and_hasher(globals.len(), BuildHasherDefault::default());
+ let mut new_bindings = GcHashMap::with_capacity(globals.len());
for (name, value) in globals.iter() {
new_bindings.insert(name.clone(), LazyVal::new_resolved(value.clone()));
}
@@ -333,7 +340,7 @@
/// Executes code creating a new stack frame
pub fn push<T>(
&self,
- e: Option<&ExprLocation>,
+ e: &ExprLocation,
frame_desc: impl FnOnce() -> String,
f: impl FnOnce() -> Result<T>,
) -> Result<T> {
@@ -353,25 +360,21 @@
let mut data = self.data_mut();
data.stack_depth -= 1;
data.stack_generation += 1;
- // if let Some(e) = e {
- // result =
- // data.breakpoints
- // .insert(data.stack_depth, data.stack_generation, &e, result)
- // }
}
if let Err(mut err) = result {
err.trace_mut().0.push(StackTraceElement {
- location: e.cloned(),
+ location: Some(e.clone()),
desc: frame_desc(),
});
return Err(err);
}
result
}
+
/// Executes code creating a new stack frame
pub fn push_val(
&self,
- e: Option<&ExprLocation>,
+ e: &ExprLocation,
frame_desc: impl FnOnce() -> String,
f: impl FnOnce() -> Result<Val>,
) -> Result<Val> {
@@ -391,15 +394,45 @@
let mut data = self.data_mut();
data.stack_depth -= 1;
data.stack_generation += 1;
- if let Some(e) = e {
- result =
- data.breakpoints
- .insert(data.stack_depth, data.stack_generation, &e, result)
+ result = data
+ .breakpoints
+ .insert(data.stack_depth, data.stack_generation, &e, result);
+ }
+ if let Err(mut err) = result {
+ err.trace_mut().0.push(StackTraceElement {
+ location: Some(e.clone()),
+ desc: frame_desc(),
+ });
+ return Err(err);
+ }
+ result
+ }
+ /// Executes code creating a new stack frame
+ pub fn push_description<T>(
+ &self,
+ frame_desc: impl FnOnce() -> String,
+ f: impl FnOnce() -> Result<T>,
+ ) -> Result<T> {
+ {
+ let mut data = self.data_mut();
+ let stack_depth = &mut data.stack_depth;
+ if *stack_depth > self.max_stack() {
+ // Error creation uses data, so i drop guard here
+ drop(data);
+ throw!(StackOverflow);
+ } else {
+ *stack_depth += 1;
}
}
+ let result = f();
+ {
+ let mut data = self.data_mut();
+ data.stack_depth -= 1;
+ data.stack_generation += 1;
+ }
if let Err(mut err) = result {
err.trace_mut().0.push(StackTraceElement {
- location: e.cloned(),
+ location: None,
desc: frame_desc(),
});
return Err(err);
@@ -451,7 +484,12 @@
}
pub fn manifest(&self, val: Val) -> Result<IStr> {
- self.run_in_state(|| val.manifest(&self.manifest_format()))
+ self.run_in_state(|| {
+ push_description_frame(
+ || format!("manifestification"),
+ || val.manifest(&self.manifest_format()),
+ )
+ })
}
pub fn manifest_multi(&self, val: Val) -> Result<Vec<(IStr, IStr)>> {
self.run_in_state(|| val.manifest_multi(&self.manifest_format()))
@@ -464,8 +502,7 @@
pub fn with_tla(&self, val: Val) -> Result<Val> {
self.run_in_state(|| {
Ok(match val {
- Val::Func(func) => push_frame(
- None,
+ Val::Func(func) => push_description_frame(
|| "during TLA call".to_owned(),
|| {
func.evaluate_map(
@@ -511,7 +548,6 @@
&code,
&ParserSettings {
file_name: source.clone(),
- loc_data: true,
},
)
.map_err(|e| ImportSyntaxError {
@@ -570,7 +606,7 @@
self.settings_mut().import_resolver = resolver;
}
- pub fn add_native(&self, name: IStr, cb: Gc<NativeCallback>) {
+ pub fn add_native(&self, name: IStr, cb: Cc<NativeCallback>) {
self.settings_mut().ext_natives.insert(name, cb);
}
@@ -603,13 +639,20 @@
}
}
+pub fn cc_ptr_eq<T>(a: &Cc<T>, b: &Cc<T>) -> bool {
+ let a = &a as &T;
+ let b = &b as &T;
+ std::ptr::eq(a, b)
+}
+
#[cfg(test)]
pub mod tests {
use super::Val;
use crate::{
- error::Error::*, native::NativeCallbackHandler, primitive_equals, EvaluationState,
+ error::Error::*, gc::TraceBox, native::NativeCallbackHandler, primitive_equals,
+ EvaluationState,
};
- use jrsonnet_gc::{Finalize, Gc, Trace};
+ use gcmodule::{Cc, Trace};
use jrsonnet_interner::IStr;
use jrsonnet_parser::*;
use std::{
@@ -624,11 +667,11 @@
state.run_in_state(|| {
state
.push(
- Some(&ExprLocation(PathBuf::from("test1.jsonnet").into(), 10, 20)),
+ &ExprLocation(PathBuf::from("test1.jsonnet").into(), 10, 20),
|| "outer".to_owned(),
|| {
state.push(
- Some(&ExprLocation(PathBuf::from("test2.jsonnet").into(), 30, 40)),
+ &ExprLocation(PathBuf::from("test2.jsonnet").into(), 30, 40),
|| "inner".to_owned(),
|| Err(RuntimeError("".into()).into()),
)?;
@@ -1028,7 +1071,6 @@
"{ x: 1, y: 2 } == { x: 1, y: 2 }",
&ParserSettings {
file_name: PathBuf::from("equality").into(),
- loc_data: true,
}
)
);
@@ -1042,14 +1084,11 @@
evaluator.with_stdlib();
- #[derive(Trace, Finalize)]
+ #[derive(Trace)]
struct NativeAdd;
impl NativeCallbackHandler for NativeAdd {
- fn call(&self, from: Option<Rc<Path>>, args: &[Val]) -> crate::error::Result<Val> {
- assert_eq!(
- &from.unwrap() as &Path,
- &PathBuf::from("native_caller.jsonnet")
- );
+ fn call(&self, from: Rc<Path>, args: &[Val]) -> crate::error::Result<Val> {
+ assert_eq!(&from as &Path, &PathBuf::from("native_caller.jsonnet"));
match (&args[0], &args[1]) {
(Val::Num(a), Val::Num(b)) => Ok(Val::Num(a + b)),
(_, _) => unreachable!(),
@@ -1058,12 +1097,12 @@
}
evaluator.settings_mut().ext_natives.insert(
"native_add".into(),
- Gc::new(NativeCallback::new(
+ Cc::new(NativeCallback::new(
ParamsDesc(Rc::new(vec![
Param("a".into(), None),
Param("b".into(), None),
])),
- Box::new(NativeAdd),
+ TraceBox(Box::new(NativeAdd)),
)),
);
evaluator.evaluate_snippet_raw(
crates/jrsonnet-evaluator/src/map.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/map.rs
+++ b/crates/jrsonnet-evaluator/src/map.rs
@@ -1,23 +1,21 @@
-use jrsonnet_gc::{Gc, Trace};
+use gcmodule::{Cc, Trace};
use jrsonnet_interner::IStr;
-use rustc_hash::FxHashMap;
-use crate::LazyVal;
+use crate::{GcHashMap, LazyVal};
#[derive(Trace)]
-#[trivially_drop]
+#[force_tracking]
pub struct LayeredHashMapInternals {
parent: Option<LayeredHashMap>,
- current: FxHashMap<IStr, LazyVal>,
+ current: GcHashMap<IStr, LazyVal>,
}
#[derive(Trace)]
-#[trivially_drop]
-pub struct LayeredHashMap(Gc<LayeredHashMapInternals>);
+pub struct LayeredHashMap(Cc<LayeredHashMapInternals>);
impl LayeredHashMap {
- pub fn extend(self, new_layer: FxHashMap<IStr, LazyVal>) -> Self {
- Self(Gc::new(LayeredHashMapInternals {
+ pub fn extend(self, new_layer: GcHashMap<IStr, LazyVal>) -> Self {
+ Self(Cc::new(LayeredHashMapInternals {
parent: Some(self),
current: new_layer,
}))
@@ -49,9 +47,9 @@
impl Default for LayeredHashMap {
fn default() -> Self {
- Self(Gc::new(LayeredHashMapInternals {
+ Self(Cc::new(LayeredHashMapInternals {
parent: None,
- current: FxHashMap::default(),
+ current: GcHashMap::new(),
}))
}
}
crates/jrsonnet-evaluator/src/native.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/native.rs
+++ b/crates/jrsonnet-evaluator/src/native.rs
@@ -1,27 +1,27 @@
#![allow(clippy::type_complexity)]
+use crate::gc::TraceBox;
use crate::{error::Result, Val};
-use jrsonnet_gc::Trace;
+use gcmodule::Trace;
use jrsonnet_parser::ParamsDesc;
use std::fmt::Debug;
use std::path::Path;
use std::rc::Rc;
pub trait NativeCallbackHandler: Trace {
- fn call(&self, from: Option<Rc<Path>>, args: &[Val]) -> Result<Val>;
+ fn call(&self, from: Rc<Path>, args: &[Val]) -> Result<Val>;
}
#[derive(Trace)]
-#[trivially_drop]
pub struct NativeCallback {
pub params: ParamsDesc,
- handler: Box<dyn NativeCallbackHandler>,
+ handler: TraceBox<dyn NativeCallbackHandler>,
}
impl NativeCallback {
- pub fn new(params: ParamsDesc, handler: Box<dyn NativeCallbackHandler>) -> Self {
+ pub fn new(params: ParamsDesc, handler: TraceBox<dyn NativeCallbackHandler>) -> Self {
Self { params, handler }
}
- pub fn call(&self, caller: Option<Rc<Path>>, args: &[Val]) -> Result<Val> {
+ pub fn call(&self, caller: Rc<Path>, args: &[Val]) -> Result<Val> {
self.handler.call(caller, args)
}
}
crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -1,15 +1,15 @@
+use crate::gc::{GcHashMap, GcHashSet, TraceBox};
use crate::operator::evaluate_add_op;
-use crate::{Bindable, LazyBinding, LazyVal, Result, Val};
-use jrsonnet_gc::{Gc, GcCell, Trace};
+use crate::{cc_ptr_eq, Bindable, LazyBinding, LazyVal, Result, Val};
+use gcmodule::{Cc, Trace};
use jrsonnet_interner::IStr;
use jrsonnet_parser::{ExprLocation, Visibility};
-use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
-use std::collections::HashMap;
+use rustc_hash::FxHashMap;
+use std::cell::RefCell;
+use std::fmt::Debug;
use std::hash::{Hash, Hasher};
-use std::{fmt::Debug, hash::BuildHasherDefault};
#[derive(Debug, Trace)]
-#[trivially_drop]
pub struct ObjMember {
pub add: bool,
pub visibility: Visibility,
@@ -24,19 +24,18 @@
// Field => This
type CacheKey = (IStr, ObjValue);
#[derive(Trace)]
-#[trivially_drop]
+#[force_tracking]
pub struct ObjValueInternals {
super_obj: Option<ObjValue>,
- assertions: Gc<Vec<Box<dyn ObjectAssertion>>>,
- assertions_ran: GcCell<FxHashSet<ObjValue>>,
+ assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,
+ assertions_ran: RefCell<GcHashSet<ObjValue>>,
this_obj: Option<ObjValue>,
- this_entries: Gc<FxHashMap<IStr, ObjMember>>,
- value_cache: GcCell<FxHashMap<CacheKey, Option<Val>>>,
+ this_entries: Cc<GcHashMap<IStr, ObjMember>>,
+ value_cache: RefCell<GcHashMap<CacheKey, Option<Val>>>,
}
#[derive(Clone, Trace)]
-#[trivially_drop]
-pub struct ObjValue(pub(crate) Gc<ObjValueInternals>);
+pub struct ObjValue(pub(crate) Cc<ObjValueInternals>);
impl Debug for ObjValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(super_obj) = self.0.super_obj.as_ref() {
@@ -65,20 +64,20 @@
impl ObjValue {
pub fn new(
super_obj: Option<Self>,
- this_entries: Gc<FxHashMap<IStr, ObjMember>>,
- assertions: Gc<Vec<Box<dyn ObjectAssertion>>>,
+ this_entries: Cc<GcHashMap<IStr, ObjMember>>,
+ assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,
) -> Self {
- Self(Gc::new(ObjValueInternals {
+ Self(Cc::new(ObjValueInternals {
super_obj,
assertions,
- assertions_ran: GcCell::new(FxHashSet::default()),
+ assertions_ran: RefCell::new(GcHashSet::new()),
this_obj: None,
this_entries,
- value_cache: GcCell::new(FxHashMap::default()),
+ value_cache: RefCell::new(GcHashMap::new()),
}))
}
pub fn new_empty() -> Self {
- Self::new(None, Gc::new(FxHashMap::default()), Gc::new(Vec::new()))
+ Self::new(None, Cc::new(GcHashMap::new()), Cc::new(Vec::new()))
}
pub fn extend_from(&self, super_obj: Self) -> Self {
match &self.0.super_obj {
@@ -95,13 +94,13 @@
}
}
pub fn with_this(&self, this_obj: Self) -> Self {
- Self(Gc::new(ObjValueInternals {
+ Self(Cc::new(ObjValueInternals {
super_obj: self.0.super_obj.clone(),
assertions: self.0.assertions.clone(),
- assertions_ran: GcCell::new(FxHashSet::default()),
+ assertions_ran: RefCell::new(GcHashSet::new()),
this_obj: Some(this_obj),
this_entries: self.0.this_entries.clone(),
- value_cache: GcCell::new(FxHashMap::default()),
+ value_cache: RefCell::new(GcHashMap::new()),
}))
}
@@ -117,14 +116,14 @@
}
/// Run callback for every field found in object
- pub(crate) fn enum_fields(&self, handler: &mut impl FnMut(&IStr, &Visibility) -> bool) -> bool {
+ pub(crate) fn enum_fields(&self, handler: &mut impl FnMut(&IStr, &ObjMember) -> bool) -> bool {
if let Some(s) = &self.0.super_obj {
if s.enum_fields(handler) {
return true;
}
}
for (name, member) in self.0.this_entries.iter() {
- if handler(name, &member.visibility) {
+ if handler(name, &member) {
return true;
}
}
@@ -133,8 +132,8 @@
pub fn fields_visibility(&self) -> FxHashMap<IStr, bool> {
let mut out = FxHashMap::default();
- self.enum_fields(&mut |name, visibility| {
- match visibility {
+ self.enum_fields(&mut |name, member| {
+ match member.visibility {
Visibility::Normal => {
let entry = out.entry(name.to_owned());
entry.or_insert(true);
@@ -211,9 +210,9 @@
}
pub fn extend_with_field(self, key: IStr, value: ObjMember) -> Self {
- let mut new = FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default());
+ let mut new = GcHashMap::with_capacity(1);
new.insert(key, value);
- Self::new(Some(self), Gc::new(new), Gc::new(Vec::new()))
+ Self::new(Some(self), Cc::new(new), Cc::new(Vec::new()))
}
fn get_raw(&self, key: IStr, real_this: Option<&Self>) -> Result<Option<Val>> {
@@ -270,13 +269,13 @@
}
pub fn ptr_eq(a: &Self, b: &Self) -> bool {
- Gc::ptr_eq(&a.0, &b.0)
+ cc_ptr_eq(&a.0, &b.0)
}
}
impl PartialEq for ObjValue {
fn eq(&self, other: &Self) -> bool {
- Gc::ptr_eq(&self.0, &other.0)
+ cc_ptr_eq(&self.0, &other.0)
}
}
@@ -289,8 +288,8 @@
pub struct ObjValueBuilder {
super_obj: Option<ObjValue>,
- map: FxHashMap<IStr, ObjMember>,
- assertions: Vec<Box<dyn ObjectAssertion>>,
+ map: GcHashMap<IStr, ObjMember>,
+ assertions: Vec<TraceBox<dyn ObjectAssertion>>,
}
impl ObjValueBuilder {
pub fn new() -> Self {
@@ -299,10 +298,7 @@
pub fn with_capacity(capacity: usize) -> Self {
Self {
super_obj: None,
- map: HashMap::with_capacity_and_hasher(
- capacity,
- BuildHasherDefault::<FxHasher>::default(),
- ),
+ map: GcHashMap::with_capacity(capacity),
assertions: Vec::new(),
}
}
@@ -315,7 +311,7 @@
self
}
- pub fn assert(&mut self, assertion: Box<dyn ObjectAssertion>) -> &mut Self {
+ pub fn assert(&mut self, assertion: TraceBox<dyn ObjectAssertion>) -> &mut Self {
self.assertions.push(assertion);
self
}
@@ -330,7 +326,7 @@
}
pub fn build(self) -> ObjValue {
- ObjValue::new(self.super_obj, Gc::new(self.map), Gc::new(self.assertions))
+ ObjValue::new(self.super_obj, Cc::new(self.map), Cc::new(self.assertions))
}
}
impl Default for ObjValueBuilder {
@@ -364,15 +360,15 @@
pub fn hide(self) -> Self {
self.with_visibility(Visibility::Hidden)
}
- pub fn with_location(mut self, location: Option<ExprLocation>) -> Self {
- self.location = location;
+ pub fn with_location(mut self, location: ExprLocation) -> Self {
+ self.location = Some(location);
self
}
pub fn value(self, value: Val) -> &'v mut ObjValueBuilder {
self.binding(LazyBinding::Bound(LazyVal::new_resolved(value)))
}
- pub fn bindable(self, bindable: Box<dyn Bindable>) -> &'v mut ObjValueBuilder {
- self.binding(LazyBinding::Bindable(Gc::new(bindable)))
+ pub fn bindable(self, bindable: TraceBox<dyn Bindable>) -> &'v mut ObjValueBuilder {
+ self.binding(LazyBinding::Bindable(Cc::new(bindable)))
}
pub fn binding(self, binding: LazyBinding) -> &'v mut ObjValueBuilder {
self.value.map.insert(
crates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/trace/mod.rs
+++ b/crates/jrsonnet-evaluator/src/trace/mod.rs
@@ -119,16 +119,21 @@
.trace()
.0
.iter()
- .map(|el| {
- el.location.as_ref().map(|l| {
- use std::fmt::Write;
- let mut resolved_path = self.resolver.resolve(&l.0);
+ .map(|el| &el.location)
+ .map(|location| {
+ use std::fmt::Write;
+ if let Some(location) = location {
+ let mut resolved_path = self.resolver.resolve(&location.0);
// TODO: Process all trace elements first
- let location = evaluation_state.map_source_locations(&l.0, &[l.1, l.2]);
+ let location = evaluation_state
+ .map_source_locations(&location.0, &[location.1, location.2]);
write!(resolved_path, ":").unwrap();
print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();
- resolved_path
- })
+ write!(resolved_path, ":").unwrap();
+ Some(resolved_path)
+ } else {
+ None
+ }
})
.collect::<Vec<_>>();
let align = file_names
@@ -139,15 +144,19 @@
.unwrap_or(0);
for (el, file) in error.trace().0.iter().zip(file_names) {
writeln!(out)?;
- write!(
- out,
- "{:<p$}{:<w$}: {}",
- "",
- file.unwrap_or_else(|| "".to_owned()),
- el.desc,
- p = self.padding,
- w = align
- )?;
+ if let Some(file) = file {
+ write!(
+ out,
+ "{:<p$}{:<w$} {}",
+ "",
+ file,
+ el.desc,
+ p = self.padding,
+ w = align
+ )?;
+ } else {
+ write!(out, "{:<p$}{}", "", el.desc, p = self.padding,)?;
+ }
}
Ok(())
}
@@ -178,7 +187,7 @@
start_end[0].column,
)?;
} else {
- write!(out, " at {}", desc,)?;
+ write!(out, " during {}", desc)?;
}
}
Ok(())
@@ -206,25 +215,21 @@
} = error.error()
{
writeln!(out)?;
- let mut offset = error.location.offset;
- if offset >= source_code.len() {
- offset = source_code.len() - 1;
- }
- let mut location = offset_to_location(source_code, &[offset])
+ let offset = error.location.offset;
+ let location = offset_to_location(source_code, &[offset])
.into_iter()
.next()
.unwrap();
- if location.column >= 1 {
- location.column -= 1;
- }
+ let mut end_location = location.clone();
+ end_location.offset += 1;
self.print_snippet(
out,
source_code,
path,
&location,
- &location,
- "^ syntax error",
+ &end_location,
+ "syntax error",
)?;
}
let trace = &error.trace();
@@ -289,7 +294,7 @@
annotation_type: AnnotationType::Error,
range: (
start.offset - start.line_start_offset,
- end.offset - start.line_start_offset,
+ (end.offset - start.line_start_offset).min(source_fragment.len()),
),
}],
}],
crates/jrsonnet-evaluator/src/typed.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed.rs
+++ b/crates/jrsonnet-evaluator/src/typed.rs
@@ -1,11 +1,7 @@
use std::{fmt::Display, rc::Rc};
-use crate::{
- error::{Error, LocError, Result},
- push_frame, Val,
-};
-use jrsonnet_gc::Trace;
-use jrsonnet_parser::ExprLocation;
+use crate::{Val, error::{Error, LocError, Result}, push_description_frame};
+use gcmodule::Trace;
use jrsonnet_types::{ComplexValType, ValType};
use thiserror::Error;
@@ -22,12 +18,11 @@
}
#[derive(Debug, Error, Clone, Trace)]
-#[trivially_drop]
pub enum TypeError {
#[error("expected {0}, got {1}")]
ExpectedGot(ComplexValType, ValType),
#[error("missing property {0} from {1:?}")]
- MissingProperty(Rc<str>, ComplexValType),
+ MissingProperty(#[skip_trace] Rc<str>, ComplexValType),
#[error("every failed from {0}:\n{1}")]
UnionFailed(ComplexValType, TypeLocErrorList),
#[error(
@@ -44,7 +39,6 @@
}
#[derive(Debug, Clone, Trace)]
-#[trivially_drop]
pub struct TypeLocError(Box<TypeError>, ValuePathStack);
impl From<TypeError> for TypeLocError {
fn from(e: TypeError) -> Self {
@@ -67,7 +61,6 @@
}
#[derive(Debug, Clone, Trace)]
-#[trivially_drop]
pub struct TypeLocErrorList(Vec<TypeLocError>);
impl Display for TypeLocErrorList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -97,13 +90,12 @@
}
}
-fn push_type(
- location: Option<&ExprLocation>,
+fn push_type_description(
error_reason: impl Fn() -> String,
path: impl Fn() -> ValuePathItem,
item: impl Fn() -> Result<()>,
) -> Result<()> {
- push_frame(location, error_reason, || match item() {
+ push_description_frame(error_reason, || match item() {
Ok(_) => Ok(()),
Err(mut e) => {
if let Error::TypeError(e) = &mut e.error_mut() {
@@ -131,9 +123,8 @@
}
#[derive(Clone, Debug, Trace)]
-#[trivially_drop]
enum ValuePathItem {
- Field(Rc<str>),
+ Field(#[skip_trace] Rc<str>),
Index(u64),
}
impl Display for ValuePathItem {
@@ -147,7 +138,6 @@
}
#[derive(Clone, Debug, Trace)]
-#[trivially_drop]
struct ValuePathStack(Vec<ValuePathItem>);
impl Display for ValuePathStack {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -183,8 +173,7 @@
Self::Array(elem_type) => match value {
Val::Arr(a) => {
for (i, item) in a.iter().enumerate() {
- push_type(
- None,
+ push_type_description(
|| format!("array index {}", i),
|| ValuePathItem::Index(i as u64),
|| elem_type.check(&item.clone()?),
@@ -197,8 +186,7 @@
Self::ArrayRef(elem_type) => match value {
Val::Arr(a) => {
for (i, item) in a.iter().enumerate() {
- push_type(
- None,
+ push_type_description(
|| format!("array index {}", i),
|| ValuePathItem::Index(i as u64),
|| elem_type.check(&item.clone()?),
@@ -212,8 +200,7 @@
Val::Obj(obj) => {
for (k, v) in elems.iter() {
if let Some(got_v) = obj.get((*k).into())? {
- push_type(
- None,
+ push_type_description(
|| format!("property {}", k),
|| ValuePathItem::Field((*k).into()),
|| v.check(&got_v),
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -6,40 +6,40 @@
ManifestYamlOptions,
},
},
+ cc_ptr_eq,
error::{Error::*, LocError},
evaluate,
function::{parse_function_call, parse_function_call_map, place_args},
+ gc::TraceBox,
native::NativeCallback,
throw, Context, ObjValue, Result,
};
-use jrsonnet_gc::{Gc, GcCell, Trace};
+use gcmodule::{Cc, Trace};
use jrsonnet_interner::IStr;
use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};
use jrsonnet_types::ValType;
-use std::{collections::HashMap, fmt::Debug, rc::Rc};
+use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
pub trait LazyValValue: Trace {
fn get(self: Box<Self>) -> Result<Val>;
}
#[derive(Trace)]
-#[trivially_drop]
enum LazyValInternals {
Computed(Val),
Errored(LocError),
- Waiting(Box<dyn LazyValValue>),
+ Waiting(TraceBox<dyn LazyValValue>),
Pending,
}
#[derive(Clone, Trace)]
-#[trivially_drop]
-pub struct LazyVal(Gc<GcCell<LazyValInternals>>);
+pub struct LazyVal(Cc<RefCell<LazyValInternals>>);
impl LazyVal {
- pub fn new(f: Box<dyn LazyValValue>) -> Self {
- Self(Gc::new(GcCell::new(LazyValInternals::Waiting(f))))
+ pub fn new(f: TraceBox<dyn LazyValValue>) -> Self {
+ Self(Cc::new(RefCell::new(LazyValInternals::Waiting(f))))
}
pub fn new_resolved(val: Val) -> Self {
- Self(Gc::new(GcCell::new(LazyValInternals::Computed(val))))
+ Self(Cc::new(RefCell::new(LazyValInternals::Computed(val))))
}
pub fn evaluate(&self) -> Result<Val> {
match &*self.0.borrow() {
@@ -55,7 +55,7 @@
} else {
unreachable!()
};
- let new_value = match value.get() {
+ let new_value = match value.0.get() {
Ok(v) => v,
Err(e) => {
*self.0.borrow_mut() = LazyValInternals::Errored(e.clone());
@@ -74,12 +74,11 @@
}
impl PartialEq for LazyVal {
fn eq(&self, other: &Self) -> bool {
- Gc::ptr_eq(&self.0, &other.0)
+ cc_ptr_eq(&self.0, &other.0)
}
}
#[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
pub struct FuncDesc {
pub name: IStr,
pub ctx: Context,
@@ -88,14 +87,13 @@
}
#[derive(Debug, Trace)]
-#[trivially_drop]
pub enum FuncVal {
/// Plain function implemented in jsonnet
Normal(FuncDesc),
/// Standard library function
Intrinsic(IStr),
/// Library functions implemented in native
- NativeExt(IStr, Gc<NativeCallback>),
+ NativeExt(IStr, Cc<NativeCallback>),
}
impl PartialEq for FuncVal {
@@ -122,7 +120,7 @@
pub fn evaluate(
&self,
call_ctx: Context,
- loc: Option<&ExprLocation>,
+ loc: &ExprLocation,
args: &ArgsDesc,
tailstrict: bool,
) -> Result<Val> {
@@ -145,7 +143,7 @@
for p in handler.params.0.iter() {
out_args.push(args.binding(p.0.clone())?.evaluate()?);
}
- Ok(handler.call(loc.map(|l| l.0.clone()), &out_args)?)
+ Ok(handler.call(loc.0.clone(), &out_args)?)
}
}
}
@@ -194,15 +192,15 @@
}
#[derive(Debug, Clone, Trace)]
-#[trivially_drop]
+#[force_tracking]
pub enum ArrValue {
- Lazy(Gc<Vec<LazyVal>>),
- Eager(Gc<Vec<Val>>),
+ Lazy(Cc<Vec<LazyVal>>),
+ Eager(Cc<Vec<Val>>),
Extended(Box<(Self, Self)>),
}
impl ArrValue {
pub fn new_eager() -> Self {
- Self::Eager(Gc::new(Vec::new()))
+ Self::Eager(Cc::new(Vec::new()))
}
pub fn len(&self) -> usize {
@@ -253,14 +251,14 @@
}
}
- pub fn evaluated(&self) -> Result<Gc<Vec<Val>>> {
+ pub fn evaluated(&self) -> Result<Cc<Vec<Val>>> {
Ok(match self {
Self::Lazy(vec) => {
let mut out = Vec::with_capacity(vec.len());
for item in vec.iter() {
out.push(item.evaluate()?);
}
- Gc::new(out)
+ Cc::new(out)
}
Self::Eager(vec) => vec.clone(),
Self::Extended(_v) => {
@@ -268,7 +266,7 @@
for item in self.iter() {
out.push(item?);
}
- Gc::new(out)
+ Cc::new(out)
}
})
}
@@ -294,12 +292,12 @@
Self::Lazy(vec) => {
let mut out = (&vec as &Vec<_>).clone();
out.reverse();
- Self::Lazy(Gc::new(out))
+ Self::Lazy(Cc::new(out))
}
Self::Eager(vec) => {
let mut out = (&vec as &Vec<_>).clone();
out.reverse();
- Self::Eager(Gc::new(out))
+ Self::Eager(Cc::new(out))
}
Self::Extended(b) => Self::Extended(Box::new((b.1.reversed(), b.0.reversed()))),
}
@@ -312,7 +310,7 @@
out.push(mapper(value?)?);
}
- Ok(Self::Eager(Gc::new(out)))
+ Ok(Self::Eager(Cc::new(out)))
}
pub fn filter(self, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {
@@ -325,13 +323,13 @@
}
}
- Ok(Self::Eager(Gc::new(out)))
+ Ok(Self::Eager(Cc::new(out)))
}
pub fn ptr_eq(a: &Self, b: &Self) -> bool {
match (a, b) {
- (Self::Lazy(a), Self::Lazy(b)) => Gc::ptr_eq(a, b),
- (Self::Eager(a), Self::Eager(b)) => Gc::ptr_eq(a, b),
+ (Self::Lazy(a), Self::Lazy(b)) => cc_ptr_eq(a, b),
+ (Self::Eager(a), Self::Eager(b)) => cc_ptr_eq(a, b),
_ => false,
}
}
@@ -339,13 +337,13 @@
impl From<Vec<LazyVal>> for ArrValue {
fn from(v: Vec<LazyVal>) -> Self {
- Self::Lazy(Gc::new(v))
+ Self::Lazy(Cc::new(v))
}
}
impl From<Vec<Val>> for ArrValue {
fn from(v: Vec<Val>) -> Self {
- Self::Eager(Gc::new(v))
+ Self::Eager(Cc::new(v))
}
}
@@ -355,7 +353,6 @@
}
#[derive(Debug, Clone, Trace)]
-#[trivially_drop]
pub enum Val {
Bool(bool),
Null,
@@ -363,7 +360,7 @@
Num(f64),
Arr(ArrValue),
Obj(ObjValue),
- Func(Gc<FuncVal>),
+ Func(Cc<FuncVal>),
}
macro_rules! matches_unwrap {
@@ -402,7 +399,7 @@
pub fn unwrap_arr(self) -> Result<ArrValue> {
Ok(matches_unwrap!(self, Self::Arr(v), v))
}
- pub fn unwrap_func(self) -> Result<Gc<FuncVal>> {
+ pub fn unwrap_func(self) -> Result<Cc<FuncVal>> {
Ok(matches_unwrap!(self, Self::Func(v), v))
}
pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {
crates/jrsonnet-interner/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-interner/Cargo.toml
+++ b/crates/jrsonnet-interner/Cargo.toml
@@ -9,4 +9,4 @@
[dependencies]
serde = { version = "1.0" }
rustc-hash = "1.1.0"
-jrsonnet-gc = { version = "0.4.2", features = ["derive"] }
+gcmodule = { git = "https://github.com/CertainLach/gcmodule", branch = "jrsonnet" }
crates/jrsonnet-interner/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-interner/src/lib.rs
+++ b/crates/jrsonnet-interner/src/lib.rs
@@ -1,4 +1,4 @@
-use jrsonnet_gc::{unsafe_empty_trace, Finalize, Trace};
+use gcmodule::Trace;
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
use std::{
@@ -11,9 +11,10 @@
#[derive(Clone, PartialOrd, Ord, Eq)]
pub struct IStr(Rc<str>);
-impl Finalize for IStr {}
-unsafe impl Trace for IStr {
- unsafe_empty_trace!();
+impl Trace for IStr {
+ fn is_type_tracked() -> bool {
+ false
+ }
}
impl Deref for IStr {
crates/jrsonnet-parser/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-parser/Cargo.toml
+++ b/crates/jrsonnet-parser/Cargo.toml
@@ -15,10 +15,9 @@
jrsonnet-interner = { path = "../jrsonnet-interner", version = "0.4.2" }
peg = "0.7.0"
-unescape = "0.1.0"
serde = { version = "1.0", features = ["derive", "rc"], optional = true }
-jrsonnet-gc = { version = "0.4.2", features = ["derive"] }
+gcmodule = { git = "https://github.com/CertainLach/gcmodule", branch = "jrsonnet" }
[dev-dependencies]
jrsonnet-stdlib = { path = "../jrsonnet-stdlib", version = "0.4.2" }
crates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -1,4 +1,4 @@
-use jrsonnet_gc::{unsafe_empty_trace, Finalize, Trace};
+use gcmodule::Trace;
use jrsonnet_interner::IStr;
#[cfg(feature = "deserialize")]
use serde::Deserialize;
@@ -14,7 +14,6 @@
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
pub enum FieldName {
/// {fixed: 2}
Fixed(IStr),
@@ -25,7 +24,6 @@
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Trace)]
-#[trivially_drop]
pub enum Visibility {
/// :
Normal,
@@ -44,13 +42,11 @@
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Clone, Debug, PartialEq, Trace)]
-#[trivially_drop]
pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
pub struct FieldMember {
pub name: FieldName,
pub plus: bool,
@@ -62,7 +58,6 @@
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
pub enum Member {
Field(FieldMember),
BindStmt(BindSpec),
@@ -72,7 +67,6 @@
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Trace)]
-#[trivially_drop]
pub enum UnaryOpType {
Plus,
Minus,
@@ -99,7 +93,6 @@
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Trace)]
-#[trivially_drop]
pub enum BinaryOpType {
Mul,
Div,
@@ -167,21 +160,13 @@
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
pub struct Param(pub IStr, pub Option<LocExpr>);
/// Defined function parameters
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Trace)]
pub struct ParamsDesc(pub Rc<Vec<Param>>);
-
-/// Safety:
-/// AST is acyclic, and there should be no gc pointers
-unsafe impl Trace for ParamsDesc {
- unsafe_empty_trace!();
-}
-impl Finalize for ParamsDesc {}
impl Deref for ParamsDesc {
type Target = Vec<Param>;
@@ -193,7 +178,6 @@
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
pub struct ArgsDesc {
pub unnamed: Vec<LocExpr>,
pub named: Vec<(IStr, LocExpr)>,
@@ -207,7 +191,6 @@
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, Clone, PartialEq, Trace)]
-#[trivially_drop]
pub struct BindSpec {
pub name: IStr,
pub params: Option<ParamsDesc>,
@@ -217,19 +200,16 @@
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
pub struct IfSpecData(pub LocExpr);
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
pub struct ForSpecData(pub IStr, pub LocExpr);
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
pub enum CompSpec {
IfSpec(IfSpecData),
ForSpec(ForSpecData),
@@ -238,7 +218,6 @@
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
pub struct ObjComp {
pub pre_locals: Vec<BindSpec>,
pub key: LocExpr,
@@ -251,7 +230,6 @@
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
pub enum ObjBody {
MemberList(Vec<Member>),
ObjComp(ObjComp),
@@ -260,7 +238,6 @@
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq, Clone, Copy, Trace)]
-#[trivially_drop]
pub enum LiteralType {
This,
Super,
@@ -273,7 +250,6 @@
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
pub struct SliceDesc {
pub start: Option<LocExpr>,
pub end: Option<LocExpr>,
@@ -284,7 +260,6 @@
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
pub enum Expr {
Literal(LiteralType),
@@ -354,7 +329,7 @@
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Clone, PartialEq, Trace)]
-#[trivially_drop]
+#[skip_trace]
pub struct ExprLocation(pub Rc<Path>, pub usize, pub usize);
impl ExprLocation {
pub fn belongs_to(&self, other: &ExprLocation) -> bool {
@@ -371,14 +346,8 @@
/// Holds AST expression and its location in source file
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
-#[derive(Clone, PartialEq)]
-pub struct LocExpr(pub Rc<Expr>, pub Option<ExprLocation>);
-/// Safety:
-/// AST is acyclic, and there should be no gc pointers
-unsafe impl Trace for LocExpr {
- unsafe_empty_trace!();
-}
-impl Finalize for LocExpr {}
+#[derive(Clone, PartialEq, Trace)]
+pub struct LocExpr(pub Rc<Expr>, pub ExprLocation);
impl Debug for LocExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -387,24 +356,7 @@
} else {
write!(f, "{:?}", self.0)?;
}
- if let Some(loc) = &self.1 {
- write!(f, " from {:?}", loc)?;
- }
+ write!(f, " from {:?}", self.1)?;
Ok(())
}
-}
-
-/// Creates LocExpr from Expr and ExprLocation components
-#[macro_export]
-macro_rules! loc_expr {
- ($expr:expr, $need_loc:expr,($name:expr, $start:expr, $end:expr)) => {
- LocExpr(
- std::rc::Rc::new($expr),
- if $need_loc {
- Some(ExprLocation($name, $start, $end))
- } else {
- None
- },
- )
- };
}
crates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/lib.rs
+++ b/crates/jrsonnet-parser/src/lib.rs
@@ -9,9 +9,9 @@
pub use expr::*;
pub use jrsonnet_interner::IStr;
pub use peg;
+mod unescape;
pub struct ParserSettings {
- pub loc_data: bool,
pub file_name: Rc<Path>,
}
@@ -103,10 +103,20 @@
lines:("\n" {"\n"} / [' ' | '\t']*<{prefix.len()}> s:whole_line() {s})*
[' ' | '\t']*<, {prefix.len() - 1}> "|||"
{let mut l = empty_lines.to_owned(); l.push_str(first_line); l.extend(lines); l}
+
+ rule hex_char()
+ = quiet! { ['0'..='9' | 'a'..='f' | 'A'..='F'] } / expected!("<hex char>")
+
+ rule string_char(c: rule<()>)
+ = (!['\\']!c()[_])+
+ / "\\\\"
+ / "\\u" hex_char() hex_char() hex_char() hex_char()
+ / "\\x" hex_char() hex_char()
+ / ['\\'] (quiet! { ['b' | 'f' | 'n' | 'r' | 't'] / c() } / expected!("<escape character>"))
pub rule string() -> String
- = quiet!{ "\"" str:$(("\\\"" / "\\\\" / (!['"'][_]))*) "\"" {unescape::unescape(str).unwrap()}
- / "'" str:$(("\\'" / "\\\\" / (!['\''][_]))*) "'" {unescape::unescape(str).unwrap()}
- / "@'" str:$(("''" / (!['\''][_]))*) "'" {str.replace("''", "'")}
+ = ['"'] str:$(string_char(<['"']>)*) ['"'] {? unescape::unescape(str).ok_or("<escaped string>")}
+ / ['\''] str:$(string_char(<['\'']>)*) ['\''] {? unescape::unescape(str).ok_or("<escaped string>")}
+ / quiet!{ "@'" str:$(("''" / (!['\''][_]))*) "'" {str.replace("''", "'")}
/ "@\"" str:$(("\"\"" / (!['"'][_]))*) "\"" {str.replace("\"\"", "\"")}
/ string_block() } / expected!("<string>")
@@ -177,6 +187,8 @@
= n:number() { expr::Expr::Num(n) }
pub rule var_expr(s: &ParserSettings) -> Expr
= n:$(id()) { expr::Expr::Var(n.into()) }
+ pub rule id_loc(s: &ParserSettings) -> LocExpr
+ = a:position!() n:$(id()) b:position!() { LocExpr(Rc::new(expr::Expr::Str(n.into())), ExprLocation(s.file_name.clone(), a,b)) }
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,
@@ -240,7 +252,7 @@
use UnaryOpType::*;
rule expr(s: &ParserSettings) -> LocExpr
= precedence! {
- start:position!() v:@ end:position!() { loc_expr!(v, s.loc_data, (s.file_name.clone(), start, end)) }
+ start:position!() v:@ end:position!() { LocExpr(Rc::new(v), ExprLocation(s.file_name.clone(), start, end)) }
--
a:(@) _ binop(<"||">) _ b:@ {expr_bin!(a Or b)}
--
@@ -276,7 +288,7 @@
unaryop(<"~">) _ b:@ {expr_un!(BitNot b)}
--
a:(@) _ "[" _ e:slice_desc(s) _ "]" {Expr::Slice(a, e)}
- a:(@) _ "." _ e:$(id()) {Expr::Index(a, el!(Expr::Str(e.into())))}
+ a:(@) _ "." _ a:position!() e:id_loc(s) b:position!() {Expr::Index(a, e)}
a:(@) _ "[" _ e:expr(s) _ "]" {Expr::Index(a, e)}
a:(@) _ "(" _ args:args(s) _ ")" ts:(_ keyword("tailstrict"))? {Expr::Apply(a, args, ts.is_some())}
a:(@) _ "{" _ body:objinside(s) _ "}" {Expr::ObjExtend(a, body)}
@@ -292,13 +304,6 @@
pub type ParseError = peg::error::ParseError<peg::str::LineCol>;
pub fn parse(str: &str, settings: &ParserSettings) -> Result<LocExpr, ParseError> {
jsonnet_parser::jsonnet(str, settings)
-}
-
-#[macro_export]
-macro_rules! el {
- ($expr:expr) => {
- LocExpr(std::rc::Rc::new($expr), None)
- };
}
#[cfg(test)]
@@ -313,53 +318,39 @@
parse(
$s,
&ParserSettings {
- loc_data: false,
- file_name: PathBuf::from("/test.jsonnet").into(),
+ file_name: PathBuf::from("test.jsonnet").into(),
},
)
.unwrap()
};
}
- macro_rules! el_loc {
- ($expr:expr, $loc:expr$(,)?) => {
- LocExpr(std::rc::Rc::new($expr), Some($loc))
+ macro_rules! el {
+ ($expr:expr, $from:expr, $to:expr$(,)?) => {
+ LocExpr(
+ std::rc::Rc::new($expr),
+ ExprLocation(PathBuf::from("test.jsonnet").into(), $from, $to),
+ )
};
- }
-
- mod expressions {
- use super::*;
-
- pub fn basic_math() -> LocExpr {
- el!(Expr::BinaryOp(
- el!(Expr::Num(2.0)),
- Add,
- el!(Expr::BinaryOp(
- el!(Expr::Num(2.0)),
- Mul,
- el!(Expr::Num(2.0)),
- )),
- ))
- }
}
#[test]
fn multiline_string() {
assert_eq!(
parse!("|||\n Hello world!\n a\n|||"),
- el!(Expr::Str("Hello world!\n a\n".into())),
+ el!(Expr::Str("Hello world!\n a\n".into()), 0, 31),
);
assert_eq!(
parse!("|||\n Hello world!\n a\n|||"),
- el!(Expr::Str("Hello world!\n a\n".into())),
+ el!(Expr::Str("Hello world!\n a\n".into()), 0, 27),
);
assert_eq!(
parse!("|||\n\t\tHello world!\n\t\t\ta\n|||"),
- el!(Expr::Str("Hello world!\n\ta\n".into())),
+ el!(Expr::Str("Hello world!\n\ta\n".into()), 0, 27),
);
assert_eq!(
parse!("|||\n Hello world!\n a\n |||"),
- el!(Expr::Str("Hello world!\n a\n".into())),
+ el!(Expr::Str("Hello world!\n a\n".into()), 0, 30),
);
}
@@ -376,20 +367,20 @@
fn string_escaping() {
assert_eq!(
parse!(r#""Hello, \"world\"!""#),
- el!(Expr::Str(r#"Hello, "world"!"#.into())),
+ el!(Expr::Str(r#"Hello, "world"!"#.into()), 0, 19),
);
assert_eq!(
parse!(r#"'Hello \'world\'!'"#),
- el!(Expr::Str("Hello 'world'!".into())),
+ el!(Expr::Str("Hello 'world'!".into()), 0, 18),
);
- assert_eq!(parse!(r#"'\\\\'"#), el!(Expr::Str("\\\\".into())),);
+ assert_eq!(parse!(r#"'\\\\'"#), el!(Expr::Str("\\\\".into()), 0, 6));
}
#[test]
fn string_unescaping() {
assert_eq!(
parse!(r#""Hello\nWorld""#),
- el!(Expr::Str("Hello\nWorld".into())),
+ el!(Expr::Str("Hello\nWorld".into()), 0, 14),
);
}
@@ -397,7 +388,7 @@
fn string_verbantim() {
assert_eq!(
parse!(r#"@"Hello\n""World""""#),
- el!(Expr::Str("Hello\\n\"World\"".into())),
+ el!(Expr::Str("Hello\\n\"World\"".into()), 0, 19),
);
}
@@ -405,49 +396,95 @@
fn imports() {
assert_eq!(
parse!("import \"hello\""),
- el!(Expr::Import(PathBuf::from("hello"))),
+ el!(Expr::Import(PathBuf::from("hello")), 0, 14),
);
assert_eq!(
parse!("importstr \"garnish.txt\""),
- el!(Expr::ImportStr(PathBuf::from("garnish.txt")))
+ el!(Expr::ImportStr(PathBuf::from("garnish.txt")), 0, 23)
);
}
#[test]
fn empty_object() {
- assert_eq!(parse!("{}"), el!(Expr::Obj(ObjBody::MemberList(vec![]))));
+ assert_eq!(
+ parse!("{}"),
+ el!(Expr::Obj(ObjBody::MemberList(vec![])), 0, 2)
+ );
}
#[test]
fn basic_math() {
assert_eq!(
parse!("2+2*2"),
- el!(Expr::BinaryOp(
- el!(Expr::Num(2.0)),
- Add,
- el!(Expr::BinaryOp(
- el!(Expr::Num(2.0)),
- Mul,
- el!(Expr::Num(2.0))
- ))
- ))
+ el!(
+ Expr::BinaryOp(
+ el!(Expr::Num(2.0), 0, 1),
+ Add,
+ el!(
+ Expr::BinaryOp(el!(Expr::Num(2.0), 2, 3), Mul, el!(Expr::Num(2.0), 4, 5)),
+ 2,
+ 5
+ )
+ ),
+ 0,
+ 5
+ )
);
}
#[test]
fn basic_math_with_indents() {
- assert_eq!(parse!("2 + 2 * 2 "), expressions::basic_math());
+ assert_eq!(
+ parse!("2 + 2 * 2 "),
+ el!(
+ Expr::BinaryOp(
+ el!(Expr::Num(2.0), 0, 1),
+ Add,
+ el!(
+ Expr::BinaryOp(el!(Expr::Num(2.0), 7, 8), Mul, el!(Expr::Num(2.0), 13, 14),),
+ 7,
+ 14
+ ),
+ ),
+ 0,
+ 14
+ )
+ );
}
#[test]
fn basic_math_parened() {
assert_eq!(
parse!("2+(2+2*2)"),
- el!(Expr::BinaryOp(
- el!(Expr::Num(2.0)),
- Add,
- el!(Expr::Parened(expressions::basic_math())),
- ))
+ el!(
+ Expr::BinaryOp(
+ el!(Expr::Num(2.0), 0, 1),
+ Add,
+ el!(
+ Expr::Parened(el!(
+ Expr::BinaryOp(
+ el!(Expr::Num(2.0), 3, 4),
+ Add,
+ el!(
+ Expr::BinaryOp(
+ el!(Expr::Num(2.0), 5, 6),
+ Mul,
+ el!(Expr::Num(2.0), 7, 8),
+ ),
+ 5,
+ 8
+ ),
+ ),
+ 3,
+ 8
+ )),
+ 2,
+ 9
+ ),
+ ),
+ 0,
+ 9
+ )
);
}
@@ -456,15 +493,23 @@
fn comments() {
assert_eq!(
parse!("2//comment\n+//comment\n3/*test*/*/*test*/4"),
- el!(Expr::BinaryOp(
- el!(Expr::Num(2.0)),
- Add,
- el!(Expr::BinaryOp(
- el!(Expr::Num(3.0)),
- Mul,
- el!(Expr::Num(4.0))
- ))
- ))
+ el!(
+ Expr::BinaryOp(
+ el!(Expr::Num(2.0), 0, 1),
+ Add,
+ el!(
+ Expr::BinaryOp(
+ el!(Expr::Num(3.0), 22, 23),
+ Mul,
+ el!(Expr::Num(4.0), 40, 41)
+ ),
+ 22,
+ 41
+ )
+ ),
+ 0,
+ 41
+ )
);
}
@@ -473,11 +518,11 @@
fn comment_escaping() {
assert_eq!(
parse!("2/*\\*/+*/ - 22"),
- el!(Expr::BinaryOp(
- el!(Expr::Num(2.0)),
- Sub,
- el!(Expr::Num(22.0))
- ))
+ el!(
+ Expr::BinaryOp(el!(Expr::Num(2.0), 0, 1), Sub, el!(Expr::Num(22.0), 12, 14)),
+ 0,
+ 14
+ )
);
}
@@ -492,27 +537,46 @@
#[test]
fn array_comp() {
use Expr::*;
+ /*
+ `ArrComp(Apply(Index(Var("std") from "test.jsonnet":1-4, Var("deepJoin") from "test.jsonnet":5-13) from "test.jsonnet":1-13, ArgsDesc { unnamed: [Var("x") from "test.jsonnet":14-15], named: [] }, false) from "test.jsonnet":1-16, [ForSpec(ForSpecData("x", Var("arr") from "test.jsonnet":26-29))]) from "test.jsonnet":0-30`,
+ `ArrComp(Apply(Index(Var("std") from "test.jsonnet":1-4, Str("deepJoin") from "test.jsonnet":5-13) from "test.jsonnet":1-13, ArgsDesc { unnamed: [Var("x") from "test.jsonnet":14-15], named: [] }, false) from "test.jsonnet":1-16, [ForSpec(ForSpecData("x", Var("arr") from "test.jsonnet":26-29))]) from "test.jsonnet":0-30`
+ */
assert_eq!(
parse!("[std.deepJoin(x) for x in arr]"),
- el!(ArrComp(
- el!(Apply(
- el!(Index(el!(Var("std".into())), el!(Str("deepJoin".into())))),
- ArgsDesc::new(vec![el!(Var("x".into()))], vec![]),
- false,
- )),
- vec![CompSpec::ForSpec(ForSpecData(
- "x".into(),
- el!(Var("arr".into()))
- ))]
- )),
+ el!(
+ ArrComp(
+ el!(
+ Apply(
+ el!(
+ Index(
+ el!(Var("std".into()), 1, 4),
+ el!(Str("deepJoin".into()), 5, 13)
+ ),
+ 1,
+ 13
+ ),
+ ArgsDesc::new(vec![el!(Var("x".into()), 14, 15)], vec![]),
+ false,
+ ),
+ 1,
+ 16
+ ),
+ vec![CompSpec::ForSpec(ForSpecData(
+ "x".into(),
+ el!(Var("arr".into()), 26, 29)
+ ))]
+ ),
+ 0,
+ 30
+ ),
)
}
#[test]
fn reserved() {
use Expr::*;
- assert_eq!(parse!("null"), el!(Literal(LiteralType::Null)));
- assert_eq!(parse!("nulla"), el!(Var("nulla".into())));
+ assert_eq!(parse!("null"), el!(Literal(LiteralType::Null), 0, 4));
+ assert_eq!(parse!("nulla"), el!(Var("nulla".into()), 0, 5));
}
#[test]
@@ -525,11 +589,15 @@
use Expr::*;
assert_eq!(
parse!("!a && !b"),
- el!(BinaryOp(
- el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into())))),
- And,
- el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()))))
- ))
+ el!(
+ BinaryOp(
+ el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()), 1, 2)), 0, 2),
+ And,
+ el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()), 7, 8)), 6, 8)
+ ),
+ 0,
+ 8
+ )
);
}
@@ -538,11 +606,15 @@
use Expr::*;
assert_eq!(
parse!("!a / !b"),
- el!(BinaryOp(
- el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into())))),
- Div,
- el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()))))
- ))
+ el!(
+ BinaryOp(
+ el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()), 1, 2)), 0, 2),
+ Div,
+ el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()), 6, 7)), 5, 7)
+ ),
+ 0,
+ 7
+ )
);
}
@@ -551,10 +623,14 @@
use Expr::*;
assert_eq!(
parse!("!!a"),
- el!(UnaryOp(
- UnaryOpType::Not,
- el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()))))
- ))
+ el!(
+ UnaryOp(
+ UnaryOpType::Not,
+ el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()), 2, 3)), 1, 3)
+ ),
+ 0,
+ 3
+ )
)
}
@@ -587,55 +663,44 @@
fn add_location_info_to_all_sub_expressions() {
use Expr::*;
- let file_name: std::rc::Rc<std::path::Path> = PathBuf::from("/test.jsonnet").into();
+ let file_name: std::rc::Rc<std::path::Path> = PathBuf::from("test.jsonnet").into();
let expr = parse(
"{} { local x = 1, x: x } + {}",
&ParserSettings {
- loc_data: true,
file_name: file_name.clone(),
},
)
.unwrap();
assert_eq!(
expr,
- el_loc!(
+ el!(
BinaryOp(
- el_loc!(
+ el!(
ObjExtend(
- el_loc!(
- Obj(ObjBody::MemberList(vec![])),
- ExprLocation(file_name.clone(), 0, 2)
- ),
+ el!(Obj(ObjBody::MemberList(vec![])), 0, 2),
ObjBody::MemberList(vec![
Member::BindStmt(BindSpec {
name: "x".into(),
params: None,
- value: el_loc!(
- Num(1.0),
- ExprLocation(file_name.clone(), 15, 16)
- )
+ value: el!(Num(1.0), 15, 16)
}),
Member::Field(FieldMember {
name: FieldName::Fixed("x".into()),
plus: false,
params: None,
visibility: Visibility::Normal,
- value: el_loc!(
- Var("x".into()),
- ExprLocation(file_name.clone(), 21, 22)
- ),
+ value: el!(Var("x".into()), 21, 22),
})
])
),
- ExprLocation(file_name.clone(), 0, 24)
+ 0,
+ 24
),
BinaryOpType::Add,
- el_loc!(
- Obj(ObjBody::MemberList(vec![])),
- ExprLocation(file_name.clone(), 27, 29)
- ),
+ el!(Obj(ObjBody::MemberList(vec![])), 27, 29),
),
- ExprLocation(file_name.clone(), 0, 29),
+ 0,
+ 29
),
);
}
crates/jrsonnet-parser/src/unescape.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-parser/src/unescape.rs
@@ -0,0 +1,38 @@
+pub fn unescape(s: &str) -> Option<String> {
+ let mut chars = s.chars();
+ let mut out = String::with_capacity(s.len());
+
+ while let Some(c) = chars.next() {
+ if c != '\\' {
+ out.push(c);
+ continue;
+ }
+ match chars.next()? {
+ c @ ('\\' | '"' | '\'') => out.push(c),
+ 'b' => out.push('\u{0008}'),
+ 'f' => out.push('\u{000c}'),
+ 'n' => out.push('\n'),
+ 'r' => out.push('\r'),
+ 't' => out.push('\t'),
+ 'u' => {
+ let c = IntoIterator::into_iter([
+ chars.next()?,
+ chars.next()?,
+ chars.next()?,
+ chars.next()?,
+ ])
+ .map(|c| c.to_digit(16))
+ .try_fold(0u32, |acc, v| Some((acc << 8) | (v?)))?;
+ out.push(char::from_u32(c)?)
+ }
+ 'x' => {
+ let c = IntoIterator::into_iter([chars.next()?, chars.next()?])
+ .map(|c| c.to_digit(16))
+ .try_fold(0u32, |acc, v| Some((acc << 8) | (v?)))?;
+ out.push(char::from_u32(c)?)
+ }
+ _ => return None,
+ }
+ }
+ Some(out)
+}
crates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/std.jsonnet
+++ b/crates/jrsonnet-stdlib/src/std.jsonnet
@@ -370,7 +370,7 @@
ch;
std.foldl(function(a, b) a + trans(b), std.stringChars(str), ''),
- manifestJson(value):: std.manifestJsonEx(value, ' '),
+ manifestJson(value):: std.manifestJsonEx(value, ' ') tailstrict,
manifestJsonEx:: $intrinsic(manifestJsonEx),
crates/jrsonnet-types/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-types/Cargo.toml
+++ b/crates/jrsonnet-types/Cargo.toml
@@ -8,4 +8,4 @@
[dependencies]
peg = "0.7.0"
-jrsonnet-gc = { version = "0.4.2", features = ["derive"] }
+gcmodule = { git = "https://github.com/CertainLach/gcmodule", branch = "jrsonnet" }
crates/jrsonnet-types/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-types/src/lib.rs
+++ b/crates/jrsonnet-types/src/lib.rs
@@ -1,6 +1,6 @@
#![allow(clippy::redundant_closure_call)]
-use jrsonnet_gc::Trace;
+use gcmodule::Trace;
use std::fmt::Display;
#[macro_export]
@@ -82,7 +82,6 @@
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]
-#[trivially_drop]
pub enum ValType {
Bool,
Null,
@@ -115,7 +114,7 @@
}
#[derive(Debug, Clone, PartialEq, Trace)]
-#[trivially_drop]
+#[skip_trace]
pub enum ComplexValType {
Any,
Char,