Skip to content

Commit

Permalink
refactor: move more stdlib functions to builtins
Browse files Browse the repository at this point in the history
  • Loading branch information
CertainLach committed May 1, 2024
1 parent ad68a24 commit f9beb89
Show file tree
Hide file tree
Showing 17 changed files with 747 additions and 417 deletions.
338 changes: 139 additions & 199 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions crates/jrsonnet-cli/src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use clap::{Parser, ValueEnum};
use jrsonnet_evaluator::manifest::{
JsonFormat, ManifestFormat, StringFormat, ToStringFormat, YamlStreamFormat,
};
use jrsonnet_stdlib::{TomlFormat, YamlFormat};
use jrsonnet_stdlib::{TomlFormat, XmlJsonmlFormat, YamlFormat};

#[derive(Clone, Copy, ValueEnum)]
pub enum ManifestFormatName {
Expand All @@ -13,6 +13,7 @@ pub enum ManifestFormatName {
Json,
Yaml,
Toml,
XmlJsonml,
}

#[derive(Parser)]
Expand Down Expand Up @@ -70,10 +71,11 @@ impl ManifestOpts {
#[cfg(feature = "exp-preserve-order")]
preserve_order,
)),
ManifestFormatName::XmlJsonml => Box::new(XmlJsonmlFormat::cli()),
}
};
if self.yaml_stream {
Box::new(YamlStreamFormat(format))
Box::new(YamlStreamFormat::cli(format))
} else {
format
}
Expand Down
42 changes: 23 additions & 19 deletions crates/jrsonnet-evaluator/src/arr/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::any::Any;
use std::{
any::Any,
num::{NonZeroU32, NonZeroUsize},

Check warning on line 3 in crates/jrsonnet-evaluator/src/arr/mod.rs

View workflow job for this annotation

GitHub Actions / Test Suite

unused import: `NonZeroUsize`

Check warning on line 3 in crates/jrsonnet-evaluator/src/arr/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `NonZeroUsize`

warning: unused import: `NonZeroUsize` --> crates/jrsonnet-evaluator/src/arr/mod.rs:3:20 | 3 | num::{NonZeroU32, NonZeroUsize}, | ^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default
};

use jrsonnet_gcmodule::{Cc, Trace};
use jrsonnet_interner::IBytes;
Expand Down Expand Up @@ -100,28 +103,29 @@ impl ArrValue {
Self::new(RangeArray::new_inclusive(a, b))
}

