Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: teach alternation update-assignment operator (//=) #142

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/msrv.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ jobs:
- name: Check jaq-core
working-directory: jaq-core
run: cargo check
- name: Check jaq-syn
working-directory: jaq-syn
run: cargo check
- name: Check jaq-interpret
working-directory: jaq-interpret
run: cargo check
Comment on lines +19 to +24
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be needed, because jaq-core depends on jaq-interpret, which depends again on jaq-syn.


- uses: dtolnay/rust-toolchain@1.64
- name: Check jaq-std
Expand Down
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ Here is an overview that summarises:
## Basics

- [x] Identity (`.`)
- [x] Recursion (`..`)
- [x] Recursive descent (`..`)
- [x] Basic data types (null, boolean, number, string, array, object)
- [x] if-then-else (`if .a < .b then .a else .b end`)
- [x] Folding (`reduce .[] as $x (0; . + $x)`, `foreach .[] as $x (0; . + $x; . + .)`)
Expand All @@ -229,9 +229,9 @@ Here is an overview that summarises:
- [x] Plain assignment (`=`)
- [x] Update assignment (`|=`, `+=`, `-=`)
- [x] Alternation (`//`)
- [x] Logic (`or`, `and`)
- [x] Logical (`or`, `and`)
- [x] Equality and comparison (`.a == .b`, `.a < .b`)
- [x] Arithmetic (`+`, `-`, `*`, `/`, `%`)
- [x] Arithmetical (`+`, `-`, `*`, `/`, `%`)
- [x] Negation (`-`)
- [x] Error suppression (`?`)

Expand Down Expand Up @@ -277,7 +277,7 @@ Their definitions are at [`std.jq`](jaq-std/src/std.jq).
- [x] Array filters (`transpose`, `first`, `last`, `nth(10)`, `flatten`, `min`, `max`)
- [x] Object-array conversion (`to_entries`, `from_entries`, `with_entries`)
- [x] Universal/existential (`all`, `any`)
- [x] Recursion (`walk`)
- [x] Recursive application (`walk`)
- [x] I/O (`input`)
- [x] Regular expressions (`test`, `scan`, `match`, `capture`, `splits`, `sub`, `gsub`)
- [x] Time (`fromdate`, `todate`)
Expand Down
2 changes: 2 additions & 0 deletions jaq-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ rust-version = "1.63"

[features]
default = ["std", "format", "log", "math", "parse_json", "regex", "time"]
unstable = ["unstable-flag", "jaq-interpret/unstable"]
unstable-flag = ["jaq-interpret/unstable-flag"]
std = []
format = ["aho-corasick", "base64", "urlencoding"]
math = ["libm"]
Expand Down
31 changes: 21 additions & 10 deletions jaq-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ use jaq_interpret::{Error, FilterT, Native, RunPtr, UpdatePtr, Val, ValR, ValRs}
/// but not `now`, `debug`, `fromdateiso8601`, ...
///
/// Does not return filters from the standard library, such as `map`.
pub fn minimal() -> impl Iterator<Item = (String, usize, Native)> {
pub fn minimal(
#[cfg(feature = "unstable-flag")]
#[allow(unused_variables)]
unstable: bool,
) -> impl Iterator<Item = (String, usize, Native)> {
run(CORE_RUN).chain(upd(CORE_UPDATE))
}

Expand All @@ -42,15 +46,20 @@ pub fn minimal() -> impl Iterator<Item = (String, usize, Native)> {
feature = "regex",
feature = "time",
))]
pub fn core() -> impl Iterator<Item = (String, usize, Native)> {
minimal()
.chain(run(STD))
.chain(run(FORMAT))
.chain(upd(LOG))
.chain(run(MATH))
.chain(run(PARSE_JSON))
.chain(run(REGEX))
.chain(run(TIME))
pub fn core(
#[cfg(feature = "unstable-flag")] unstable: bool,
) -> impl Iterator<Item = (String, usize, Native)> {
minimal(
#[cfg(feature = "unstable-flag")]
unstable,
)
.chain(run(STD))
.chain(run(FORMAT))
.chain(upd(LOG))
.chain(run(MATH))
.chain(run(PARSE_JSON))
.chain(run(REGEX))
.chain(run(TIME))
}

fn run<'a>(fs: &'a [(&str, usize, RunPtr)]) -> impl Iterator<Item = (String, usize, Native)> + 'a {
Expand Down Expand Up @@ -86,6 +95,8 @@ fn length(v: &Val) -> ValR {
Val::Str(s) => Ok(Val::Int(s.chars().count() as isize)),
Val::Arr(a) => Ok(Val::Int(a.len() as isize)),
Val::Obj(o) => Ok(Val::Int(o.len() as isize)),
#[cfg(feature = "unstable-flag")]
_ => unimplemented!(),
}
}

Expand Down
47 changes: 36 additions & 11 deletions jaq-core/tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,57 @@
use serde_json::Value;

fn yields(x: jaq_interpret::Val, f: &str, ys: impl Iterator<Item = jaq_interpret::ValR>) {
#[cfg(not(feature = "unstable-flag"))]
pub const UNSTABLE: bool = false;
#[cfg(feature = "unstable-flag")]
pub const UNSTABLE: bool = false;

#[track_caller]
fn yields(
#[cfg_attr(not(feature = "unstable-flag"), allow(unused_variables))] unstable: bool,
x: jaq_interpret::Val,
f: &str,
ys: impl Iterator<Item = jaq_interpret::ValR>,
) {
let mut ctx = jaq_interpret::ParseCtx::new(Vec::new());
ctx.insert_natives(jaq_core::core());
ctx.insert_natives(jaq_core::core(
#[cfg(feature = "unstable-flag")]
unstable,
));

let (f, errs) = jaq_parse::parse(f, jaq_parse::main());
let (f, errs) = jaq_parse::parse(
#[cfg(feature = "unstable-flag")]
unstable,
f,
jaq_parse::main(
#[cfg(feature = "unstable-flag")]
unstable,
),
);
assert!(errs.is_empty());
ctx.yields(x, f.unwrap(), ys)
}

pub fn fail(x: Value, f: &str, err: jaq_interpret::Error) {
yields(x.into(), f, core::iter::once(Err(err)))
#[track_caller]
pub fn fail(unstable: bool, x: Value, f: &str, err: jaq_interpret::Error) {
yields(unstable, x.into(), f, core::iter::once(Err(err)))
}

pub fn give(x: Value, f: &str, y: Value) {
yields(x.into(), f, core::iter::once(Ok(y.into())))
#[track_caller]
pub fn give(unstable: bool, x: Value, f: &str, y: Value) {
yields(unstable, x.into(), f, core::iter::once(Ok(y.into())))
}

pub fn gives<const N: usize>(x: Value, f: &str, ys: [Value; N]) {
yields(x.into(), f, ys.into_iter().map(|y| Ok(y.into())))
#[track_caller]
pub fn gives<const N: usize>(unstable: bool, x: Value, f: &str, ys: [Value; N]) {
yields(unstable, x.into(), f, ys.into_iter().map(|y| Ok(y.into())))
}

#[macro_export]
macro_rules! yields {
($func_name:ident, $filter:expr, $output: expr) => {
($func_name:ident, $unstable:expr, $filter:expr, $output: expr) => {
#[test]
fn $func_name() {
give(json!(null), $filter, json!($output))
give($unstable, json!(null), $filter, json!($output))
}
};
}