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