/// # Panics
/// If step == 0
#[must_use]
pub fn slice(
self,
from: Option<usize>,
to: Option<usize>,
step: Option<usize>,
) -> Option<Self> {
let len = self.len();
let from = from.unwrap_or(0);
let to = to.unwrap_or(len).min(len);
let step = step.unwrap_or(1);

if from >= to || step == 0 {
return None;
pub fn slice(self, index: Option<i32>, end: Option<i32>, step: Option<NonZeroU32>) -> Self {
let get_idx = |pos: Option<i32>, len: usize, default| match pos {
Some(v) if v < 0 => len.saturating_sub((-v) as usize),
Some(v) => (v as usize).min(len),
None => default,
};
let index = get_idx(index, self.len(), 0);
let end = get_idx(end, self.len(), self.len());
let step = step.unwrap_or_else(|| NonZeroU32::new(1).expect("1 != 0"));

if index >= end {
return Self::empty();
}

Some(Self::new(SliceArray {
Self::new(SliceArray {
inner: self,
from: from as u32,
to: to as u32,
step: step as u32,
}))
from: index as u32,
to: end as u32,
step: step.get(),
})
}

/// Array length.
Expand Down
30 changes: 28 additions & 2 deletions crates/jrsonnet-evaluator/src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,28 @@ impl ManifestFormat for StringFormat {
}
}

pub struct YamlStreamFormat<I>(pub I);
pub struct YamlStreamFormat<I> {
inner: I,
c_document_end: bool,
end_newline: bool,
}
impl<I> YamlStreamFormat<I> {
pub fn std_yaml_stream(inner: I, c_document_end: bool) -> Self {
Self {
inner,
c_document_end,
// Stdlib format always inserts newline at the end
end_newline: true,
}
}
pub fn cli(inner: I) -> Self {
Self {
inner,
c_document_end: true,
end_newline: false,
}
}
}
impl<I: ManifestFormat> ManifestFormat for YamlStreamFormat<I> {
fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {
let Val::Arr(arr) = val else {
Expand All @@ -353,11 +374,16 @@ impl<I: ManifestFormat> ManifestFormat for YamlStreamFormat<I> {
for v in arr.iter() {
let v = v?;
out.push_str("---\n");
self.0.manifest_buf(v, out)?;
self.inner.manifest_buf(v, out)?;
out.push('\n');
}
}
if self.c_document_end {
out.push_str("...");
}
if self.end_newline {
out.push('\n');
}
Ok(())
}
}
Expand Down
26 changes: 6 additions & 20 deletions crates/jrsonnet-evaluator/src/val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::{
cell::RefCell,
fmt::{self, Debug, Display},
mem::replace,
num::{NonZeroU32, NonZeroUsize},

Check warning on line 5 in crates/jrsonnet-evaluator/src/val.rs

View workflow job for this annotation

GitHub Actions / Test Suite

unused import: `NonZeroUsize`

Check warning on line 5 in crates/jrsonnet-evaluator/src/val.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `NonZeroUsize`

warning: unused import: `NonZeroUsize` --> crates/jrsonnet-evaluator/src/val.rs:5:20 | 5 | num::{NonZeroU32, NonZeroUsize}, | ^^^^^^^^^^^^
rc::Rc,
};

Expand Down Expand Up @@ -277,26 +278,11 @@ impl IndexableVal {
.into(),
))
}
Self::Arr(arr) => {
let get_idx = |pos: Option<i32>, len: usize, default| match pos {
Some(v) if v < 0 => len.saturating_sub((-v) as usize),
Some(v) => (v as usize).min(len),
None => default,
};
let index = get_idx(index, arr.len(), 0);
let end = get_idx(end, arr.len(), arr.len());
let step = step.as_deref().copied().unwrap_or(1);

if index >= end {
return Ok(Self::Arr(ArrValue::empty()));
}

Ok(Self::Arr(
arr.clone()
.slice(Some(index), Some(end), Some(step))
.expect("arguments checked"),
))
}
Self::Arr(arr) => Ok(Self::Arr(arr.clone().slice(
index,
end,
step.map(|v| NonZeroU32::new(v.value() as u32).expect("bounded != 0")),
))),
}
}
}
Expand Down
74 changes: 48 additions & 26 deletions crates/jrsonnet-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ where

Ok(Some(attr))
}
fn remove_attr<I>(attrs: &mut Vec<Attribute>, ident: I)
where
Ident: PartialEq<I>,
{
attrs.retain(|a| !a.path().is_ident(&ident));
}

fn path_is(path: &Path, needed: &str) -> bool {
path.leading_colon.is_none()
Expand Down Expand Up @@ -121,10 +127,21 @@ impl Parse for BuiltinAttrs {
}
}

enum Optionality {
Required,
Optional,
Default(Expr),
}
impl Optionality {
fn is_optional(&self) -> bool {
!matches!(self, Self::Required)
}
}

