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