Skip to main content

jay/frontend/
j.rs

1//! J frontend: lexer and the sentence parser, lowering to the shared IR.
2//!
3//! Word formation and sentence parsing follow the model published in the J
4//! Dictionary. Words are formed left to right; a sentence is then executed
5//! right to left by pushing words onto a stack and matching the leftmost four
6//! stack slots against the parse table after every push.
7
8use std::collections::{HashMap, HashSet};
9use std::ops::Range;
10use std::sync::Arc;
11
12use crate::array::{Array, Data};
13use crate::error::{Error, ErrorKind, Result, Span};
14use crate::frontend::{Segment, SourceParts};
15use crate::ir::{Branch, Control, ExplicitDef, Expr, Scope};
16use crate::verb::{
17    BoolDyad, DyadOp, Enclose, MonadOp, Power, Prim, ScalarDyad, ScalarMonad, Verb, WindowKind,
18    RANK_INF,
19};
20
21/// Parse a J program (one sentence per line) into IR statements.
22///
23/// Sentences are parsed in order over a table of the names that have been
24/// given verbs, because a name's part of speech decides how the sentence
25/// around it parses. A sentence that names a verb records it and produces
26/// no work; a later sentence that reads the name gets the verb substituted
27/// into it. That is enough for the straight-line programs this frontend
28/// compiles: there is no control flow for a definition to reach backwards
29/// through, and reassigning the name simply rebinds it from there on.
30pub fn parse(src: &SourceParts) -> Result<Vec<Expr>> {
31    let mut scope = Names::default();
32    let lines = lex(src)?;
33    let mut out = Vec::new();
34    let mut i = 0usize;
35    while i < lines.len() {
36        // A definition whose body is written on the lines below swallows
37        // them, so the sentence that comes out may span several of them.
38        let sentence = collect_definitions(&lines, &mut i, &mut scope, true)?;
39        if sentence.is_empty() {
40            continue;
41        }
42        out.push(scope.parse_sentence(sentence)?);
43    }
44    Ok(out)
45}
46
47/// What a name of modifier class stands for.
48#[derive(Clone, Debug)]
49enum Modifier {
50    /// A primitive modifier, by the spelling it is written with.
51    Prim(&'static str),
52    /// An explicit adverb or conjunction, by the body it was written with.
53    Explicit(Arc<ModSource>),
54}
55
56impl Modifier {
57    /// How the modifier names itself in `explain` and in diagnostics.
58    fn spelling(&self) -> String {
59        match self {
60            Modifier::Prim(g) => (*g).to_string(),
61            Modifier::Explicit(src) => src.name.clone(),
62        }
63    }
64}
65
66/// The parts of speech a sentence is read against. A name's part of speech
67/// decides how the sentence around it parses, so the table is carried from
68/// sentence to sentence and into every definition body.
69#[derive(Clone, Default)]
70struct Names {
71    verbs: HashMap<String, Verb>,
72    /// Names given an adverb or a conjunction (`m =. /`), by what they
73    /// stand for and whether it is a conjunction. A modifier is applied
74    /// when a sentence is parsed, so the name has to be resolved then too,
75    /// exactly as a verb name is.
76    mods: HashMap<String, (bool, Modifier)>,
77    /// Names that hold a value by the time a sentence is read. Only the
78    /// diagnostics need this: a name that is neither a verb nor a value is
79    /// an undefined name, not a sentence the parser has yet to learn.
80    nouns: HashSet<String>,
81    /// The value of a name given a literal, for the operands that have to
82    /// be known while the sentence is read. A gerund is data, so
83    /// `` g =. +`- `` and then `g@.1` needs what g holds; an assignment
84    /// whose value is not settled at parse time takes the name back out.
85    consts: HashMap<String, Array>,
86}
87
88impl Names {
89    /// Parse one sentence against the table and note what it did to the
90    /// names it mentions.
91    fn parse_sentence(&mut self, mut sentence: Vec<Frag>) -> Result<Expr> {
92        substitute_names(&mut sentence, &self.verbs, &self.mods);
93        let whole = sentence_span(&sentence);
94        let frag = reduce_to_fragment(sentence, self)?;
95        // A sentence that names a modifier is settled here rather than in
96        // the IR: what the name stands for is a parser object, and the
97        // node the sentence lowers to carries only its spelling.
98        if let Some(Frag::ModDef(name, conj, m, span)) = frag {
99            let spelling = m.spelling();
100            self.mods.insert(name.clone(), (conj, m));
101            self.verbs.remove(&name);
102            self.nouns.remove(&name);
103            return Ok(Expr::ModDef { name, spelling, conjunction: conj, span });
104        }
105        let stmt = lower_sentence(frag, whole)?;
106        self.record(&stmt);
107        Ok(stmt)
108    }
109
110    /// Note what a parsed sentence did to the names it mentions.
111    fn record(&mut self, stmt: &Expr) {
112        match stmt {
113            Expr::VerbDef { name, verb, .. } => {
114                self.verbs.insert(name.clone(), verb.clone());
115                self.mods.remove(name);
116                self.nouns.remove(name);
117            }
118            // A name given a noun stops being a verb, at any depth: J lets
119            // a name change part of speech, and the oracle agrees.
120            other => {
121                let mut assigned = Vec::new();
122                assigned_names(other, &mut assigned);
123                for name in assigned {
124                    self.verbs.remove(&name);
125                    self.mods.remove(&name);
126                    self.nouns.insert(name.clone());
127                    match literal_assigned(other, &name) {
128                        Some(a) => self.consts.insert(name, a),
129                        None => self.consts.remove(&name),
130                    };
131                }
132            }
133        }
134    }
135}
136
137/// The literal a sentence gave a name, where the whole sentence is that one
138/// assignment and its value is settled already.
139fn literal_assigned(stmt: &Expr, name: &str) -> Option<Array> {
140    match stmt {
141        Expr::Assign { name: n, value, .. } if n == name => match &**value {
142            Expr::Const(a, _) => Some(a.clone()),
143            _ => None,
144        },
145        _ => None,
146    }
147}
148
149// ------------------------------------------------------- explicit definitions
150
151/// J's control words. `for_i.` and its relatives carry the name they bind,
152/// which is why the suffix is kept apart from the word.
153const CONTROL_WORDS: [&str; 18] = [
154    "if.", "do.", "else.", "elseif.", "end.", "while.", "whilst.", "for.", "select.", "case.",
155    "fcase.", "return.", "break.", "continue.", "try.", "catch.", "catcht.", "throw.",
156];
157
158/// A control word and, for `for_i.`, the name it binds.
159fn control_word(word: &str) -> Option<(&'static str, Option<String>)> {
160    if let Some(w) = CONTROL_WORDS.iter().copied().find(|&w| w == word) {
161        return Some((w, None));
162    }
163    for (stem, w) in [("for_", "for."), ("goto_", "goto."), ("label_", "label.")] {
164        if let Some(rest) = word.strip_prefix(stem) {
165            let name = rest.strip_suffix('.')?;
166            if !name.is_empty() && is_j_name(name) {
167                return Some((w, Some(name.to_string())));
168            }
169        }
170    }
171    None
172}
173
174fn is_j_name(s: &str) -> bool {
175    let mut cs = s.chars();
176    cs.next().is_some_and(|c| c.is_ascii_alphabetic())
177        && s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
178}
179
180/// One piece of a definition body: a run of ordinary words, or a control
181/// word. A line break ends a sentence, and so does every control word.
182#[derive(Clone, Debug)]
183enum Item {
184    Sentence(Vec<Frag>),
185    Word { word: &'static str, suffix: Option<String>, span: Span },
186}
187
188impl Item {
189    fn word(&self) -> Option<&'static str> {
190        match self {
191            Item::Word { word, .. } => Some(word),
192            Item::Sentence(_) => None,
193        }
194    }
195
196    fn span(&self) -> Span {
197        match self {
198            Item::Word { span, .. } => *span,
199            Item::Sentence(f) => sentence_span(f),
200        }
201    }
202}
203
204/// Collapse every explicit definition in one line into a verb fragment.
205///
206/// `lines[*i]` is the line to read; `*i` advances past it and past any lines
207/// a definition took for its body. At the top level a control word is a
208/// spelling error, which is what the reference calls it.
209fn collect_definitions(
210    lines: &[Vec<Frag>],
211    i: &mut usize,
212    scope: &mut Names,
213    top_level: bool,
214) -> Result<Vec<Frag>> {
215    let mut sentence = lines[*i].clone();
216    *i += 1;
217    // `f =. 3 : '… f …'` calls itself by name, so the body has to be parsed
218    // with `f` already a verb. The name is resolved when it is applied.
219    let self_name = match (sentence.first(), sentence.get(1)) {
220        (Some(Frag::Name(n, _)), Some(a)) if a.is_assign() => Some(n.clone()),
221        _ => None,
222    };
223    loop {
224        let Some(open) = sentence.iter().position(|f| matches!(f, Frag::DdOpen(..))) else {
225            match find_colon_definition(&sentence) {
226                Some(at) => {
227                    take_colon_definition(&mut sentence, at, lines, i, scope, self_name.as_deref())?;
228                    continue;
229                }
230                // Every definition on the line is now one verb fragment, so
231                // a control word still standing is one nothing encloses.
232                None => {
233                    if top_level
234                        && let Some(Frag::Control(_, _, span)) =
235                            sentence.iter().find(|f| matches!(f, Frag::Control(..)))
236                    {
237                        return Err(Error::parse(
238                            "control words are only meaningful inside an explicit definition",
239                            *span,
240                        ));
241                    }
242                    return Ok(sentence);
243                }
244            }
245        };
246        take_direct_definition(&mut sentence, open, lines, i, scope, self_name.as_deref())?;
247    }
248}
249
250/// The index of the `:` of a `m : n` definition, if the line has one.
251fn find_colon_definition(sentence: &[Frag]) -> Option<usize> {
252    (1..sentence.len().saturating_sub(1)).find(|&k| {
253        matches!(&sentence[k], Frag::Conj(Modifier::Prim(":"), _))
254            && as_const(&sentence[k - 1]).is_some_and(|a| a.rank() == 0)
255            && matches!(&sentence[k + 1], Frag::Noun(Expr::Const(..)))
256    })
257}
258
259/// `m : n` — the definition whose body is a string, or the lines below when
260/// the right operand is `0`.
261fn take_colon_definition(
262    sentence: &mut Vec<Frag>,
263    at: usize,
264    lines: &[Vec<Frag>],
265    i: &mut usize,
266    scope: &mut Names,
267    self_name: Option<&str>,
268) -> Result<()> {
269    let span = Span::merge(sentence[at - 1].span(), sentence[at + 1].span());
270    let valence = as_const(&sentence[at - 1])
271        .and_then(Array::to_f64_vec)
272        .and_then(|v| v.first().copied())
273        .ok_or_else(|| Error::parse("an explicit definition starts with a number", span))?;
274    let body_arr = as_const(&sentence[at + 1]).cloned().expect("checked by the finder");
275    let body_span = sentence[at + 1].span();
276    let (dyadic, modifier) = match valence {
277        3.0 => (false, None),
278        4.0 => (true, None),
279        1.0 => (false, Some(false)),
280        2.0 => (false, Some(true)),
281        13.0 => return Err(Error::not_yet("tacit definitions (13 : '...')", span)),
282        v => return Err(Error::domain(format!("{v} is not an explicit definition"), span)),
283    };
284    let body = match &body_arr.data {
285        // `3 : 0`: the body is written on the lines below, ending with `)`.
286        Data::I64(_)
287        | Data::F64(_)
288        | Data::Bool(_)
289        | Data::Ext(_)
290        | Data::Rat(_)
291        | Data::Complex(_) => {
292            if body_arr.to_f64_vec().as_deref() != Some(&[0.0]) {
293                return Err(Error::parse("an explicit definition takes 0 or a string", body_span));
294            }
295            take_lines_until_paren(lines, i, body_span)?
296        }
297        Data::Char(chars) => {
298            let text: String = chars.as_slice().iter().collect();
299            let mut frags = Vec::new();
300            // The body sits one character past the opening quote; a doubled
301            // quote inside it shifts what follows by one column.
302            lex_line(&text, body_span.start + 1, &mut frags)?;
303            vec![frags]
304        }
305        Data::Box(_) => {
306            return Err(Error::parse("an explicit definition takes 0 or a string", body_span))
307        }
308    };
309    if let Some(conjunction) = modifier {
310        let name = if conjunction { "2 : '...'" } else { "1 : '...'" };
311        let src = mod_source(name, conjunction, body, self_name);
312        let frag = if conjunction {
313            Frag::Conj(Modifier::Explicit(Arc::new(src)), span)
314        } else {
315            Frag::Adverb(Modifier::Explicit(Arc::new(src)), span)
316        };
317        sentence.splice(at - 1..at + 2, [frag]);
318        return Ok(());
319    }
320    let name = if dyadic { "4 : '...'" } else { "3 : '...'" };
321    let verb = build_definition(body, dyadic, name, scope, self_name)?;
322    sentence.splice(at - 1..at + 2, [Frag::Verb(VerbFrag::V(verb), span)]);
323    Ok(())
324}
325
326/// The lines of a `3 : 0` body: everything up to a line that is a lone `)`.
327fn take_lines_until_paren(
328    lines: &[Vec<Frag>],
329    i: &mut usize,
330    span: Span,
331) -> Result<Vec<Vec<Frag>>> {
332    let mut body = Vec::new();
333    loop {
334        let Some(line) = lines.get(*i) else {
335            return Err(Error::parse("this definition's body has no closing `)`", span));
336        };
337        *i += 1;
338        if line.len() == 1 && matches!(line[0], Frag::RParen(_)) {
339            return Ok(body);
340        }
341        body.push(line.clone());
342    }
343}
344
345/// `{{ … }}` — the body is the words between the braces, on this line or on
346/// the lines below.
347fn take_direct_definition(
348    sentence: &mut Vec<Frag>,
349    open: usize,
350    lines: &[Vec<Frag>],
351    i: &mut usize,
352    scope: &mut Names,
353    self_name: Option<&str>,
354) -> Result<()> {
355    let open_span = sentence[open].span();
356    let Frag::DdOpen(marker, _) = sentence[open] else {
357        return Err(Error::internal("expected a direct definition's opening brackets"));
358    };
359    let mut depth = 1usize;
360    let mut body: Vec<Vec<Frag>> = Vec::new();
361    let mut tail: Vec<Frag> = Vec::new();
362    let mut close_span = open_span;
363    let mut line: Vec<Frag> = sentence[open + 1..].to_vec();
364    let mut cur: Vec<Frag> = Vec::new();
365    loop {
366        let mut closed = false;
367        for (k, f) in line.iter().enumerate() {
368            match f {
369                Frag::DdOpen(..) => {
370                    depth += 1;
371                    cur.push(f.clone());
372                }
373                Frag::DdClose(s) => {
374                    depth -= 1;
375                    if depth == 0 {
376                        close_span = *s;
377                        tail = line[k + 1..].to_vec();
378                        closed = true;
379                        break;
380                    }
381                    cur.push(f.clone());
382                }
383                _ => cur.push(f.clone()),
384            }
385        }
386        if !cur.is_empty() {
387            body.push(std::mem::take(&mut cur));
388        }
389        if closed {
390            break;
391        }
392        let Some(next) = lines.get(*i) else {
393            return Err(Error::parse("this definition has no closing `}}`", open_span));
394        };
395        *i += 1;
396        line = next.clone();
397    }
398    let span = Span::merge(open_span, close_span);
399    // The body's own words decide the part of speech, as they do in the
400    // reference: an operand name of the second position makes a
401    // conjunction, one of the first an adverb, and neither a verb. A
402    // `{{)a` marker says it outright instead.
403    let part = match marker {
404        None => {
405            if mentions(&body, "v") || mentions(&body, "n") {
406                Some(true)
407            } else if mentions(&body, "u") || mentions(&body, "m") {
408                Some(false)
409            } else {
410                None
411            }
412        }
413        Some('a') => Some(false),
414        Some('c') => Some(true),
415        Some('v' | 'm' | 'd') => None,
416        Some(other) => {
417            return Err(Error::not_yet(
418                format!("a direct definition marked `){other}`"),
419                open_span,
420            ))
421        }
422    };
423    let frag = match part {
424        Some(conjunction) => {
425            let src = mod_source("{{ ... }}", conjunction, body, self_name);
426            let m = Modifier::Explicit(Arc::new(src));
427            if conjunction { Frag::Conj(m, span) } else { Frag::Adverb(m, span) }
428        }
429        None => {
430            // `)d` and `)m` fix the valence; otherwise a body that names
431            // `x` is a dyad and nothing else.
432            let dyadic = match marker {
433                Some('d') => true,
434                Some('m') => false,
435                _ => mentions(&body, "x"),
436            };
437            let verb = build_definition(body, dyadic, "{{ ... }}", scope, self_name)?;
438            Frag::Verb(VerbFrag::V(verb), span)
439        }
440    };
441    let mut head: Vec<Frag> = sentence[..open].to_vec();
442    head.push(frag);
443    head.extend(tail);
444    *sentence = head;
445    Ok(())
446}
447
448/// True where a definition's words include this name.
449fn mentions(body: &[Vec<Frag>], name: &str) -> bool {
450    body.iter().any(|l| l.iter().any(|f| matches!(f, Frag::Name(n, _) if n == name)))
451}
452
453/// Parse a definition's body and wrap it in a verb.
454fn build_definition(
455    body: Vec<Vec<Frag>>,
456    dyadic: bool,
457    name: &str,
458    scope: &Names,
459    self_name: Option<&str>,
460) -> Result<Verb> {
461    // The body reads the names the program has already given, and binds its
462    // own arguments over them.
463    let mut inner = scope.clone();
464    inner.nouns.insert("y".to_string());
465    inner.verbs.remove("y");
466    inner.consts.remove("y");
467    if dyadic {
468        inner.nouns.insert("x".to_string());
469        inner.verbs.remove("x");
470        inner.consts.remove("x");
471    }
472    if let Some(n) = self_name {
473        inner.nouns.remove(n);
474        inner.consts.remove(n);
475        inner.verbs.insert(n.to_string(), Verb::Named(n.to_string()));
476    }
477    // A body may hold definitions of its own, and one of them may run past
478    // the end of its line, so the lines are collected before they are split
479    // into sentences.
480    let mut lines: Vec<Vec<Frag>> = Vec::new();
481    let mut k = 0usize;
482    while k < body.len() {
483        let line = collect_definitions(&body, &mut k, &mut inner, false)?;
484        if !line.is_empty() {
485            lines.push(line);
486        }
487    }
488    let items = split_items(&lines);
489    let mut cursor = Cursor { items: &items, at: 0 };
490    let stmts = parse_block(&mut cursor, &mut inner, &[])?;
491    if let Some(item) = cursor.peek() {
492        return Err(Error::parse(
493            format!("`{}` has no matching opening word", item.word().unwrap_or("word")),
494            item.span(),
495        ));
496    }
497    let pure = stmts.iter().all(block_is_pure);
498    Ok(Verb::Explicit(Arc::new(ExplicitDef {
499        name: name.to_string(),
500        left: dyadic.then(|| "x".to_string()),
501        right: "y".to_string(),
502        // J decides a definition's valence from its header (or, for a
503        // `{{ }}`, from its words): one that takes `x` is a dyad only.
504        dyad_only: dyadic,
505        result: None,
506        locals: Vec::new(),
507        body: stmts,
508        // A branch that runs nothing yields J's empty result, `i. 0 0`.
509        labels: Vec::new(),
510        empty: Some(crate::ir::empty_result()),
511        pure,
512    })))
513}
514
515// ------------------------------------------------------- explicit modifiers
516
517/// An explicit adverb or conjunction: `1 : '…'`, `2 : '…'` and the `{{ … }}`
518/// whose body names an operand.
519///
520/// The body is kept as words rather than as a parsed tree. Applying the
521/// modifier substitutes the operands into those words and parses them
522/// afresh, which is what J's own substitution rule says happens, and what
523/// lets a body that mentions no argument yield a verb of its own.
524#[derive(Debug)]
525struct ModSource {
526    /// How the definition names itself in diagnostics.
527    name: String,
528    /// Two operands rather than one.
529    conjunction: bool,
530    body: Vec<Vec<Frag>>,
531    /// The body names `x` or `y`, so it is the body of the derived VERB and
532    /// runs when that verb is applied. A body that names neither runs at
533    /// derivation instead, and what it makes is what the modifier produced.
534    deferred: bool,
535    /// The body names `x`: the derived verb is a dyad only.
536    dyadic: bool,
537    /// The name this definition is being given, so that its body can
538    /// mention it.
539    self_name: Option<String>,
540}
541
542fn mod_source(
543    name: &str,
544    conjunction: bool,
545    body: Vec<Vec<Frag>>,
546    self_name: Option<&str>,
547) -> ModSource {
548    let dyadic = mentions(&body, "x");
549    ModSource {
550        name: name.to_string(),
551        conjunction,
552        deferred: dyadic || mentions(&body, "y"),
553        dyadic,
554        self_name: self_name.map(str::to_string),
555        body,
556    }
557}
558
559thread_local! {
560    /// The explicit modifiers whose bodies are being parsed right now, by
561    /// address. A body that derives the modifier it belongs to would parse
562    /// for ever, so the nesting is what catches it.
563    static DERIVING: std::cell::RefCell<Vec<usize>> = const { std::cell::RefCell::new(Vec::new()) };
564}
565
566/// Removes the innermost derivation from the in-progress list however it
567/// ends.
568struct Deriving;
569
570impl Drop for Deriving {
571    fn drop(&mut self) {
572        DERIVING.with(|d| {
573            d.borrow_mut().pop();
574        });
575    }
576}
577
578/// Apply an explicit modifier to its operands.
579///
580/// The operands are substituted into the body under the names J gives them
581/// — `u` and `v` for verbs, `m` and `n` for nouns — and the body is parsed
582/// with those substitutions in place. A body that names an argument becomes
583/// the derived verb's body; one that does not is a sentence, and its value
584/// (usually a tacit verb) is what the derivation produced.
585fn derive_explicit(
586    src: &Arc<ModSource>,
587    u: Frag,
588    v: Option<Frag>,
589    scope: &Names,
590    span: Span,
591) -> Result<Frag> {
592    let addr = Arc::as_ptr(src) as usize;
593    let recursive = DERIVING.with(|d| {
594        let mut d = d.borrow_mut();
595        if d.contains(&addr) {
596            return true;
597        }
598        d.push(addr);
599        false
600    });
601    if recursive {
602        return Err(Error::not_yet(
603            "an explicit modifier whose body derives the modifier itself",
604            span,
605        ));
606    }
607    let _guard = Deriving;
608    let mut body = src.body.clone();
609    bind_operand(&mut body, "u", "m", &u);
610    if let Some(v) = &v {
611        bind_operand(&mut body, "v", "n", v);
612    }
613    // The body reads the names the program has given so far; the name this
614    // definition is being given is one of them, and it stands for the
615    // modifier rather than for a verb.
616    let mut inner = scope.clone();
617    if let Some(n) = &src.self_name {
618        inner.verbs.remove(n);
619        inner.nouns.remove(n);
620        inner.consts.remove(n);
621        inner.mods.insert(n.clone(), (src.conjunction, Modifier::Explicit(Arc::clone(src))));
622    }
623    if src.deferred {
624        let verb = build_definition(body, src.dyadic, &src.name, &inner, None)?;
625        return Ok(Frag::Verb(VerbFrag::V(verb), span));
626    }
627    // The derivation-time phase: the body is a sentence, parsed here and
628    // now, and what it reduces to is what the modifier made.
629    let mut lines: Vec<Vec<Frag>> = Vec::new();
630    let mut k = 0usize;
631    while k < body.len() {
632        let line = collect_definitions(&body, &mut k, &mut inner, false)?;
633        if !line.is_empty() {
634            lines.push(line);
635        }
636    }
637    if lines.len() != 1 {
638        return Err(Error::not_yet(
639            "an explicit modifier that names no argument and is more than one sentence",
640            span,
641        ));
642    }
643    let mut sentence = lines.pop().expect("checked length");
644    substitute_names(&mut sentence, &inner.verbs, &inner.mods);
645    match reduce_to_fragment(sentence, &inner)? {
646        Some(f) if f.is_real_verb() || f.is_noun() => Ok(respan(f, span)),
647        Some(f) => Err(Error::not_yet(
648            format!("an explicit modifier that produces {}", part_of_speech(&f)),
649            span,
650        )),
651        None => Err(Error::parse("syntax error", span)),
652    }
653}
654
655/// What part of speech a fragment belongs to, for a diagnostic.
656fn part_of_speech(f: &Frag) -> &'static str {
657    match f {
658        Frag::Adverb(..) => "an adverb",
659        Frag::Conj(..) => "a conjunction",
660        _ => "no value",
661    }
662}
663
664/// Put an operand in the place of the name it arrives under. A verb operand
665/// answers to `u` (or `v`), a noun one to `m` (or `n`); the other name is
666/// left alone, so a body that reaches for it reports an undefined name, as
667/// the reference does.
668fn bind_operand(body: &mut [Vec<Frag>], verb_name: &str, noun_name: &str, operand: &Frag) {
669    let wanted = if operand.is_real_verb() { verb_name } else { noun_name };
670    for line in body.iter_mut() {
671        for i in 0..line.len() {
672            let Frag::Name(n, span) = &line[i] else { continue };
673            if n != wanted {
674                continue;
675            }
676            let span = *span;
677            // An assignment to the name is a definition of it, not a use.
678            if line.get(i + 1).is_some_and(Frag::is_assign) {
679                continue;
680            }
681            line[i] = respan(operand.clone(), span);
682        }
683    }
684}
685
686/// True when nothing in this sentence can have an effect beyond its value.
687fn block_is_pure(e: &Expr) -> bool {
688    match e {
689        Expr::Const(..) | Expr::Param(..) | Expr::Name(..) => true,
690        Expr::Monad { verb, y, .. } => verb.is_pure() && block_is_pure(y),
691        Expr::Dyad { verb, x, y, .. } => {
692            verb.is_pure() && block_is_pure(x) && block_is_pure(y)
693        }
694        Expr::Assign { value, .. } => block_is_pure(value),
695        Expr::Control(c, _) => control_is_pure(c),
696        _ => false,
697    }
698}
699
700fn control_is_pure(c: &Control) -> bool {
701    let all = |b: &Vec<Expr>| b.iter().all(block_is_pure);
702    match c {
703        Control::Return | Control::Break | Control::Continue => true,
704        // J has no branch; the variant only reaches this frontend through
705        // the shared IR, and reading its target is as pure as any read.
706        Control::Branch(target) => block_is_pure(target),
707        Control::If { arms, otherwise } => {
708            arms.iter().all(|a| {
709                a.test.as_ref().is_none_or(all) && all(&a.body)
710            }) && otherwise.as_ref().is_none_or(all)
711        }
712        Control::While { test, body, .. } => all(test) && all(body),
713        Control::For { source, body, .. } => block_is_pure(source) && all(body),
714        Control::Select { subject, cases } => {
715            block_is_pure(subject)
716                && cases.iter().all(|c| c.test.as_ref().is_none_or(all) && all(&c.body))
717        }
718        Control::Try { body, catch } => all(body) && all(catch),
719    }
720}
721
722/// Split a definition's lines into sentences and control words.
723fn split_items(lines: &[Vec<Frag>]) -> Vec<Item> {
724    let mut items = Vec::new();
725    for line in lines {
726        let mut run: Vec<Frag> = Vec::new();
727        for f in line {
728            match f {
729                Frag::Control(word, suffix, span) => {
730                    if !run.is_empty() {
731                        items.push(Item::Sentence(std::mem::take(&mut run)));
732                    }
733                    items.push(Item::Word {
734                        word,
735                        suffix: suffix.clone(),
736                        span: *span,
737                    });
738                }
739                _ => run.push(f.clone()),
740            }
741        }
742        if !run.is_empty() {
743            items.push(Item::Sentence(run));
744        }
745    }
746    items
747}
748
749struct Cursor<'a> {
750    items: &'a [Item],
751    at: usize,
752}
753
754impl<'a> Cursor<'a> {
755    fn peek(&self) -> Option<&'a Item> {
756        self.items.get(self.at)
757    }
758
759    fn peek_word(&self) -> Option<&'static str> {
760        self.peek().and_then(Item::word)
761    }
762
763    fn next(&mut self) -> Option<&'a Item> {
764        let it = self.items.get(self.at);
765        if it.is_some() {
766            self.at += 1;
767        }
768        it
769    }
770
771    fn last_span(&self) -> Span {
772        self.items
773            .get(self.at.saturating_sub(1))
774            .map_or_else(|| Span::new(0, 0), Item::span)
775    }
776
777    /// Consume the word that must come next.
778    fn expect(&mut self, want: &str) -> Result<Span> {
779        match self.peek() {
780            Some(Item::Word { word, span, .. }) if *word == want => {
781                self.at += 1;
782                Ok(*span)
783            }
784            Some(other) => {
785                Err(Error::parse(format!("expected `{want}` here"), other.span()))
786            }
787            None => Err(Error::parse(format!("this block needs a `{want}`"), self.last_span())),
788        }
789    }
790}
791
792/// Parse sentences and control structures until one of `stop` is next.
793fn parse_block(cur: &mut Cursor<'_>, scope: &mut Names, stop: &[&str]) -> Result<Vec<Expr>> {
794    let mut out = Vec::new();
795    loop {
796        match cur.peek() {
797            None => return Ok(out),
798            Some(Item::Word { word, .. }) if stop.contains(word) => return Ok(out),
799            Some(Item::Sentence(frags)) => {
800                cur.at += 1;
801                out.push(scope.parse_sentence(frags.clone())?);
802            }
803            Some(Item::Word { .. }) => out.push(parse_control(cur, scope)?),
804        }
805    }
806}
807
808fn parse_control(cur: &mut Cursor<'_>, scope: &mut Names) -> Result<Expr> {
809    let Some(Item::Word { word, suffix, span }) = cur.next() else {
810        return Err(Error::internal("expected a control word"));
811    };
812    let start = *span;
813    let control = match *word {
814        "if." => parse_if(cur, scope)?,
815        "while." | "whilst." => {
816            let body_first = *word == "whilst.";
817            let test = parse_block(cur, scope, &["do."])?;
818            cur.expect("do.")?;
819            let body = parse_block(cur, scope, &["end."])?;
820            cur.expect("end.")?;
821            Control::While { test, body, body_first, until: false }
822        }
823        "for." => {
824            if let Some(name) = suffix {
825                scope.nouns.insert(name.clone());
826                scope.nouns.insert(format!("{name}_index"));
827                scope.verbs.remove(name);
828                scope.consts.remove(name);
829            }
830            let source = parse_block(cur, scope, &["do."])?;
831            cur.expect("do.")?;
832            let body = parse_block(cur, scope, &["end."])?;
833            let end = cur.expect("end.")?;
834            let source = one_expr(source, Span::merge(start, end))?;
835            Control::For { name: suffix.clone(), source: Box::new(source), body }
836        }
837        "select." => parse_select(cur, scope, start)?,
838        "try." => {
839            let body = parse_block(cur, scope, &["catch.", "catcht.", "end."])?;
840            if cur.peek_word() == Some("catcht.") {
841                return Err(Error::not_yet("throw. and catcht.", cur.last_span()));
842            }
843            let catch = if cur.peek_word() == Some("catch.") {
844                cur.expect("catch.")?;
845                parse_block(cur, scope, &["end."])?
846            } else {
847                Vec::new()
848            };
849            cur.expect("end.")?;
850            Control::Try { body, catch }
851        }
852        "return." => Control::Return,
853        "break." => Control::Break,
854        "continue." => Control::Continue,
855        "throw." | "catcht." => return Err(Error::not_yet("throw. and catcht.", start)),
856        "goto." | "label." => {
857            return Err(Error::not_yet("goto_name. and label_name.", start))
858        }
859        other => {
860            return Err(Error::parse(
861                format!("`{other}` has no matching opening word"),
862                start,
863            ))
864        }
865    };
866    let span = Span::merge(start, cur.last_span());
867    Ok(Expr::Control(Box::new(control), span))
868}
869
870fn parse_if(cur: &mut Cursor<'_>, scope: &mut Names) -> Result<Control> {
871    let mut arms = Vec::new();
872    let mut otherwise = None;
873    loop {
874        let test = parse_block(cur, scope, &["do."])?;
875        cur.expect("do.")?;
876        let body = parse_block(cur, scope, &["elseif.", "else.", "end."])?;
877        arms.push(Branch { test: Some(test), body, fall_through: false });
878        match cur.peek_word() {
879            Some("elseif.") => {
880                cur.at += 1;
881            }
882            Some("else.") => {
883                cur.at += 1;
884                otherwise = Some(parse_block(cur, scope, &["end."])?);
885                cur.expect("end.")?;
886                break;
887            }
888            _ => {
889                cur.expect("end.")?;
890                break;
891            }
892        }
893    }
894    // `elseif. do.` with no test is the reference's other spelling of
895    // `else.`: a final arm that always runs.
896    if let Some(last) = arms.last_mut() && last.test.as_ref().is_some_and(Vec::is_empty) {
897        last.test = None;
898    }
899    Ok(Control::If { arms, otherwise })
900}
901
902fn parse_select(cur: &mut Cursor<'_>, scope: &mut Names, start: Span) -> Result<Control> {
903    let subject = parse_block(cur, scope, &["case.", "fcase.", "end."])?;
904    let subject = one_expr(subject, start)?;
905    let mut cases = Vec::new();
906    loop {
907        let fall_through = match cur.peek_word() {
908            Some("case.") => false,
909            Some("fcase.") => true,
910            _ => {
911                cur.expect("end.")?;
912                break;
913            }
914        };
915        cur.at += 1;
916        let test = parse_block(cur, scope, &["do."])?;
917        cur.expect("do.")?;
918        let body = parse_block(cur, scope, &["case.", "fcase.", "end."])?;
919        // `case. do.` with no test is the default arm.
920        let test = (!test.is_empty()).then_some(test);
921        cases.push(Branch { test, body, fall_through });
922    }
923    Ok(Control::Select { subject: Box::new(subject), cases })
924}
925
926/// A block that has to be one sentence — a `for.` source, a `select.`
927/// subject. The value is the last sentence's, so the rest run for effect.
928fn one_expr(mut stmts: Vec<Expr>, span: Span) -> Result<Expr> {
929    match stmts.pop() {
930        Some(e) if stmts.is_empty() => Ok(e),
931        Some(_) => Err(Error::not_yet("several sentences where one value is needed", span)),
932        None => Err(Error::parse("this control word needs a value", span)),
933    }
934}
935
936/// Replace every name known to be a verb or a modifier by what it stands
937/// for, except where the name is the target of an assignment, which is a
938/// definition of the name rather than a use of it.
939fn substitute_names(
940    sentence: &mut [Frag],
941    verbs: &HashMap<String, Verb>,
942    mods: &HashMap<String, (bool, Modifier)>,
943) {
944    for i in 0..sentence.len() {
945        let Frag::Name(name, span) = &sentence[i] else { continue };
946        let (name, span) = (name.clone(), *span);
947        if sentence.get(i + 1).is_some_and(Frag::is_assign) {
948            continue;
949        }
950        if let Some(v) = verbs.get(&name) {
951            sentence[i] = Frag::Verb(VerbFrag::V(v.clone()), span);
952        } else if let Some((conj, m)) = mods.get(&name) {
953            sentence[i] = if *conj {
954                Frag::Conj(m.clone(), span)
955            } else {
956                Frag::Adverb(m.clone(), span)
957            };
958        }
959    }
960}
961
962/// Every name this sentence assigns a value to, inline assignments included.
963fn assigned_names(e: &Expr, out: &mut Vec<String>) {
964    match e {
965        Expr::Assign { name, value, .. } => {
966            out.push(name.clone());
967            assigned_names(value, out);
968        }
969        Expr::Monad { y, .. } => assigned_names(y, out),
970        Expr::Dyad { x, y, .. } => {
971            assigned_names(x, out);
972            assigned_names(y, out);
973        }
974        Expr::PrintPass { value, .. } => assigned_names(value, out),
975        _ => {}
976    }
977}
978
979// ---------------------------------------------------------------- fragments
980
981/// A stack fragment. The lexer emits these directly: a token and a parser
982/// fragment are the same thing in J, which is why the parse table can be
983/// stated over four adjacent stack slots.
984#[derive(Clone, Debug)]
985enum Frag {
986    /// Left edge of the sentence.
987    Mark,
988    Noun(Expr),
989    /// A name used as a value, or an assignment target.
990    Name(String, Span),
991    Verb(VerbFrag, Span),
992    Adverb(Modifier, Span),
993    Conj(Modifier, Span),
994    LParen(Span),
995    RParen(Span),
996    AssignLocal(Span),
997    AssignGlobal(Span),
998    /// A finished verb definition: `mean =. +/ % #`. It belongs to no part
999    /// of speech, so no rule reaches it and it can only end a sentence.
1000    VerbDef(String, Verb, Span),
1001    /// A finished modifier definition: `m =. /`. Like `VerbDef`, it belongs
1002    /// to no part of speech and can only end a sentence. The flag says
1003    /// whether it is a conjunction.
1004    ModDef(String, bool, Modifier, Span),
1005    /// A control word, with the name `for_i.` binds when it has one. Only a
1006    /// definition's body may hold one.
1007    Control(&'static str, Option<String>, Span),
1008    /// `{{` and `}}`, the direct definition's brackets. The opening one
1009    /// carries the letter of a `{{)a` marker where the source wrote one.
1010    DdOpen(Option<char>, Span),
1011    DdClose(Span),
1012}
1013
1014/// `[:` has the verb category but no verb of its own: it is only meaningful
1015/// as the left tine of a fork, where it caps the fork into an atop.
1016#[derive(Clone, Debug)]
1017enum VerbFrag {
1018    V(Verb),
1019    Cap,
1020}
1021
1022impl Frag {
1023    fn span(&self) -> Span {
1024        match self {
1025            Frag::Mark => Span::new(0, 0),
1026            Frag::Noun(e) => e.span(),
1027            Frag::Name(_, s)
1028            | Frag::Verb(_, s)
1029            | Frag::Adverb(_, s)
1030            | Frag::Conj(_, s)
1031            | Frag::LParen(s)
1032            | Frag::RParen(s)
1033            | Frag::AssignLocal(s)
1034            | Frag::AssignGlobal(s)
1035            | Frag::DdClose(s)
1036            | Frag::VerbDef(_, _, s)
1037            | Frag::ModDef(_, _, _, s) => *s,
1038            Frag::DdOpen(_, s) => *s,
1039            Frag::Control(_, _, s) => *s,
1040        }
1041    }
1042
1043    fn is_edge(&self) -> bool {
1044        matches!(self, Frag::Mark | Frag::AssignLocal(_) | Frag::AssignGlobal(_) | Frag::LParen(_))
1045    }
1046
1047    /// Verb category, `[:` included.
1048    fn is_verb(&self) -> bool {
1049        matches!(self, Frag::Verb(..))
1050    }
1051
1052    /// A verb that can actually be applied or bound to a modifier.
1053    fn is_real_verb(&self) -> bool {
1054        matches!(self, Frag::Verb(VerbFrag::V(_), _))
1055    }
1056
1057    /// Names are nouns in this subset; only assignment treats them apart.
1058    fn is_noun(&self) -> bool {
1059        matches!(self, Frag::Noun(_) | Frag::Name(..))
1060    }
1061
1062    fn is_adverb(&self) -> bool {
1063        matches!(self, Frag::Adverb(..))
1064    }
1065
1066    fn is_conj(&self) -> bool {
1067        matches!(self, Frag::Conj(..))
1068    }
1069
1070    fn is_avn(&self) -> bool {
1071        self.is_adverb() || self.is_verb() || self.is_noun()
1072    }
1073
1074    fn is_cavn(&self) -> bool {
1075        self.is_conj() || self.is_avn()
1076    }
1077
1078    fn is_assign(&self) -> bool {
1079        matches!(self, Frag::AssignLocal(_) | Frag::AssignGlobal(_))
1080    }
1081}
1082
1083// -------------------------------------------------------------- primitives
1084
1085const fn prim(name: &'static str, monad: MonadOp, dyad: DyadOp, ranks: [i64; 3]) -> Prim {
1086    Prim { name, monad, dyad, ranks }
1087}
1088
1089/// The primitive verbs this frontend knows, by their J spelling. Verbs whose
1090/// meaning exists in J but not here carry `NotYet` so the diagnostic arrives
1091/// at evaluation, pointing at the verb.
1092fn primitive(word: &str) -> Option<Prim> {
1093    use DyadOp as D;
1094    use MonadOp as M;
1095    use ScalarDyad as SD;
1096    use ScalarMonad as SM;
1097    const INF: i64 = RANK_INF;
1098    Some(match word {
1099        "+" => prim("+", M::Scalar(SM::Conj), D::Scalar(SD::Add), [0, 0, 0]),
1100        "-" => prim("-", M::Scalar(SM::Neg), D::Scalar(SD::Sub), [0, 0, 0]),
1101        "*" => prim("*", M::Scalar(SM::Signum), D::Scalar(SD::Mul), [0, 0, 0]),
1102        "%" => prim("%", M::Scalar(SM::Recip), D::Scalar(SD::DivJ), [0, 0, 0]),
1103        "^" => prim("^", M::Scalar(SM::Exp), D::Scalar(SD::Pow), [0, 0, 0]),
1104        "%:" => prim("%:", M::Scalar(SM::Sqrt), D::Scalar(SD::Root), [0, 0, 0]),
1105        "^." => prim("^.", M::Scalar(SM::Ln), D::Scalar(SD::Log), [0, 0, 0]),
1106        "|" => prim("|", M::Scalar(SM::Abs), D::Scalar(SD::Residue), [0, 0, 0]),
1107        "<." => prim("<.", M::Scalar(SM::Floor), D::Scalar(SD::Min), [0, 0, 0]),
1108        ">." => prim(">.", M::Scalar(SM::Ceil), D::Scalar(SD::Max), [0, 0, 0]),
1109        "=" => prim("=", M::SelfClassify, D::Scalar(SD::Eq), [INF, 0, 0]),
1110        "<" => prim("<", M::Enclose(Enclose::Always), D::Scalar(SD::Lt), [INF, 0, 0]),
1111        ">" => prim(">", M::Open, D::Scalar(SD::Gt), [0, 0, 0]),
1112        "<:" => prim("<:", M::Scalar(SM::Dec), D::Scalar(SD::Le), [0, 0, 0]),
1113        ">:" => prim(">:", M::Scalar(SM::Inc), D::Scalar(SD::Ge), [0, 0, 0]),
1114        "+:" => prim("+:", M::Scalar(SM::Double), D::Boolean(BoolDyad::Nor), [0, 0, 0]),
1115        "*:" => prim("*:", M::Scalar(SM::Square), D::Boolean(BoolDyad::Nand), [0, 0, 0]),
1116        "-:" => prim("-:", M::Scalar(SM::Halve), D::Match, [0, INF, INF]),
1117        "-." => prim("-.", M::Scalar(SM::OneMinus), D::Less, [0, INF, INF]),
1118        "*." => prim("*.", M::ComplexParts { polar: true }, D::Scalar(SD::Lcm), [0, 0, 0]),
1119        "+." => prim("+.", M::ComplexParts { polar: false }, D::Scalar(SD::Gcd), [0, 0, 0]),
1120        "~:" => prim("~:", M::NubSieve, D::Scalar(SD::Ne), [INF, 0, 0]),
1121        "~." => prim("~.", M::Nub, D::None, [INF, INF, INF]),
1122        "$" => prim("$", M::ShapeOf, D::Reshape, [INF, 1, INF]),
1123        "," => prim(",", M::Ravel, D::AppendLeading, [INF, INF, INF]),
1124        // `,.` is J's `,"_1`; `verb_for` wraps it in that rank.
1125        ",." => prim(",.", M::Ravel, D::AppendLeading, [INF, INF, INF]),
1126        ",:" => prim(",:", M::Itemize, D::Laminate, [INF, INF, INF]),
1127        "#" => prim("#", M::Tally, D::Copy, [INF, 1, INF]),
1128        "#." => prim("#.", M::DecodeBits, D::Decode, [1, 1, 1]),
1129        // The width of `#: y` comes from the largest value in the whole
1130        // argument, which is why the monad has infinite rank.
1131        "#:" => prim("#:", M::EncodeBits, D::Encode, [INF, 1, 0]),
1132        "!" => prim("!", M::Scalar(SM::Factorial), D::Scalar(SD::Binomial), [0, 0, 0]),
1133        "\":" => {
1134            prim("\":", M::Format, D::NotYet("format with a specification"), [INF, 1, INF])
1135        }
1136        "o." => prim("o.", M::Scalar(SM::Pi), D::Scalar(SD::Circle), [0, 0, 0]),
1137        "j." => prim("j.", M::Scalar(SM::Imaginary), D::Scalar(SD::MakeComplex), [0, 0, 0]),
1138        "r." => prim("r.", M::Scalar(SM::Polar), D::Scalar(SD::PolarBy), [0, 0, 0]),
1139        "{" => prim("{", M::Catalogue, D::From, [INF, 0, INF]),
1140        "{." => prim("{.", M::Head, D::Take, [INF, 1, INF]),
1141        "}." => prim("}.", M::Behead, D::Drop, [INF, 1, INF]),
1142        "{:" => prim("{:", M::Tail, D::None, [INF, INF, INF]),
1143        "}:" => prim("}:", M::Curtail, D::None, [INF, INF, INF]),
1144        "|." => prim("|.", M::Reverse, D::Rotate, [INF, 1, INF]),
1145        "|:" => prim("|:", M::TransposeAxes, D::TransposeJ, [INF, 1, INF]),
1146        "i." => prim("i.", M::IotaJ, D::IndexOf { origin: 0 }, [1, INF, INF]),
1147        "i:" => prim("i:", M::Steps, D::IndexOfLast { origin: 0 }, [0, INF, INF]),
1148        "I." => prim(
1149            "I.",
1150            M::Indices { origin: 0, boxed_coords: false },
1151            D::IntervalIndex { offset: 0, closed: false },
1152            [1, 1, INF],
1153        ),
1154        // The dyad reads the whole argument: `2 x: y` gives every value a
1155        // numerator and a denominator, which becomes a trailing axis.
1156        "x:" => prim("x:", M::ToExact, D::ExactForm, [INF, 0, INF]),
1157        "p:" => prim("p:", M::NthPrime, D::PrimeMeta, [0, 0, 0]),
1158        // The coefficients are one vector and the point one atom, so the
1159        // rank machinery evaluates a whole array of points at once.
1160        "p." => prim("p.", M::PolyRoots, D::PolyEval, [1, 1, 0]),
1161        "p.." => prim("p..", M::PolyDeriv, D::PolyIntegral, [1, 0, 1]),
1162        "$." => prim(
1163            "$.",
1164            M::NotYet("sparse arrays ($.)"),
1165            D::NotYet("sparse arrays ($.)"),
1166            [INF, INF, INF],
1167        ),
1168        "q:" => prim("q:", M::PrimeFactors, D::PrimeExponents, [0, 0, 0]),
1169        "%." => prim("%.", M::MatrixInverse, D::MatrixDivide, [2, INF, 2]),
1170        // The monad takes the whole argument: one invocation is one run of
1171        // the generator, consumed in ravel order.
1172        "?" => prim(
1173            "?",
1174            M::Roll { origin: 0, fixed: false, float_at_zero: true },
1175            D::Deal { origin: 0, fixed: false },
1176            [INF, 0, 0],
1177        ),
1178        "?." => prim(
1179            "?.",
1180            M::Roll { origin: 0, fixed: true, float_at_zero: true },
1181            D::Deal { origin: 0, fixed: true },
1182            [INF, 0, 0],
1183        ),
1184        "{::" => prim("{::", M::MapPaths, D::Fetch, [INF, INF, INF]),
1185        "e." => prim("e.", M::RazeIn, D::MemberJ, [INF, INF, INF]),
1186        "/:" => prim(
1187            "/:",
1188            M::GradeUp { origin: 0 },
1189            D::GradeSelect { down: false },
1190            [INF, INF, INF],
1191        ),
1192        "\\:" => prim(
1193            "\\:",
1194            M::GradeDown { origin: 0 },
1195            D::GradeSelect { down: true },
1196            [INF, INF, INF],
1197        ),
1198        ";" => prim(";", M::Raze, D::Link, [INF, INF, INF]),
1199        ";:" => prim(
1200            ";:",
1201            M::Words,
1202            D::NotYet("sequential machine (dyadic ;:)"),
1203            [INF, INF, INF],
1204        ),
1205        "L." => prim("L.", M::LevelOf, D::None, [INF, INF, INF]),
1206        "\"." => prim(
1207            "\".",
1208            M::Execute { apl: false },
1209            D::NotYet("numbers from text (dyadic \".)"),
1210            [1, INF, INF],
1211        ),
1212        "A." => prim("A.", M::AnagramIndex, D::AnagramFrom, [1, 0, INF]),
1213        "C." => prim("C.", M::CycleForm, D::Permute, [INF, INF, INF]),
1214        "E." => prim("E.", M::None, D::FindSeq, [INF, INF, INF]),
1215        "u:" => prim("u:", M::Unicode { pass_chars: true }, D::UnicodeForm, [INF, 0, INF]),
1216        "s:" => prim(
1217            "s:",
1218            M::NotYet("symbols (s:)"),
1219            D::NotYet("symbols (s:)"),
1220            [INF, INF, INF],
1221        ),
1222        "]" => prim("]", M::Same, D::Right, [INF, INF, INF]),
1223        "[" => prim("[", M::Same, D::Left, [INF, INF, INF]),
1224        "echo" => prim("echo", M::Echo, D::None, [INF, INF, INF]),
1225        _ => return None,
1226    })
1227}
1228
1229/// The constant nouns J spells as inflected words. `a.` is the 256
1230/// characters of J's alphabet in codepoint order; `a:` is the ace, the box
1231/// holding an empty numeric list; `_.` is the indeterminate value, which is
1232/// a NaN and prints as itself.
1233fn noun_word(word: &str) -> Option<Array> {
1234    match word {
1235        "a." => Some(Array::from_chars(
1236            (0u32..256).map(|c| char::from_u32(c).expect("a Latin-1 codepoint")).collect(),
1237        )),
1238        "a:" => Some(Array::boxed(Array::empty(crate::dtype::DType::I64))),
1239        "_." => Some(Array::scalar_f64(f64::NAN)),
1240        _ => None,
1241    }
1242}
1243
1244/// The verb a word denotes. Every word but `,.` is a bare primitive; J's
1245/// `,.` is `,"_1`, so it carries that rank.
1246fn verb_for(word: &str) -> Option<Verb> {
1247    let p = primitive(word)?;
1248    if word == ",." {
1249        return Some(Verb::Rank(Box::new(Verb::Prim(p)), [-1, -1, -1]));
1250    }
1251    Some(Verb::Prim(p))
1252}
1253
1254/// A constant verb: the noun itself, whatever the arguments are. `3:` and
1255/// the noun operand of `::` both need one.
1256fn constant_verb(n: Array) -> Verb {
1257    // `n [ (x ] y)` is n whatever the arguments are, and the noun fork has
1258    // both valences, which a bond does not.
1259    Verb::NounFork(
1260        n,
1261        Box::new(verb_for("[").expect("`[` is a primitive")),
1262        Box::new(verb_for("]").expect("`]` is a primitive")),
1263    )
1264}
1265
1266/// The spelling of a constant verb: `_9:` … `9:`, and `_:` for infinity.
1267/// The word must be complete — `3::` is the adverse conjunction after a
1268/// number, not a constant verb.
1269fn constant_verb_word(cs: &[(usize, char)], i: usize) -> Option<(usize, Array)> {
1270    let at = |k: usize| cs.get(k).map(|&(_, c)| c);
1271    let (digits, value) = match (at(i), at(i + 1), at(i + 2)) {
1272        (Some('_'), Some(':'), _) => (2, f64::INFINITY),
1273        (Some('_'), Some(d), Some(':')) if d.is_ascii_digit() => {
1274            (3, -((d as u8 - b'0') as f64))
1275        }
1276        (Some(d), Some(':'), _) if d.is_ascii_digit() => (2, (d as u8 - b'0') as f64),
1277        _ => return None,
1278    };
1279    if at(i + digits) == Some(':') {
1280        return None;
1281    }
1282    let arr = if value.is_infinite() {
1283        Array::scalar_f64(value)
1284    } else {
1285        Array::scalar_i64(value as i64)
1286    };
1287    Some((digits, arr))
1288}
1289
1290/// The verb one J spelling denotes, for the parts of the evaluator that
1291/// need to name a verb rather than parse one — the obverse table above all.
1292pub(crate) fn verb_named(word: &str) -> Option<Verb> {
1293    verb_for(word)
1294}
1295
1296const ADVERBS: [&str; 9] = ["/", "\\", "/.", "\\.", "~", "}", "f.", "M.", "b."];
1297
1298/// Conjunction spellings. The ones without a meaning here are recognised so
1299/// that their diagnostic names the conjunction rather than the word.
1300const CONJUNCTIONS: [&str; 24] = [
1301    "\"", "@", "@.", "@:", "&", "&.", "&.:", "&:", "^:", ";.", "!.", "!:", "`", "`:", ".", ":",
1302    ":.", "::", "L:", "S:", "H.", "T.", "t.", "t:",
1303];
1304
1305fn adverb(word: &str) -> Option<&'static str> {
1306    ADVERBS.iter().copied().find(|&g| g == word)
1307}
1308
1309fn conjunction(word: &str) -> Option<&'static str> {
1310    CONJUNCTIONS.iter().copied().find(|&g| g == word)
1311}
1312
1313// ------------------------------------------------------------------- lexer
1314
1315/// Split the source into sentences of fragments. Text segments are lexed;
1316/// each interpolation hole becomes a noun fragment holding its parameter.
1317fn lex(src: &SourceParts) -> Result<Vec<Vec<Frag>>> {
1318    let mut sentences: Vec<Vec<Frag>> = Vec::new();
1319    let mut cur: Vec<Frag> = Vec::new();
1320    for seg in &src.segments {
1321        match seg {
1322            Segment::Text { text, offset } => {
1323                let mut pos = 0usize;
1324                for (n, line) in text.split('\n').enumerate() {
1325                    if n > 0 && !cur.is_empty() {
1326                        sentences.push(std::mem::take(&mut cur));
1327                    }
1328                    lex_line(line, offset + pos, &mut cur)?;
1329                    pos += line.len() + 1;
1330                }
1331            }
1332            Segment::Param { index, offset, len } => {
1333                let span = Span::new(*offset, *offset + *len);
1334                cur.push(Frag::Noun(Expr::Param(*index, span)));
1335            }
1336        }
1337    }
1338    if !cur.is_empty() {
1339        sentences.push(cur);
1340    }
1341    Ok(sentences)
1342}
1343
1344/// A numeric word's value. Kept apart from `Array` so that a list of words
1345/// can pick one element type for the whole vector.
1346#[derive(Clone, Debug)]
1347enum Num {
1348    I(i64),
1349    F(f64),
1350    /// An extended-precision integer: `123x`.
1351    X(crate::exact::Ext),
1352    /// A rational: `1r3`.
1353    R(crate::exact::Rat),
1354    C(crate::complex::Cx),
1355}
1356
1357fn lex_line(text: &str, base: usize, out: &mut Vec<Frag>) -> Result<()> {
1358    let cs: Vec<(usize, char)> = text.char_indices().collect();
1359    let at = |i: usize| cs.get(i).map(|&(_, c)| c);
1360    let off = |i: usize| cs.get(i).map(|&(o, _)| o).unwrap_or(text.len());
1361    let span = |a: usize, b: usize| Span::new(base + off(a), base + off(b));
1362    let mut i = 0usize;
1363    while i < cs.len() {
1364        let c = cs[i].1;
1365        if c.is_whitespace() {
1366            i += 1;
1367            continue;
1368        }
1369        // `NB.` is only a comment at the start of a word, which is where
1370        // this loop always stands.
1371        if c == 'N' && at(i + 1) == Some('B') && at(i + 2) == Some('.') {
1372            break;
1373        }
1374        if c == '\'' {
1375            let start = i;
1376            i += 1;
1377            let mut chars: Vec<char> = Vec::new();
1378            loop {
1379                match at(i) {
1380                    None => {
1381                        return Err(Error::parse(
1382                            "unterminated string literal",
1383                            span(start, cs.len()),
1384                        ));
1385                    }
1386                    Some('\'') if at(i + 1) == Some('\'') => {
1387                        chars.push('\'');
1388                        i += 2;
1389                    }
1390                    Some('\'') => {
1391                        i += 1;
1392                        break;
1393                    }
1394                    Some(ch) => {
1395                        chars.push(ch);
1396                        i += 1;
1397                    }
1398                }
1399            }
1400            // One character is an atom; anything else is a vector.
1401            let shape = if chars.len() == 1 { vec![] } else { vec![chars.len()] };
1402            let arr = Array::new(shape, Data::Char(chars.into()));
1403            out.push(Frag::Noun(Expr::Const(arr, span(start, i))));
1404            continue;
1405        }
1406        if let Some((len, n)) = constant_verb_word(&cs, i) {
1407            out.push(Frag::Verb(VerbFrag::V(constant_verb(n)), span(i, i + len)));
1408            i += len;
1409            continue;
1410        }
1411        if starts_number(&cs, i) {
1412            // Numeric words separated only by blanks form one vector.
1413            let start = i;
1414            let mut nums: Vec<Num> = Vec::new();
1415            let mut end;
1416            loop {
1417                let ws = i;
1418                while at(i).is_some_and(|c| c.is_ascii_alphanumeric() || c == '.' || c == '_') {
1419                    i += 1;
1420                }
1421                nums.push(parse_number(&text[off(ws)..off(i)], span(ws, i))?);
1422                end = i;
1423                let mut k = i;
1424                while at(k).is_some_and(char::is_whitespace) {
1425                    k += 1;
1426                }
1427                // A constant verb (`3:`) ends the numeric word rather than
1428                // joining it: `2 3: 4` is 2, the verb `3:`, and 4.
1429                if k < cs.len()
1430                    && starts_number(&cs, k)
1431                    && constant_verb_word(&cs, k).is_none()
1432                {
1433                    i = k;
1434                } else {
1435                    break;
1436                }
1437            }
1438            out.push(Frag::Noun(Expr::Const(num_array(&nums), span(start, end))));
1439            continue;
1440        }
1441        if c.is_ascii_alphabetic() {
1442            let start = i;
1443            i += 1;
1444            while at(i).is_some_and(|c| c.is_ascii_alphanumeric() || c == '_') {
1445                i += 1;
1446            }
1447            // An alphabetic word may be inflected into a primitive (`i.`,
1448            // `p..`), a modifier (`f.`, `L:`) or a control word (`if.`,
1449            // `for_i.`). The longer inflection wins where it names
1450            // something: `p..` is one word, not `p.` and the dot.
1451            let mut inflected = None;
1452            if matches!(at(i), Some('.') | Some(':')) {
1453                let most = if matches!(at(i + 1), Some('.') | Some(':')) { 2 } else { 1 };
1454                for n in (1..=most).rev() {
1455                    let word = &text[off(start)..off(i + n)];
1456                    let sp = span(start, i + n);
1457                    let frag = if let Some(v) = verb_for(word) {
1458                        Frag::Verb(VerbFrag::V(v), sp)
1459                    } else if let Some(value) = noun_word(word) {
1460                        Frag::Noun(Expr::Const(value, sp))
1461                    } else if let Some(g) = adverb(word) {
1462                        Frag::Adverb(Modifier::Prim(g), sp)
1463                    } else if let Some(g) = conjunction(word) {
1464                        Frag::Conj(Modifier::Prim(g), sp)
1465                    } else if let Some((cw, suffix)) = control_word(word) {
1466                        Frag::Control(cw, suffix, sp)
1467                    } else {
1468                        continue;
1469                    };
1470                    inflected = Some((frag, n));
1471                    break;
1472                }
1473            }
1474            if let Some((frag, n)) = inflected {
1475                i += n;
1476                out.push(frag);
1477                continue;
1478            }
1479            let word = &text[off(start)..off(i)];
1480            match verb_for(word) {
1481                Some(v) => out.push(Frag::Verb(VerbFrag::V(v), span(start, i))),
1482                None => out.push(Frag::Name(word.to_string(), span(start, i))),
1483            }
1484            continue;
1485        }
1486        // `{{` and `}}` bracket J's direct definition; neither is two words.
1487        if c == '{' && at(i + 1) == Some('{') {
1488            // `{{)a` and its relatives state the definition's part of
1489            // speech instead of leaving it to the words of the body. The
1490            // reference takes the marker only where nothing follows it on
1491            // the line, and reads `{{)a u y }}` as a domain error.
1492            let marker = match (at(i + 2), at(i + 3)) {
1493                (Some(')'), Some(m)) if m.is_ascii_alphabetic() => Some(m),
1494                _ => None,
1495            };
1496            if let Some(m) = marker {
1497                if cs[i + 4..].iter().any(|&(_, c)| !c.is_whitespace()) {
1498                    return Err(Error::parse(
1499                        format!("`)`{m} names the part of speech of a direct definition, \
1500                                 and has to be the last thing on its line"),
1501                        span(i, i + 4),
1502                    ));
1503                }
1504                out.push(Frag::DdOpen(Some(m), span(i, i + 4)));
1505                i += 4;
1506                continue;
1507            }
1508            out.push(Frag::DdOpen(None, span(i, i + 2)));
1509            i += 2;
1510            continue;
1511        }
1512        if c == '}' && at(i + 1) == Some('}') {
1513            out.push(Frag::DdClose(span(i, i + 2)));
1514            i += 2;
1515            continue;
1516        }
1517        // A symbol word is one character plus a trailing inflection, which
1518        // always binds: `~:` is one word, never `~` followed by `:`. The
1519        // parentheses are the exception; they are never inflected.
1520        let inflectable = c != '(' && c != ')';
1521        let mut len =
1522            if inflectable && matches!(at(i + 1), Some('.') | Some(':')) { 2 } else { 1 };
1523        // A doubly inflected word (`&.:`) exists only where the table says
1524        // it does; everything else stops at one inflection.
1525        if len == 2 && at(i + 2) == Some(':') {
1526            let w = &text[off(i)..off(i + 3)];
1527            if conjunction(w).is_some() || verb_for(w).is_some() {
1528                len = 3;
1529            }
1530        }
1531        let word = &text[off(i)..off(i + len)];
1532        match symbol_frag(word, span(i, i + len)) {
1533            Some(frag) => {
1534                out.push(frag);
1535                i += len;
1536            }
1537            None => {
1538                return Err(Error::parse(format!("unknown word: {word}"), span(i, i + len)));
1539            }
1540        }
1541    }
1542    Ok(())
1543}
1544
1545fn symbol_frag(word: &str, span: Span) -> Option<Frag> {
1546    Some(match word {
1547        "(" => Frag::LParen(span),
1548        ")" => Frag::RParen(span),
1549        "=." => Frag::AssignLocal(span),
1550        "=:" => Frag::AssignGlobal(span),
1551        "[:" => Frag::Verb(VerbFrag::Cap, span),
1552        // `$:` stands for the explicit definition it is written in.
1553        "$:" => Frag::Verb(VerbFrag::V(Verb::SelfRef), span),
1554        // An inflected verb wins over the adverb its stem spells: `~.` is
1555        // the nub, never `~` followed by an inflection.
1556        _ => {
1557            if let Some(v) = verb_for(word) {
1558                Frag::Verb(VerbFrag::V(v), span)
1559            } else if let Some(g) = adverb(word) {
1560                Frag::Adverb(Modifier::Prim(g), span)
1561            } else {
1562                Frag::Conj(Modifier::Prim(conjunction(word)?), span)
1563            }
1564        }
1565    })
1566}
1567
1568/// A numeric word starts with a digit, or with `_` used as a negative sign
1569/// or as infinity (`_`, `__`) — but not as the start of a name.
1570fn starts_number(cs: &[(usize, char)], i: usize) -> bool {
1571    let c = cs[i].1;
1572    if c.is_ascii_digit() {
1573        return true;
1574    }
1575    if c != '_' {
1576        return false;
1577    }
1578    match cs.get(i + 1).map(|&(_, c)| c) {
1579        None => true,
1580        Some(d) => d.is_ascii_digit() || d == '.' || !d.is_alphanumeric(),
1581    }
1582}
1583
1584fn parse_number(word: &str, span: Span) -> Result<Num> {
1585    // `_.` is the indeterminate value, not a number with a decimal point.
1586    if word == "_." {
1587        return Ok(Num::F(f64::NAN));
1588    }
1589    // `1x` is an extended-precision integer; `1x1` is a multiple of e, and
1590    // `1p1` a multiple of π. The letter is the separator in both, and it
1591    // binds LOOSEST: `1ar1p1` is the polar value `1ar1` scaled by π.
1592    if let Some(k) = word.find(['p', 'x']) {
1593        if word[k + 1..].is_empty() {
1594            // A trailing `x` is the extended-precision suffix, and only a
1595            // whole decimal number carries it: `1.5x` and `1e10x` are
1596            // ill-formed, as they are in the reference.
1597            if word.as_bytes()[k] == b'x' {
1598                return extended_literal(&word[..k], word, span);
1599            }
1600            return Err(Error::parse(format!("invalid number: {word}"), span));
1601        }
1602        let base =
1603            if word.as_bytes()[k] == b'p' { std::f64::consts::PI } else { std::f64::consts::E };
1604        let mantissa = plain_number(&word[..k], word, span)?;
1605        let exponent = plain_number(&word[k + 1..], word, span)?;
1606        return Ok(scale(mantissa, base, exponent));
1607    }
1608    // `3j4` is the rectangular form. A `b` earlier in the word makes the
1609    // `j` a base-literal digit instead (`36bj` is 19).
1610    if let Some(k) = word.find('j') && !word[..k].contains('b') {
1611        let re = as_f64(plain_number(&word[..k], word, span)?);
1612        let im = as_f64(plain_number(&word[k + 1..], word, span)?);
1613        return Ok(Num::C([re, im]));
1614    }
1615    // `1ad45` and `1ar1` are the polar forms: a magnitude, then the angle
1616    // in degrees or in radians.
1617    if let Some(k) = word.find("ad").or_else(|| word.find("ar")) && !word[..k].contains('b') {
1618        let magnitude = as_f64(plain_number(&word[..k], word, span)?);
1619        let angle = as_f64(plain_number(&word[k + 2..], word, span)?);
1620        return Ok(Num::C(if word.as_bytes()[k + 1] == b'd' {
1621            crate::complex::from_degrees(magnitude, angle)
1622        } else {
1623            crate::complex::from_radians(magnitude, angle)
1624        }));
1625    }
1626    // `3r4` is a rational, and `1r_2` spells its negative denominator with
1627    // J's own negative sign. A `b` earlier in the word makes the `r` a
1628    // base-literal digit instead.
1629    if let Some(k) = word.find('r') && !word[..k].contains('b') {
1630        return rational_literal(&word[..k], &word[k + 1..], word, span);
1631    }
1632    if let Some(k) = word.find('b') {
1633        return base_literal(&word[..k], &word[k + 1..], word, span);
1634    }
1635    plain_number(word, word, span)
1636}
1637
1638/// `123x`: the digits as an extended-precision integer. The value is exact
1639/// however many digits it has, which is the whole point of the suffix.
1640fn extended_literal(digits: &str, word: &str, span: Span) -> Result<Num> {
1641    Ok(Num::X(whole_digits(digits, word, span)?))
1642}
1643
1644/// `3r4`: a rational. A zero denominator is J's infinity rather than a
1645/// number — the only spelling that leaves the exact types on sight.
1646fn rational_literal(num: &str, den: &str, word: &str, span: Span) -> Result<Num> {
1647    use num_traits::Zero;
1648    let num = whole_digits(num, word, span)?;
1649    let den = whole_digits(den, word, span)?;
1650    if den.is_zero() {
1651        if num.is_zero() {
1652            return Ok(Num::I(0));
1653        }
1654        return Ok(Num::F(if num.sign() == num_bigint::Sign::Minus {
1655            f64::NEG_INFINITY
1656        } else {
1657            f64::INFINITY
1658        }));
1659    }
1660    Ok(Num::R(
1661        crate::exact::Rat::new(num, den).ok_or_else(|| Error::internal("a zero denominator"))?,
1662    ))
1663}
1664
1665/// One run of decimal digits, with J's `_` as the negative sign.
1666fn whole_digits(word: &str, whole: &str, span: Span) -> Result<crate::exact::Ext> {
1667    let invalid = || Error::parse(format!("invalid number: {whole}"), span);
1668    let (digits, negative) = match word.strip_prefix('_') {
1669        Some(rest) => (rest, true),
1670        None => (word, false),
1671    };
1672    if digits.is_empty() || !digits.bytes().all(|b| b.is_ascii_digit()) {
1673        return Err(invalid());
1674    }
1675    let v: crate::exact::Ext = digits.parse().map_err(|_| invalid())?;
1676    Ok(if negative { -v } else { v })
1677}
1678
1679/// A mantissa scaled by a power of π or e. Either half may be complex —
1680/// `1p1j1` is π to the power `1j1`.
1681fn scale(mantissa: Num, base: f64, exponent: Num) -> Num {
1682    if matches!(mantissa, Num::C(_)) || matches!(exponent, Num::C(_)) {
1683        let m = as_cx(mantissa);
1684        let f = crate::complex::pow([base, 0.0], as_cx(exponent));
1685        return Num::C(crate::complex::mul(m, f));
1686    }
1687    Num::F(as_f64(mantissa) * base.powf(as_f64(exponent)))
1688}
1689
1690fn as_cx(n: Num) -> crate::complex::Cx {
1691    match n {
1692        Num::C(z) => z,
1693        other => [as_f64(other), 0.0],
1694    }
1695}
1696
1697fn as_f64(n: Num) -> f64 {
1698    match n {
1699        Num::I(v) => v as f64,
1700        Num::F(v) => v,
1701        Num::X(v) => crate::exact::ext_to_f64(&v),
1702        Num::R(v) => v.to_f64(),
1703        // A complex part is itself written as a plain number, so this is
1704        // never reached from a well-formed literal.
1705        Num::C(z) => z[0],
1706    }
1707}
1708
1709/// `mBd…`: the digits `d…` read in base `m`. Digits run `0`–`9` then `a`–`z`,
1710/// and a `_` in front of them negates the value, as the reference does.
1711fn base_literal(base: &str, digits: &str, word: &str, span: Span) -> Result<Num> {
1712    let invalid = || Error::parse(format!("invalid number: {word}"), span);
1713    let base = as_f64(plain_number(base, word, span)?);
1714    let (digits, negative) = match digits.strip_prefix('_') {
1715        Some(rest) => (rest, true),
1716        None => (digits, false),
1717    };
1718    if digits.is_empty() {
1719        return Err(invalid());
1720    }
1721    let mut value = 0.0f64;
1722    for ch in digits.chars() {
1723        let d = match ch {
1724            '0'..='9' => ch as u32 - '0' as u32,
1725            'a'..='z' => ch as u32 - 'a' as u32 + 10,
1726            _ => return Err(invalid()),
1727        };
1728        value = value * base + f64::from(d);
1729    }
1730    if negative {
1731        value = -value;
1732    }
1733    // An exact whole number stays an integer, as the reference prints it.
1734    if value.fract() == 0.0 && value.abs() < 9.007_199_254_740_992e15 {
1735        return Ok(Num::I(value as i64));
1736    }
1737    Ok(Num::F(value))
1738}
1739
1740/// One constituent of a literal — a whole one, a mantissa, an exponent, or
1741/// half of a complex or polar form. Every part is itself a number in the
1742/// same grammar, which is what makes `1ar1p1` and `1p1j1` read.
1743fn plain_number(word: &str, whole: &str, span: Span) -> Result<Num> {
1744    if word.is_empty() {
1745        return Err(Error::parse(format!("invalid number: {whole}"), span));
1746    }
1747    if word.contains(['j', 'p', 'x', 'b', 'r']) || word.contains("ad") || word.contains("ar") {
1748        return parse_number(word, span);
1749    }
1750    parse_plain(word, span)
1751}
1752
1753fn parse_plain(word: &str, span: Span) -> Result<Num> {
1754    if word == "_" {
1755        return Ok(Num::F(f64::INFINITY));
1756    }
1757    if word == "__" {
1758        return Ok(Num::F(f64::NEG_INFINITY));
1759    }
1760    let invalid = || Error::parse(format!("invalid number: {word}"), span);
1761    // `_` is J's negative sign, in the mantissa and after `e`.
1762    let mut norm = String::with_capacity(word.len());
1763    for (k, ch) in word.char_indices() {
1764        if ch == '_' {
1765            if k != 0 && !word[..k].ends_with('e') {
1766                return Err(invalid());
1767            }
1768            norm.push('-');
1769        } else {
1770            norm.push(ch);
1771        }
1772    }
1773    // Exponent notation yields a float, as a fractional part does.
1774    if norm.contains('.') || norm.contains('e') {
1775        return norm.parse::<f64>().map(Num::F).map_err(|_| invalid());
1776    }
1777    // Digits that overflow a machine word are a float, as they are in J;
1778    // the `x` suffix is what asks for an exact value instead.
1779    match norm.parse::<i64>() {
1780        Ok(v) => Ok(Num::I(v)),
1781        Err(_) => norm.parse::<f64>().map(Num::F).map_err(|_| invalid()),
1782    }
1783}
1784
1785/// One numeric word list as an array. The widest type any word reached
1786/// carries the whole vector: `1 2 3x` is extended throughout, and one
1787/// rational or float among the words pulls its neighbours up with it.
1788fn num_array(nums: &[Num]) -> Array {
1789    use crate::exact::{Ext, Rat};
1790    let shape = if nums.len() == 1 { vec![] } else { vec![nums.len()] };
1791    let has = |f: fn(&Num) -> bool| nums.iter().any(f);
1792    if has(|n| matches!(n, Num::C(_))) {
1793        let data = nums.iter().map(|n| as_cx(n.clone())).collect();
1794        return Array::new(shape, Data::Complex(data));
1795    }
1796    if has(|n| matches!(n, Num::F(_))) {
1797        let data = nums.iter().map(|n| as_f64(n.clone())).collect();
1798        return Array::new(shape, Data::F64(data));
1799    }
1800    if has(|n| matches!(n, Num::R(_))) {
1801        let data = nums
1802            .iter()
1803            .map(|n| match n {
1804                Num::I(v) => Rat::from_int(Ext::from(*v)),
1805                Num::X(v) => Rat::from_int(v.clone()),
1806                Num::R(v) => v.clone(),
1807                Num::F(_) | Num::C(_) => Rat::zero(),
1808            })
1809            .collect();
1810        return Array::new(shape, Data::Rat(data));
1811    }
1812    if has(|n| matches!(n, Num::X(_))) {
1813        let data = nums
1814            .iter()
1815            .map(|n| match n {
1816                Num::I(v) => Ext::from(*v),
1817                Num::X(v) => v.clone(),
1818                _ => Ext::default(),
1819            })
1820            .collect();
1821        return Array::new(shape, Data::Ext(data));
1822    }
1823    let data = nums
1824        .iter()
1825        .map(|n| match n {
1826            Num::I(v) => *v,
1827            _ => 0,
1828        })
1829        .collect();
1830    Array::new(shape, Data::I64(data))
1831}
1832
1833// ------------------------------------------------------------------ parser
1834
1835#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1836enum Rule {
1837    Monad1,
1838    Monad2,
1839    Dyad3,
1840    Adverb4,
1841    Conj5,
1842    Fork6,
1843    Bident7,
1844    Assign8,
1845    Paren9,
1846}
1847
1848/// Run the parse table over a sentence's words. The result is the one
1849/// fragment left standing, or None where the sentence did not reduce to
1850/// one — which is the reference's syntax error.
1851fn reduce_to_fragment(tokens: Vec<Frag>, scope: &Names) -> Result<Option<Frag>> {
1852    check_parens(&tokens)?;
1853    let mut stack: Vec<Frag> = Vec::new();
1854    for frag in tokens.into_iter().rev() {
1855        stack.insert(0, frag);
1856        reduce(&mut stack, scope)?;
1857    }
1858    stack.insert(0, Frag::Mark);
1859    reduce(&mut stack, scope)?;
1860    if stack.len() == 2 {
1861        return Ok(Some(stack.pop().expect("checked length")));
1862    }
1863    Ok(None)
1864}
1865
1866/// The IR statement a finished sentence stands for. `whole` is the span of
1867/// the sentence, for the complaint that it has no reading at all.
1868fn lower_sentence(frag: Option<Frag>, whole: Span) -> Result<Expr> {
1869    match frag {
1870        Some(f @ (Frag::Noun(_) | Frag::Name(..))) => as_noun(f),
1871        Some(Frag::VerbDef(name, verb, span)) => Ok(Expr::VerbDef { name, verb, span }),
1872        Some(Frag::ModDef(name, conjunction, m, span)) => {
1873            Ok(Expr::ModDef { name, spelling: m.spelling(), conjunction, span })
1874        }
1875        Some(Frag::Verb(VerbFrag::V(_), span)) => {
1876            Err(Error::not_yet("tacit verb definitions (a sentence that is a verb)", span))
1877        }
1878        Some(Frag::Adverb(_, span) | Frag::Conj(_, span)) => Err(Error::not_yet(
1879            "displaying a modifier (a sentence that is an adverb or a conjunction)",
1880            span,
1881        )),
1882        _ => Err(Error::parse("syntax error", whole)),
1883    }
1884}
1885
1886/// Report an unbalanced parenthesis at the parenthesis itself, before the
1887/// sentence is reduced: the reduction would otherwise blame whatever
1888/// fragments the stray one left stranded beside each other.
1889fn check_parens(tokens: &[Frag]) -> Result<()> {
1890    let mut open: Vec<Span> = Vec::new();
1891    for frag in tokens {
1892        match frag {
1893            Frag::LParen(s) => open.push(*s),
1894            Frag::RParen(s) => {
1895                if open.pop().is_none() {
1896                    return Err(Error::parse("this `)` has no opening `(`", *s));
1897                }
1898            }
1899            _ => {}
1900        }
1901    }
1902    match open.pop() {
1903        None => Ok(()),
1904        Some(s) => Err(Error::parse("this `(` has no closing `)`", s)),
1905    }
1906}
1907
1908fn sentence_span(tokens: &[Frag]) -> Span {
1909    tokens
1910        .iter()
1911        .map(Frag::span)
1912        .reduce(Span::merge)
1913        .unwrap_or_else(|| Span::new(0, 0))
1914}
1915
1916fn reduce(stack: &mut Vec<Frag>, scope: &Names) -> Result<()> {
1917    while apply(stack, scope)? {}
1918    Ok(())
1919}
1920
1921/// The parse table: the first matching row wins, and matching restarts after
1922/// every reduction. Slot 0 is the leftmost (most recently pushed) fragment.
1923fn match_rule(s: &[Frag]) -> Option<Rule> {
1924    let is = |i: usize, f: fn(&Frag) -> bool| s.get(i).is_some_and(f);
1925    // Slot 0 is only ever context: an edge, or a fragment that keeps the
1926    // reduction from reaching further left than it should.
1927    let ctx = |i: usize| s.get(i).is_some_and(|f| f.is_edge() || f.is_avn());
1928    let verb_or_noun =
1929        |i: usize| s.get(i).is_some_and(|f| f.is_real_verb() || f.is_noun());
1930    if is(0, Frag::is_edge) && is(1, Frag::is_real_verb) && is(2, Frag::is_noun) {
1931        return Some(Rule::Monad1);
1932    }
1933    if ctx(0) && is(1, Frag::is_verb) && is(2, Frag::is_real_verb) && is(3, Frag::is_noun) {
1934        return Some(Rule::Monad2);
1935    }
1936    if ctx(0) && is(1, Frag::is_noun) && is(2, Frag::is_real_verb) && is(3, Frag::is_noun) {
1937        return Some(Rule::Dyad3);
1938    }
1939    if ctx(0) && verb_or_noun(1) && is(2, Frag::is_adverb) {
1940        return Some(Rule::Adverb4);
1941    }
1942    if ctx(0) && verb_or_noun(1) && is(2, Frag::is_conj) && verb_or_noun(3) {
1943        return Some(Rule::Conj5);
1944    }
1945    if ctx(0)
1946        && s.get(1).is_some_and(|f| f.is_verb() || f.is_noun())
1947        && is(2, Frag::is_real_verb)
1948        && is(3, Frag::is_real_verb)
1949    {
1950        return Some(Rule::Fork6);
1951    }
1952    if is(0, Frag::is_edge) && is(1, Frag::is_cavn) && is(2, Frag::is_cavn) {
1953        return Some(Rule::Bident7);
1954    }
1955    if is(0, Frag::is_noun) && is(1, Frag::is_assign) && is(2, Frag::is_cavn) {
1956        return Some(Rule::Assign8);
1957    }
1958    if matches!(s.first(), Some(Frag::LParen(_)))
1959        && is(1, Frag::is_cavn)
1960        && matches!(s.get(2), Some(Frag::RParen(_)))
1961    {
1962        return Some(Rule::Paren9);
1963    }
1964    None
1965}
1966
1967fn take(stack: &mut Vec<Frag>, range: Range<usize>) -> Vec<Frag> {
1968    stack.drain(range).collect()
1969}
1970
1971/// The fragment, pointing at `to` instead of at its own words. Removing a
1972/// pair of parentheses uses it so that the fragment left behind still
1973/// covers the brackets it was written in.
1974fn respan(f: Frag, to: Span) -> Frag {
1975    match f {
1976        Frag::Noun(mut e) => {
1977            e.set_span(to);
1978            Frag::Noun(e)
1979        }
1980        Frag::Name(n, _) => Frag::Name(n, to),
1981        Frag::Verb(v, _) => Frag::Verb(v, to),
1982        Frag::Adverb(a, _) => Frag::Adverb(a, to),
1983        Frag::Conj(c, _) => Frag::Conj(c, to),
1984        other => other,
1985    }
1986}
1987
1988fn apply(stack: &mut Vec<Frag>, scope: &Names) -> Result<bool> {
1989    let Some(rule) = match_rule(stack) else {
1990        return Ok(false);
1991    };
1992    match rule {
1993        Rule::Monad1 => {
1994            let mut t = take(stack, 1..3);
1995            let y = t.pop().expect("two slots");
1996            let v = t.pop().expect("two slots");
1997            let frag = monad(v, y)?;
1998            stack.insert(1, frag);
1999        }
2000        Rule::Monad2 => {
2001            let mut t = take(stack, 2..4);
2002            let y = t.pop().expect("two slots");
2003            let v = t.pop().expect("two slots");
2004            let frag = monad(v, y)?;
2005            stack.insert(2, frag);
2006        }
2007        Rule::Dyad3 => {
2008            let mut t = take(stack, 1..4);
2009            let y = t.pop().expect("three slots");
2010            let v = t.pop().expect("three slots");
2011            let x = t.pop().expect("three slots");
2012            let frag = dyad(x, v, y)?;
2013            stack.insert(1, frag);
2014        }
2015        Rule::Adverb4 => {
2016            let mut t = take(stack, 1..3);
2017            let a = t.pop().expect("two slots");
2018            let u = t.pop().expect("two slots");
2019            let frag = apply_adverb(u, a, scope)?;
2020            stack.insert(1, frag);
2021        }
2022        Rule::Conj5 => {
2023            let mut t = take(stack, 1..4);
2024            let v = t.pop().expect("three slots");
2025            let c = t.pop().expect("three slots");
2026            let u = t.pop().expect("three slots");
2027            let frag = apply_conj(u, c, v, scope)?;
2028            stack.insert(1, frag);
2029        }
2030        Rule::Fork6 => {
2031            let mut t = take(stack, 1..4);
2032            let h = t.pop().expect("three slots");
2033            let g = t.pop().expect("three slots");
2034            let f = t.pop().expect("three slots");
2035            let frag = apply_fork(f, g, h)?;
2036            stack.insert(1, frag);
2037        }
2038        Rule::Bident7 => {
2039            let mut t = take(stack, 1..3);
2040            let b = t.pop().expect("two slots");
2041            let a = t.pop().expect("two slots");
2042            let frag = apply_bident(a, b, &scope.nouns)?;
2043            stack.insert(1, frag);
2044        }
2045        Rule::Assign8 => {
2046            let mut t = take(stack, 0..3);
2047            let value = t.pop().expect("three slots");
2048            let assign = t.pop().expect("three slots");
2049            let target = t.pop().expect("three slots");
2050            let scope = match assign {
2051                Frag::AssignGlobal(_) => Scope::Global,
2052                _ => Scope::Local,
2053            };
2054            let frag = apply_assign(target, value, scope)?;
2055            stack.insert(0, frag);
2056        }
2057        Rule::Paren9 => {
2058            let mut t = take(stack, 0..3);
2059            let close = t.pop().expect("three slots");
2060            let inner = t.pop().expect("three slots");
2061            let open = t.pop().expect("three slots");
2062            let outer = Span::merge(open.span(), close.span());
2063            stack.insert(0, respan(inner, outer));
2064        }
2065    }
2066    Ok(true)
2067}
2068
2069// --------------------------------------------------------------- lowering
2070
2071fn as_noun(f: Frag) -> Result<Expr> {
2072    match f {
2073        Frag::Noun(e) => Ok(e),
2074        Frag::Name(n, s) => Ok(Expr::Name(n, s)),
2075        other => Err(Error::internal(format!("expected a noun fragment, got {other:?}"))),
2076    }
2077}
2078
2079fn as_verb(f: Frag) -> Result<(Verb, Span)> {
2080    match f {
2081        Frag::Verb(VerbFrag::V(v), s) => Ok((v, s)),
2082        other => Err(Error::internal(format!("expected a verb fragment, got {other:?}"))),
2083    }
2084}
2085
2086/// The literal array behind a noun fragment, if it is one. Derived verbs that
2087/// capture a noun (rank specifications, noun forks) need the value now.
2088fn as_const(f: &Frag) -> Option<&Array> {
2089    match f {
2090        Frag::Noun(Expr::Const(a, _)) => Some(a),
2091        _ => None,
2092    }
2093}
2094
2095/// A noun fragment's value, where it is a literal or an expression over
2096/// literals that settles at compile time. An index specification such as
2097/// `(<a:;1)` is written out rather than typed in, so a modifier capturing
2098/// one has to fold it.
2099fn noun_value(f: &Frag) -> Option<Array> {
2100    if let Some(a) = as_const(f) {
2101        return Some(a.clone());
2102    }
2103    let Frag::Noun(e) = f else { return None };
2104    let cfg = crate::verb::EvalCfg {
2105        agreement: crate::verb::Agreement::LeadingPrefix,
2106        fmt: crate::fmt::FmtOpts::J,
2107        tol: crate::verb::Tol::J,
2108        rules: crate::frontend::Rules::default(),
2109    };
2110    crate::ir::fold_const(e, cfg)
2111}
2112
2113fn monad(v: Frag, y: Frag) -> Result<Frag> {
2114    let (verb, vspan) = as_verb(v)?;
2115    let y = as_noun(y)?;
2116    let span = Span::merge(vspan, y.span());
2117    Ok(Frag::Noun(Expr::Monad { verb, y: Box::new(y), span }))
2118}
2119
2120fn dyad(x: Frag, v: Frag, y: Frag) -> Result<Frag> {
2121    let x = as_noun(x)?;
2122    let (verb, vspan) = as_verb(v)?;
2123    let y = as_noun(y)?;
2124    let span = Span::merge(Span::merge(x.span(), vspan), y.span());
2125    Ok(Frag::Noun(Expr::Dyad { verb, x: Box::new(x), y: Box::new(y), span }))
2126}
2127
2128fn apply_adverb(u: Frag, a: Frag, scope: &Names) -> Result<Frag> {
2129    let Frag::Adverb(m, aspan) = a else {
2130        return Err(Error::internal("expected an adverb fragment"));
2131    };
2132    let span = Span::merge(u.span(), aspan);
2133    let glyph = match m {
2134        Modifier::Prim(g) => g,
2135        Modifier::Explicit(src) => return derive_explicit(&src, u, None, scope, span),
2136    };
2137    // `}` takes either operand: `m}` amends at the indices m, and `u}`
2138    // computes them from the arguments instead.
2139    if glyph == "}" {
2140        if !u.is_real_verb() {
2141            let m = noun_value(&u)
2142                .ok_or_else(|| Error::not_yet("amend over a computed index", span))?;
2143            return Ok(Frag::Verb(VerbFrag::V(Verb::Amend(m)), span));
2144        }
2145        let (v, _) = as_verb(u)?;
2146        return Ok(Frag::Verb(VerbFrag::V(Verb::AmendVerb(Box::new(v))), span));
2147    }
2148    // `b.` takes either operand too: a noun names one of the thirty-two
2149    // boolean functions, a verb asks after the verb's own characteristics.
2150    if glyph == "b." && !u.is_real_verb() {
2151        let m = as_const(&u)
2152            .and_then(Array::to_i64_vec)
2153            .and_then(|v| v.first().copied())
2154            .filter(|&m| (0..32).contains(&m))
2155            .ok_or_else(|| {
2156                Error::not_yet("a boolean function outside `0 b.` … `31 b.`", span)
2157            })?;
2158        let p = crate::verb::Prim {
2159            name: "b.",
2160            monad: MonadOp::None,
2161            dyad: DyadOp::TruthTable(m as u8),
2162            ranks: [crate::verb::RANK_INF, 0, 0],
2163        };
2164        return Ok(Frag::Verb(VerbFrag::V(Verb::Prim(p)), span));
2165    }
2166    if !u.is_real_verb() {
2167        return Err(Error::not_yet("noun-operand adverbs", span));
2168    }
2169    let (v, _) = as_verb(u)?;
2170    let derived = match glyph {
2171        "/" => Verb::Reduce(Box::new(v)),
2172        "\\" => Verb::Windowed(Box::new(v), WindowKind::Prefix),
2173        "\\." => Verb::Windowed(Box::new(v), WindowKind::Suffix),
2174        "~" => Verb::Commute(Box::new(v)),
2175        "/." => Verb::Key(Box::new(v)),
2176        // Names are already substituted where they were used, so a fixed
2177        // verb is the verb itself.
2178        "f." => v,
2179        "M." => Verb::Memo(Box::new(v), Default::default()),
2180        "b." => Verb::Characteristics(Box::new(v)),
2181        _ => return Err(Error::not_yet(format!("adverb ({glyph})"), span)),
2182    };
2183    Ok(Frag::Verb(VerbFrag::V(derived), span))
2184}
2185
2186fn apply_conj(u: Frag, c: Frag, v: Frag, scope: &Names) -> Result<Frag> {
2187    let Frag::Conj(m, cspan) = c else {
2188        return Err(Error::internal("expected a conjunction fragment"));
2189    };
2190    let span = Span::merge(Span::merge(u.span(), cspan), v.span());
2191    let glyph = match m {
2192        Modifier::Prim(g) => g,
2193        Modifier::Explicit(src) => return derive_explicit(&src, u, Some(v), scope, span),
2194    };
2195    match glyph {
2196        "\"" => {
2197            let f = verb_operand(u, span)?;
2198            if v.is_verb() {
2199                return Err(Error::not_yet("verb rank (u\"v)", span));
2200            }
2201            let ranks = rank_spec(&v, span)?;
2202            Ok(Frag::Verb(VerbFrag::V(Verb::Rank(Box::new(f), ranks)), span))
2203        }
2204        "@:" => {
2205            let f = verb_operand(u, span)?;
2206            let g = verb_operand(v, span)?;
2207            Ok(Frag::Verb(VerbFrag::V(Verb::Atop(Box::new(f), Box::new(g))), span))
2208        }
2209        // `u@v` is `u@:v` applied at v's own ranks: one v-cell at a time,
2210        // with u run on each result. That difference in rank is all that
2211        // separates the two spellings.
2212        "@" => {
2213            let f = verb_operand(u, span)?;
2214            let g = verb_operand(v, span)?;
2215            let ranks = g.ranks();
2216            let atop = Verb::Atop(Box::new(f), Box::new(g));
2217            Ok(Frag::Verb(VerbFrag::V(Verb::Rank(Box::new(atop), ranks)), span))
2218        }
2219        "&" => compose(u, v, false, span),
2220        "&:" => compose(u, v, true, span),
2221        // `u&.>` is the one under that is not built out of an inverse:
2222        // opening each box and boxing the result again is J's each.
2223        "&." if is_open(&v) => {
2224            let f = verb_operand(u, span)?;
2225            Ok(Frag::Verb(VerbFrag::V(Verb::Each(Box::new(f), Enclose::Always)), span))
2226        }
2227        // `u&.v` is `v^:_1 @: u &: v`: v prepares both arguments, u runs on
2228        // what it made, and v's obverse puts the answer back. `&.` does it
2229        // at v's monadic rank, `&.:` on the arguments whole — the same
2230        // difference `&` and `&:` have.
2231        "&." | "&.:" => {
2232            let f = verb_operand(u, span)?;
2233            let g = verb_operand(v, span)?;
2234            let back = obverse_of(&g, span)?;
2235            let composed = Verb::Compose(Box::new(f), Box::new(g.clone()));
2236            let under = Verb::Atop(Box::new(back), Box::new(composed));
2237            if glyph == "&.:" {
2238                return Ok(Frag::Verb(VerbFrag::V(under), span));
2239            }
2240            let rank = g.ranks()[0];
2241            Ok(Frag::Verb(VerbFrag::V(Verb::Rank(Box::new(under), [rank; 3])), span))
2242        }
2243        "^:" => {
2244            let f = verb_operand(u, span)?;
2245            if v.is_verb() {
2246                // `u^:v` asks v for the number of applications; the while
2247                // loop is that verb under `^:_`.
2248                let g = verb_operand(v, span)?;
2249                let p = Verb::PowerV(Box::new(f), Box::new(g));
2250                return Ok(Frag::Verb(VerbFrag::V(p), span));
2251            }
2252            // A negative power runs the obverse that many times, which is
2253            // what makes `u^:_1` the inverse.
2254            // A negative count runs the obverse that many times, whether it
2255            // was written plainly or in a box (`u^:(<_3)`).
2256            let negative = noun_value(&v).is_some_and(|a| {
2257                let inner = match a.as_boxes() {
2258                    Some([b]) => b.clone(),
2259                    _ => a,
2260                };
2261                inner.to_f64_vec().is_some_and(|n| n.len() == 1 && n[0] < 0.0)
2262            });
2263            let p = power_spec(&v, span)?;
2264            let f = if negative { obverse_of(&f, span)? } else { f };
2265            Ok(Frag::Verb(VerbFrag::V(Verb::PowerN(Box::new(f), p)), span))
2266        }
2267        ";." => {
2268            let f = verb_operand(u, span)?;
2269            let n = one_atom(&v, "cut", span)?;
2270            if n.fract() != 0.0 || !matches!(n as i64, -3..=3) {
2271                return Err(Error::not_yet(format!("cut (u;.{n})"), span));
2272            }
2273            Ok(Frag::Verb(VerbFrag::V(Verb::Cut(Box::new(f), n as i64)), span))
2274        }
2275        // `u!.n` is the tolerance for the verbs whose meaning uses one; on
2276        // any other verb J's `!.` specifies a fill, which is its own
2277        // feature and not this one.
2278        "!." => {
2279            let f = verb_operand(u, span)?;
2280            // `|.!.f` is the fill shift: the fit specifies what the places
2281            // an item left behind are filled with, not a tolerance.
2282            if matches!(&f, Verb::Prim(p) if p.name == "|.") {
2283                let fill = as_const(&v)
2284                    .cloned()
2285                    .ok_or_else(|| Error::not_yet("a computed fill (|.!.n)", span))?;
2286                return Ok(Frag::Verb(VerbFrag::V(Verb::ShiftFill(fill)), span));
2287            }
2288            let n = one_atom(&v, "fit", span)?;
2289            if !f.uses_tolerance() {
2290                return Err(Error::not_yet(
2291                    format!("fill specification ({}!.n)", f.name()),
2292                    span,
2293                ));
2294            }
2295            // J refuses a tolerance above 2^-34, and so does libjay.
2296            if !(0.0..=LARGEST_TOLERANCE).contains(&n) {
2297                return Err(Error::domain(
2298                    format!("a comparison tolerance must be between 0 and {LARGEST_TOLERANCE}"),
2299                    span,
2300                ));
2301            }
2302            Ok(Frag::Verb(VerbFrag::V(Verb::Fit(Box::new(f), n)), span))
2303        }
2304        // `u :. v` declares v to be u's obverse; it changes nothing about
2305        // how u applies, only what `^:_1` and `&.` may then do with it.
2306        ":." => {
2307            let f = verb_operand(u, span)?;
2308            let g = verb_operand(v, span)?;
2309            Ok(Frag::Verb(
2310                VerbFrag::V(Verb::WithObverse(Box::new(f), Box::new(g))),
2311                span,
2312            ))
2313        }
2314        // `u@.v` picks one verb of the gerund u by v's value at the
2315        // arguments; a noun on the right picks one now and for good.
2316        "@." => {
2317            let vs = gerund_verbs(&u, scope, span)?;
2318            if v.is_verb() {
2319                let w = verb_operand(v, span)?;
2320                return Ok(Frag::Verb(VerbFrag::V(Verb::Agenda(vs, Box::new(w))), span));
2321            }
2322            let at = one_atom(&v, "agenda", span)?;
2323            if at.fract() != 0.0 {
2324                return Err(Error::parse("an agenda index must be a whole number", span));
2325            }
2326            let picked = crate::verb::pick_gerund(&vs, at as i64, span)?;
2327            Ok(Frag::Verb(VerbFrag::V(picked), span))
2328        }
2329        // `u`v` ties two entities into a gerund, which is ordinary boxed
2330        // data: one box per atomic representation, catenated.
2331        "`" => {
2332            let left = tie_side(&u, scope, span)?;
2333            let right = tie_side(&v, scope, span)?;
2334            let tied = crate::verb::catenate(&left, &right, true, true, span)?;
2335            Ok(Frag::Noun(Expr::Const(tied, span)))
2336        }
2337        // `u :: v` answers a refusal of u by running v instead. A noun on
2338        // the right is the constant verb yielding it, as J reads it.
2339        "::" => {
2340            let f = verb_operand(u, span)?;
2341            let g = if v.is_noun() {
2342                constant_verb(bond_noun(&v, span)?)
2343            } else {
2344                verb_operand(v, span)?
2345            };
2346            Ok(Frag::Verb(VerbFrag::V(Verb::Adverse(Box::new(f), Box::new(g))), span))
2347        }
2348        // `u L: n` and `u S: n` apply u at a boxing level: `L:` puts each
2349        // answer back in the box its operand came from, `S:` spreads them
2350        // into one array.
2351        "L:" | "S:" => {
2352            let f = verb_operand(u, span)?;
2353            let n = one_atom(&v, "level", span)?;
2354            if n.fract() != 0.0 || !n.is_finite() {
2355                return Err(Error::not_yet(format!("a level of {n} ({glyph})"), span));
2356            }
2357            let level = Verb::Level {
2358                u: Box::new(f),
2359                level: n as i64,
2360                spread: glyph == "S:",
2361            };
2362            Ok(Frag::Verb(VerbFrag::V(level), span))
2363        }
2364        // `` m`:n ``: 0 applies every verb of the gerund to the arguments
2365        // and frames the answers, 3 inserts them between the items of y,
2366        // and 6 is the train the gerund spells, which is built here.
2367        "`:" => {
2368            if u.is_verb() {
2369                return Err(Error::domain(
2370                    "`: reads a gerund, which is boxed data, not a verb",
2371                    span,
2372                ));
2373            }
2374            let vs = gerund_verbs(&u, scope, span)?;
2375            let n = one_atom(&v, "evoke gerund", span)?;
2376            if vs.is_empty() {
2377                return Err(Error::domain("an evoked gerund is empty", span));
2378            }
2379            match n {
2380                0.0 | 3.0 => {
2381                    Ok(Frag::Verb(VerbFrag::V(Verb::Evoke(vs, n as i64)), span))
2382                }
2383                6.0 => train_of(vs, span),
2384                _ => Err(Error::domain(
2385                    format!("`:{n} is not one of the evoke forms 0, 3 and 6"),
2386                    span,
2387                )),
2388            }
2389        }
2390        // `m H. n`: the generalised hypergeometric function, m the
2391        // numerator parameters and n the denominator ones. Both are nouns,
2392        // and an empty list on either side is the ordinary case of none.
2393        "H." => {
2394            let num = series_parameters(&u, span)?;
2395            let den = series_parameters(&v, span)?;
2396            Ok(Frag::Verb(VerbFrag::V(Verb::Hypergeometric { num, den }), span))
2397        }
2398        // Threads reach outside the expression, which the sandbox closes;
2399        // libjay's own parallelism is not something a sentence asks for.
2400        // That is a property of libjay, not a queue position.
2401        "T." => Err(Error::sandbox(
2402            "T. starts J's own threads, which libjay does not open",
2403            span,
2404        )),
2405        // `u t. n` schedules u in one of J's thread pools and answers with
2406        // a pyx — a task, not a value. The sandbox does not open those
2407        // threads, which is libjay's own policy and not a queue position.
2408        // The reference rejects `t:` outright — an invalid inflection, as
2409        // it does `d.`, `D.` and `D:`. There is nothing here to implement.
2410        "t:" => Err(Error::new(
2411            ErrorKind::Language,
2412            "t: is not a J inflection; the reference rejects the spelling",
2413            Some(span),
2414        )),
2415        "t." => Err(Error::sandbox(
2416            "t. runs a verb in one of J's thread pools, which libjay does not open",
2417            span,
2418        )),
2419        "." => Err(Error::not_yet("the inner product (u . v)", span)),
2420        "!:" => foreign(&u, &v, span),
2421        // `u : v` is J's monad/dyad conjunction. The explicit definitions
2422        // spelled `3 : '…'` and `4 : '…'` are read by the lexer and never
2423        // reach here.
2424        ":" => Err(Error::not_yet("the monad-dyad conjunction (u : v)", span)),
2425        _ => Err(Error::not_yet(format!("the conjunction {glyph}"), span)),
2426    }
2427}
2428
2429/// `u&v` and `u&:v`, in all three shapes the conjunction takes.
2430///
2431/// With two verbs it composes: monadically `u v y`, dyadically
2432/// `(v x) u (v y)` — and `&` runs that at v's monadic rank on both sides
2433/// while `&:` runs it on the arguments whole. With a noun on either side it
2434/// bonds that noun into the dyad, giving a verb with a monadic valence only;
2435/// `&:` takes no noun at all.
2436fn compose(u: Frag, v: Frag, infinite: bool, span: Span) -> Result<Frag> {
2437    let verb = |v: Verb| Ok(Frag::Verb(VerbFrag::V(v), span));
2438    if infinite || (!u.is_noun() && !v.is_noun()) {
2439        let f = verb_operand(u, span)?;
2440        let g = verb_operand(v, span)?;
2441        let monadic_rank = g.ranks()[0];
2442        let composed = Verb::Compose(Box::new(f), Box::new(g));
2443        if infinite {
2444            return verb(composed);
2445        }
2446        return verb(Verb::Rank(Box::new(composed), [monadic_rank; 3]));
2447    }
2448    if u.is_noun() && v.is_noun() {
2449        return Err(Error::not_yet("noun-operand conjunctions", span));
2450    }
2451    // A bond applies its verb dyadically to the WHOLE argument: `m&v y` is
2452    // `m v y`, and its rank is infinite whatever v's is — `1 2&+ b. 0`
2453    // reports `_ _ _`, and `1 2&+ i. 2 2` agrees row by row rather than
2454    // pairing the noun with every atom.
2455    if u.is_noun() {
2456        let m = bond_noun(&u, span)?;
2457        let g = as_verb(v)?.0;
2458        return verb(Verb::BondLeft(m, Box::new(g)));
2459    }
2460    let f = as_verb(u)?.0;
2461    let n = bond_noun(&v, span)?;
2462    verb(Verb::BondRight(Box::new(f), n))
2463}
2464
2465/// The largest comparison tolerance `!.` accepts, as J's does: 2^-34.
2466const LARGEST_TOLERANCE: f64 = 5.820_766_091_346_741e-11;
2467
2468/// A conjunction's single numeric noun operand.
2469/// One side's parameter list for `m H. n`: a numeric list, known now.
2470fn series_parameters(f: &Frag, span: Span) -> Result<Vec<crate::complex::Cx>> {
2471    let Some(arr) = as_const(f) else {
2472        return Err(Error::not_yet("computed hypergeometric parameters (m H. n)", span));
2473    };
2474    if arr.count() == 0 {
2475        return Ok(Vec::new());
2476    }
2477    if arr.rank() > 1 {
2478        return Err(Error::parse("a hypergeometric parameter list is a vector", span));
2479    }
2480    match arr.data.cast(crate::dtype::DType::Complex) {
2481        Some(Data::Complex(v)) => Ok(v.as_slice().to_vec()),
2482        _ => Err(Error::parse("hypergeometric parameters are numbers", span)),
2483    }
2484}
2485
2486/// `m !: n`: J's foreigns, the family that reaches outside the language.
2487///
2488/// Three of them are libjay's. `1!:1` reads a line from the input source
2489/// and `1!:2` writes one to the output sink — the two halves of the stdio
2490/// the sandbox opens — and `3!:0` names an element type, which computes
2491/// and touches nothing.
2492///
2493/// The rest divide in two, and the division is the whole point of the
2494/// dispatcher. A foreign that would reach a file, a directory, the host or
2495/// a script is closed by the sandbox and no release will open it; one that
2496/// only computes is a queue position, and names itself as one.
2497fn foreign(u: &Frag, v: &Frag, span: Span) -> Result<Frag> {
2498    let family = foreign_number(u, span)?;
2499    let member = foreign_number(v, span)?;
2500    let prim = |name, monad, dyad| {
2501        Ok(Frag::Verb(
2502            VerbFrag::V(Verb::Prim(Prim { name, monad, dyad, ranks: [RANK_INF; 3] })),
2503            span,
2504        ))
2505    };
2506    let closed = |what: &str| {
2507        Err(Error::sandbox(format!("{family}!:{member} {what}, which is outside the program"), span))
2508    };
2509    match (family, member) {
2510        (1, 1) => prim("1!:1", MonadOp::ReadStream, DyadOp::None),
2511        (1, 2) => prim("1!:2", MonadOp::None, DyadOp::WriteStream),
2512        (3, 0) => prim("3!:0", MonadOp::TypeCode, DyadOp::None),
2513        // `5!:1 <'name'` is the atomic representation of what the name
2514        // stands for — the same boxed data a gerund is made of.
2515        (5, 1) => prim("5!:1", MonadOp::AtomicRep, DyadOp::None),
2516        (0, _) => closed("runs a script file"),
2517        // The rest of the file family: stdin and stdout are the streams the
2518        // sandbox opens, and every other member of it is the filesystem.
2519        (1, _) => closed("reaches the filesystem"),
2520        (2, _) => closed("reaches the host — its environment, its shell, its processes"),
2521        (6, _) => closed("reads the clock"),
2522        (15, _) => closed("calls into a shared library"),
2523        _ => Err(Error::not_yet(format!("the foreign {family}!:{member}"), span)),
2524    }
2525}
2526
2527/// One side of `m !: n`: a whole number, known now. A foreign is chosen by
2528/// its two numbers, so neither may be computed.
2529fn foreign_number(f: &Frag, span: Span) -> Result<i64> {
2530    if f.is_verb() {
2531        return Err(Error::parse("a foreign is spelled m!:n, with two numbers", span));
2532    }
2533    let Some(arr) = as_const(f) else {
2534        return Err(Error::not_yet("a computed foreign number (m!:n)", span));
2535    };
2536    match arr.to_i64_vec().as_deref() {
2537        Some([n]) if *n >= 0 => Ok(*n),
2538        _ => Err(Error::parse("a foreign is spelled m!:n, with two whole numbers", span)),
2539    }
2540}
2541
2542fn one_atom(f: &Frag, what: &str, span: Span) -> Result<f64> {
2543    let Some(arr) = as_const(f) else {
2544        return Err(Error::not_yet(format!("a computed {what} specification"), span));
2545    };
2546    let Some(vals) = arr.to_f64_vec() else {
2547        return Err(Error::parse(format!("{what} takes a numeric operand"), span));
2548    };
2549    match vals[..] {
2550        [n] => Ok(n),
2551        _ => Err(Error::parse(format!("{what} takes one atom"), span)),
2552    }
2553}
2554
2555/// The array a bonded noun operand holds; it has to be known now.
2556fn bond_noun(f: &Frag, span: Span) -> Result<Array> {
2557    as_const(f)
2558        .cloned()
2559        .ok_or_else(|| Error::not_yet("bonds over a non-literal noun", span))
2560}
2561
2562/// True for the fragment holding the primitive `>`, the only right operand
2563/// `&.` accepts.
2564fn is_open(f: &Frag) -> bool {
2565    matches!(f, Frag::Verb(VerbFrag::V(Verb::Prim(p)), _) if p.monad == MonadOp::Open)
2566}
2567
2568fn verb_operand(f: Frag, span: Span) -> Result<Verb> {
2569    if f.is_noun() {
2570        return Err(Error::not_yet("noun-operand conjunctions", span));
2571    }
2572    Ok(as_verb(f)?.0)
2573}
2574
2575/// `u"n`: 1 atom applies to every valence, 2 atoms are `left right` with the
2576/// monadic rank taken from the right, 3 atoms are given in full.
2577fn rank_spec(f: &Frag, span: Span) -> Result<[i64; 3]> {
2578    let Some(arr) = as_const(f) else {
2579        return Err(Error::not_yet("computed rank specifications", span));
2580    };
2581    let Some(vals) = arr.to_f64_vec() else {
2582        return Err(Error::parse("rank must be numeric", span));
2583    };
2584    if vals.is_empty() || vals.len() > 3 {
2585        return Err(Error::parse("rank takes 1 to 3 atoms", span));
2586    }
2587    let mut r = Vec::with_capacity(vals.len());
2588    for x in vals {
2589        if x == f64::INFINITY {
2590            r.push(RANK_INF);
2591        } else if x == f64::NEG_INFINITY {
2592            r.push(-RANK_INF);
2593        } else if x.fract() != 0.0 {
2594            return Err(Error::parse("rank must be an integer", span));
2595        } else {
2596            r.push(x as i64);
2597        }
2598    }
2599    Ok(match r.len() {
2600        1 => [r[0], r[0], r[0]],
2601        2 => [r[1], r[0], r[1]],
2602        _ => [r[0], r[1], r[2]],
2603    })
2604}
2605
2606/// `u^:n`: one nonnegative integer atom, or `_` for "iterate until the
2607/// result stops changing".
2608fn power_spec(f: &Frag, span: Span) -> Result<Power> {
2609    let Some(arr) = noun_value(f) else {
2610        return Err(Error::not_yet("computed power (u^:n)", span));
2611    };
2612    let arr = &arr;
2613    // A boxed count traces the applications rather than taking one of
2614    // them: `u^:(<n)` is `u^:(i.n)`, and `u^:a:` traces to convergence.
2615    if let Some(boxes) = arr.as_boxes() {
2616        let [inner] = boxes else {
2617            return Err(Error::parse("a boxed power takes one box", span));
2618        };
2619        if inner.count() == 0 {
2620            return Ok(Power::ConvergeTrace);
2621        }
2622        let Some(vals) = inner.to_f64_vec() else {
2623            return Err(Error::parse("power must be numeric", span));
2624        };
2625        let [n] = vals[..] else {
2626            return Err(Error::not_yet("a boxed list of power counts (u^:(<n))", span));
2627        };
2628        if n.fract() != 0.0 || n.abs() > 1e6 {
2629            return Err(Error::parse("a boxed power must be a whole count", span));
2630        }
2631        // `u^:(<n)` is `u^:(i.n)`: n counts, downwards where n is negative.
2632        if n == 0.0 {
2633            return Err(Error::domain("a boxed power traces at least one application", span));
2634        }
2635        // A negative n counts the same way with the obverse, which the
2636        // caller has already put in the verb's place.
2637        return Ok(Power::Each((0..n.abs() as u64).collect()));
2638    }
2639    let Some(vals) = arr.to_f64_vec() else {
2640        return Err(Error::parse("power must be numeric", span));
2641    };
2642    if vals.len() > 1 {
2643        // A list of counts gives one answer each, framed.
2644        let mut counts = Vec::with_capacity(vals.len());
2645        for n in &vals {
2646            if n.fract() != 0.0 || *n < 0.0 || *n > 1e6 {
2647                return Err(Error::not_yet("a power count outside 0 … 1e6", span));
2648            }
2649            counts.push(*n as u64);
2650        }
2651        return Ok(Power::Each(counts));
2652    }
2653    let [n] = vals[..] else {
2654        return Err(Error::not_yet("power over a list of counts (u^:n)", span));
2655    };
2656    if n == f64::INFINITY {
2657        return Ok(Power::Converge);
2658    }
2659    if n.fract() != 0.0 {
2660        return Err(Error::parse("power must be a whole number", span));
2661    }
2662    if n < 0.0 {
2663        // A negative power is the obverse applied that many times; the
2664        // caller substitutes the obverse for the verb.
2665        return Ok(Power::Times((-n) as u64));
2666    }
2667    Ok(Power::Times(n as u64))
2668}
2669
2670/// The obverse of a verb, or the diagnostic naming the verb that has none.
2671pub(crate) fn obverse_of(v: &Verb, span: Span) -> Result<Verb> {
2672    crate::verb::obverse(v).ok_or_else(|| {
2673        Error::not_yet(format!("the obverse of {} (no inverse is known)", v.name()), span)
2674    })
2675}
2676
2677/// One side of `` u`v ``: a verb becomes the box holding its atomic
2678/// representation, a noun stands for itself. Catenating the two is the tie,
2679/// which is why `` u`v`w `` builds up left to right with no special case.
2680fn tie_side(f: &Frag, scope: &Names, span: Span) -> Result<Array> {
2681    if f.is_real_verb() {
2682        let (v, _) = as_verb(f.clone())?;
2683        return Ok(Array::boxed(verb_ar(&v, span)?.to_array()));
2684    }
2685    noun_in_scope(f, scope)
2686        .ok_or_else(|| Error::not_yet("a tie over a computed noun", span))
2687}
2688
2689/// A verb's atomic representation, with the diagnostic for the verbs libjay
2690/// has no J spelling to give.
2691fn verb_ar(v: &Verb, span: Span) -> Result<crate::gerund::Ar> {
2692    crate::gerund::verb_ar(v).ok_or_else(|| {
2693        Error::not_yet(format!("the atomic representation of {}", v.name()), span)
2694    })
2695}
2696
2697/// A noun fragment's value, a name that holds a literal included. A gerund
2698/// is data, so `` g =. +`- `` and then `g@.1` has to find what g holds.
2699fn noun_in_scope(f: &Frag, scope: &Names) -> Option<Array> {
2700    if let Frag::Name(n, _) = f {
2701        return scope.consts.get(n).cloned();
2702    }
2703    noun_value(f)
2704}
2705
2706/// The verbs a gerund holds. A lone verb is a gerund of one, and boxed data
2707/// is read as the atomic representations it is.
2708fn gerund_verbs(f: &Frag, scope: &Names, span: Span) -> Result<Vec<Verb>> {
2709    if f.is_real_verb() {
2710        return Ok(vec![as_verb(f.clone())?.0]);
2711    }
2712    let arr = noun_in_scope(f, scope)
2713        .ok_or_else(|| Error::not_yet("a gerund computed at run time", span))?;
2714    let Some(items) = arr.as_boxes() else {
2715        return Err(Error::domain("a gerund is boxed data", span));
2716    };
2717    items.iter().map(|a| ar_verb(a, scope, span)).collect()
2718}
2719
2720/// One atomic representation as the verb it stands for.
2721fn ar_verb(a: &Array, scope: &Names, span: Span) -> Result<Verb> {
2722    let ar = crate::gerund::Ar::from_array(a)
2723        .ok_or_else(|| Error::domain("this is not an atomic representation", span))?;
2724    let (v, _) = as_verb(ar_frag(&ar, scope, span)?)?;
2725    Ok(v)
2726}
2727
2728/// One atomic representation as the fragment it stands for: a verb, or the
2729/// noun a modifier takes as an operand.
2730fn ar_frag(ar: &crate::gerund::Ar, scope: &Names, span: Span) -> Result<Frag> {
2731    use crate::gerund::Ar;
2732    match ar {
2733        Ar::Noun(a) => Ok(Frag::Noun(Expr::Const(a.clone(), span))),
2734        Ar::Prim(word) => {
2735            if word == "[:" {
2736                return Ok(Frag::Verb(VerbFrag::Cap, span));
2737            }
2738            match verb_for(word) {
2739                Some(v) => Ok(Frag::Verb(VerbFrag::V(v), span)),
2740                None => Err(Error::domain(
2741                    format!("`{word}` is not a verb an atomic representation may name"),
2742                    span,
2743                )),
2744            }
2745        }
2746        Ar::Train(parts) => {
2747            let frags: Result<Vec<Frag>> =
2748                parts.iter().map(|p| ar_frag(p, scope, span)).collect();
2749            let mut frags = frags?;
2750            match frags.len() {
2751                2 => {
2752                    let b = frags.pop().expect("two parts");
2753                    let a = frags.pop().expect("two parts");
2754                    apply_bident(a, b, &scope.nouns)
2755                }
2756                3 => {
2757                    let h = frags.pop().expect("three parts");
2758                    let g = frags.pop().expect("three parts");
2759                    let f = frags.pop().expect("three parts");
2760                    apply_fork(f, g, h)
2761                }
2762                _ => Err(Error::domain("a train is two or three parts", span)),
2763            }
2764        }
2765        Ar::Derived(word, ops) => {
2766            let frags: Result<Vec<Frag>> = ops.iter().map(|p| ar_frag(p, scope, span)).collect();
2767            let mut frags = frags?;
2768            if let Some(glyph) = adverb(word) {
2769                if frags.len() != 1 {
2770                    return Err(Error::domain(format!("{glyph} takes one operand"), span));
2771                }
2772                let u = frags.pop().expect("one operand");
2773                return apply_adverb(u, Frag::Adverb(Modifier::Prim(glyph), span), scope);
2774            }
2775            if let Some(glyph) = conjunction(word) {
2776                if frags.len() != 2 {
2777                    return Err(Error::domain(format!("{glyph} takes two operands"), span));
2778                }
2779                let v = frags.pop().expect("two operands");
2780                let u = frags.pop().expect("two operands");
2781                return apply_conj(u, Frag::Conj(Modifier::Prim(glyph), span), v, scope);
2782            }
2783            Err(Error::domain(
2784                format!("`{word}` is not a modifier an atomic representation may name"),
2785                span,
2786            ))
2787        }
2788    }
2789}
2790
2791/// The train a gerund spells: `` `:6 `` groups the verbs from the right,
2792/// three at a time, which is how J reads a train written out.
2793fn train_of(vs: Vec<Verb>, span: Span) -> Result<Frag> {
2794    let mut frags: Vec<Frag> =
2795        vs.into_iter().map(|v| Frag::Verb(VerbFrag::V(v), span)).collect();
2796    while frags.len() > 3 {
2797        let h = frags.pop().expect("three or more");
2798        let g = frags.pop().expect("three or more");
2799        let f = frags.pop().expect("three or more");
2800        frags.push(apply_fork(f, g, h)?);
2801    }
2802    match frags.len() {
2803        1 => Ok(frags.pop().expect("one")),
2804        2 => {
2805            let b = frags.pop().expect("two");
2806            let a = frags.pop().expect("two");
2807            apply_bident(a, b, &HashSet::new())
2808        }
2809        _ => {
2810            let h = frags.pop().expect("three");
2811            let g = frags.pop().expect("three");
2812            let f = frags.pop().expect("three");
2813            apply_fork(f, g, h)
2814        }
2815    }
2816}
2817
2818fn apply_fork(f: Frag, g: Frag, h: Frag) -> Result<Frag> {
2819    let span = Span::merge(Span::merge(f.span(), g.span()), h.span());
2820    let (gv, _) = as_verb(g)?;
2821    let (hv, _) = as_verb(h)?;
2822    match f {
2823        // `[: g h` is g atop h: the left tine produces nothing to fork over.
2824        Frag::Verb(VerbFrag::Cap, _) => {
2825            Ok(Frag::Verb(VerbFrag::V(Verb::Atop(Box::new(gv), Box::new(hv))), span))
2826        }
2827        Frag::Verb(VerbFrag::V(fv), _) => Ok(Frag::Verb(
2828            VerbFrag::V(Verb::Fork(Box::new(fv), Box::new(gv), Box::new(hv))),
2829            span,
2830        )),
2831        noun => {
2832            let Some(arr) = as_const(&noun) else {
2833                return Err(Error::not_yet("noun forks over a non-literal noun", span));
2834            };
2835            Ok(Frag::Verb(
2836                VerbFrag::V(Verb::NounFork(arr.clone(), Box::new(gv), Box::new(hv))),
2837                span,
2838            ))
2839        }
2840    }
2841}
2842
2843fn apply_bident(a: Frag, b: Frag, nouns: &HashSet<String>) -> Result<Frag> {
2844    let span = Span::merge(a.span(), b.span());
2845    // A name here is not a verb, or it would have been substituted; if it
2846    // is not a value either, that is what is wrong with the sentence, and
2847    // it is what the reference reports.
2848    if let Frag::Name(n, nspan) = &a && !nouns.contains(n) {
2849        return Err(Error::new(
2850            ErrorKind::Value,
2851            format!("undefined name: {n}"),
2852            Some(*nspan),
2853        ));
2854    }
2855    if a.is_real_verb() && b.is_real_verb() {
2856        let (f, _) = as_verb(a)?;
2857        let (g, _) = as_verb(b)?;
2858        return Ok(Frag::Verb(VerbFrag::V(Verb::Hook(Box::new(f), Box::new(g))), span));
2859    }
2860    // Two verbs are the only pair J makes a train of. Anything else here —
2861    // a noun beside a noun, a noun beside a verb, a leftover modifier — is
2862    // a sentence the language does not have a reading for, which is what
2863    // the reference calls a syntax error. It is not a queue position.
2864    if matches!(a, Frag::Verb(VerbFrag::Cap, _)) {
2865        return Err(Error::parse("`[:` caps a fork; it has no verb of its own", span));
2866    }
2867    Err(Error::parse("syntax error", span))
2868}
2869
2870fn apply_assign(target: Frag, value: Frag, scope: Scope) -> Result<Frag> {
2871    let span = Span::merge(target.span(), value.span());
2872    match target {
2873        // `=.` names a local and `=:` a global; the two differ only inside
2874        // an explicit definition, which is the only thing with a local
2875        // frame to name.
2876        Frag::Name(name, _) => match value {
2877            // Naming a verb is settled here, at parse time: `parse` records
2878            // the name and substitutes the verb into later sentences.
2879            Frag::Verb(VerbFrag::V(verb), _) => Ok(Frag::VerbDef(name, verb, span)),
2880            Frag::Verb(VerbFrag::Cap, _) => Err(Error::not_yet("assigning [: on its own", span)),
2881            // Naming a modifier is settled at parse time too: the name
2882            // stands for the spelling wherever a later sentence writes it.
2883            Frag::Adverb(m, _) => Ok(Frag::ModDef(name, false, m, span)),
2884            Frag::Conj(m, _) => Ok(Frag::ModDef(name, true, m, span)),
2885            v if v.is_noun() => {
2886                let value = as_noun(v)?;
2887                Ok(Frag::Noun(Expr::Assign { name, value: Box::new(value), scope, span }))
2888            }
2889            other => Err(Error::internal(format!("cannot assign {other:?}"))),
2890        },
2891        Frag::Noun(_) => Err(Error::not_yet("multiple assignment", span)),
2892        other => Err(Error::internal(format!("expected an assignment target, got {other:?}"))),
2893    }
2894}
2895
2896#[cfg(test)]
2897mod tests {
2898    use super::*;
2899    use crate::dtype::DType;
2900    use crate::error::ErrorKind;
2901    use rstest::rstest;
2902
2903    fn parse_str(src: &str) -> Result<Vec<Expr>> {
2904        parse(&SourceParts::from_source(src).expect("source parts"))
2905    }
2906
2907    /// Parse literal text with no interpolation. `{. ` and `}.` are J words
2908    /// that `from_source` would read as a hole, so those tests take the
2909    /// pre-split path instead.
2910    fn one_literal(src: &str) -> Expr {
2911        let sp = SourceParts::from_parts(&[src], &[]);
2912        let mut s = parse(&sp).unwrap_or_else(|e| panic!("parse of {src:?} failed: {e}"));
2913        assert_eq!(s.len(), 1, "expected one sentence in {src:?}");
2914        s.pop().expect("one sentence")
2915    }
2916
2917    fn stmts(src: &str) -> Vec<Expr> {
2918        parse_str(src).unwrap_or_else(|e| panic!("parse of {src:?} failed: {e}"))
2919    }
2920
2921    /// The single statement of a one-sentence program.
2922    fn one(src: &str) -> Expr {
2923        let mut s = stmts(src);
2924        assert_eq!(s.len(), 1, "expected one sentence in {src:?}");
2925        s.pop().expect("one sentence")
2926    }
2927
2928    fn err(src: &str) -> Error {
2929        match parse_str(src) {
2930            Ok(v) => panic!("expected an error for {src:?}, got {v:?}"),
2931            Err(e) => e,
2932        }
2933    }
2934
2935    // The shape inspectors return owned copies so that a test can inspect
2936    // the result of `one(...)` in one expression.
2937
2938    fn konst(e: &Expr) -> Array {
2939        match e {
2940            Expr::Const(a, _) => a.clone(),
2941            other => panic!("expected a constant, got {other:?}"),
2942        }
2943    }
2944
2945    fn ints(e: &Expr) -> Vec<i64> {
2946        konst(e).as_i64_slice().expect("integer data").to_vec()
2947    }
2948
2949    fn prim_of(v: &Verb) -> Prim {
2950        match v {
2951            Verb::Prim(p) => *p,
2952            other => panic!("expected a primitive, got {other:?}"),
2953        }
2954    }
2955
2956    fn monad_of(e: &Expr) -> (Verb, Expr) {
2957        match e {
2958            Expr::Monad { verb, y, .. } => (verb.clone(), (**y).clone()),
2959            other => panic!("expected a monad, got {other:?}"),
2960        }
2961    }
2962
2963    fn dyad_of(e: &Expr) -> (Verb, Expr, Expr) {
2964        match e {
2965            Expr::Dyad { verb, x, y, .. } => (verb.clone(), (**x).clone(), (**y).clone()),
2966            other => panic!("expected a dyad, got {other:?}"),
2967        }
2968    }
2969
2970    // ------------------------------------------------------------- literals
2971
2972    #[test]
2973    fn single_number_is_an_atom() {
2974        let e = one("5");
2975        assert_eq!(konst(&e).shape, Vec::<usize>::new());
2976        assert_eq!(ints(&e), vec![5]);
2977        assert_eq!(e.span(), Span::new(0, 1));
2978    }
2979
2980    #[test]
2981    fn adjacent_numbers_merge_into_one_vector() {
2982        let e = one("1 2 3");
2983        assert_eq!(konst(&e).shape, vec![3]);
2984        assert_eq!(ints(&e), vec![1, 2, 3]);
2985        assert_eq!(e.span(), Span::new(0, 5));
2986    }
2987
2988    #[test]
2989    fn a_float_makes_the_whole_vector_float() {
2990        let a = konst(&one("1 2.5 3"));
2991        assert_eq!(a.dtype(), DType::F64);
2992        assert_eq!(a.as_f64_slice(), Some(&[1.0, 2.5, 3.0][..]));
2993    }
2994
2995    #[test]
2996    fn negatives_and_infinities() {
2997        let a = konst(&one("_3 1.5 _ __"));
2998        assert_eq!(a.shape, vec![4]);
2999        let v = a.as_f64_slice().expect("float vector");
3000        assert_eq!(v[0], -3.0);
3001        assert_eq!(v[1], 1.5);
3002        assert!(v[2].is_infinite() && v[2] > 0.0);
3003        assert!(v[3].is_infinite() && v[3] < 0.0);
3004    }
3005
3006    #[test]
3007    fn negative_integers_stay_integers() {
3008        let a = konst(&one("_3 _4"));
3009        assert_eq!(a.dtype(), DType::I64);
3010        assert_eq!(a.as_i64_slice(), Some(&[-3i64, -4][..]));
3011    }
3012
3013    #[rstest]
3014    #[case("1e3", 1000.0)]
3015    #[case("1e_3", 0.001)]
3016    #[case("2.5e2", 250.0)]
3017    #[case("_1.5", -1.5)]
3018    fn exponent_and_sign_forms(#[case] src: &str, #[case] want: f64) {
3019        let a = konst(&one(src));
3020        assert_eq!(a.dtype(), DType::F64);
3021        assert_eq!(a.to_f64_vec().expect("numeric"), vec![want]);
3022    }
3023
3024    #[test]
3025    fn adjacent_numbers_stop_at_a_non_number() {
3026        // `i.` after a vector is a separate word, not numeric characters.
3027        let (_, x, y) = dyad_of(&one("2 3 i. 4"));
3028        assert_eq!(konst(&x).shape, vec![2]);
3029        assert_eq!(konst(&y).shape, Vec::<usize>::new());
3030    }
3031
3032    #[test]
3033    fn string_of_several_characters_is_a_vector() {
3034        let e = one("'abc'");
3035        let a = konst(&e);
3036        assert_eq!(a.shape, vec![3]);
3037        assert_eq!(a.data, Data::Char(vec!['a', 'b', 'c'].into()));
3038        assert_eq!(e.span(), Span::new(0, 5));
3039    }
3040
3041    #[test]
3042    fn one_character_string_is_an_atom() {
3043        let a = konst(&one("'a'"));
3044        assert_eq!(a.shape, Vec::<usize>::new());
3045        assert_eq!(a.data, Data::Char(vec!['a'].into()));
3046    }
3047
3048    #[test]
3049    fn empty_string_is_an_empty_vector() {
3050        let a = konst(&one("''"));
3051        assert_eq!(a.shape, vec![0]);
3052        assert_eq!(a.dtype(), DType::Char);
3053    }
3054
3055    #[test]
3056    fn doubled_quote_is_an_escaped_quote() {
3057        let a = konst(&one("'it''s'"));
3058        assert_eq!(a.shape, vec![4]);
3059        assert_eq!(a.data, Data::Char(vec!['i', 't', '\'', 's'].into()));
3060    }
3061
3062    #[test]
3063    fn unterminated_string_is_a_parse_error() {
3064        let e = err("'abc");
3065        assert_eq!(e.kind, ErrorKind::Parse);
3066        assert!(e.msg.contains("unterminated"), "{}", e.msg);
3067        assert_eq!(e.span, Some(Span::new(0, 4)));
3068    }
3069
3070    // ------------------------------------------------------------- comments
3071
3072    #[test]
3073    fn comment_runs_to_end_of_line() {
3074        let e = one("1 2 NB. and the rest + - ' is ignored");
3075        assert_eq!(konst(&e).shape, vec![2]);
3076    }
3077
3078    #[test]
3079    fn comment_only_line_yields_no_sentence() {
3080        assert!(stmts("NB. nothing here").is_empty());
3081        let s = stmts("NB. header\n5");
3082        assert_eq!(s.len(), 1);
3083        assert_eq!(ints(&s[0]), vec![5]);
3084    }
3085
3086    #[test]
3087    fn nb_inside_a_name_is_not_a_comment() {
3088        // `aNB` is a name; only a whole word `NB.` starts a comment.
3089        match one("aNB") {
3090            Expr::Name(n, _) => assert_eq!(n, "aNB"),
3091            other => panic!("expected a name, got {other:?}"),
3092        }
3093    }
3094
3095    // -------------------------------------------------------------- parsing
3096
3097    #[test]
3098    fn empty_program_has_no_sentences() {
3099        assert!(stmts("").is_empty());
3100        assert!(stmts("\n\n").is_empty());
3101    }
3102
3103    #[test]
3104    fn trains_of_dyads_are_right_associative() {
3105        let e = one("1 + 2 + 3");
3106        let (v, x, y) = dyad_of(&e);
3107        assert_eq!(prim_of(&v).name, "+");
3108        assert_eq!(ints(&x), vec![1]);
3109        let (v2, x2, y2) = dyad_of(&y);
3110        assert_eq!(prim_of(&v2).name, "+");
3111        assert_eq!(ints(&x2), vec![2]);
3112        assert_eq!(ints(&y2), vec![3]);
3113        assert_eq!(e.span(), Span::new(0, 9));
3114    }
3115
3116    #[test]
3117    fn a_verb_with_no_left_argument_is_a_monad() {
3118        let e = one("- 5");
3119        let (v, y) = monad_of(&e);
3120        assert_eq!(prim_of(&v).monad, MonadOp::Scalar(ScalarMonad::Neg));
3121        assert_eq!(ints(&y), vec![5]);
3122        assert_eq!(e.span(), Span::new(0, 3));
3123    }
3124
3125    #[test]
3126    fn a_verb_with_a_left_argument_is_a_dyad() {
3127        let (v, _, _) = dyad_of(&one("1 - 5"));
3128        assert_eq!(prim_of(&v).dyad, DyadOp::Scalar(ScalarDyad::Sub));
3129    }
3130
3131    #[test]
3132    fn a_monad_binds_to_the_right_inside_a_dyad() {
3133        let (v, x, y) = dyad_of(&one("2 * - 3"));
3134        assert_eq!(prim_of(&v).name, "*");
3135        assert_eq!(ints(&x), vec![2]);
3136        let (mv, my) = monad_of(&y);
3137        assert_eq!(prim_of(&mv).name, "-");
3138        assert_eq!(ints(&my), vec![3]);
3139    }
3140
3141    #[test]
3142    fn parentheses_group_the_left_argument() {
3143        let (v, x, y) = dyad_of(&one("(1 + 2) * 3"));
3144        assert_eq!(prim_of(&v).name, "*");
3145        let (iv, _, _) = dyad_of(&x);
3146        assert_eq!(prim_of(&iv).name, "+");
3147        // The parentheses are dropped, but the span still covers them, so
3148        // that a caret under the group underlines something balanced.
3149        assert_eq!(x.span(), Span::new(0, 7));
3150        assert_eq!(ints(&y), vec![3]);
3151    }
3152
3153    #[test]
3154    fn names_are_nouns() {
3155        match one("x") {
3156            Expr::Name(n, s) => {
3157                assert_eq!(n, "x");
3158                assert_eq!(s, Span::new(0, 1));
3159            }
3160            other => panic!("expected a name, got {other:?}"),
3161        }
3162        let (_, x, y) = dyad_of(&one("x + y"));
3163        assert!(matches!(x, Expr::Name(..)));
3164        assert!(matches!(y, Expr::Name(..)));
3165    }
3166
3167    #[test]
3168    fn echo_is_a_verb() {
3169        let (v, y) = monad_of(&one("echo 5"));
3170        assert_eq!(prim_of(&v).monad, MonadOp::Echo);
3171        assert_eq!(ints(&y), vec![5]);
3172    }
3173
3174    #[test]
3175    fn inflected_letter_words_are_primitives() {
3176        let (v, _) = monad_of(&one("i. 3"));
3177        let p = prim_of(&v);
3178        assert_eq!(p.monad, MonadOp::IotaJ);
3179        assert_eq!(p.ranks, [1, RANK_INF, RANK_INF]);
3180    }
3181
3182    #[rstest]
3183    #[case("|: 1 2 3", MonadOp::TransposeAxes)]
3184    #[case("$ 1 2 3", MonadOp::ShapeOf)]
3185    #[case("# 1 2 3", MonadOp::Tally)]
3186    #[case(", 1 2 3", MonadOp::Ravel)]
3187    #[case("%: 1 2 3", MonadOp::Scalar(ScalarMonad::Sqrt))]
3188    #[case("<. 1.5", MonadOp::Scalar(ScalarMonad::Floor))]
3189    fn inflected_symbol_words(#[case] src: &str, #[case] want: MonadOp) {
3190        let (v, _) = monad_of(&one(src));
3191        assert_eq!(prim_of(&v).monad, want);
3192    }
3193
3194    #[rstest]
3195    #[case("{. 1 2 3", MonadOp::Head, DyadOp::Take)]
3196    #[case("}. 1 2 3", MonadOp::Behead, DyadOp::Drop)]
3197    fn brace_words(#[case] src: &str, #[case] monad: MonadOp, #[case] dyad: DyadOp) {
3198        let (v, _) = monad_of(&one_literal(src));
3199        let p = prim_of(&v);
3200        assert_eq!(p.monad, monad);
3201        assert_eq!(p.dyad, dyad);
3202        assert_eq!(p.ranks, [RANK_INF, 1, RANK_INF]);
3203    }
3204
3205    #[test]
3206    fn a_brace_word_takes_a_left_argument() {
3207        let (v, x, y) = dyad_of(&one_literal("2 {. 1 2 3"));
3208        assert_eq!(prim_of(&v).dyad, DyadOp::Take);
3209        assert_eq!(ints(&x), vec![2]);
3210        assert_eq!(konst(&y).shape, vec![3]);
3211    }
3212
3213    #[rstest]
3214    #[case("2 $ 1 2 3", DyadOp::Reshape)]
3215    #[case("2 [ 3", DyadOp::Left)]
3216    #[case("2 ] 3", DyadOp::Right)]
3217    #[case("2 <. 3", DyadOp::Scalar(ScalarDyad::Min))]
3218    #[case("2 >: 3", DyadOp::Scalar(ScalarDyad::Ge))]
3219    fn dyadic_primitives(#[case] src: &str, #[case] want: DyadOp) {
3220        let (v, _, _) = dyad_of(&one(src));
3221        assert_eq!(prim_of(&v).dyad, want);
3222    }
3223
3224    #[test]
3225    fn unimplemented_meanings_reach_the_verb_not_the_parser() {
3226        let (v, _, _) = dyad_of(&one("2 ;: 'a b'"));
3227        assert_eq!(prim_of(&v).dyad, DyadOp::NotYet("sequential machine (dyadic ;:)"));
3228        let (v, _) = monad_of(&one("\": 1 2"));
3229        assert_eq!(prim_of(&v).dyad, DyadOp::NotYet("format with a specification"));
3230    }
3231
3232    #[test]
3233    fn multiple_sentences_become_multiple_statements() {
3234        let s = stmts("a =. 1 2\n+/ a\n");
3235        assert_eq!(s.len(), 2);
3236        assert!(matches!(s[0], Expr::Assign { .. }));
3237        assert!(matches!(s[1], Expr::Monad { .. }));
3238    }
3239
3240    // ------------------------------------------------------------ modifiers
3241
3242    #[test]
3243    fn an_adverb_binds_before_the_verb_is_applied() {
3244        let e = one("+/ 1 2 3");
3245        let (v, y) = monad_of(&e);
3246        match &v {
3247            Verb::Reduce(inner) => assert_eq!(prim_of(inner).name, "+"),
3248            other => panic!("expected a reduction, got {other:?}"),
3249        }
3250        assert_eq!(konst(&y).shape, vec![3]);
3251        assert_eq!(e.span(), Span::new(0, 8));
3252    }
3253
3254    #[test]
3255    fn rank_applies_to_the_derived_verb() {
3256        let (v, _) = monad_of(&one("+/\"1 m"));
3257        match &v {
3258            Verb::Rank(inner, ranks) => {
3259                assert_eq!(*ranks, [1, 1, 1]);
3260                assert!(matches!(**inner, Verb::Reduce(_)), "got {inner:?}");
3261            }
3262            other => panic!("expected a ranked verb, got {other:?}"),
3263        }
3264    }
3265
3266    #[rstest]
3267    #[case("+\"1 m", [1, 1, 1])]
3268    #[case("+\"1 2 m", [2, 1, 2])]
3269    #[case("+\"0 1 2 m", [0, 1, 2])]
3270    #[case("+\"_ m", [RANK_INF, RANK_INF, RANK_INF])]
3271    #[case("+\"_1 m", [-1, -1, -1])]
3272    #[case("+\"2.0 m", [2, 2, 2])]
3273    fn rank_specifications(#[case] src: &str, #[case] want: [i64; 3]) {
3274        let (v, _) = monad_of(&one(src));
3275        assert_eq!(v.ranks(), want);
3276    }
3277
3278    #[test]
3279    fn rank_must_be_one_to_three_integer_atoms() {
3280        let e = err("+\"1 2 3 4 m");
3281        assert_eq!(e.kind, ErrorKind::Parse);
3282        assert!(e.msg.contains("1 to 3 atoms"), "{}", e.msg);
3283        let e = err("+\"1.5 m");
3284        assert_eq!(e.kind, ErrorKind::Parse);
3285        assert!(e.msg.contains("integer"), "{}", e.msg);
3286        let e = err("+\"'a' m");
3287        assert_eq!(e.kind, ErrorKind::Parse);
3288        assert!(e.msg.contains("numeric"), "{}", e.msg);
3289    }
3290
3291    #[test]
3292    fn verb_rank_is_not_supported_yet() {
3293        let e = err("+\"- m");
3294        assert_eq!(e.kind, ErrorKind::NotYet);
3295        assert!(e.msg.contains("verb rank"), "{}", e.msg);
3296    }
3297
3298    #[test]
3299    fn computed_rank_is_not_supported_yet() {
3300        let e = err("+\"{r} m");
3301        assert_eq!(e.kind, ErrorKind::NotYet);
3302        assert!(e.msg.contains("computed rank"), "{}", e.msg);
3303    }
3304
3305    #[test]
3306    fn atop_conjunction() {
3307        let (v, _) = monad_of(&one("+/ @: , y"));
3308        match &v {
3309            Verb::Atop(f, g) => {
3310                assert!(matches!(**f, Verb::Reduce(_)), "got {f:?}");
3311                assert_eq!(prim_of(g).name, ",");
3312            }
3313            other => panic!("expected an atop, got {other:?}"),
3314        }
3315    }
3316
3317    #[rstest]
3318    #[case("+ ^: {n} y", "computed power")]
3319    #[case("(+/ % #) ^: _1 y", "the obverse of")]
3320    #[case("(+/ % #) &. , y", "the obverse of")]
3321    #[case("(1 + 2) & , y", "bonds over a non-literal noun")]
3322        fn other_conjunctions_are_not_supported_yet(#[case] src: &str, #[case] msg: &str) {
3323        let e = err(src);
3324        assert_eq!(e.kind, ErrorKind::NotYet);
3325        assert!(e.msg.contains(msg), "{}", e.msg);
3326    }
3327
3328    #[test]
3329    fn atop_at_rank_and_compose() {
3330        // `u@v` is `u@:v` at v's ranks; `u&v` is the composition at v's
3331        // monadic rank; `u&:v` is that composition on the arguments whole.
3332        let (v, _) = monad_of(&one("+/ @ (,\"1) y"));
3333        match &v {
3334            Verb::Rank(inner, ranks) => {
3335                assert_eq!(*ranks, [1, 1, 1]);
3336                assert!(matches!(**inner, Verb::Atop(..)), "got {inner:?}");
3337            }
3338            other => panic!("expected a ranked atop, got {other:?}"),
3339        }
3340        let (v, _) = monad_of(&one("+ & (*:\"0) y"));
3341        match &v {
3342            Verb::Rank(inner, ranks) => {
3343                assert_eq!(*ranks, [0, 0, 0]);
3344                assert!(matches!(**inner, Verb::Compose(..)), "got {inner:?}");
3345            }
3346            other => panic!("expected a ranked composition, got {other:?}"),
3347        }
3348        let (v, _) = monad_of(&one("+ &: *: y"));
3349        assert!(matches!(v, Verb::Compose(..)), "got {v:?}");
3350    }
3351
3352    #[test]
3353    fn a_noun_operand_bonds_the_conjunction() {
3354        // `m&v y` is `m v y` whole. The bond's own rank is infinite — J's
3355        // `1 2&+ b. 0` reports `_ _ _` — and the verb inside it applies its
3356        // own ranks to the pair.
3357        let (v, _) = monad_of(&one("1 & + y"));
3358        match &v {
3359            Verb::BondLeft(a, g) => {
3360                assert_eq!(a.as_i64_slice(), Some(&[1i64][..]));
3361                assert_eq!(prim_of(g).name, "+");
3362            }
3363            other => panic!("expected a left bond, got {other:?}"),
3364        }
3365        assert_eq!(v.ranks(), [crate::verb::RANK_INF; 3]);
3366        let (v, _) = monad_of(&one("{. & 2 y"));
3367        assert!(matches!(v, Verb::BondRight(..)), "got {v:?}");
3368        assert_eq!(v.ranks(), [crate::verb::RANK_INF; 3]);
3369    }
3370
3371    #[test]
3372    fn window_scan_and_commute_adverbs() {
3373        let (v, _) = monad_of(&one("+/\\ 1 2 3"));
3374        match &v {
3375            Verb::Windowed(u, WindowKind::Prefix) => assert!(matches!(**u, Verb::Reduce(_))),
3376            other => panic!("expected a prefix application, got {other:?}"),
3377        }
3378        // The window size is the left argument, so the derived verb has both
3379        // valences and its left cell is an atom.
3380        assert_eq!(v.ranks(), [RANK_INF, 0, RANK_INF]);
3381        let (v, _, _) = dyad_of(&one("2 +/\\ 1 2 3"));
3382        assert!(matches!(v, Verb::Windowed(_, WindowKind::Prefix)));
3383        let (v, _) = monad_of(&one("+/\\. 1 2 3"));
3384        assert!(matches!(v, Verb::Windowed(_, WindowKind::Suffix)));
3385        let (v, _) = monad_of(&one("+~ 1 2 3"));
3386        match &v {
3387            Verb::Commute(u) => assert_eq!(prim_of(u).name, "+"),
3388            other => panic!("expected a commute, got {other:?}"),
3389        }
3390        let (v, _) = monad_of(&one("+:^:3 (1)"));
3391        assert!(matches!(v, Verb::PowerN(_, Power::Times(3))));
3392        let (v, _) = monad_of(&one("%:^:_ (100)"));
3393        assert!(matches!(v, Verb::PowerN(_, Power::Converge)));
3394    }
3395
3396    #[test]
3397    fn the_key_adverb_derives_a_verb() {
3398        match one("+/. 1 2 3") {
3399            Expr::Monad { verb: Verb::Key(_), .. } => {}
3400            other => panic!("expected a key, got {other:?}"),
3401        }
3402    }
3403
3404    #[test]
3405    fn noun_operand_adverbs_are_not_supported_yet() {
3406        let e = err("1/ 2");
3407        assert_eq!(e.kind, ErrorKind::NotYet);
3408        assert!(e.msg.contains("noun-operand adverbs"), "{}", e.msg);
3409    }
3410
3411    #[test]
3412    fn noun_operand_conjunctions_are_not_supported_yet() {
3413        let e = err("1 @: + y");
3414        assert_eq!(e.kind, ErrorKind::NotYet);
3415        assert!(e.msg.contains("noun-operand conjunctions"), "{}", e.msg);
3416    }
3417
3418    // --------------------------------------------------------------- trains
3419
3420    #[test]
3421    fn three_verbs_in_parentheses_are_a_fork() {
3422        let (v, y) = monad_of(&one("(+/ % #) 1 2 3"));
3423        match &v {
3424            Verb::Fork(f, g, h) => {
3425                assert!(matches!(**f, Verb::Reduce(_)), "got {f:?}");
3426                assert_eq!(prim_of(g).name, "%");
3427                assert_eq!(prim_of(h).name, "#");
3428            }
3429            other => panic!("expected a fork, got {other:?}"),
3430        }
3431        assert_eq!(konst(&y).shape, vec![3]);
3432    }
3433
3434    #[test]
3435    fn a_noun_left_tine_is_a_noun_fork() {
3436        let (v, _) = monad_of(&one("(2 + #) 1 2 3"));
3437        match &v {
3438            Verb::NounFork(a, g, h) => {
3439                assert_eq!(a.as_i64_slice(), Some(&[2i64][..]));
3440                assert_eq!(prim_of(g).name, "+");
3441                assert_eq!(prim_of(h).name, "#");
3442            }
3443            other => panic!("expected a noun fork, got {other:?}"),
3444        }
3445    }
3446
3447    #[test]
3448    fn two_verbs_in_parentheses_are_a_hook() {
3449        let (v, _) = monad_of(&one("(+ #) 1 2 3"));
3450        match &v {
3451            Verb::Hook(f, g) => {
3452                assert_eq!(prim_of(f).name, "+");
3453                assert_eq!(prim_of(g).name, "#");
3454            }
3455            other => panic!("expected a hook, got {other:?}"),
3456        }
3457    }
3458
3459    #[test]
3460    fn cap_makes_a_fork_an_atop() {
3461        let (v, _) = monad_of(&one("([: +/ ,) 1 2 3"));
3462        match &v {
3463            Verb::Atop(f, g) => {
3464                assert!(matches!(**f, Verb::Reduce(_)), "got {f:?}");
3465                assert_eq!(prim_of(g).name, ",");
3466            }
3467            other => panic!("expected an atop, got {other:?}"),
3468        }
3469    }
3470
3471    #[test]
3472    fn a_five_verb_train_folds_from_the_right() {
3473        // (a b c d e) is a fork whose right tine is the fork (c d e).
3474        let (v, _) = monad_of(&one("(] , [ , ]) 1 2 3"));
3475        match &v {
3476            Verb::Fork(f, g, h) => {
3477                assert_eq!(prim_of(f).name, "]");
3478                assert_eq!(prim_of(g).name, ",");
3479                assert!(matches!(**h, Verb::Fork(..)), "got {h:?}");
3480            }
3481            other => panic!("expected a fork, got {other:?}"),
3482        }
3483    }
3484
3485    #[test]
3486    fn a_noun_fork_needs_a_literal_noun() {
3487        let e = err("({n} + #) 1 2 3");
3488        assert_eq!(e.kind, ErrorKind::NotYet);
3489        assert!(e.msg.contains("noun forks"), "{}", e.msg);
3490    }
3491
3492    #[test]
3493    fn cap_is_never_applied_as_a_verb() {
3494        // `[:` has no meaning of its own; it only caps a fork. Here it is
3495        // left over beside the result of `# 1 2 3`.
3496        let e = err("[: # 1 2 3");
3497        assert_eq!(e.kind, ErrorKind::Parse);
3498        assert!(e.msg.contains("caps a fork"), "{}", e.msg);
3499    }
3500
3501    #[test]
3502    fn two_nouns_side_by_side_are_a_syntax_error() {
3503        // The reference reads no train here, and neither does libjay.
3504        let e = err("'ab' 'cd'");
3505        assert_eq!(e.kind, ErrorKind::Parse);
3506        assert_eq!(e.msg, "syntax error");
3507    }
3508
3509    #[test]
3510    fn a_sentence_that_is_a_verb_is_not_supported_yet() {
3511        let e = err("+/ % #");
3512        assert_eq!(e.kind, ErrorKind::NotYet);
3513        assert!(e.msg.contains("tacit"), "{}", e.msg);
3514    }
3515
3516    // ----------------------------------------------------------- assignment
3517
3518    #[rstest]
3519    #[case("x =. 5", Scope::Local)]
3520    #[case("x =: 5", Scope::Global)]
3521    fn assignment_yields_an_assign_node(#[case] src: &str, #[case] want: Scope) {
3522        match one(src) {
3523            Expr::Assign { name, value, scope, span } => {
3524                assert_eq!(name, "x");
3525                assert_eq!(ints(&value), vec![5]);
3526                assert_eq!(scope, want);
3527                assert_eq!(span, Span::new(0, 6));
3528            }
3529            other => panic!("expected an assignment, got {other:?}"),
3530        }
3531    }
3532
3533    #[test]
3534    fn assignment_in_expression_position() {
3535        let (v, x, y) = dyad_of(&one("y + x =. 3"));
3536        assert_eq!(prim_of(&v).name, "+");
3537        assert!(matches!(x, Expr::Name(..)));
3538        match y {
3539            Expr::Assign { name, span, .. } => {
3540                assert_eq!(name, "x");
3541                assert_eq!(span, Span::new(4, 10));
3542            }
3543            other => panic!("expected an assignment, got {other:?}"),
3544        }
3545    }
3546
3547    #[test]
3548    fn assignment_takes_the_whole_right_hand_sentence() {
3549        match one("x =. 1 + 2") {
3550            Expr::Assign { value, .. } => {
3551                let (v, _, _) = dyad_of(&value);
3552                assert_eq!(prim_of(&v).name, "+");
3553            }
3554            other => panic!("expected an assignment, got {other:?}"),
3555        }
3556    }
3557
3558    // ---------------------------------------------------- naming a verb
3559
3560    #[test]
3561    fn assigning_a_verb_names_it_and_runs_nothing() {
3562        let s = stmts("mean =. +/ % #");
3563        assert_eq!(s.len(), 1);
3564        match &s[0] {
3565            Expr::VerbDef { name, verb, span } => {
3566                assert_eq!(name, "mean");
3567                assert!(matches!(verb, Verb::Fork(..)), "got {verb:?}");
3568                assert_eq!(*span, Span::new(0, 14));
3569            }
3570            other => panic!("expected a verb definition, got {other:?}"),
3571        }
3572    }
3573
3574    #[test]
3575    fn a_named_verb_applies_in_a_later_sentence() {
3576        let s = stmts("mean =. +/ % #\nmean 1 2 3 4");
3577        assert_eq!(s.len(), 2);
3578        let (v, y) = monad_of(&s[1]);
3579        assert!(matches!(v, Verb::Fork(..)), "got {v:?}");
3580        assert_eq!(konst(&y).shape, vec![4]);
3581    }
3582
3583    #[test]
3584    fn a_named_verb_is_a_verb_inside_a_train_and_under_a_conjunction() {
3585        let (v, _) = monad_of(&stmts("mean =. +/ % #\n(mean - {.) 1 2 3 4").pop().expect("two"));
3586        match &v {
3587            Verb::Fork(f, g, h) => {
3588                assert!(matches!(**f, Verb::Fork(..)), "got {f:?}");
3589                assert_eq!(prim_of(g).name, "-");
3590                assert_eq!(prim_of(h).name, "{.");
3591            }
3592            other => panic!("expected a fork, got {other:?}"),
3593        }
3594        let (v, _) = monad_of(&stmts("mean =. +/ % #\nmean\"1 m").pop().expect("two"));
3595        match &v {
3596            Verb::Rank(inner, r) => {
3597                assert_eq!(*r, [1, 1, 1]);
3598                assert!(matches!(**inner, Verb::Fork(..)), "got {inner:?}");
3599            }
3600            other => panic!("expected a ranked verb, got {other:?}"),
3601        }
3602    }
3603
3604    #[test]
3605    fn redefinition_rebinds_from_that_sentence_on() {
3606        let s = stmts("f =. +/\nf 1 2 3\nf =. #\nf 1 2 3");
3607        assert_eq!(s.len(), 4);
3608        assert!(matches!(monad_of(&s[1]).0, Verb::Reduce(_)));
3609        assert_eq!(prim_of(&monad_of(&s[3]).0).name, "#");
3610    }
3611
3612    #[test]
3613    fn a_name_may_change_part_of_speech_in_either_direction() {
3614        // The oracle accepts both; the last assignment decides.
3615        let s = stmts("a =. 1 2 3\na =. +/\na 1 2 3");
3616        assert!(matches!(s[0], Expr::Assign { .. }));
3617        assert!(matches!(s[1], Expr::VerbDef { .. }));
3618        assert!(matches!(monad_of(&s[2]).0, Verb::Reduce(_)));
3619        let s = stmts("f =. +/\nf =. 10 20\nf");
3620        assert!(matches!(s[0], Expr::VerbDef { .. }));
3621        assert!(matches!(s[1], Expr::Assign { .. }));
3622        assert!(matches!(s[2], Expr::Name(..)));
3623    }
3624
3625    #[test]
3626    fn an_undefined_name_applied_as_a_verb_is_a_value_error() {
3627        // The reference says `value error: zz`, pointing at the name.
3628        let e = err("zz 1 2 3");
3629        assert_eq!(e.kind, ErrorKind::Value);
3630        assert_eq!(e.msg, "undefined name: zz");
3631        assert_eq!(e.span, Some(Span::new(0, 2)));
3632        // A name that does hold a value is a different complaint: two
3633        // nouns side by side, which the reference calls a syntax error.
3634        let e = err("a =. 5\na 1 2 3");
3635        assert_eq!(e.kind, ErrorKind::Parse);
3636        assert_eq!(e.msg, "syntax error");
3637    }
3638
3639    #[test]
3640    fn assignment_names_an_adverb_or_a_conjunction() {
3641        match one("insert =. /") {
3642            Expr::ModDef { name, spelling, conjunction, .. } => {
3643                assert_eq!(name, "insert");
3644                assert_eq!(spelling, "/");
3645                assert!(!conjunction);
3646            }
3647            other => panic!("expected a modifier definition, got {other:?}"),
3648        }
3649        match one("atop =. @") {
3650            Expr::ModDef { spelling, conjunction, .. } => {
3651                assert_eq!(spelling, "@");
3652                assert!(conjunction);
3653            }
3654            other => panic!("expected a modifier definition, got {other:?}"),
3655        }
3656        // The name is a modifier from there on, so the sentence that uses
3657        // it parses around it as the glyph would.
3658        let s = stmts("insert =. /\n+ insert 1 2 3");
3659        assert!(matches!(s[1], Expr::Monad { verb: Verb::Reduce(_), .. }), "{:?}", s[1]);
3660    }
3661
3662    #[test]
3663    fn a_sentence_that_is_a_modifier_is_a_named_gap() {
3664        let e = err("insert =. /\ninsert");
3665        assert_eq!(e.kind, ErrorKind::NotYet);
3666        assert!(e.msg.contains("displaying a modifier"), "{}", e.msg);
3667    }
3668
3669    #[rstest]
3670    #[case("f =. 3 : 'y + 1'", None)]
3671    #[case("f =. 4 : 'x + y'", Some("x"))]
3672    #[case("f =. {{ y + 1 }}", None)]
3673    #[case("f =. {{ x + y }}", Some("x"))]
3674    fn an_explicit_definition_names_a_verb(#[case] src: &str, #[case] left: Option<&str>) {
3675        match one(src) {
3676            Expr::VerbDef { name, verb: Verb::Explicit(d), .. } => {
3677                assert_eq!(name, "f");
3678                assert_eq!(d.left.as_deref(), left);
3679                assert_eq!(d.right, "y");
3680                assert_eq!(d.body.len(), 1);
3681            }
3682            other => panic!("expected an explicit verb definition, got {other:?}"),
3683        }
3684    }
3685
3686    #[rstest]
3687    #[case("f =. 13 : 'y + 1'", "tacit definitions")]
3688    fn definition_forms_libjay_has_not_are_named(#[case] src: &str, #[case] msg: &str) {
3689        let e = err(src);
3690        assert_eq!(e.kind, ErrorKind::NotYet);
3691        assert!(e.msg.contains(msg), "{}", e.msg);
3692    }
3693
3694    /// `1 :` and `2 :` say the part of speech; a `{{ }}` leaves it to the
3695    /// operand names its body uses.
3696    #[rstest]
3697    #[case("f =. 1 : 'y + 1'", Some(false))]
3698    #[case("f =. 2 : 'u v y'", Some(true))]
3699    #[case("f =. {{ y + 1 }}", None)]
3700    #[case("f =. {{ u y }}", Some(false))]
3701    #[case("f =. {{ m + y }}", Some(false))]
3702    #[case("f =. {{ u v y }}", Some(true))]
3703    #[case("f =. {{ v y }}", Some(true))]
3704    #[case("f =. {{ n + y }}", Some(true))]
3705    #[case("f =. {{ u n y }}", Some(true))]
3706    #[case("f =. {{)a\nu y\n}}", Some(false))]
3707    #[case("f =. {{)c\nu v y\n}}", Some(true))]
3708    #[case("f =. {{)v\ny\n}}", None)]
3709    fn an_explicit_definitions_part_of_speech(#[case] src: &str, #[case] want: Option<bool>) {
3710        match (one(src), want) {
3711            (Expr::ModDef { name, conjunction, .. }, Some(conj)) => {
3712                assert_eq!(name, "f");
3713                assert_eq!(conjunction, conj, "{src:?}");
3714            }
3715            (Expr::VerbDef { name, .. }, None) => assert_eq!(name, "f"),
3716            (other, _) => panic!("expected {want:?} for {src:?}, got {other:?}"),
3717        }
3718    }
3719
3720    #[test]
3721    fn a_control_word_outside_a_definition_is_a_parse_error() {
3722        let e = err("if. 1 do. 2 end.");
3723        assert_eq!(e.kind, ErrorKind::Parse);
3724        assert!(e.msg.contains("only meaningful inside an explicit definition"), "{}", e.msg);
3725    }
3726
3727    #[test]
3728    fn multiple_assignment_is_not_supported_yet() {
3729        let e = err("'a b' =. 1 2");
3730        assert_eq!(e.kind, ErrorKind::NotYet);
3731        assert!(e.msg.contains("multiple assignment"), "{}", e.msg);
3732    }
3733
3734    // -------------------------------------------------------- interpolation
3735
3736    #[test]
3737    fn a_hole_is_a_noun() {
3738        let e = one("{a} + 1");
3739        let (_, x, y) = dyad_of(&e);
3740        match x {
3741            Expr::Param(i, s) => {
3742                assert_eq!(i, 0);
3743                assert_eq!(s, Span::new(0, 3));
3744            }
3745            other => panic!("expected a parameter, got {other:?}"),
3746        }
3747        assert_eq!(ints(&y), vec![1]);
3748        assert_eq!(e.span(), Span::new(0, 7));
3749    }
3750
3751    #[test]
3752    fn holes_are_numbered_and_shared_by_name() {
3753        let sp = SourceParts::from_source("{a} + {b} + {a}").expect("source parts");
3754        assert_eq!(sp.param_names, vec!["a".to_string(), "b".to_string()]);
3755        let e = parse(&sp).expect("parse").pop().expect("one sentence");
3756        let (_, x, y) = dyad_of(&e);
3757        assert!(matches!(x, Expr::Param(0, _)));
3758        let (_, x2, y2) = dyad_of(&y);
3759        assert!(matches!(x2, Expr::Param(1, _)));
3760        assert!(matches!(y2, Expr::Param(0, _)));
3761    }
3762
3763    #[rstest]
3764    #[case("3j4", 3.0, 4.0)]
3765    #[case("_1j_2", -1.0, -2.0)]
3766    #[case("1e1j2", 10.0, 2.0)]
3767    #[case("2ad90", 0.0, 2.0)]
3768    #[case("1ad180", -1.0, 0.0)]
3769    fn complex_literals(#[case] src: &str, #[case] re: f64, #[case] im: f64) {
3770        let a = konst(&one(src));
3771        assert_eq!(a.dtype(), DType::Complex);
3772        let z = a.as_complex_slice().expect("complex data")[0];
3773        assert!((z[0] - re).abs() < 1e-12 && (z[1] - im).abs() < 1e-12, "{z:?}");
3774    }
3775
3776    #[test]
3777    fn a_hole_takes_a_verb_like_any_noun() {
3778        let (v, y) = monad_of(&one("+/ {data}"));
3779        assert!(matches!(v, Verb::Reduce(_)));
3780        assert!(matches!(y, Expr::Param(0, _)));
3781    }
3782
3783    #[test]
3784    fn braces_inside_a_string_are_not_holes() {
3785        let sp = SourceParts::from_source("'{a}'").expect("source parts");
3786        assert!(sp.param_names.is_empty());
3787        let a = konst(&parse(&sp).expect("parse")[0]);
3788        assert_eq!(a.data, Data::Char(vec!['{', 'a', '}'].into()));
3789    }
3790
3791    #[test]
3792    fn parts_of_one_sentence_lex_across_a_hole() {
3793        // The t-string path: literal parts with a hole between them.
3794        let sp = SourceParts::from_parts(&["1 + ", " * 2"], &["v"]);
3795        assert_eq!(sp.display, "1 + {v} * 2");
3796        let e = parse(&sp).expect("parse").pop().expect("one sentence");
3797        let (_, x, y) = dyad_of(&e);
3798        assert_eq!(ints(&x), vec![1]);
3799        let (_, x2, y2) = dyad_of(&y);
3800        assert!(matches!(x2, Expr::Param(0, _)));
3801        assert_eq!(ints(&y2), vec![2]);
3802    }
3803
3804    #[test]
3805    fn spans_of_later_sentences_index_the_whole_source() {
3806        let src = "5\n1 + 2";
3807        let s = stmts(src);
3808        assert_eq!(s[1].span(), Span::new(2, 7));
3809        assert_eq!(&src[2..7], "1 + 2");
3810    }
3811
3812    // --------------------------------------------------------------- errors
3813
3814    #[test]
3815    fn unknown_word_reports_its_span() {
3816        let e = err("1 [. 2");
3817        assert_eq!(e.kind, ErrorKind::Parse);
3818        assert_eq!(e.msg, "unknown word: [.");
3819        assert_eq!(e.span, Some(Span::new(2, 4)));
3820    }
3821
3822    #[test]
3823    fn an_inflected_unknown_word_is_reported_whole() {
3824        let e = err("1 ]: 2");
3825        assert_eq!(e.msg, "unknown word: ]:");
3826        assert_eq!(e.span, Some(Span::new(2, 4)));
3827    }
3828
3829    /// The exact suffixes read; the forms that spell no number do not.
3830    #[rstest]
3831    #[case("1.5x", 0, 4)]
3832    #[case("1e10x", 0, 5)]
3833    fn a_fractional_extended_literal_is_ill_formed(
3834        #[case] src: &str,
3835        #[case] start: usize,
3836        #[case] end: usize,
3837    ) {
3838        let e = err(src);
3839        assert_eq!(e.kind, ErrorKind::Parse);
3840        assert!(e.msg.contains("invalid number"), "{}", e.msg);
3841        assert_eq!(e.span, Some(Span::new(start, end)));
3842    }
3843
3844    #[test]
3845    fn a_malformed_number_is_a_parse_error() {
3846        let e = err("1.2.3");
3847        assert_eq!(e.kind, ErrorKind::Parse);
3848        assert!(e.msg.contains("invalid number"), "{}", e.msg);
3849    }
3850
3851    #[test]
3852    fn an_unbalanced_sentence_is_a_syntax_error() {
3853        // The parenthesis itself is what is wrong, so that is what the
3854        // span covers.
3855        let e = err("(1 + 2");
3856        assert_eq!(e.kind, ErrorKind::Parse);
3857        assert!(e.msg.contains("no closing"), "{}", e.msg);
3858        assert_eq!(e.span, Some(Span::new(0, 1)));
3859    }
3860
3861    #[test]
3862    fn a_stray_right_parenthesis_is_a_syntax_error() {
3863        let e = err("1 + 2)");
3864        assert_eq!(e.kind, ErrorKind::Parse);
3865        assert!(e.msg.contains("no opening"), "{}", e.msg);
3866        assert_eq!(e.span, Some(Span::new(5, 6)));
3867    }
3868
3869    #[test]
3870    fn the_error_of_a_later_sentence_points_at_that_sentence() {
3871        let e = err("1 + 2\n3 [. 4");
3872        assert_eq!(e.span, Some(Span::new(8, 10)));
3873    }
3874}