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