Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions jaq-core/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,10 @@ pub(crate) enum Term<T = TermId> {
/// Singleton object (`{f: g}`)
ObjSingle(T, T),

/// Bound variable (`$x`), label (`label $x`), or filter argument (`a`)
/// Bound variable (`$x`) or filter argument (`a`)
Var(VarId),
Break(VarId),
Yield(VarId),

/// Call to a filter (`filter`, `filter(…)`)
CallDef(TermId, Box<[Arg<T>]>, VarSkip, CallType),
Expand Down Expand Up @@ -324,6 +326,16 @@ pub(crate) enum Bind<V, L = V, F = V> {
Fun(F),
}

impl<V, L, F> Bind<V, L, F> {
pub(crate) fn get_label(&self) -> Option<&L> {
if let Self::Label(l) = self {
Some(l)
} else {
None
}
}
}

struct Locals<S> {
// usize = number of vars
funs: MapVec<(S, Arity), (Fun<S>, usize)>,
Expand Down Expand Up @@ -636,7 +648,12 @@ impl<'s, F> Compiler<&'s str, F> {
Arr(t) => Term::Arr(self.iterm(t.map_or_else(|| Call("!empty", Vec::new()), |t| *t))),
Neg(t) => Term::Neg(self.iterm(*t)),
Label(x, t) => Term::Label(self.with_label(x, |c| c.iterm(*t))),
Break(x) => self.break_(x),
Break(x) => self
.label(x)
.map_or_else(|| self.fail(x, Undefined::Label), Term::Break),
Yield(x) => self
.label(x)
.map_or_else(|| self.fail(x, Undefined::Label), Term::Yield),
IfThenElse(if_thens, else_) => {
let f = |(if_, then_)| (self.iterm(if_), self.iterm_tr(then_, tr));
let if_thens: Vec<_> = if_thens.into_iter().map(f).collect();
Expand Down Expand Up @@ -860,11 +877,12 @@ impl<'s, F> Compiler<&'s str, F> {
self.fail(x, Undefined::Var)
}

fn break_(&mut self, x: &'s str) -> Term {
if let Some(l) = self.locals.vars.bound.get_last(&Bind::Label(x)) {
return Term::Var(self.locals.vars.total - l);
}
self.fail(x, Undefined::Label)
fn label(&mut self, x: &'s str) -> Option<usize> {
self.locals
.vars
.bound
.get_last(&Bind::Label(x))
.map(|l| self.locals.vars.total - l)
}

fn obj_entry(&mut self, k: parse::Term<&'s str>, v: Option<parse::Term<&'s str>>) -> Term {
Expand Down
1 change: 1 addition & 0 deletions jaq-core/src/exn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub(crate) enum Inner<'a, V> {
/// If this can be observed by users, then this is a bug.
TailCall(Box<(&'a TermId, Vars<V>, CallInput<V>)>),
Break(usize),
Yield(Box<(usize, V)>),
}

#[derive(Clone, Debug)]
Expand Down
29 changes: 25 additions & 4 deletions jaq-core/src/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,12 +239,14 @@ fn bind_run<'a, D: DataT, T: Clone + 'a>(

fn label_run<'a, D: DataT, T: 'a>(
cv: Cv<'a, D, T>,
from: fn(D::V<'a>) -> ValX<'a, T, D::V<'a>>,
run: impl Fn(Cv<'a, D, T>) -> ValXs<'a, T, D::V<'a>>,
) -> ValXs<'a, T, D::V<'a>> {
let ctx = cv.0.cons_label();
let labels = ctx.labels;
Box::new(run((ctx, cv.1)).map_while(move |y| match y {
Err(Exn(exn::Inner::Break(b))) if b == labels => None,
Err(Exn(exn::Inner::Yield(y))) if y.0 == labels => Some(from(y.1)),
y => Some(y),
}))
}
Expand Down Expand Up @@ -549,8 +551,14 @@ impl Id {
Ast::Var(v) => match cv.0.vars.get(*v).unwrap() {
Bind::Var(v) => box_once(Ok(v.clone())),
Bind::Fun((id, vars)) => id.run((cv.0.with_vars(vars.clone()), cv.1)),
Bind::Label(l) => box_once(Err(Exn(exn::Inner::Break(*l)))),
Bind::Label(_) => panic!(),
},
Ast::Break(v) => box_once(Err(Exn(exn::Inner::Break(
*cv.0.vars.get(*v).unwrap().get_label().unwrap(),
)))),
Ast::Yield(v) => box_once(Err(Exn(exn::Inner::Yield(
(*cv.0.vars.get(*v).unwrap().get_label().unwrap(), cv.1).into(),
)))),
Ast::CallDef(id, args, skip, call_typ) => {
let data = cv.0.data.clone();
let with_vars = move |vars| Ctx {
Expand All @@ -566,7 +574,7 @@ impl Id {
let cvs = bind_vars(args, cv.0.with_vars(Vars::new([])), cv, Clone::clone);
flat_map_then(cvs, |cv| (cv.0.lut().funs[*id].run)(cv))
}
Ast::Label(id) => label_run(cv, |cv| id.run(cv)),
Ast::Label(id) => label_run(cv, Ok, |cv| id.run(cv)),
}
}

Expand All @@ -576,7 +584,8 @@ impl Id {
/// In particular, `v | path(f)` in context `c` yields the same paths as
/// `f.paths((c, (v, Default::default())))`.
pub fn paths<'a, D: DataT>(&self, cv: Cvp<'a, D>) -> ValPathXs<'a, D::V<'a>> {
let err = |v| box_once(Err(Exn::from(Error::path_expr(v))));
let err1 = |v| Err(Exn::from(Error::path_expr(v)));
let err = |v| box_once(err1(v));
let proj_cv = |cv: &Cvp<'a, D>| (cv.0.clone(), cv.1 .0.clone());
let proj_val = |(val, _path): &(D::V<'a>, _)| val.clone();
match &cv.0.lut().terms[self.0] {
Expand Down Expand Up @@ -633,6 +642,12 @@ impl Id {
Bind::Fun(l) => l.0.paths((cv.0.with_vars(l.1.clone()), cv.1)),
Bind::Label(l) => box_once(Err(Exn(exn::Inner::Break(*l)))),
},
Ast::Break(v) => box_once(Err(Exn(exn::Inner::Break(
*cv.0.vars.get(*v).unwrap().get_label().unwrap(),
)))),
Ast::Yield(v) => box_once(Err(Exn(exn::Inner::Yield(
(*cv.0.vars.get(*v).unwrap().get_label().unwrap(), cv.1 .0).into(),
)))),
Ast::Fold(xs, pat, init, update, fold_type) => {
let xs = rc_lazy_list::List::from_iter(run_and_bind(xs, proj_cv(&cv), pat));
fold_run(xs, cv, init, update, fold_type, |f, cv| f.paths(cv))
Expand All @@ -648,7 +663,7 @@ impl Id {
let (into, from) = (exn::CallInput::Paths, exn::CallInput::unwrap_paths);
def_run(id, call_typ, cvs, Id::paths, with_vars, into, from)
}
Ast::Label(id) => label_run(cv, |cv| id.paths(cv)),
Ast::Label(id) => label_run(cv, err1, |cv| id.paths(cv)),
Ast::Native(id, args) => {
let cvs = bind_vars(args, cv.0.with_vars(Vars::new([])), cv, proj_val);
flat_map_then(cvs, |cv| (cv.0.lut().funs[*id].paths)(cv))
Expand Down Expand Up @@ -718,6 +733,12 @@ impl Id {
Bind::Fun(l) => l.0.update((cv.0.with_vars(l.1.clone()), cv.1), f),
Bind::Label(l) => box_once(Err(Exn(exn::Inner::Break(*l)))),
},
Ast::Break(v) => box_once(Err(Exn(exn::Inner::Break(
*cv.0.vars.get(*v).unwrap().get_label().unwrap(),
)))),
Ast::Yield(v) => box_once(Err(Exn(exn::Inner::Yield(
(*cv.0.vars.get(*v).unwrap().get_label().unwrap(), cv.1).into(),
)))),
Ast::CallDef(id, args, skip, _call_typ) => {
let init = cv.1.clone();
let cvs = bind_vars(args, cv.0.clone().skip_vars(*skip), cv, Clone::clone);
Expand Down
3 changes: 3 additions & 0 deletions jaq-core/src/load/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ pub enum Term<S> {
Label(S, Box<Self>),
/// Break out from control flow to location variable, e.g. `break $x`
Break(S),
/// TODO!!!
Yield(S),

/// `reduce` and `foreach`, e.g. `reduce .[] as $x (0; .+$x)`
Fold(S, Box<Self>, Pattern<S>, Vec<Self>),
Expand Down Expand Up @@ -515,6 +517,7 @@ impl<'s, 't> Parser<'s, 't> {
Term::Label(x, Box::new(tm))
}
Some(Token("break", _)) => Term::Break(self.var()?),
Some(Token("yield", _)) => Term::Yield(self.var()?),
Some(Token(fold @ ("reduce" | "foreach"), _)) => {
let xs = self.atom()?;
self.just("as")?;
Expand Down
Loading