enum ArgInfo {
Normal {
ty: Box<Type>,
is_option: bool,
optionality: Optionality,
name: Option<String>,
cfg_attrs: Vec<Attribute>,
},
Expand All @@ -138,7 +155,7 @@ enum ArgInfo {
}

impl ArgInfo {
fn parse(name: &str, arg: &FnArg) -> Result<Self> {
fn parse(name: &str, arg: &mut FnArg) -> Result<Self> {
let FnArg::Typed(arg) = arg else {
unreachable!()
};
Expand All @@ -163,17 +180,20 @@ impl ArgInfo {
_ => {}
}

let (is_option, ty) = if let Some(ty) = extract_type_from_option(ty)? {
let (optionality, ty) = if let Some(default) = parse_attr::<_, _>(&arg.attrs, "default")? {
remove_attr(&mut arg.attrs, "default");
(Optionality::Default(default), ty.clone())
} else if let Some(ty) = extract_type_from_option(ty)? {
if type_is_path(ty, "Thunk").is_some() {
return Ok(Self::Lazy {
is_option: true,
name: ident.map(|v| v.to_string()),
});
}

(true, Box::new(ty.clone()))
(Optionality::Optional, Box::new(ty.clone()))
} else {
(false, ty.clone())
(Optionality::Required, ty.clone())
};

let cfg_attrs = arg
Expand All @@ -185,7 +205,7 @@ impl ArgInfo {

Ok(Self::Normal {
ty,
is_option,
optionality,
name: ident.map(|v| v.to_string()),
cfg_attrs,
})
Expand All @@ -201,18 +221,14 @@ pub fn builtin(
let item_fn = item.clone();

Check warning on line 221 in crates/jrsonnet-macros/src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

redundant clone

warning: redundant clone --> crates/jrsonnet-macros/src/lib.rs:221:20 | 221 | let item_fn = item.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use --> crates/jrsonnet-macros/src/lib.rs:221:16 | 221 | let item_fn = item.clone(); | ^^^^ = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone = note: `-W clippy::redundant-clone` implied by `-W clippy::nursery` = help: to override `-W clippy::nursery` add `#[allow(clippy::redundant_clone)]`
let item_fn: ItemFn = parse_macro_input!(item_fn);

match builtin_inner(attr, item_fn, item.into()) {
match builtin_inner(attr, item_fn) {
Ok(v) => v.into(),
Err(e) => e.into_compile_error().into(),
}
}

#[allow(clippy::too_many_lines)]
fn builtin_inner(
attr: BuiltinAttrs,
fun: ItemFn,
item: proc_macro2::TokenStream,
) -> syn::Result<TokenStream> {
fn builtin_inner(attr: BuiltinAttrs, mut fun: ItemFn) -> syn::Result<TokenStream> {
let ReturnType::Type(_, result) = &fun.sig.output else {
return Err(Error::new(
fun.sig.span(),
Expand All @@ -224,23 +240,24 @@ fn builtin_inner(
let args = fun
.sig
.inputs
.iter()
.iter_mut()
.map(|arg| ArgInfo::parse(&name, arg))
.collect::<Result<Vec<_>>>()?;

let params_desc = args.iter().filter_map(|a| match a {
ArgInfo::Normal {
is_option,
optionality,
name,
cfg_attrs,
..
} => {
let name = name
.as_ref()
.map_or_else(|| quote! {None}, |n| quote! {ParamName::new_static(#n)});
let is_optional = optionality.is_optional();
Some(quote! {
#(#cfg_attrs)*
BuiltinParam::new(#name, #is_option),
BuiltinParam::new(#name, #is_optional),
})
}
ArgInfo::Lazy { is_option, name } => {
Expand Down Expand Up @@ -270,7 +287,7 @@ fn builtin_inner(
.map(|(id, a)| match a {
ArgInfo::Normal {
ty,
is_option,
optionality,
name,
cfg_attrs,
} => {
Expand All @@ -279,17 +296,22 @@ fn builtin_inner(
|| format!("argument <{}> evaluation", #name),
|| <#ty>::from_untyped(value.evaluate()?),
)?};
let value = if *is_option {
quote! {if let Some(value) = &parsed[#id] {
let value = match optionality {
Optionality::Required => quote! {{
let value = parsed[#id].as_ref().expect("args shape is checked");
#eval
},},
Optionality::Optional => quote! {if let Some(value) = &parsed[#id] {
Some(#eval)
} else {
None
},}
} else {
quote! {{
let value = parsed[#id].as_ref().expect("args shape is checked");
},},
Optionality::Default(expr) => quote! {if let Some(value) = &parsed[#id] {
#eval
},}
} else {
let v: #ty = #expr;
v
},},
};
quote! {
#(#cfg_attrs)*
Expand All @@ -302,7 +324,7 @@ fn builtin_inner(
Some(value.clone())
} else {
None
}}
},}
} else {
quote! {
parsed[#id].as_ref().expect("args shape is correct").clone(),
Expand Down Expand Up @@ -343,7 +365,7 @@ fn builtin_inner(
};

Ok(quote! {
#item
#fun

#[doc(hidden)]
#[allow(non_camel_case_types)]
Expand Down Expand Up @@ -373,7 +395,7 @@ fn builtin_inner(
fn params(&self) -> &[BuiltinParam] {
PARAMS
}
#[allow(unused_variable)]
#[allow(unused_variables)]
fn call(&self, ctx: Context, location: CallLocation, args: &dyn ArgsLike) -> Result<Val> {
let parsed = parse_builtin_call(ctx.clone(), &PARAMS, args, false)?;

Expand Down
Loading

0 comments on commit f9beb89

Please sign in to comment.