Skip to main content

jay/
ir.rs

1//! The language-agnostic program representation and its evaluator.
2
3use std::collections::HashMap;
4use std::sync::Arc;
5
6use crate::array::{Array, Data};
7use crate::error::{Error, ErrorKind, Result, Span};
8use crate::fmt::{format_array, FmtOpts};
9use crate::frontend::{ControlStrictness, Lang, Rules};
10use crate::fuse::FusedKernel;
11use crate::verb::{arrays_match, Agreement, Ctx, Env, EvalCfg, Tol, Verb};
12
13/// Where an assignment puts its name. The two differ only inside an
14/// explicit definition, which is the only thing that has a local frame.
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub enum Scope {
17    /// J `=.`, APL's default inside a definition: the running definition's
18    /// own frame, discarded when it returns.
19    Local,
20    /// J `=:`: the program's names, visible to everything that runs later.
21    Global,
22    /// APL `⍺←`: the local frame, but only where the name has no value yet.
23    /// A left argument that was supplied keeps the value it arrived with.
24    LocalDefault,
25}
26
27#[derive(Clone, Debug)]
28pub enum Expr {
29    Const(Array, Span),
30    /// A bound parameter, by position in `Program::params`.
31    Param(usize, Span),
32    /// A name assigned earlier in the same program.
33    Name(String, Span),
34    /// Yields the assigned value in expression position; a whole sentence
35    /// that is an assignment displays nothing at the top level.
36    Assign { name: String, value: Box<Expr>, scope: Scope, span: Span },
37    /// APL `A[i;j]←v`: the named value with the part the brackets select
38    /// replaced. The name is read, a copy is written, and the copy takes
39    /// the name's place. An elided slot selects its whole axis.
40    AmendIndex {
41        name: String,
42        slots: Vec<Option<Expr>>,
43        value: Box<Expr>,
44        origin: i64,
45        scope: Scope,
46        span: Span,
47    },
48    /// A control-flow sentence (J's control words, APL's `:If` family).
49    /// Its value is the value of the last sentence the branch it chose
50    /// executed. J holds one to an explicit definition's body, as the
51    /// reference does; APL's stands outside a definition too.
52    Control(Box<Control>, Span),
53    Monad { verb: Verb, y: Box<Expr>, span: Span },
54    Dyad { verb: Verb, x: Box<Expr>, y: Box<Expr>, span: Span },
55    /// APL `⎕← expr` and `⍞← expr`: print, pass the value through. `bare`
56    /// is the `⍞←` form, which writes the characters and nothing else;
57    /// `⎕←` ends the line.
58    PrintPass { value: Box<Expr>, bare: bool, span: Span },
59    /// APL `⍞` and `⎕` standing where a value belongs: one line of input.
60    /// `eval` is the `⎕` form, which runs the line as APL rather than
61    /// taking its characters.
62    Input { eval: bool, span: Span },
63    /// A chain of elementwise verbs evaluated in one blockwise pass (see
64    /// [`crate::fuse`]). `inputs` are the subtrees the chain reads; `orig`
65    /// is the chain itself, which runs whenever the kernel declines.
66    Fused { kernel: FusedKernel, inputs: Vec<Expr>, orig: Box<Expr>, span: Span },
67    /// A marker the fusion pass leaves when it has rewritten the program
68    /// across sentence boundaries: it does nothing and yields nothing, and
69    /// carries the sentences the program was compiled from so that
70    /// [`crate::fuse::unfused`] can rebuild them.
71    Elided { orig: Vec<Expr>, span: Span },
72    /// A sentence that named a verb (J `mean =. +/ % #`). The frontend has
73    /// already substituted the verb into the later sentences that use the
74    /// name, so nothing runs here; the node is kept so that the sentence
75    /// still yields no value, and so that [`Program::explain`] can show it.
76    VerbDef { name: String, verb: Verb, span: Span },
77    /// A sentence that named an adverb or a conjunction (J `m =. /`). A
78    /// modifier is applied when the sentence holding it is parsed, so this
79    /// node carries only what the name stands for; like
80    /// [`Expr::VerbDef`] it runs nothing and yields nothing.
81    ModDef { name: String, spelling: String, conjunction: bool, span: Span },
82}
83
84/// A control-flow sentence. Every body is a block: a list of sentences whose
85/// value is the last one's.
86#[derive(Clone, Debug)]
87pub enum Control {
88    /// `if. T do. B elseif. T do. B else. B end.`, and APL's `:If` family.
89    /// The arms are tested in order; `otherwise` is the `else.` body.
90    If { arms: Vec<Branch>, otherwise: Option<Vec<Expr>> },
91    /// `while.` and `whilst.`, APL's `:While` and `:Repeat`. `body_first`
92    /// runs the body once before the first test; `until` inverts the test.
93    While { test: Vec<Expr>, body: Vec<Expr>, body_first: bool, until: bool },
94    /// `for. y do. B end.` / `for_i.` / `:For i :In y`. One name binds each
95    /// item and `<name>_index` its position; several — APL's `:For a b :In
96    /// y` — take the item apart and bind one of its own items each. No name
97    /// binds nothing, which is `for.` without a suffix.
98    For { names: Vec<String>, source: Box<Expr>, body: Vec<Expr> },
99    /// `select. T case. S do. B end.` and `:Select`. A case with no test is
100    /// the default (`case. do.`, `:Else`); `fall_through` is `fcase.`.
101    Select { subject: Box<Expr>, cases: Vec<Branch> },
102    /// `try. B catch. B end.`. The catch block runs on a language error;
103    /// a gap in libjay itself is never caught.
104    Try { body: Vec<Expr>, catch: Vec<Expr> },
105    /// `return.` / `:Return`: leave the definition with the value in hand.
106    Return,
107    /// `break.` / `:Leave`: leave the innermost loop.
108    Break,
109    /// APL `→ e`: continue at the line e names. An empty value falls
110    /// through to the next line; anything that is not a line of this
111    /// definition — `→0` above all — leaves it.
112    Branch(Box<Expr>),
113    /// `continue.` / `:Continue`: start the innermost loop's next iteration.
114    Continue,
115    /// A dfn's guard, `cond:expr`: the body is the dfn's answer when the
116    /// condition holds, and the definition returns there.
117    ///
118    /// It is not an `:If` with one arm, because it reads its condition
119    /// more strictly: Dyalog wants exactly one 0 or 1, and refuses `2`,
120    /// `1 1`, `⍬` and a character alike.
121    Guard { test: Vec<Expr>, body: Vec<Expr> },
122}
123
124/// One arm of an `if.` or `select.`: a test (absent for the default arm) and
125/// the body to run when it holds.
126#[derive(Clone, Debug)]
127pub struct Branch {
128    pub test: Option<Vec<Expr>>,
129    pub body: Vec<Expr>,
130    /// `fcase.`: run the next arm's body too, without testing it.
131    pub fall_through: bool,
132    /// APL `:CaseList`: the test yields a LIST of candidates and the arm is
133    /// taken where the subject matches any one of its items, rather than
134    /// the list as a whole.
135    pub list: bool,
136}
137
138/// The right-argument name a NILADIC APL definition carries. No sentence
139/// can write it, so the body cannot read the argument it never gets, and a
140/// definition wearing it is called by naming it rather than applying it.
141pub const NILADIC: &str = "(no argument)";
142
143/// An explicit definition: J's `3 : '…'`, `4 : '…'` and `{{ … }}`, APL's
144/// `{…}` and `∇`-defined functions.
145#[derive(Debug)]
146pub struct ExplicitDef {
147    /// How the definition names itself in diagnostics and `explain`.
148    pub name: String,
149    /// The names the arguments arrive under: `(left, right)`. A definition
150    /// with no left name has no dyadic valence.
151    pub left: Option<String>,
152    pub right: String,
153    /// True where a left name is part of the definition's valence rather
154    /// than a name the body may or may not read: J's `4 : '…'` and a `{{ }}`
155    /// that mentions `x` are dyads and nothing else, while an APL dfn that
156    /// names `⍺` still runs monadically and finds `⍺` undefined.
157    pub dyad_only: bool,
158    /// True where a left argument the definition has NO name for is simply
159    /// dropped rather than refused: a dfn is ambivalent whatever its body
160    /// mentions, so `3 {⍵×2} 5` is 10, while a `∇`-definition or a J
161    /// `3 : '…'` refuses the argument it cannot bind.
162    pub spare_left: bool,
163    /// The name the result is read from when the body does not yield one
164    /// (an APL `∇`-definition's `Z←`); None means the body's own value.
165    pub result: Option<String>,
166    /// Names the header declares local (APL's `;name` list).
167    pub locals: Vec<String>,
168    pub body: Vec<Expr>,
169    /// The value a body that ran nothing yields; None makes that an error.
170    pub empty: Option<Array>,
171    /// APL's branch labels: each label with the body statement it names.
172    /// A label's value is its line number, which is one more than its
173    /// position here, and `→` takes one of those numbers.
174    pub labels: Vec<(String, usize)>,
175    /// The dfns this one is written INSIDE, outermost first, by
176    /// [`ExplicitDef::id`]. A dfn's body reads the names its enclosing
177    /// dfns made local — `{a←10 ⋄ {a+⍵} ⍵} 5` is 15 — and this is what
178    /// tells a running body which frames on the stack are its own
179    /// lexical parents rather than an unrelated caller's. Empty for
180    /// everything that is not a nested dfn.
181    pub enclosing: Vec<u64>,
182    /// This definition's identity among the dfns of one compilation, so
183    /// that a nested one can name it in `enclosing`. Zero where nothing
184    /// is nested inside.
185    pub id: u64,
186    /// True when running the body can have no effect beyond its result.
187    pub pure: bool,
188}
189
190impl Expr {
191    /// How deeply this tree nests, counted WITHOUT recursing — the point
192    /// of the measurement is that walking such a tree is what runs out of
193    /// stack, so the measurement itself must not.
194    pub(crate) fn depth(&self) -> usize {
195        let mut deepest = 0usize;
196        let mut stack: Vec<(&Expr, usize)> = vec![(self, 1)];
197        while let Some((e, d)) = stack.pop() {
198            deepest = deepest.max(d);
199            let kids: Vec<&Expr> = match e {
200                Expr::Const(..)
201                | Expr::Param(..)
202                | Expr::Name(..)
203                | Expr::Control(..)
204                | Expr::VerbDef { .. }
205                | Expr::Input { .. }
206                | Expr::ModDef { .. } => Vec::new(),
207                Expr::Assign { value, .. } | Expr::PrintPass { value, .. } => vec![value],
208                Expr::AmendIndex { slots, value, .. } => {
209                    slots.iter().flatten().chain(std::iter::once(&**value)).collect()
210                }
211                Expr::Monad { y, .. } => vec![y],
212                Expr::Dyad { x, y, .. } => vec![x, y],
213                Expr::Fused { inputs, orig, .. } => {
214                    inputs.iter().chain(std::iter::once(&**orig)).collect()
215                }
216                Expr::Elided { orig, .. } => orig.iter().collect(),
217            };
218            stack.extend(kids.into_iter().map(|c| (c, d + 1)));
219        }
220        deepest
221    }
222
223    pub fn span(&self) -> Span {
224        match self {
225            Expr::Const(_, s) | Expr::Param(_, s) | Expr::Name(_, s) => *s,
226            Expr::Control(_, s) => *s,
227            Expr::AmendIndex { span, .. } | Expr::Input { span, .. } => *span,
228            Expr::Assign { span, .. }
229            | Expr::Monad { span, .. }
230            | Expr::Dyad { span, .. }
231            | Expr::PrintPass { span, .. }
232            | Expr::Fused { span, .. }
233            | Expr::Elided { span, .. }
234            | Expr::VerbDef { span, .. }
235            | Expr::ModDef { span, .. } => *span,
236        }
237    }
238
239    /// Widen (or move) the source this node points at. A parenthesised
240    /// expression uses it to take in its own brackets, so that a caret
241    /// under it underlines something balanced.
242    pub fn set_span(&mut self, to: Span) {
243        match self {
244            Expr::Const(_, s) | Expr::Param(_, s) | Expr::Name(_, s) => *s = to,
245            Expr::Control(_, s) => *s = to,
246            Expr::AmendIndex { span, .. } | Expr::Input { span, .. } => *span = to,
247            Expr::Assign { span, .. }
248            | Expr::Monad { span, .. }
249            | Expr::Dyad { span, .. }
250            | Expr::PrintPass { span, .. }
251            | Expr::Fused { span, .. }
252            | Expr::Elided { span, .. }
253            | Expr::VerbDef { span, .. }
254            | Expr::ModDef { span, .. } => *span = to,
255        }
256    }
257
258    /// Sentences whose top level is an assignment, explicit output, or the
259    /// pass's record of what the program was yield no value to the
260    /// sequence.
261    fn is_silent(&self) -> bool {
262        matches!(
263            self,
264            Expr::Assign { .. }
265                | Expr::AmendIndex { .. }
266                | Expr::PrintPass { .. }
267                | Expr::Elided { .. }
268                | Expr::VerbDef { .. }
269                | Expr::ModDef { .. }
270        )
271    }
272
273    /// Sentences whose value is SHY: it flows to whatever consumes it, and
274    /// a session does not display it. An assignment has one — inside a
275    /// definition, where an assignment is a value rather than nothing —
276    /// and so does `⎕←`, which has displayed the value already. At the top
277    /// level `is_silent` gets to those two first: there the sequence keeps
278    /// no value at all.
279    fn is_shy(&self) -> bool {
280        matches!(self, Expr::Assign { .. } | Expr::AmendIndex { .. } | Expr::PrintPass { .. })
281    }
282
283    /// Whether this sentence ends by APPLYING a verb, so that the
284    /// application's own shyness is the sentence's. Everything else — a
285    /// name, a constant, a fused chain — answers with a value of its own.
286    fn is_application(&self) -> bool {
287        matches!(self, Expr::Monad { .. } | Expr::Dyad { .. })
288    }
289}
290
291#[derive(Clone, Debug)]
292pub struct ParamSpec {
293    pub name: String,
294}
295
296/// A compiled program: immutable, reusable, holds no data bindings.
297#[derive(Clone, Debug)]
298pub struct Program {
299    pub stmts: Vec<Expr>,
300    pub params: Vec<ParamSpec>,
301    /// The source as the user would recognise it (interpolations shown as
302    /// `{name}`); all spans point into this string.
303    pub display_src: String,
304    pub agreement: Agreement,
305    pub fmt: FmtOpts,
306    /// The dialect this program was compiled under, resolved.
307    pub rules: Rules,
308}
309
310/// What an instrumented run saw at one node.
311#[derive(Clone, Debug)]
312pub(crate) struct Note {
313    pub shape: Vec<usize>,
314    pub dtype: crate::dtype::DType,
315    /// How the value's buffer was laid out — worth saying only when it was
316    /// not the row-major order everything assumes.
317    pub layout: crate::array::Layout,
318    /// For a fused node: whether the kernel itself produced the value, and
319    /// the reason it declined when it did not.
320    pub kernel_ran: Option<bool>,
321    pub decline: Option<crate::fuse::Decline>,
322    /// For a fused node in a run that was given a device: where the
323    /// arithmetic happened.
324    pub placement: crate::device::Placement,
325}
326
327/// Notes from one run, keyed by the address of the node in the tree that
328/// ran. Explaining borrows the same `Program`, so the addresses still name
329/// the same nodes; nothing outside this crate ever sees them.
330pub(crate) type Trace = HashMap<usize, Note>;
331
332pub(crate) fn key(e: &Expr) -> usize {
333    std::ptr::from_ref(e) as usize
334}
335
336/// What a run produced: the value of the last sentence, and whether a
337/// session would display it.
338///
339/// A SHY value is one an APL session keeps to itself: the answer of a
340/// definition that came from an assignment. The value is the same either
341/// way — it flows to whatever consumes it, is assigned, is printed by a
342/// caller that asks — and only a caller that displays results unasked
343/// needs the flag.
344#[derive(Clone, Debug)]
345pub struct Outcome {
346    /// None when the last sentence yields no value at all: an assignment,
347    /// `⎕←`, a definition of a name.
348    pub value: Option<Array>,
349    /// Meaningful only where there is a value; false for J, which has no
350    /// shy results.
351    pub shy: bool,
352}
353
354impl Program {
355    /// Execute with one value per parameter, in `params` order.
356    /// Returns None when the last sentence yields no value.
357    ///
358    /// The run has no input source: an expression that reads one — APL's
359    /// `⍞` and `⎕`, J's `1!:1` — says so rather than reading anything.
360    /// [`Program::run_io`] is the same run with a source attached.
361    pub fn run(&self, args: &[Array], out: &mut dyn FnMut(&str)) -> Result<Option<Array>> {
362        Ok(self.exec(args, out, None, &mut None, None)?.value)
363    }
364
365    /// [`Program::run`], keeping what the value alone does not say: whether
366    /// a session would display it. Only a caller that prints results —
367    /// a REPL, a transcript — needs the difference.
368    pub fn run_detail(&self, args: &[Array], out: &mut dyn FnMut(&str)) -> Result<Outcome> {
369        self.exec(args, out, None, &mut None, None)
370    }
371
372    /// Execute with both halves of the sandbox's stdio wired: `out` takes
373    /// the program's output, `inp` answers its reads with one line at a
374    /// time (no terminator) and None once the input has ended.
375    pub fn run_io(
376        &self,
377        args: &[Array],
378        out: &mut dyn FnMut(&str),
379        inp: &mut dyn FnMut() -> Option<String>,
380    ) -> Result<Option<Array>> {
381        Ok(self.exec(args, out, Some(inp), &mut None, None)?.value)
382    }
383
384    /// [`Program::run_io`] with the fused kernels placed on `device`.
385    pub fn run_on_io(
386        &self,
387        device: &crate::device::Device,
388        args: &[Array],
389        out: &mut dyn FnMut(&str),
390        inp: &mut dyn FnMut() -> Option<String>,
391    ) -> Result<Option<Array>> {
392        Ok(self.exec(args, out, Some(inp), &mut None, Some(device))?.value)
393    }
394
395    /// Execute with the fused kernels placed on `device`.
396    ///
397    /// Placement is not binding: the program, its data and its diagnostics
398    /// are the same whatever device is named here. What a device will not
399    /// take runs on the CPU, and `explain` says which and why.
400    pub fn run_on(
401        &self,
402        device: &crate::device::Device,
403        args: &[Array],
404        out: &mut dyn FnMut(&str),
405    ) -> Result<Option<Array>> {
406        Ok(self.exec(args, out, None, &mut None, Some(device))?.value)
407    }
408
409    /// Execute and record every node's result shape and dtype. The trace is
410    /// returned even when a sentence fails, so that a partial explanation
411    /// still shows what did run.
412    pub(crate) fn trace(
413        &self,
414        args: &[Array],
415        out: &mut dyn FnMut(&str),
416        device: Option<&crate::device::Device>,
417    ) -> (Result<Option<Array>>, Trace) {
418        let mut rec = Some(Trace::new());
419        let r = self.exec(args, out, None, &mut rec, device).map(|o| o.value);
420        (r, rec.expect("the recorder stays in place"))
421    }
422
423    fn exec(
424        &self,
425        args: &[Array],
426        out: &mut dyn FnMut(&str),
427        inp: crate::verb::InputFn<'_>,
428        rec: &mut Option<Trace>,
429        device: Option<&crate::device::Device>,
430    ) -> Result<Outcome> {
431        if args.len() != self.params.len() {
432            let names: Vec<&str> = self.params.iter().map(|p| p.name.as_str()).collect();
433            let wanted = if names.is_empty() {
434                "no arguments".to_string()
435            } else {
436                format!("one value for each of {}", names.join(", "))
437            };
438            return Err(Error::new(
439                ErrorKind::Value,
440                format!("this program takes {wanted}, and was given {}", args.len()),
441                None,
442            ));
443        }
444        let cfg = EvalCfg {
445            agreement: self.agreement,
446            fmt: self.fmt,
447            tol: self.rules.tol(),
448            rules: self.rules,
449        };
450        let mut env = Env::new(args.to_vec());
451        let mut inp = inp;
452        let inp = crate::verb::reborrow_input(&mut inp);
453        let mut ctx = Ctx { cfg, out, inp, env: &mut env, device, shy: false };
454        // Only APL has shy results. A J definition whose last sentence is
455        // an assignment answers with the assigned value, displayed like
456        // any other.
457        let shyness = self.rules.lang == Lang::Apl;
458        let mut last = Outcome { value: None, shy: false };
459        for stmt in &self.stmts {
460            // A loop contains its own `:Leave`, and there is no definition
461            // out here for a `:Return` or a `→` to leave.
462            let (v, flow) = eval_stmt(stmt, &mut ctx, rec)?;
463            if flow != Flow::Normal {
464                return Err(Error::new(
465                    ErrorKind::Domain,
466                    "this control word leaves a definition, and there is none here",
467                    None,
468                ));
469            }
470            last = if stmt.is_silent() {
471                Outcome { value: None, shy: false }
472            } else {
473                Outcome { value: v, shy: shyness && ctx.shy }
474            };
475        }
476        Ok(last)
477    }
478
479    pub fn render_error(&self, e: &Error) -> String {
480        e.render(&self.display_src)
481    }
482
483    /// What this expression became, as text: one section per sentence,
484    /// giving the structure the frontend and the fusion pass produced.
485    ///
486    /// With one value per parameter (or none, for a program that takes
487    /// none) the program is also run, and every node is annotated with the
488    /// shape and dtype it produced — a fused node with whether its kernel
489    /// ran, and why not when it did not. The run is the ordinary one, so it
490    /// has the ordinary effects; output it makes is discarded here, and an
491    /// error stops the annotations and is reported at the end.
492    pub fn explain(&self, args: Option<&[Array]>) -> String {
493        crate::explain::explain(self, args, None)
494    }
495
496    /// [`Program::explain`], with the run placed on `device`: every fused
497    /// node then also says where its arithmetic happened, and why it was
498    /// not the device when it was not.
499    pub fn explain_on(
500        &self,
501        device: &crate::device::Device,
502        args: Option<&[Array]>,
503    ) -> String {
504        crate::explain::explain(self, args, Some(device))
505    }
506}
507
508/// Why a block stopped. `Normal` is falling off the end of it.
509#[derive(Clone, Copy, Debug, PartialEq, Eq)]
510pub(crate) enum Flow {
511    Normal,
512    Return,
513    Break,
514    Continue,
515    /// APL `→`: continue at this statement of the definition's body.
516    Goto(usize),
517}
518
519/// Run a block of sentences: the value is the last sentence's, and an
520/// assignment yields the value it assigned (the top level is the one place
521/// that discards it, and `Program::exec` applies that rule itself).
522pub(crate) fn run_block(
523    stmts: &[Expr],
524    last: Option<Array>,
525    ctx: &mut Ctx<'_>,
526    rec: &mut Option<Trace>,
527) -> Result<(Option<Array>, Flow)> {
528    let mut last = last;
529    // The value handed in was produced by the caller's own last sentence,
530    // and `ctx.shy` still describes it.
531    let mut shy = last.is_some() && ctx.shy;
532    for stmt in stmts {
533        let (v, flow) = eval_stmt(stmt, ctx, rec)?;
534        // `return.` and its relatives produce nothing of their own: the
535        // value in hand is what the definition hands back.
536        if let Some(v) = v {
537            last = Some(v);
538            shy = ctx.shy;
539        }
540        if flow != Flow::Normal {
541            ctx.shy = shy;
542            return Ok((last, flow));
543        }
544    }
545    ctx.shy = shy;
546    Ok((last, Flow::Normal))
547}
548
549/// One sentence, control words included. The value of a control sentence is
550/// the value of the last sentence the branch it chose ran.
551fn eval_stmt(
552    e: &Expr,
553    ctx: &mut Ctx<'_>,
554    rec: &mut Option<Trace>,
555) -> Result<(Option<Array>, Flow)> {
556    let Expr::Control(c, span) = e else {
557        let v = eval(e, ctx, rec)?;
558        // An application hands out the shyness the verb left behind; every
559        // other sentence's is the shape of the sentence itself.
560        if !e.is_application() {
561            ctx.shy = e.is_shy();
562        }
563        return Ok((Some(v), Flow::Normal));
564    };
565    ctx.shy = false;
566    let (v, flow) = eval_control(c, *span, ctx, rec)?;
567    // A branch that ran and produced nothing yields whatever the language
568    // gives an untaken branch: J's empty `i. 0 0`, and nothing at all in
569    // APL, where a function with no result is an error. A branch that left
570    // early yields nothing either way, so the value in hand survives.
571    let v = match (v, flow) {
572        (Some(v), _) => Some(v),
573        (None, Flow::Normal) => {
574            // Nothing ran, so nothing is being kept quiet.
575            ctx.shy = false;
576            ctx.env.current_def().and_then(|d| d.empty.clone())
577        }
578        (None, _) => {
579            ctx.shy = false;
580            None
581        }
582    };
583    if let (Some(t), Some(v)) = (rec.as_mut(), v.as_ref()) {
584        t.insert(
585            key(e),
586            Note {
587                shape: v.shape.clone(),
588                dtype: v.dtype(),
589                layout: v.layout(),
590                kernel_ran: None,
591                decline: None,
592                placement: crate::device::Placement::Default,
593            },
594        );
595    }
596    Ok((v, flow))
597}
598
599/// The value of a branch that executed nothing: J's `i. 0 0`.
600pub(crate) fn empty_result() -> Array {
601    Array::new(vec![0, 0], Data::I64(Vec::new().into()))
602}
603
604/// J's truth: an empty condition is true, and otherwise the first atom
605/// decides. Characters count by their code point, as the reference does.
606fn is_true(a: &Array, span: Span) -> Result<bool> {
607    if a.is_sparse() {
608        return is_true(&a.densified(), span);
609    }
610    if a.count() == 0 {
611        return Ok(true);
612    }
613    match &a.data {
614        Data::I64(v) => Ok(v.as_slice()[0] != 0),
615        Data::F64(v) => Ok(v.as_slice()[0] != 0.0),
616        Data::Bool(v) => Ok(v.as_slice()[0] != 0),
617        Data::Char(v) => Ok(v.as_slice()[0] as u32 != 0),
618        Data::Complex(v) => Ok(v.as_slice()[0] != crate::complex::ZERO),
619        Data::Ext(v) => Ok(v.as_slice()[0] != crate::exact::Ext::default()),
620        Data::Rat(v) => Ok(!v.as_slice()[0].is_zero()),
621        Data::Box(_) => Err(Error::domain("a condition must be numeric, not boxed", span)),
622        Data::Symbol(_) => {
623            Err(Error::domain("a condition must be numeric, not a symbol", span))
624        }
625    }
626}
627
628/// Bind one iteration of a `for.` loop. One name takes the item whole and
629/// `<name>_index` its position; several take the item apart, one of its own
630/// items each, and the item has to have exactly that many.
631fn bind_for_names(
632    names: &[String],
633    item: &Array,
634    i: usize,
635    ctx: &mut Ctx<'_>,
636    span: Span,
637) -> Result<()> {
638    if names.len() == 1 {
639        ctx.env.assign(names[0].clone(), item.clone(), Scope::Local);
640        ctx.env.assign(
641            format!("{}_index", names[0]),
642            Array::scalar_i64(i as i64),
643            Scope::Local,
644        );
645        return Ok(());
646    }
647    let have = if item.rank() == 0 { 1 } else { item.shape[0] };
648    if have != names.len() {
649        return Err(Error::new(
650            ErrorKind::Length,
651            format!("{} names bind an item of {have}", names.len()),
652            Some(span),
653        ));
654    }
655    for (k, name) in names.iter().enumerate() {
656        let part = if item.rank() == 0 { item.clone() } else { item.item(k) };
657        ctx.env.assign(name.clone(), crate::verb::open_cell(&part), Scope::Local);
658    }
659    Ok(())
660}
661
662/// Whether a control structure's condition holds.
663///
664/// The lenient reading is the one both languages ship: an empty condition
665/// is true and otherwise the first atom decides. Dyalog reads a condition
666/// strictly instead — one element and no more — so `:If 1 1` is an error
667/// there where it takes the first here.
668fn condition_holds(a: &Array, ctx: &Ctx<'_>, span: Span) -> Result<bool> {
669    if ctx.cfg.rules.control_strictness == ControlStrictness::Strict && a.count() != 1 {
670        return Err(Error::domain("a condition must be a single value", span));
671    }
672    is_true(a, span)
673}
674
675/// Whether a `:CaseList` arm's list holds the subject. The comparison is
676/// the one `:Case` makes, item by item.
677fn any_item_matches(subject: &Array, list: &Array, tol: Tol) -> bool {
678    if list.rank() == 0 {
679        return arrays_match(subject, &crate::verb::open_cell(list), tol);
680    }
681    (0..list.shape[0])
682        .any(|i| arrays_match(subject, &crate::verb::open_cell(&list.item(i)), tol))
683}
684
685/// Whether a dfn's guard holds. Its condition is read strictly: exactly
686/// one element, and that element 0 or 1. `{2:1 ⋄ 0}`, `{1 1:1 ⋄ 0}`,
687/// `{⍬:1 ⋄ 0}` and `{'x':1 ⋄ 0}` are all refused, where a control
688/// structure's `:If` takes the first element of whatever it is given.
689fn guard_holds(a: &Array, span: Span) -> Result<bool> {
690    if a.is_sparse() {
691        return guard_holds(&a.densified(), span);
692    }
693    let refuse = || Error::domain("a guard's condition must be a single 0 or 1", span);
694    if a.count() != 1 {
695        return Err(refuse());
696    }
697    match a.to_i64_vec().as_deref() {
698        Some([0]) => Ok(false),
699        Some([1]) => Ok(true),
700        _ => Err(refuse()),
701    }
702}
703
704fn eval_control(
705    c: &Control,
706    span: Span,
707    ctx: &mut Ctx<'_>,
708    rec: &mut Option<Trace>,
709) -> Result<(Option<Array>, Flow)> {
710    match c {
711        Control::Return => Ok((None, Flow::Return)),
712        // `→ e`: an empty target falls through, a line number of this
713        // definition jumps to it, and anything else leaves.
714        Control::Branch(target) => {
715            let to = eval(target, ctx, rec)?;
716            if to.count() == 0 {
717                return Ok((None, Flow::Normal));
718            }
719            let line = to
720                .to_i64_vec()
721                .and_then(|v| v.first().copied())
722                .ok_or_else(|| Error::domain("a branch target is a line number", span))?;
723            let lines = ctx.env.current_def().map_or(0, |d| d.body.len() as i64);
724            if line >= 1 && line <= lines {
725                return Ok((None, Flow::Goto(line as usize - 1)));
726            }
727            Ok((None, Flow::Return))
728        }
729        Control::Break => Ok((None, Flow::Break)),
730        Control::Continue => Ok((None, Flow::Continue)),
731        Control::Guard { test, body } => {
732            let (t, flow) = run_block(test, None, ctx, rec)?;
733            if flow != Flow::Normal {
734                return Ok((t, flow));
735            }
736            let Some(v) = &t else {
737                return Err(Error::domain("a guard's condition produced no value", span));
738            };
739            if guard_holds(v, span)? {
740                return run_block(body, None, ctx, rec);
741            }
742            Ok((None, Flow::Normal))
743        }
744        Control::If { arms, otherwise } => {
745            for arm in arms {
746                let test = arm.test.as_deref().unwrap_or(&[]);
747                let (t, flow) = run_block(test, None, ctx, rec)?;
748                if flow != Flow::Normal {
749                    return Ok((t, flow));
750                }
751                let taken = match &t {
752                    Some(v) => condition_holds(v, ctx, span)?,
753                    None => true,
754                };
755                if taken {
756                    return run_block(&arm.body, None, ctx, rec);
757                }
758            }
759            match otherwise {
760                Some(body) => run_block(body, None, ctx, rec),
761                None => Ok((None, Flow::Normal)),
762            }
763        }
764        Control::While { test, body, body_first, until } => {
765            let mut last = None;
766            let mut first = *body_first;
767            loop {
768                if !first {
769                    let (t, flow) = run_block(test, None, ctx, rec)?;
770                    if flow != Flow::Normal {
771                        return Ok((t, flow));
772                    }
773                    let mut go = match &t {
774                        Some(v) => condition_holds(v, ctx, span)?,
775                        None => false,
776                    };
777                    if *until {
778                        go = !go;
779                    }
780                    if !go {
781                        return Ok((last, Flow::Normal));
782                    }
783                }
784                first = false;
785                let (v, flow) = run_block(body, last, ctx, rec)?;
786                last = v;
787                match flow {
788                    Flow::Normal | Flow::Continue => {}
789                    Flow::Break => return Ok((last, Flow::Normal)),
790                    // A branch out of a loop leaves the loop, and the
791                    // definition's own statement list takes it from there.
792                    other => return Ok((last, other)),
793                }
794            }
795        }
796        Control::For { names, source, body } => {
797            let src = eval(source, ctx, rec)?;
798            let n = if src.rank() == 0 { 1 } else { src.shape[0] };
799            let mut last = None;
800            for i in 0..n {
801                if !names.is_empty() {
802                    let item = if src.rank() == 0 { src.clone() } else { src.item(i) };
803                    // APL binds the item's CONTENTS — `:For p :In (1 2)(3 4)`
804                    // gives `p` a pair of numbers, not an enclosure of one —
805                    // where J leaves its boxes shut.
806                    let item = if ctx.cfg.rules.lang == Lang::Apl {
807                        crate::verb::open_cell(&item)
808                    } else {
809                        item
810                    };
811                    bind_for_names(names, &item, i, ctx, span)?;
812                }
813                let (v, flow) = run_block(body, last, ctx, rec)?;
814                last = v;
815                match flow {
816                    Flow::Normal | Flow::Continue => {}
817                    Flow::Break => return Ok((last, Flow::Normal)),
818                    // A branch out of a loop leaves the loop, and the
819                    // definition's own statement list takes it from there.
820                    other => return Ok((last, other)),
821                }
822            }
823            Ok((last, Flow::Normal))
824        }
825        Control::Select { subject, cases } => {
826            let subject = eval(subject, ctx, rec)?;
827            let tol = ctx.cfg.tol;
828            let mut running = false;
829            let mut last = None;
830            for case in cases {
831                if !running {
832                    match &case.test {
833                        None => running = true,
834                        Some(test) => {
835                            let (t, flow) = run_block(test, None, ctx, rec)?;
836                            if flow != Flow::Normal {
837                                return Ok((t, flow));
838                            }
839                            // The reference compares with match (`-:`), not
840                            // membership: `case. 1 2` takes the list 1 2.
841                            // `:CaseList 1 2` is the membership arm, and
842                            // takes either.
843                            running = t.is_some_and(|v| {
844                                if case.list {
845                                    any_item_matches(&subject, &v, tol)
846                                } else {
847                                    arrays_match(&subject, &v, tol)
848                                }
849                            });
850                        }
851                    }
852                }
853                if running {
854                    let (v, flow) = run_block(&case.body, last, ctx, rec)?;
855                    last = v;
856                    if flow != Flow::Normal {
857                        return Ok((last, flow));
858                    }
859                    if !case.fall_through {
860                        return Ok((last, Flow::Normal));
861                    }
862                    // `fcase.` runs the next body without testing it.
863                    running = true;
864                }
865            }
866            Ok((last, Flow::Normal))
867        }
868        Control::Try { body, catch } => {
869            // The catch block answers for the languages' own errors. A gap
870            // in libjay is not one of them: swallowing a "not supported
871            // yet" would turn a promise into a wrong answer.
872            match run_block(body, None, ctx, rec) {
873                Ok(r) => Ok(r),
874                Err(e) if matches!(e.kind, ErrorKind::NotYet | ErrorKind::Internal) => Err(e),
875                Err(_) => run_block(catch, None, ctx, rec),
876            }
877        }
878    }
879}
880
881/// Apply an explicit definition. `x` is None for a monadic application.
882pub(crate) fn call_explicit(
883    def: &Arc<ExplicitDef>,
884    x: Option<&Array>,
885    y: &Array,
886    ctx: &mut Ctx<'_>,
887    span: Span,
888) -> Result<Array> {
889    if x.is_some() && def.left.is_none() && !def.spare_left {
890        return Err(Error::new(
891            ErrorKind::Domain,
892            format!("{} has no dyadic definition", def.name),
893            Some(span),
894        ));
895    }
896    if x.is_none() && def.dyad_only {
897        return Err(Error::new(
898            ErrorKind::Domain,
899            format!(
900                "{} has no monadic definition: it names {}",
901                def.name,
902                def.left.as_deref().unwrap_or("a left argument")
903            ),
904            Some(span),
905        ));
906    }
907    let mut frame: HashMap<String, Array> = HashMap::new();
908    frame.insert(def.right.clone(), y.clone());
909    if let (Some(name), Some(v)) = (&def.left, x) {
910        frame.insert(name.clone(), v.clone());
911    }
912    // A label's value is its line number, which is what `→` takes.
913    for (label, at) in &def.labels {
914        frame.insert(label.clone(), Array::scalar_i64(*at as i64 + 1));
915    }
916    ctx.env.enter(frame, Arc::clone(def), span)?;
917    let mut rec = None;
918    let out = run_body(&def.body, ctx, &mut rec);
919    let frame = ctx.env.leave();
920    let value = out?;
921    // The body's shyness, which `run_body` left in the context, is the
922    // call's: a definition whose answer came from an assignment answers
923    // shyly, and the sentence that applied it does not display it.
924    let body_shy = ctx.shy;
925    ctx.shy = false;
926    // An APL `∇`-definition names its result; the body's own value is not
927    // it, and a definition that never assigned the name has no result.
928    if let Some(name) = &def.result {
929        return frame.get(name).cloned().ok_or_else(|| {
930            Error::new(
931                ErrorKind::Value,
932                format!("{} did not set its result {name}", def.name),
933                Some(span),
934            )
935        });
936    }
937    match value {
938        Some(v) => {
939            ctx.shy = body_shy;
940            Ok(v)
941        }
942        None => def.empty.clone().ok_or_else(|| {
943            Error::new(
944                ErrorKind::Value,
945                format!("{} produced no result", def.name),
946                Some(span),
947            )
948        }),
949    }
950}
951
952/// How many statements a branching definition may run before libjay stops
953/// it. A `→` loop has no other bound, and an unbounded one would hang.
954const BRANCH_LIMIT: usize = 1 << 22;
955
956/// A definition's body, statement by statement, with `→` free to move the
957/// place it runs from. The value is the last statement that produced one.
958fn run_body(
959    stmts: &[Expr],
960    ctx: &mut Ctx<'_>,
961    rec: &mut Option<Trace>,
962) -> Result<Option<Array>> {
963    let mut last = None;
964    let mut shy = false;
965    let mut at = 0usize;
966    let mut steps = 0usize;
967    while at < stmts.len() {
968        steps += 1;
969        if steps > BRANCH_LIMIT {
970            return Err(Error::new(
971                ErrorKind::Domain,
972                format!("a definition branched more than {BRANCH_LIMIT} times"),
973                Some(stmts[at].span()),
974            )
975            .note("a loop written with → needs a branch that leaves it"));
976        }
977        let (v, flow) = eval_stmt(&stmts[at], ctx, rec)?;
978        if let Some(v) = v {
979            last = Some(v);
980            shy = ctx.shy;
981        }
982        match flow {
983            Flow::Normal => at += 1,
984            Flow::Goto(to) => at = to,
985            _ => break,
986        }
987    }
988    ctx.shy = shy;
989    Ok(last)
990}
991
992/// A noun expression's value where the whole of it can be settled now:
993/// constants combined by pure verbs, with no name, no bound parameter and
994/// no control flow anywhere in it. Modifiers that capture a noun operand
995/// use this, so a written-out `(<a:;1)}` is as good as a literal.
996pub(crate) fn fold_const(e: &Expr, cfg: EvalCfg) -> Option<Array> {
997    fn closed(e: &Expr) -> bool {
998        match e {
999            Expr::Const(..) => true,
1000            Expr::Monad { verb, y, .. } => verb.is_pure() && closed(y),
1001            Expr::Dyad { verb, x, y, .. } => verb.is_pure() && closed(x) && closed(y),
1002            _ => false,
1003        }
1004    }
1005    if !closed(e) {
1006        return None;
1007    }
1008    cfg.pure(|ctx| eval(e, ctx, &mut None).ok())
1009}
1010
1011fn eval(e: &Expr, ctx: &mut Ctx<'_>, rec: &mut Option<Trace>) -> Result<Array> {
1012    // The walk is recursive, so a deeply nested sentence would run out of
1013    // stack; the ceiling turns that into a diagnostic.
1014    let _depth = crate::verb::Nesting::enter(e.span())?;
1015    let v = eval_node(e, ctx, rec)?;
1016    if let Some(t) = rec.as_mut() {
1017        // A fused node has already left what it knows about its kernel.
1018        let (kernel_ran, decline, placement) = t.get(&key(e)).map_or(
1019            (None, None, crate::device::Placement::Default),
1020            |n| (n.kernel_ran, n.decline, n.placement.clone()),
1021        );
1022        t.insert(
1023            key(e),
1024            Note {
1025                shape: v.shape.clone(),
1026                dtype: v.dtype(),
1027                layout: v.layout(),
1028                kernel_ran,
1029                decline,
1030                placement,
1031            },
1032        );
1033    }
1034    Ok(v)
1035}
1036
1037fn eval_node(e: &Expr, ctx: &mut Ctx<'_>, rec: &mut Option<Trace>) -> Result<Array> {
1038    match e {
1039        Expr::Const(a, _) => Ok(a.clone()),
1040        Expr::Param(i, _) => ctx.env.arg(*i),
1041        Expr::Name(n, span) => ctx.env.get(n).ok_or_else(|| {
1042            Error::new(ErrorKind::Value, format!("undefined name: {n}"), Some(*span))
1043        }),
1044        Expr::Assign { name, value, scope, .. } => {
1045            let v = eval(value, ctx, rec)?;
1046            ctx.env.assign(name.clone(), v.clone(), *scope);
1047            Ok(v)
1048        }
1049        Expr::AmendIndex { name, slots, value, origin, scope, span } => {
1050            let base = ctx.env.get(name).ok_or_else(|| {
1051                Error::new(ErrorKind::Value, format!("undefined name: {name}"), Some(*span))
1052            })?;
1053            // The sentence reads right to left, so the value comes first.
1054            let v = eval(value, ctx, rec)?;
1055            let mut idx = Vec::with_capacity(slots.len());
1056            for slot in slots {
1057                idx.push(match slot {
1058                    Some(e) => Some(eval(e, ctx, rec)?),
1059                    None => None,
1060                });
1061            }
1062            // Amending a sparse array writes into its dense expansion; the
1063            // stored form is not preserved across the write.
1064            let out = crate::verb::amend_at(
1065                &base.densified(),
1066                &idx,
1067                &v.densified(),
1068                *origin,
1069                ctx.cfg.near(),
1070                *span,
1071            )?;
1072            ctx.env.assign(name.clone(), out.clone(), *scope);
1073            Ok(out)
1074        }
1075        // A control sentence is run by `eval_stmt`, which is the only place
1076        // its signal has anywhere to go.
1077        Expr::Control(..) => {
1078            Err(Error::internal("a control sentence appeared in expression position"))
1079        }
1080        Expr::Monad { verb, y, span } => {
1081            let vy = eval(y, ctx, rec)?;
1082            verb.monad(&vy, ctx, *span)
1083        }
1084        Expr::Dyad { verb, x, y, span } => {
1085            // The right argument evaluates first: both languages read
1086            // sentences right to left, and inline assignments rely on it.
1087            let vy = eval(y, ctx, rec)?;
1088            let vx = eval(x, ctx, rec)?;
1089            verb.dyad(&vx, &vy, ctx, *span)
1090        }
1091        Expr::PrintPass { value, bare, .. } => {
1092            let v = eval(value, ctx, rec)?;
1093            let text = format_array(&v, &ctx.cfg.fmt);
1094            (ctx.out)(&text);
1095            // `⍞←` writes the characters and nothing else, so that several
1096            // of them build one line; `⎕←` ends the line it wrote.
1097            if !bare {
1098                (ctx.out)("\n");
1099            }
1100            Ok(v)
1101        }
1102        // `⍞` takes the line as characters; `⎕` runs it as APL, through the
1103        // same machinery `⍎` uses, over the names the program already has.
1104        Expr::Input { eval: run_it, span } => {
1105            let line = ctx.read_line(*span)?;
1106            if !run_it {
1107                return Ok(Array::from_chars(line.chars().collect()));
1108            }
1109            crate::verb::execute_source(&line, true, ctx, *span)
1110        }
1111        Expr::Fused { kernel, inputs, orig, .. } => {
1112            let mut vals = Vec::with_capacity(inputs.len());
1113            for e in inputs {
1114                // A fused kernel reads flat buffers, so a sparse leaf is
1115                // expanded before the chain sees it.
1116                vals.push(eval(e, ctx, rec)?.densified());
1117            }
1118            let (ran, placement) = crate::fuse::eval_on(ctx.device, kernel, &vals);
1119            if let Some(t) = rec.as_mut() {
1120                let decline =
1121                    if ran.is_none() { crate::fuse::decline_reason(kernel, &vals) } else { None };
1122                // Shape and dtype arrive from the wrapper above; only the
1123                // kernel's own story is recorded here.
1124                t.insert(
1125                    key(e),
1126                    Note {
1127                        shape: Vec::new(),
1128                        dtype: crate::dtype::DType::I64,
1129                        layout: crate::array::Layout::RowMajor,
1130                        kernel_ran: Some(ran.is_some()),
1131                        decline,
1132                        placement,
1133                    },
1134                );
1135            }
1136            match ran {
1137                Some(a) => Ok(a),
1138                // The kernel does not cover this data. The chain it came
1139                // from does, including whatever error it raises; it runs
1140                // over the values just computed, not over the leaves again.
1141                None => {
1142                    let tree = crate::fuse::fallback_tree(kernel, orig, &vals);
1143                    // The fallback tree is temporary, so its nodes are not
1144                    // ones an explanation can name: it runs unrecorded.
1145                    let v = eval(&tree, ctx, &mut None)?;
1146                    Ok(crate::fuse::fallback_finish(kernel, v))
1147                }
1148            }
1149        }
1150        // Naming a verb records it so that a definition can call itself by
1151        // name; the sentence is silent, so the value is never read.
1152        Expr::VerbDef { name, verb, .. } => {
1153            ctx.env.define(name.clone(), verb.clone());
1154            Ok(Array::scalar_i64(0))
1155        }
1156        // A record of what the program was, and a named modifier, which the
1157        // parser has already applied everywhere it is used: silent
1158        // sentences whose value is never read.
1159        Expr::Elided { .. } | Expr::ModDef { .. } => Ok(Array::scalar_i64(0)),
1160    }
1161}