From 476a6fad523050e6c7564ca4543c619fcaed4fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20F=C3=A4rber?= <01mf02@gmail.com> Date: Tue, 20 Aug 2024 09:34:23 +0200 Subject: [PATCH] Move jaq-syn into jaq-interpret. --- Cargo.lock | 6 +- Cargo.toml | 2 +- jaq-core/Cargo.toml | 1 - jaq-core/tests/common/mod.rs | 2 +- jaq-interpret/Cargo.toml | 7 ++- jaq-interpret/src/compile.rs | 21 +++---- jaq-interpret/src/error.rs | 2 +- jaq-interpret/src/json.rs | 2 +- jaq-interpret/src/lib.rs | 5 +- .../src => jaq-interpret/src/load}/lex.rs | 0 .../load.rs => jaq-interpret/src/load/mod.rs | 38 +++++++++++- .../src => jaq-interpret/src/load}/parse.rs | 21 +++---- .../src/load}/prec_climb.rs | 0 .../src => jaq-interpret/src/load}/test.rs | 0 {jaq-syn => jaq-interpret}/src/ops.rs | 0 jaq-interpret/src/path.rs | 53 ++++++++++++---- jaq-interpret/src/val.rs | 2 +- jaq-interpret/tests/common/mod.rs | 2 +- jaq-play/Cargo.toml | 2 +- jaq-std/Cargo.toml | 3 +- jaq-std/src/lib.rs | 5 +- jaq-std/tests/common/mod.rs | 2 +- jaq-syn/Cargo.toml | 19 ------ jaq-syn/src/lib.rs | 42 ------------- jaq-syn/src/path.rs | 62 ------------------- jaq/Cargo.toml | 1 - jaq/src/main.rs | 32 +++++----- 27 files changed, 131 insertions(+), 201 deletions(-) rename {jaq-syn/src => jaq-interpret/src/load}/lex.rs (100%) rename jaq-syn/src/load.rs => jaq-interpret/src/load/mod.rs (91%) rename {jaq-syn/src => jaq-interpret/src/load}/parse.rs (98%) rename {jaq-syn/src => jaq-interpret/src/load}/prec_climb.rs (100%) rename {jaq-syn/src => jaq-interpret/src/load}/test.rs (100%) rename {jaq-syn => jaq-interpret}/src/ops.rs (100%) delete mode 100644 jaq-syn/Cargo.toml delete mode 100644 jaq-syn/src/lib.rs delete mode 100644 jaq-syn/src/path.rs diff --git a/Cargo.lock b/Cargo.lock index 14b7b0e8e..82e11f0af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -241,7 +241,6 @@ dependencies = [ "jaq-core", "jaq-interpret", "jaq-std", - "jaq-syn", "memmap2", "mimalloc", "tempfile", @@ -258,7 +257,6 @@ dependencies = [ "chrono", "hifijson", "jaq-interpret", - "jaq-syn", "libm", "log", "regex", @@ -274,9 +272,9 @@ dependencies = [ "dyn-clone", "hifijson", "indexmap", - "jaq-syn", "once_cell", "serde_json", + "typed-arena", ] [[package]] @@ -291,7 +289,6 @@ dependencies = [ "jaq-core", "jaq-interpret", "jaq-std", - "jaq-syn", "js-sys", "log", "unicode-width", @@ -305,7 +302,6 @@ version = "1.6.0" dependencies = [ "jaq-core", "jaq-interpret", - "jaq-syn", "serde_json", ] diff --git a/Cargo.toml b/Cargo.toml index bd439b975..93e767b94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [workspace] members = [ - "jaq-syn", "jaq-interpret", "jaq-core", "jaq-std", + "jaq-json", "jaq", "jaq-play", ] diff --git a/jaq-core/Cargo.toml b/jaq-core/Cargo.toml index fefe441ee..b96d4784c 100644 --- a/jaq-core/Cargo.toml +++ b/jaq-core/Cargo.toml @@ -30,5 +30,4 @@ base64 = { version = "0.22", optional = true } urlencoding = { version = "2.1.3", optional = true } [dev-dependencies] -jaq-syn = { version = "1.6.0", path = "../jaq-syn" } serde_json = "1.0" diff --git a/jaq-core/tests/common/mod.rs b/jaq-core/tests/common/mod.rs index a87674c15..59088277d 100644 --- a/jaq-core/tests/common/mod.rs +++ b/jaq-core/tests/common/mod.rs @@ -2,7 +2,7 @@ use jaq_interpret::json::{Error, Val, ValR}; use serde_json::Value; fn yields(x: Val, code: &str, ys: impl Iterator) { - use jaq_syn::load::{Arena, File, Loader}; + use jaq_interpret::load::{Arena, File, Loader}; let arena = Arena::default(); let loader = Loader::new([]); diff --git a/jaq-interpret/Cargo.toml b/jaq-interpret/Cargo.toml index 6a2baeb8e..5053bdf55 100644 --- a/jaq-interpret/Cargo.toml +++ b/jaq-interpret/Cargo.toml @@ -16,10 +16,11 @@ default = ["std", "hifijson", "serde_json"] std = [] [dependencies] -jaq-syn = { version = "1.1.0", path = "../jaq-syn" } -ahash = "0.8.6" dyn-clone = "1.0" +once_cell = "1.16.0" +typed-arena = "2.0.2" + +ahash = "0.8.6" hifijson = { version = "0.2.0", optional = true } indexmap = "2.0" -once_cell = "1.16.0" serde_json = { version = "1.0.81", optional = true } diff --git a/jaq-interpret/src/compile.rs b/jaq-interpret/src/compile.rs index 1cd4f78a5..9a3ba4e8c 100644 --- a/jaq-interpret/src/compile.rs +++ b/jaq-interpret/src/compile.rs @@ -1,8 +1,9 @@ //! Program compilation. +use crate::load::{self, lex, parse}; use crate::{Bind, Filter}; +use crate::{MathOp, OrdOp}; use alloc::{boxed::Box, string::String, vec::Vec}; -use jaq_syn::{load, parse, MathOp, OrdOp}; type NativeId = usize; type ModId = usize; @@ -332,7 +333,7 @@ impl<'s, F> Compiler<&'s str, F> { y } - fn module(&mut self, m: jaq_syn::load::Module<&'s str>) { + fn module(&mut self, m: load::Module<&'s str>) { self.imported_mods.clear(); self.included_mods.clear(); for (mid, as_) in m.mods { @@ -458,7 +459,7 @@ impl<'s, F> Compiler<&'s str, F> { Term::Fold(fold, xs, init, update) } BinOp(l, op, r) => { - use jaq_syn::parse::BinaryOp::*; + use parse::BinaryOp::*; let (l, r) = match op { Comma => (self.iterm_tr(*l), self.iterm_tr(*r)), Alt => (self.iterm(*l), self.iterm_tr(*r)), @@ -478,21 +479,13 @@ impl<'s, F> Compiler<&'s str, F> { } } Path(t, path) => { - use crate::path::Part; - use jaq_syn::path::Part::{Index, Range}; let t = self.iterm(*t); - let path = path.into_iter().map(|(p, opt)| match p { - Index(i) => (Part::Index(self.iterm(i)), opt), - Range(lower, upper) => { - let lower = lower.map(|f| self.iterm(f)); - let upper = upper.map(|f| self.iterm(f)); - (Part::Range(lower, upper), opt) - } - }); + let path = path.0.into_iter(); + let path = path.map(|(p, opt)| (p.map(|f| self.iterm(f)), opt)); Term::Path(t, crate::path::Path(path.collect())) } Str(fmt, parts) => { - use jaq_syn::lex::StrPart; + use lex::StrPart; let fmt = match fmt { Some(fmt) => self.iterm(Call(fmt, Vec::new())), None => self.lut.insert_term(Term::ToString), diff --git a/jaq-interpret/src/error.rs b/jaq-interpret/src/error.rs index 5c1f04739..6dccbfe4f 100644 --- a/jaq-interpret/src/error.rs +++ b/jaq-interpret/src/error.rs @@ -15,7 +15,7 @@ pub enum Error { /// Expected a value of given type, but got something else Type(V, Type), /// `1 - "a"` - MathOp(V, jaq_syn::MathOp, V), + MathOp(V, crate::MathOp, V), /// `{} | .[0]` or `[] | has("a")` or `{} | has(0)` Index(V, V), diff --git a/jaq-interpret/src/json.rs b/jaq-interpret/src/json.rs index 2bf031a85..b79fb473d 100644 --- a/jaq-interpret/src/json.rs +++ b/jaq-interpret/src/json.rs @@ -4,13 +4,13 @@ use crate::box_iter::{box_once, BoxIter}; use crate::error::Type; use crate::val::{Range, ValT}; use crate::Exn; +use crate::{path::Opt, MathOp}; use alloc::string::{String, ToString}; use alloc::{boxed::Box, rc::Rc, vec::Vec}; use core::cmp::Ordering; use core::fmt::{self, Debug}; #[cfg(feature = "hifijson")] use hifijson::{LexAlloc, Token}; -use jaq_syn::{path::Opt, MathOp}; /// JSON value with sharing. /// diff --git a/jaq-interpret/src/lib.rs b/jaq-interpret/src/lib.rs index 13cf97f58..43ff403f2 100644 --- a/jaq-interpret/src/lib.rs +++ b/jaq-interpret/src/lib.rs @@ -56,7 +56,9 @@ pub(crate) mod exn; mod filter; mod into_iter; pub mod json; -mod path; +pub mod load; +mod ops; +pub mod path; mod rc_iter; mod rc_lazy_list; mod rc_list; @@ -73,6 +75,7 @@ pub use rc_iter::RcIter; pub use val::{ValR, ValT, ValX, ValXs}; use alloc::string::String; +use ops::{MathOp, OrdOp}; use rc_list::List as RcList; use stack::Stack; diff --git a/jaq-syn/src/lex.rs b/jaq-interpret/src/load/lex.rs similarity index 100% rename from jaq-syn/src/lex.rs rename to jaq-interpret/src/load/lex.rs diff --git a/jaq-syn/src/load.rs b/jaq-interpret/src/load/mod.rs similarity index 91% rename from jaq-syn/src/load.rs rename to jaq-interpret/src/load/mod.rs index ef2aac017..2471a0124 100644 --- a/jaq-syn/src/load.rs +++ b/jaq-interpret/src/load/mod.rs @@ -1,9 +1,20 @@ //! Combined file loading, lexing, and parsing for multiple modules. -use crate::lex::{self, Token}; -use crate::parse::{self, Def, Term}; +pub mod lex; +pub mod parse; +mod prec_climb; +pub mod test; + +pub use lex::Lexer; +pub use parse::Parser; + +use crate::ops::{MathOp, OrdOp}; +use crate::path; + use alloc::string::String; use alloc::vec::Vec; +use lex::Token; +use parse::{Def, Term}; #[cfg(feature = "std")] extern crate std; @@ -278,3 +289,26 @@ fn parse_defs(code: &str) -> Result>>, Error<& .parse(|p| p.module(|p| p.defs())) .map_err(|e| Error::Parse(e.into_iter().map(conv_err).collect())) } + +/// Lex a string and parse resulting tokens, returning [`None`] if any error occurred. +/// +/// Example: +/// +/// ~~~ +/// # use jaq_syn::parse; +/// let t = parse("[] | .[]", |p| p.term()); +/// ~~~ +pub fn parse<'s, T: Default, F>(s: &'s str, f: F) -> Option +where + F: for<'t> FnOnce(&mut Parser<'s, 't>) -> parse::Result<'s, 't, T>, +{ + Parser::new(&Lexer::new(s).lex().ok()?).parse(f).ok() +} + +/// Return the span of a string slice `part` relative to a string slice `whole`. +/// +/// The caller must ensure that `part` is fully contained inside `whole`. +pub fn span(whole: &str, part: &str) -> core::ops::Range { + let start = part.as_ptr() as usize - whole.as_ptr() as usize; + start..start + part.len() +} diff --git a/jaq-syn/src/parse.rs b/jaq-interpret/src/load/parse.rs similarity index 98% rename from jaq-syn/src/parse.rs rename to jaq-interpret/src/load/parse.rs index dfa2159bf..094eb2e01 100644 --- a/jaq-syn/src/parse.rs +++ b/jaq-interpret/src/load/parse.rs @@ -1,8 +1,8 @@ //! Parsing. -use crate::lex::{StrPart, Tok, Token}; -use crate::path::{self, Path}; -use crate::{prec_climb, MathOp, OrdOp}; +use super::lex::{StrPart, Tok, Token}; +use super::path::{self, Path}; +use super::{prec_climb, MathOp, OrdOp}; use alloc::{boxed::Box, vec::Vec}; /// Parse error, storing what we expected and what we got instead. @@ -156,18 +156,17 @@ impl Term { /// `..`, also known as `recurse/0`, is defined as `., (.[]? | ..)`. pub(crate) fn recurse(recurse: S) -> Self { - use Term::*; // `[]?` let path = (path::Part::Range(None, None), path::Opt::Optional); // `.[]?` (returns array/object elements or nothing instead) - let path = Term::Path(Id.into(), Vec::from([path])); + let path = Term::Path(Term::Id.into(), Path(Vec::from([path]))); // `..` let f = Term::Call(recurse, Vec::new()); // .[]? | .. let pipe = Term::Pipe(path.into(), None, f.into()); // ., (.[]? | ..) - Term::BinOp(Id.into(), BinaryOp::Comma, pipe.into()) + Term::BinOp(Term::Id.into(), BinaryOp::Comma, pipe.into()) } /// `{}[]` returns zero values. @@ -177,7 +176,7 @@ impl Term { // `{}` let obj = Term::Obj(Vec::new()); // `{}[]` - Term::Path(obj.into(), Vec::from([path])) + Term::Path(obj.into(), Path(Vec::from([path]))) } } @@ -505,8 +504,8 @@ impl<'s, 't> Parser<'s, 't> { if let Some(key) = key { let head = (path::Part::Index(key), self.opt()); - let path = core::iter::once(head).chain(self.path()?).collect(); - Term::Path(Box::new(Term::Id), path) + let path = core::iter::once(head).chain(self.path()?.0).collect(); + Term::Path(Box::new(Term::Id), Path(path)) } else { Term::Id } @@ -530,7 +529,7 @@ impl<'s, 't> Parser<'s, 't> { }; let path = self.path()?; - Ok(if path.is_empty() { + Ok(if path.0.is_empty() { tm } else { Term::Path(Box::new(tm), path) @@ -592,7 +591,7 @@ impl<'s, 't> Parser<'s, 't> { path.push((path::Part::Index(key), self.opt())); path.extend(core::iter::from_fn(|| self.path_part_opt())); } - Ok(path) + Ok(Path(path)) } /// Parse `[]`, `[t]`, `[t:]`, `[t:t]`, `[:t]` (all without brackets). diff --git a/jaq-syn/src/prec_climb.rs b/jaq-interpret/src/load/prec_climb.rs similarity index 100% rename from jaq-syn/src/prec_climb.rs rename to jaq-interpret/src/load/prec_climb.rs diff --git a/jaq-syn/src/test.rs b/jaq-interpret/src/load/test.rs similarity index 100% rename from jaq-syn/src/test.rs rename to jaq-interpret/src/load/test.rs diff --git a/jaq-syn/src/ops.rs b/jaq-interpret/src/ops.rs similarity index 100% rename from jaq-syn/src/ops.rs rename to jaq-interpret/src/ops.rs diff --git a/jaq-interpret/src/path.rs b/jaq-interpret/src/path.rs index 4716c7044..f19dabfc0 100644 --- a/jaq-interpret/src/path.rs +++ b/jaq-interpret/src/path.rs @@ -1,25 +1,55 @@ +//! Paths and their parts. + use crate::box_iter::{box_once, flat_map_with, map_with, BoxIter}; use crate::results::then; use crate::val::{ValR, ValT, ValX, ValXs}; use alloc::{boxed::Box, vec::Vec}; -use jaq_syn::path::Opt; +/// Path such as `.[].a?[1:]`. #[derive(Clone, Debug)] pub struct Path(pub Vec<(Part, Opt)>); -/// Part of a path. -/// -/// This is identical to [`jaq_syn::path::Part`], but we cannot use that here directly -/// because that way, we could not implement new methods for that type. +/// Part of a path, such as `[]`, `a`, and `[1:]` in `.[].a?[1:]`. #[derive(Clone, Debug)] pub enum Part { + /// Access arrays with integer and objects with string indices Index(I), - /// if both are `None`, return iterator over whole array/object + /// Iterate over arrays with optional range bounds and over objects without bounds + /// If both are `None`, return iterator over whole array/object Range(Option, Option), } +/// Optionality of a path part, i.e. whether `?` is present. +/// +/// For example, `[] | .a` fails with an error, while `[] | .a?` returns nothing. +/// By default, path parts are *essential*, meaning that they fail. +/// Annotating them with `?` makes them *optional*. +#[derive(Copy, Clone, Debug)] +pub enum Opt { + /// Return nothing if the input cannot be accessed with the path + Optional, + /// Fail if the input cannot be accessed with the path + Essential, +} + +impl Default for Part { + fn default() -> Self { + Self::Range(None, None) + } +} + +impl Opt { + /// If `self` is optional, return `x`, else fail with `f(x)`. + pub(crate) fn fail(self, x: T, f: impl FnOnce(T) -> E) -> Result { + match self { + Self::Optional => Ok(x), + Self::Essential => Err(f(x)), + } + } +} + impl<'a, U: Clone + 'a, E: Clone + 'a, T: Clone + IntoIterator> + 'a> Path { - pub fn explode(self) -> impl Iterator, E>> + 'a { + pub(crate) fn explode(self) -> impl Iterator, E>> + 'a { Path(Vec::new()) .combinations(self.0.into_iter()) .map(Path::transpose) @@ -45,11 +75,11 @@ impl<'a, U: Clone + 'a> Path { } impl<'a, V: ValT + 'a> Path { - pub fn run(self, v: V) -> BoxIter<'a, ValR> { + pub(crate) fn run(self, v: V) -> BoxIter<'a, ValR> { run(self.0.into_iter(), v) } - pub fn update(mut self, v: V, f: F) -> ValX<'a, V> + pub(crate) fn update(mut self, v: V, f: F) -> ValX<'a, V> where F: Fn(V) -> ValXs<'a, V>, { @@ -134,7 +164,7 @@ impl<'a, U: Clone + 'a, F: IntoIterator + Clone + 'a> Part { } impl Path { - pub fn map_ref<'a, U>(&'a self, mut f: impl FnMut(&'a T) -> U) -> Path { + pub(crate) fn map_ref<'a, U>(&'a self, mut f: impl FnMut(&'a T) -> U) -> Path { let path = self.0.iter(); let path = path.map(move |(part, opt)| (part.as_ref().map(&mut f), *opt)); Path(path.collect()) @@ -142,7 +172,8 @@ impl Path { } impl Part { - fn map U>(self, mut f: F) -> Part { + /// Apply a function to the contained indices. + pub(crate) fn map U>(self, mut f: F) -> Part { use Part::{Index, Range}; match self { Index(i) => Index(f(i)), diff --git a/jaq-interpret/src/val.rs b/jaq-interpret/src/val.rs index c6dfbfc1c..3cce7a900 100644 --- a/jaq-interpret/src/val.rs +++ b/jaq-interpret/src/val.rs @@ -1,7 +1,7 @@ use crate::box_iter::BoxIter; +use crate::path::Opt; use core::fmt::Display; use core::ops::{Add, Div, Mul, Neg, Rem, Sub}; -use jaq_syn::path::Opt; // Makes `f64::from_str` accessible as intra-doc link. #[cfg(doc)] diff --git a/jaq-interpret/tests/common/mod.rs b/jaq-interpret/tests/common/mod.rs index 5a48bd831..930a79de4 100644 --- a/jaq-interpret/tests/common/mod.rs +++ b/jaq-interpret/tests/common/mod.rs @@ -2,8 +2,8 @@ use jaq_interpret::json::{Error, Val, ValR}; use serde_json::Value; fn yields(x: Val, code: &str, ys: impl Iterator) { + use jaq_interpret::load::{Arena, File, Loader}; use jaq_interpret::{Compiler, Native}; - use jaq_syn::load::{Arena, File, Loader}; let arena = Arena::default(); let loader = Loader::new([]); diff --git a/jaq-play/Cargo.toml b/jaq-play/Cargo.toml index 04b7ec88e..bc20fb2ab 100644 --- a/jaq-play/Cargo.toml +++ b/jaq-play/Cargo.toml @@ -16,10 +16,10 @@ wasm-opt = false crate-type = ["cdylib", "rlib"] [dependencies] -jaq-syn = { version = "1.1.0", path = "../jaq-syn" } jaq-interpret = { version = "1.2.0", path = "../jaq-interpret" } jaq-core = { version = "1.2.0", path = "../jaq-core" } jaq-std = { version = "1.2.0", path = "../jaq-std" } + aho-corasick = "1.1.2" codesnake = { version = "0.2" } hifijson = "0.2" diff --git a/jaq-std/Cargo.toml b/jaq-std/Cargo.toml index 89351b1e3..9cd901e70 100644 --- a/jaq-std/Cargo.toml +++ b/jaq-std/Cargo.toml @@ -11,9 +11,8 @@ keywords = ["json", "query", "jq"] rust-version = "1.64" [dependencies] -jaq-syn = { version = "1.6.0", path = "../jaq-syn" } +jaq-interpret = { version = "1.5.0", path = "../jaq-interpret" } [dev-dependencies] -jaq-interpret = { version = "1.2.0", path = "../jaq-interpret" } jaq-core = { version = "1.4.0", path = "../jaq-core" } serde_json = "1.0" diff --git a/jaq-std/src/lib.rs b/jaq-std/src/lib.rs index df93896f2..68b1521b7 100644 --- a/jaq-std/src/lib.rs +++ b/jaq-std/src/lib.rs @@ -8,8 +8,9 @@ #![warn(missing_docs)] extern crate alloc; +use jaq_interpret::load; /// Return the standard library. -pub fn std() -> alloc::vec::Vec> { - jaq_syn::parse(include_str!("std.jq"), |p| p.defs()).unwrap() +pub fn std() -> alloc::vec::Vec> { + load::parse(include_str!("std.jq"), |p| p.defs()).unwrap() } diff --git a/jaq-std/tests/common/mod.rs b/jaq-std/tests/common/mod.rs index a762089eb..05e478e1e 100644 --- a/jaq-std/tests/common/mod.rs +++ b/jaq-std/tests/common/mod.rs @@ -2,7 +2,7 @@ use jaq_interpret::json::{Error, Val, ValR}; use serde_json::Value; fn yields(x: Val, code: &str, ys: impl Iterator) { - use jaq_syn::load::{Arena, File, Loader}; + use jaq_interpret::load::{Arena, File, Loader}; let arena = Arena::default(); let loader = Loader::new(jaq_std::std()); diff --git a/jaq-syn/Cargo.toml b/jaq-syn/Cargo.toml deleted file mode 100644 index 6d71b67d9..000000000 --- a/jaq-syn/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "jaq-syn" -version = "1.6.0" -authors = ["Michael Färber "] -edition = "2021" -license = "MIT" -readme = "../README.md" -description = "Parser for the jq language" -repository = "https://github.com/01mf02/jaq" -keywords = ["json", "query", "jq"] -categories = ["parser-implementations"] -rust-version = "1.63" - -[features] -default = ["std"] -std = [] - -[dependencies] -typed-arena = "2.0.2" diff --git a/jaq-syn/src/lib.rs b/jaq-syn/src/lib.rs deleted file mode 100644 index 7c56dd2cb..000000000 --- a/jaq-syn/src/lib.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! JSON query language parser. -#![no_std] -#![forbid(unsafe_code)] -#![warn(missing_docs)] - -extern crate alloc; - -pub mod lex; -pub mod load; -mod ops; -pub mod parse; -pub mod path; -mod prec_climb; -pub mod test; - -pub use lex::Lexer; -pub use load::Loader; -pub use ops::{MathOp, OrdOp}; -pub use parse::Parser; - -/// Lex a string and parse resulting tokens, returning [`None`] if any error occurred. -/// -/// Example: -/// -/// ~~~ -/// # use jaq_syn::parse; -/// let t = parse("[] | .[]", |p| p.term()); -/// ~~~ -pub fn parse<'s, T: Default, F>(s: &'s str, f: F) -> Option -where - F: for<'t> FnOnce(&mut Parser<'s, 't>) -> parse::Result<'s, 't, T>, -{ - Parser::new(&Lexer::new(s).lex().ok()?).parse(f).ok() -} - -/// Return the span of a string slice `part` relative to a string slice `whole`. -/// -/// The caller must ensure that `part` is fully contained inside `whole`. -pub fn span(whole: &str, part: &str) -> core::ops::Range { - let start = part.as_ptr() as usize - whole.as_ptr() as usize; - start..start + part.len() -} diff --git a/jaq-syn/src/path.rs b/jaq-syn/src/path.rs deleted file mode 100644 index 9b0c443fb..000000000 --- a/jaq-syn/src/path.rs +++ /dev/null @@ -1,62 +0,0 @@ -//! Value access and iteration. -use alloc::vec::Vec; - -/// A path such as `.[].a?[1:]`. -pub type Path = Vec<(Part, Opt)>; - -/// A part of a path, such as `[]`, `a`, and `[1:]` in `.[].a?[1:]`. -#[derive(Clone, Debug)] -pub enum Part { - /// Access arrays with integer and objects with string indices - Index(I), - /// Iterate over arrays with optional range bounds and over objects without bounds - Range(Option, Option), -} - -impl Default for Part { - fn default() -> Self { - Self::Range(None, None) - } -} - -/// Optionality of a path part, i.e. whether `?` is present. -/// -/// For example, `[] | .a` fails with an error, while `[] | .a?` returns nothing. -/// By default, path parts are *essential*, meaning that they fail. -/// Annotating them with `?` makes them *optional*. -#[derive(Copy, Clone, Debug)] -pub enum Opt { - /// Return nothing if the input cannot be accessed with the path - Optional, - /// Fail if the input cannot be accessed with the path - Essential, -} - -impl Part { - /// Apply a function to the contained indices. - pub fn map(self, mut f: impl FnMut(I) -> J) -> Part { - match self { - Self::Index(i) => Part::Index(f(i)), - Self::Range(l, h) => Part::Range(l.map(&mut f), h.map(f)), - } - } -} - -impl Opt { - /// If `self` is optional, return `x`, else fail with `f(x)`. - pub fn fail(self, x: T, f: impl FnOnce(T) -> E) -> Result { - match self { - Self::Optional => Ok(x), - Self::Essential => Err(f(x)), - } - } - - /// If `self` is optional, return all items of the iterator that are `Ok` and succeed, - /// else return all items of the iterator and fail if any is `Err`. - pub fn collect(self, iter: impl Iterator>) -> Result, E> { - match self { - Self::Optional => Ok(iter.filter_map(Result::ok).collect()), - Self::Essential => iter.collect(), - } - } -} diff --git a/jaq/Cargo.toml b/jaq/Cargo.toml index 6f4081377..ee1f3f233 100644 --- a/jaq/Cargo.toml +++ b/jaq/Cargo.toml @@ -15,7 +15,6 @@ rust-version = "1.64" default = ["mimalloc"] [dependencies] -jaq-syn = { version = "1.6.0", path = "../jaq-syn" } jaq-interpret = { version = "1.2.0", path = "../jaq-interpret" } jaq-core = { version = "1.2.0", path = "../jaq-core" } jaq-std = { version = "1.5.0", path = "../jaq-std" } diff --git a/jaq/src/main.rs b/jaq/src/main.rs index e7c0f1ba7..6d413aff0 100644 --- a/jaq/src/main.rs +++ b/jaq/src/main.rs @@ -1,6 +1,6 @@ use clap::{Parser, ValueEnum}; use core::fmt::{self, Display, Formatter}; -use jaq_interpret::{compile, Ctx, Native, RcIter, Val}; +use jaq_interpret::{compile, load, Ctx, Native, RcIter, Val}; use std::io::{self, BufRead, Write}; use std::path::{Path, PathBuf}; use std::process::{ExitCode, Termination}; @@ -263,7 +263,7 @@ fn args_named(var_val: &[(String, Val)]) -> Val { fn parse(path: &str, code: &str, vars: &[String]) -> Result<(Vec, Filter), Vec> { use compile::Compiler; - use jaq_syn::load::{import, Arena, File, Loader}; + use load::{import, Arena, File, Loader}; let vars: Vec<_> = vars.iter().map(|v| format!("${v}")).collect(); let arena = Arena::default(); @@ -286,8 +286,8 @@ fn parse(path: &str, code: &str, vars: &[String]) -> Result<(Vec, Filter), Ok((vals, filter)) } -fn load_errors(errs: jaq_syn::load::Errors<&str>) -> Vec { - use jaq_syn::load::Error; +fn load_errors(errs: load::Errors<&str>) -> Vec { + use load::Error; let errs = errs.into_iter().map(|(file, err)| { let code = file.code; @@ -392,7 +392,7 @@ fn collect_if<'a, T: 'a, E: 'a>( } } -type FileReports = (jaq_syn::load::File, Vec); +type FileReports = (load::File, Vec); #[derive(Debug)] enum Error { @@ -626,20 +626,18 @@ impl Color { } fn report_io(code: &str, (path, error): (&str, String)) -> Report { - let path_range = jaq_syn::span(code, path); + let path_range = load::span(code, path); Report { message: format!("could not load file {}: {}", path, error), labels: [(path_range, [(error, None)].into(), Color::Red)].into(), } } -fn report_lex(code: &str, (expected, found): jaq_syn::lex::Error<&str>) -> Report { - use jaq_syn::span; - +fn report_lex(code: &str, (expected, found): load::lex::Error<&str>) -> Report { // truncate found string to its first character let found = &found[..found.char_indices().nth(1).map_or(found.len(), |(i, _)| i)]; - let found_range = span(code, found); + let found_range = load::span(code, found); let found = match found { "" => [("unexpected end of input".to_string(), None)].into(), c => [("unexpected character ", None), (c, Some(Color::Red))] @@ -649,10 +647,10 @@ fn report_lex(code: &str, (expected, found): jaq_syn::lex::Error<&str>) -> Repor let label = (found_range, found, Color::Red); let labels = match expected { - jaq_syn::lex::Expect::Delim(open) => { + load::lex::Expect::Delim(open) => { let text = [("unclosed delimiter ", None), (open, Some(Color::Yellow))] .map(|(s, c)| (s.into(), c)); - Vec::from([(span(code, open), text.into(), Color::Yellow), label]) + Vec::from([(load::span(code, open), text.into(), Color::Yellow), label]) } _ => Vec::from([label]), }; @@ -663,8 +661,8 @@ fn report_lex(code: &str, (expected, found): jaq_syn::lex::Error<&str>) -> Repor } } -fn report_parse(code: &str, (expected, found): jaq_syn::parse::Error<&str>) -> Report { - let found_range = jaq_syn::span(code, found); +fn report_parse(code: &str, (expected, found): load::parse::Error<&str>) -> Report { + let found_range = load::span(code, found); let found = if found.is_empty() { "unexpected end of input" @@ -680,7 +678,7 @@ fn report_parse(code: &str, (expected, found): jaq_syn::parse::Error<&str>) -> R } fn report_compile(code: &str, (found, undefined): compile::Error<&str>) -> Report { - let found_range = jaq_syn::span(code, found); + let found_range = load::span(code, found); let message = format!("undefined {}", undefined.as_str()); let found = [(message.clone(), None)].into(); @@ -713,7 +711,7 @@ impl Report { } } -fn run_test(test: jaq_syn::test::Test) -> Result<(Val, Val), Error> { +fn run_test(test: load::test::Test) -> Result<(Val, Val), Error> { let (ctx, filter) = parse("", &test.filter, &[]).map_err(Error::Report)?; let inputs = RcIter::new(Box::new(core::iter::empty())); @@ -733,7 +731,7 @@ fn run_test(test: jaq_syn::test::Test) -> Result<(Val, Val), Error> { fn run_tests(file: std::fs::File) -> ExitCode { let lines = io::BufReader::new(file).lines().map(Result::unwrap); - let tests = jaq_syn::test::Parser::new(lines); + let tests = load::test::Parser::new(lines); let (mut passed, mut total) = (0, 0); for test in tests {