1use std::collections::HashMap;
10use std::sync::Arc;
11
12use crate::array::{Array, Data};
13use crate::error::{Error, Result, Span};
14use crate::frontend::{
15 DefaultArg, DfnResult, FirstDisclose, IndexForm, NestedModel, Rules, Segment, SourceParts,
16};
17use crate::ir::{Branch, Control, ExplicitDef, Expr, Scope};
18use crate::verb::{
19 BoolDyad, DyadOp, Enclose, MonadOp, Power, Prim, ScalarDyad, ScalarMonad, Verb, WindowKind,
20 RANK_INF,
21};
22
23pub fn parse(src: &SourceParts, d: Rules) -> Result<Vec<Expr>> {
27 let sentences = lex(src, d)?;
28 let mut verbs: HashMap<String, Verb> = HashMap::new();
29 let mut stmts = Vec::with_capacity(sentences.len());
30 let mut i = 0usize;
31 while i < sentences.len() {
32 if matches!(sentences[i].first().map(|t| &t.kind), Some(Tok::Del)) {
33 let stmt = parse_tradfn(&sentences, &mut i, d, &mut verbs)?;
34 stmts.push(stmt);
35 continue;
36 }
37 let sentence = sentences[i].clone();
38 i += 1;
39 if let Some(stmt) = parse_statement(sentence, d, &mut verbs, false)? {
40 stmts.push(stmt);
41 }
42 }
43 Ok(stmts)
44}
45
46fn parse_statement(
49 sentence: Vec<Token>,
50 d: Rules,
51 verbs: &mut HashMap<String, Verb>,
52 in_def: bool,
53) -> Result<Option<Expr>> {
54 let sentence = substitute_verbs(sentence, verbs);
55 let sentence = fold_dfns(sentence, d, verbs)?;
56 if let [name, assign, func] = &sentence[..]
59 && let (Tok::Name(n), Tok::Assign) = (&name.kind, &assign.kind)
60 {
61 let named = match &func.kind {
64 Tok::Func(v) => Some(v.clone()),
65 Tok::UserOp { def, omega } => Some(unapplied_op(def.clone(), *omega)),
66 _ => None,
67 };
68 if let Some(v) = named {
69 let span = Span::merge(name.span, func.span);
70 if !in_def {
71 verbs.insert(n.clone(), v.clone());
72 }
73 return Ok(Some(Expr::VerbDef { name: n.clone(), verb: v, span }));
74 }
75 }
76 let toks = fold_axes(fold_operators(unwrap_lone_operators(sentence), d)?, d)?;
77 if toks.is_empty() {
78 return Ok(None);
79 }
80 if let [name, assign, rest @ ..] = &toks[..]
84 && let (Tok::Name(n), Tok::Assign) = (&name.kind, &assign.kind)
85 && let Some(v) = tine_run(rest, d)?
86 {
87 let span = Span::merge(name.span, toks[toks.len() - 1].span);
88 if !in_def {
89 verbs.insert(n.clone(), v.clone());
90 }
91 return Ok(Some(Expr::VerbDef { name: n.clone(), verb: v, span }));
92 }
93 if let Some(t) = toks.iter().find(|t| matches!(t.kind, Tok::Control(_))) {
94 return Err(Error::parse(
95 "control structures are only meaningful inside a ∇ definition",
96 t.span,
97 ));
98 }
99 if let Some(t) = toks.iter().find(|t| matches!(t.kind, Tok::Arrow)) {
100 return Err(Error::parse(
101 "→ branches, and only a line of a ∇ definition may begin with it",
102 t.span,
103 ));
104 }
105 let hint = Span::merge(toks[0].span, toks[toks.len() - 1].span);
106 if let Some(e) = indexed_assignment(&toks, d, hint)? {
109 return Ok(Some(e));
110 }
111 parse_range(&toks, 0, toks.len(), hint, d).map(Some)
112}
113
114fn substitute_verbs(mut toks: Vec<Token>, verbs: &HashMap<String, Verb>) -> Vec<Token> {
117 for i in 0..toks.len() {
118 let Tok::Name(n) = &toks[i].kind else { continue };
119 if matches!(toks.get(i + 1).map(|t| &t.kind), Some(Tok::Assign)) {
120 continue;
121 }
122 if let Some(v) = verbs.get(n) {
123 toks[i].kind = match as_user_op(v) {
126 Some((def, omega)) => Tok::UserOp { def, omega },
127 None if is_niladic(v) => Tok::Niladic(v.clone()),
128 None => Tok::Func(v.clone()),
129 };
130 }
131 }
132 toks
133}
134
135#[derive(Clone, Copy, Debug, PartialEq, Eq)]
140enum OpGlyph {
141 Slash,
143 SlashBar,
145 Backslash,
147 BackslashBar,
149 Rank,
151 Commute,
153 Power,
155 JotDot,
157 Over,
159 Under,
161 Stencil,
163 Jot,
165 Each,
167 Before,
169 Key,
171 Dot,
174 Variant,
176}
177
178impl OpGlyph {
179 fn glyph(self) -> char {
180 match self {
181 OpGlyph::Slash => '/',
182 OpGlyph::SlashBar => '⌿',
183 OpGlyph::Backslash => '\\',
184 OpGlyph::BackslashBar => '⍀',
185 OpGlyph::Rank => '⍤',
186 OpGlyph::Commute => '⍨',
187 OpGlyph::Power => '⍣',
188 OpGlyph::JotDot | OpGlyph::Jot => '∘',
189 OpGlyph::Over => '⍥',
190 OpGlyph::Under => '⍢',
191 OpGlyph::Stencil => '⌺',
192 OpGlyph::Each => '¨',
193 OpGlyph::Before => '⍛',
194 OpGlyph::Key => '⌸',
195 OpGlyph::Dot => '.',
196 OpGlyph::Variant => '⍠',
197 }
198 }
199}
200
201#[derive(Clone, Debug)]
202enum Tok {
203 Value(Array),
205 Nums(Array),
209 Param(usize),
211 Name(String),
212 Func(Verb),
214 Op(OpGlyph),
216 Assign,
217 Quad { quote: bool },
220 LParen,
221 RParen,
222 LBracket,
223 RBracket,
224 Semi,
227 LBrace,
229 RBrace,
230 Separator,
233 Colon,
235 Arrow,
237 Niladic(Verb),
240 UserOp { def: Verb, omega: bool },
243 Del,
245 Control(&'static str),
247}
248
249#[derive(Clone, Debug)]
250struct Token {
251 kind: Tok,
252 span: Span,
253}
254
255fn is_operand_end(k: &Tok) -> bool {
258 matches!(
259 k,
260 Tok::Value(_)
261 | Tok::Nums(_)
262 | Tok::Param(_)
263 | Tok::Name(_)
264 | Tok::Niladic(_)
265 | Tok::Quad { .. }
270 | Tok::RParen
271 | Tok::RBracket
272 )
273}
274
275fn unapplied_op(def: Verb, omega: bool) -> Verb {
279 Verb::UserDerived {
280 def: Box::new(def),
281 alpha: Box::new(Verb::Named("⍺⍺".to_string())),
282 omega: omega.then(|| Box::new(Verb::Named("⍵⍵".to_string()))),
283 }
284}
285
286fn as_user_op(v: &Verb) -> Option<(Verb, bool)> {
289 match v {
290 Verb::UserDerived { def, alpha, omega }
291 if matches!(&**alpha, Verb::Named(n) if n == "⍺⍺") =>
292 {
293 Some(((**def).clone(), omega.is_some()))
294 }
295 _ => None,
296 }
297}
298
299fn is_niladic(v: &Verb) -> bool {
301 matches!(v, Verb::Explicit(d) if d.left.is_none() && d.right == crate::ir::NILADIC)
302}
303
304fn literal(k: &Tok) -> Option<&Array> {
306 match k {
307 Tok::Value(a) | Tok::Nums(a) => Some(a),
308 _ => None,
309 }
310}
311
312fn prim_for(ch: char, d: Rules) -> Option<Prim> {
325 use DyadOp as D;
326 use MonadOp as M;
327 use ScalarDyad as SD;
328 use ScalarMonad as SM;
329 let origin = d.origin;
330 let p = match ch {
331 '+' => Prim {
332 name: "+",
333 monad: M::Scalar(SM::Conj),
334 dyad: D::Scalar(SD::Add),
335 ranks: [0, 0, 0],
336 },
337 '-' => {
338 Prim { name: "-", monad: M::Scalar(SM::Neg), dyad: D::Scalar(SD::Sub), ranks: [0, 0, 0] }
339 }
340 '×' => Prim {
341 name: "×",
342 monad: M::Scalar(SM::Signum),
343 dyad: D::Scalar(SD::Mul),
344 ranks: [0, 0, 0],
345 },
346 '÷' => Prim {
347 name: "÷",
348 monad: M::Scalar(SM::Recip),
349 dyad: D::Scalar(SD::DivApl),
350 ranks: [0, 0, 0],
351 },
352 '⌈' => Prim {
353 name: "⌈",
354 monad: M::Scalar(SM::Ceil),
355 dyad: D::Scalar(SD::Max),
356 ranks: [0, 0, 0],
357 },
358 '⌊' => Prim {
359 name: "⌊",
360 monad: M::Scalar(SM::Floor),
361 dyad: D::Scalar(SD::Min),
362 ranks: [0, 0, 0],
363 },
364 '*' => {
365 Prim { name: "*", monad: M::Scalar(SM::Exp), dyad: D::Scalar(SD::Pow), ranks: [0, 0, 0] }
366 }
367 '|' => Prim {
368 name: "|",
369 monad: M::Scalar(SM::Abs),
370 dyad: D::Scalar(SD::Residue),
371 ranks: [0, 0, 0],
372 },
373 '=' => Prim { name: "=", monad: M::None, dyad: D::Scalar(SD::Eq), ranks: [0, 0, 0] },
374 '≠' => Prim {
375 name: "≠",
376 monad: M::NubSieve,
377 dyad: D::Scalar(SD::Ne),
378 ranks: [RANK_INF, 0, 0],
379 },
380 '<' => Prim { name: "<", monad: M::None, dyad: D::Scalar(SD::Lt), ranks: [0, 0, 0] },
381 '≤' => Prim { name: "≤", monad: M::None, dyad: D::Scalar(SD::Le), ranks: [0, 0, 0] },
382 '>' => Prim { name: ">", monad: M::None, dyad: D::Scalar(SD::Gt), ranks: [0, 0, 0] },
383 '≥' => Prim { name: "≥", monad: M::None, dyad: D::Scalar(SD::Ge), ranks: [0, 0, 0] },
384 '⍴' => Prim {
385 name: "⍴",
386 monad: M::ShapeOf,
387 dyad: D::Reshape,
388 ranks: [RANK_INF, 1, RANK_INF],
389 },
390 '⍳' => Prim {
391 name: "⍳",
392 monad: M::IotaApl { origin },
393 dyad: D::IndexOf { origin },
394 ranks: [RANK_INF, RANK_INF, RANK_INF],
398 },
399 '∊' => Prim {
400 name: "∊",
401 monad: M::Enlist,
402 dyad: D::MemberApl,
403 ranks: [RANK_INF, RANK_INF, RANK_INF],
404 },
405 '∪' => Prim {
406 name: "∪",
407 monad: M::Nub,
408 dyad: D::Union,
409 ranks: [RANK_INF, RANK_INF, RANK_INF],
410 },
411 '∩' => Prim {
412 name: "∩",
413 monad: M::None,
414 dyad: D::Intersect,
415 ranks: [RANK_INF, RANK_INF, RANK_INF],
416 },
417 '∧' => Prim { name: "∧", monad: M::None, dyad: D::Scalar(SD::Lcm), ranks: [0, 0, 0] },
418 '∨' => Prim { name: "∨", monad: M::None, dyad: D::Scalar(SD::Gcd), ranks: [0, 0, 0] },
419 '⍱' => Prim {
420 name: "⍱",
421 monad: M::None,
422 dyad: D::Boolean(BoolDyad::Nor),
423 ranks: [0, 0, 0],
424 },
425 '⍲' => Prim {
426 name: "⍲",
427 monad: M::None,
428 dyad: D::Boolean(BoolDyad::Nand),
429 ranks: [0, 0, 0],
430 },
431 '⍟' => Prim {
432 name: "⍟",
433 monad: M::Scalar(SM::Ln),
434 dyad: D::Scalar(SD::Log),
435 ranks: [0, 0, 0],
436 },
437 '~' => Prim {
438 name: "~",
439 monad: M::Scalar(SM::Not),
440 dyad: D::Less,
441 ranks: [0, RANK_INF, RANK_INF],
442 },
443 '≡' => Prim {
444 name: "≡",
445 monad: M::Depth,
446 dyad: D::Match,
447 ranks: [RANK_INF, RANK_INF, RANK_INF],
448 },
449 '⍋' => Prim {
450 name: "⍋",
451 monad: M::GradeUp { origin },
452 dyad: D::CollateGrade { down: false, origin },
453 ranks: [RANK_INF, RANK_INF, RANK_INF],
454 },
455 '⍒' => Prim {
456 name: "⍒",
457 monad: M::GradeDown { origin },
458 dyad: D::CollateGrade { down: true, origin },
459 ranks: [RANK_INF, RANK_INF, RANK_INF],
460 },
461 '⊖' | '⌽' => Prim {
464 name: if ch == '⊖' { "⊖" } else { "⌽" },
465 monad: M::Reverse,
466 dyad: D::Rotate,
467 ranks: [RANK_INF, 1, RANK_INF],
468 },
469 '⍪' => Prim {
470 name: "⍪",
471 monad: M::TableOf,
472 dyad: D::AppendLeading,
473 ranks: [RANK_INF, RANK_INF, RANK_INF],
474 },
475 '!' => Prim {
476 name: "!",
477 monad: M::Scalar(SM::Factorial),
478 dyad: D::Scalar(SD::Binomial),
479 ranks: [0, 0, 0],
480 },
481 '⍕' => Prim {
482 name: "⍕",
483 monad: M::Format,
484 dyad: D::FormatSpec,
485 ranks: [RANK_INF, 1, RANK_INF],
486 },
487 '⊥' => Prim {
493 name: "⊥",
494 monad: M::None,
495 dyad: D::DecodeApl,
496 ranks: [RANK_INF, RANK_INF, RANK_INF],
497 },
498 '⊤' => Prim {
499 name: "⊤",
500 monad: M::None,
501 dyad: D::EncodeApl,
502 ranks: [RANK_INF, RANK_INF, RANK_INF],
503 },
504 '⍉' => Prim {
505 name: "⍉",
506 monad: M::TransposeAxes,
507 dyad: D::TransposeApl,
508 ranks: [RANK_INF, RANK_INF, RANK_INF],
509 },
510 '↑' => Prim {
513 name: "↑",
514 monad: match d.first_disclose {
515 FirstDisclose::UpIsFirst => M::First,
516 FirstDisclose::UpIsMix => return None,
517 },
518 dyad: D::Take,
519 ranks: [RANK_INF, 1, RANK_INF],
520 },
521 '⊂' => Prim {
522 name: "⊂",
523 monad: match d.nested_model {
526 NestedModel::Floating => M::Enclose(Enclose::ExceptSimpleScalar),
527 NestedModel::Grounded => return None,
528 },
529 dyad: D::PartitionEnclose,
530 ranks: [RANK_INF, RANK_INF, RANK_INF],
531 },
532 '⊆' => Prim {
535 name: "⊆",
536 monad: M::Nest,
537 dyad: D::PartitionEnclose,
538 ranks: [RANK_INF, RANK_INF, RANK_INF],
539 },
540 '⍸' => Prim {
543 name: "⍸",
544 monad: M::Indices { origin, boxed_coords: true },
545 dyad: D::IntervalIndex { offset: origin - 1, closed: true },
546 ranks: [RANK_INF, 1, RANK_INF],
547 },
548 '⌷' => Prim {
551 name: "⌷",
552 monad: match d.index_form {
553 IndexForm::ScalarPerAxis => M::Same,
554 IndexForm::AxisVectors => return None,
555 },
556 dyad: D::Squad { origin },
557 ranks: [RANK_INF, RANK_INF, RANK_INF],
558 },
559 '?' => Prim {
560 name: "?",
561 monad: M::Roll { origin, fixed: false, float_at_zero: false },
562 dyad: D::Deal { origin, fixed: false },
563 ranks: [RANK_INF, 0, 0],
564 },
565 '⌹' => Prim {
566 name: "⌹",
567 monad: M::MatrixInverse,
568 dyad: D::MatrixDivide,
569 ranks: [2, RANK_INF, 2],
570 },
571 '⊃' => Prim {
572 name: "⊃",
573 monad: match d.first_disclose {
574 FirstDisclose::UpIsFirst => M::Open,
575 FirstDisclose::UpIsMix => return None,
576 },
577 dyad: D::Pick { origin },
578 ranks: [0, RANK_INF, RANK_INF],
579 },
580 '↓' => Prim {
581 name: "↓",
582 monad: M::Split,
583 dyad: D::Drop,
584 ranks: [RANK_INF, 1, RANK_INF],
585 },
586 ',' => Prim {
587 name: ",",
588 monad: M::Ravel,
589 dyad: D::AppendLast,
590 ranks: [RANK_INF, RANK_INF, RANK_INF],
591 },
592 '≢' => Prim {
593 name: "≢",
594 monad: M::Tally,
595 dyad: D::NotMatch,
596 ranks: [RANK_INF, RANK_INF, RANK_INF],
597 },
598 '⊢' => Prim {
599 name: "⊢",
600 monad: M::Same,
601 dyad: D::Right,
602 ranks: [RANK_INF, RANK_INF, RANK_INF],
603 },
604 '⊣' => Prim {
605 name: "⊣",
606 monad: M::Same,
607 dyad: D::Left,
608 ranks: [RANK_INF, RANK_INF, RANK_INF],
609 },
610 '○' => Prim {
611 name: "○",
612 monad: M::Scalar(SM::Pi),
613 dyad: D::Scalar(SD::Circle),
614 ranks: [0, 0, 0],
615 },
616 '⍷' => Prim {
617 name: "⍷",
618 monad: M::None,
619 dyad: D::FindSeq,
620 ranks: [RANK_INF, RANK_INF, RANK_INF],
621 },
622 '⍎' => Prim {
623 name: "⍎",
624 monad: M::Execute { apl: true },
625 dyad: D::None,
626 ranks: [1, RANK_INF, RANK_INF],
627 },
628 _ => return None,
629 };
630 Some(p)
631}
632
633fn verb_for(ch: char, d: Rules) -> Option<Verb> {
637 let p = prim_for(ch, d)?;
638 if ch == '⌽' {
639 return Some(Verb::Rank(Box::new(Verb::Prim(p)), [1, 0, 1]));
640 }
641 Some(Verb::Prim(p))
642}
643
644fn quad_name(name: &str, d: Rules, span: Span) -> Result<Tok> {
650 let chars = |s: &str| Tok::Value(Array::from_chars(s.chars().collect()));
651 Ok(match name {
652 "A" => chars("ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
653 "D" => chars("0123456789"),
654 "IO" => Tok::Value(Array::scalar_i64(d.origin)),
655 "CT" => Tok::Value(Array::scalar_f64(d.ct)),
656 "UCS" => Tok::Func(Verb::Prim(Prim {
657 name: "⎕UCS",
658 monad: MonadOp::Unicode { pass_chars: false },
659 dyad: DyadOp::None,
660 ranks: [RANK_INF, RANK_INF, RANK_INF],
661 })),
662 "TS" | "AI" | "TC" | "WA" | "SI" | "LC" | "NL" | "EX" | "FIO" | "NA" | "SH" | "CMD"
666 | "MAP" | "SVO" | "SVQ" | "TZ" | "DL" => {
667 Err(Error::sandbox(format!("⎕{name} reads outside the program"), span))?
668 }
669 other => Err(Error::not_yet(format!("the system name ⎕{other}"), span))?,
670 })
671}
672
673fn queued_glyph(ch: char) -> Option<&'static str> {
676 Some(match ch {
677 '⌶' => "I-beam (⌶)",
678 '&' => "the spawn operator (f&y)",
682 _ => return None,
683 })
684}
685
686fn op_for(ch: char) -> Option<OpGlyph> {
687 match ch {
688 '/' => Some(OpGlyph::Slash),
689 '⌿' => Some(OpGlyph::SlashBar),
690 '\\' => Some(OpGlyph::Backslash),
691 '⍀' => Some(OpGlyph::BackslashBar),
692 '⍤' => Some(OpGlyph::Rank),
693 '⍨' => Some(OpGlyph::Commute),
694 '⍣' => Some(OpGlyph::Power),
695 '∘' => Some(OpGlyph::Jot),
696 '⍥' => Some(OpGlyph::Over),
697 '⍢' => Some(OpGlyph::Under),
698 '⌺' => Some(OpGlyph::Stencil),
699 '¨' => Some(OpGlyph::Each),
700 '⍛' => Some(OpGlyph::Before),
701 '⌸' => Some(OpGlyph::Key),
702 '⍠' => Some(OpGlyph::Variant),
703 '.' => Some(OpGlyph::Dot),
706 _ => None,
707 }
708}
709
710fn expand_verb(leading: bool) -> Verb {
713 let p = Prim {
714 name: if leading { "⍀" } else { "\\" },
715 monad: MonadOp::None,
716 dyad: DyadOp::Expand,
717 ranks: if leading { [RANK_INF, 1, RANK_INF] } else { [RANK_INF, 1, 1] },
718 };
719 Verb::Prim(p)
720}
721
722fn copy_verb(leading: bool) -> Verb {
726 let p = Prim {
727 name: if leading { "⌿" } else { "/" },
728 monad: MonadOp::None,
729 dyad: DyadOp::Copy,
730 ranks: if leading { [RANK_INF, 1, RANK_INF] } else { [RANK_INF, 1, 1] },
731 };
732 Verb::Prim(p)
733}
734
735fn lex(src: &SourceParts, d: Rules) -> Result<Vec<Vec<Token>>> {
742 let mut out: Vec<Vec<Token>> = Vec::new();
743 let mut cur: Vec<Token> = Vec::new();
744 let mut in_comment = false;
746 let mut braces = 0usize;
747 for seg in &src.segments {
748 match seg {
749 Segment::Text { text, offset } => {
750 lex_text(text, *offset, d, &mut out, &mut cur, &mut in_comment, &mut braces)?;
751 }
752 Segment::Param { index, offset, len } => {
753 if !in_comment {
754 cur.push(Token {
755 kind: Tok::Param(*index),
756 span: Span::new(*offset, offset + len),
757 });
758 }
759 }
760 }
761 }
762 if !cur.is_empty() {
763 out.push(cur);
764 }
765 Ok(out)
766}
767
768#[allow(clippy::too_many_arguments)]
769fn lex_text(
770 text: &str,
771 offset: usize,
772 d: Rules,
773 out: &mut Vec<Vec<Token>>,
774 cur: &mut Vec<Token>,
775 in_comment: &mut bool,
776 braces: &mut usize,
777) -> Result<()> {
778 let mut i = 0usize;
779 while i < text.len() {
780 let ch = text[i..].chars().next().unwrap();
781 let clen = ch.len_utf8();
782 if *in_comment {
783 if ch == '\n' {
784 *in_comment = false;
785 end_sentence(out, cur);
786 }
787 i += clen;
788 continue;
789 }
790 match ch {
791 '\n' | '⋄' => {
794 if *braces > 0 {
795 cur.push(Token {
796 kind: Tok::Separator,
797 span: Span::new(offset + i, offset + i + clen),
798 });
799 } else {
800 end_sentence(out, cur);
801 }
802 i += clen;
803 }
804 ' ' | '\t' | '\r' => i += clen,
805 '⍝' => {
806 *in_comment = true;
807 i += clen;
808 }
809 '\'' => {
810 let (arr, next) = lex_string(text, i, offset)?;
811 cur.push(Token {
812 kind: Tok::Value(arr),
813 span: Span::new(offset + i, offset + next),
814 });
815 i = next;
816 }
817 '{' => {
818 *braces += 1;
819 cur.push(Token { kind: Tok::LBrace, span: Span::new(offset + i, offset + i + 1) });
820 i += 1;
821 }
822 '}' => {
823 *braces = braces.saturating_sub(1);
824 cur.push(Token { kind: Tok::RBrace, span: Span::new(offset + i, offset + i + 1) });
825 i += 1;
826 }
827 '∇' => {
828 cur.push(Token { kind: Tok::Del, span: Span::new(offset + i, offset + i + clen) });
829 i += clen;
830 }
831 '⍺' | '⍵' => {
833 let mut end = i + clen;
834 if text[end..].starts_with(ch) {
835 end += clen;
836 }
837 cur.push(Token {
838 kind: Tok::Name(text[i..end].to_string()),
839 span: Span::new(offset + i, offset + end),
840 });
841 i = end;
842 }
843 ':' => {
845 let mut j = i + 1;
846 while let Some(c) = text[j..].chars().next() {
847 if c.is_ascii_alphabetic() {
848 j += c.len_utf8();
849 } else {
850 break;
851 }
852 }
853 let span = Span::new(offset + i, offset + j);
854 match control_word(&text[i + 1..j]) {
855 Some(word) => cur.push(Token { kind: Tok::Control(word), span }),
856 None if j > i + 1 => {
857 return Err(Error::parse(
858 format!("unknown control word: {}", &text[i..j]),
859 span,
860 ));
861 }
862 None => cur.push(Token {
863 kind: Tok::Colon,
864 span: Span::new(offset + i, offset + i + 1),
865 }),
866 }
867 i = j;
868 }
869 '→' => {
870 cur.push(Token {
871 kind: Tok::Arrow,
872 span: Span::new(offset + i, offset + i + clen),
873 });
874 i += clen;
875 }
876 '⍬' => {
878 cur.push(Token {
879 kind: Tok::Value(Array::empty(crate::dtype::DType::I64)),
880 span: Span::new(offset + i, offset + i + clen),
881 });
882 i += clen;
883 }
884 '(' => {
885 cur.push(Token { kind: Tok::LParen, span: Span::new(offset + i, offset + i + 1) });
886 i += 1;
887 }
888 ')' => {
889 cur.push(Token { kind: Tok::RParen, span: Span::new(offset + i, offset + i + 1) });
890 i += 1;
891 }
892 '[' => {
893 cur.push(Token {
894 kind: Tok::LBracket,
895 span: Span::new(offset + i, offset + i + 1),
896 });
897 i += 1;
898 }
899 ']' => {
900 cur.push(Token {
901 kind: Tok::RBracket,
902 span: Span::new(offset + i, offset + i + 1),
903 });
904 i += 1;
905 }
906 ';' => {
907 cur.push(Token { kind: Tok::Semi, span: Span::new(offset + i, offset + i + 1) });
908 i += 1;
909 }
910 '←' => {
911 cur.push(Token {
912 kind: Tok::Assign,
913 span: Span::new(offset + i, offset + i + clen),
914 });
915 i += clen;
916 }
917 '⍞' => {
919 cur.push(Token {
920 kind: Tok::Quad { quote: true },
921 span: Span::new(offset + i, offset + i + clen),
922 });
923 i += clen;
924 }
925 '⎕' => {
926 let after = i + clen;
927 let mut j = after;
928 while let Some(c) = text[j..].chars().next() {
929 if c.is_alphabetic() {
930 j += c.len_utf8();
931 } else {
932 break;
933 }
934 }
935 if j > after {
936 let span = Span::new(offset + i, offset + j);
937 let name = text[after..j].to_uppercase();
938 if text[j..].trim_start().starts_with('←') {
944 quad_name(&name, d, span)?;
945 return Err(Error::language(
946 format!(
947 "⎕{name} is read-only: libjay's system names are \
948 fixed before the program runs"
949 ),
950 span,
951 ));
952 }
953 cur.push(Token { kind: quad_name(&name, d, span)?, span });
954 i = j;
955 continue;
956 }
957 cur.push(Token {
958 kind: Tok::Quad { quote: false },
959 span: Span::new(offset + i, offset + after),
960 });
961 i = after;
962 }
963 _ if num_start(text, i) => {
964 let (tok, next) = lex_number_vector(text, i, offset)?;
965 cur.push(tok);
966 i = next;
967 }
968 _ if is_name_start(ch) => {
969 let start = i;
970 i += clen;
971 while let Some(c) = text[i..].chars().next() {
972 if is_name_body(c) {
973 i += c.len_utf8();
974 } else {
975 break;
976 }
977 }
978 cur.push(Token {
979 kind: Tok::Name(text[start..i].to_string()),
980 span: Span::new(offset + start, offset + i),
981 });
982 }
983 _ => {
984 let mut end = i + clen;
985 if let Some(v) = verb_for(ch, d) {
986 cur.push(Token {
987 kind: Tok::Func(v),
988 span: Span::new(offset + i, offset + end),
989 });
990 } else if let Some(mut op) = op_for(ch) {
991 if op == OpGlyph::Jot && text[end..].starts_with('.') {
994 op = OpGlyph::JotDot;
995 end += 1;
996 }
997 cur.push(Token {
998 kind: Tok::Op(op),
999 span: Span::new(offset + i, offset + end),
1000 });
1001 } else if let Some(what) = queued_glyph(ch) {
1002 return Err(Error::not_yet(what, Span::new(offset + i, offset + end)));
1005 } else {
1006 return Err(Error::parse(
1007 format!("unknown symbol: {ch}"),
1008 Span::new(offset + i, offset + end),
1009 ));
1010 }
1011 i = end;
1012 }
1013 }
1014 }
1015 Ok(())
1016}
1017
1018fn end_sentence(out: &mut Vec<Vec<Token>>, cur: &mut Vec<Token>) {
1019 if !cur.is_empty() {
1020 out.push(std::mem::take(cur));
1021 }
1022}
1023
1024fn is_name_start(c: char) -> bool {
1025 c.is_alphabetic() || c == '∆' || c == '⍙'
1026}
1027
1028fn is_name_body(c: char) -> bool {
1029 c.is_alphanumeric() || c == '_' || c == '∆' || c == '⍙'
1030}
1031
1032fn lex_string(text: &str, start: usize, offset: usize) -> Result<(Array, usize)> {
1035 let mut chars: Vec<char> = Vec::new();
1036 let mut i = start + 1;
1037 loop {
1038 let c = match text[i..].chars().next() {
1039 Some(c) => c,
1040 None => {
1041 return Err(Error::parse(
1042 "unterminated string",
1043 Span::new(offset + start, offset + text.len()),
1044 ));
1045 }
1046 };
1047 if c == '\'' {
1048 if text[i + 1..].starts_with('\'') {
1049 chars.push('\'');
1050 i += 2;
1051 continue;
1052 }
1053 i += 1;
1054 break;
1055 }
1056 chars.push(c);
1057 i += c.len_utf8();
1058 }
1059 let shape = if chars.len() == 1 { vec![] } else { vec![chars.len()] };
1060 Ok((Array::new(shape, Data::Char(chars.into())), i))
1061}
1062
1063fn num_start(text: &str, i: usize) -> bool {
1065 let s = match text.get(i..) {
1066 Some(s) => s,
1067 None => return false,
1068 };
1069 let mut cs = s.chars();
1070 let c0 = match cs.next() {
1071 Some(c) => c,
1072 None => return false,
1073 };
1074 if c0.is_ascii_digit() {
1075 return true;
1076 }
1077 if c0 == '.' {
1078 return cs.next().is_some_and(|d| d.is_ascii_digit());
1079 }
1080 if c0 == '¯' {
1081 return match cs.next() {
1082 Some(d) if d.is_ascii_digit() => true,
1083 Some('.') => cs.next().is_some_and(|d| d.is_ascii_digit()),
1084 _ => false,
1085 };
1086 }
1087 false
1088}
1089
1090fn lex_number(text: &str, start: usize, offset: usize) -> Result<(f64, bool, usize)> {
1093 let mut i = start;
1094 let mut buf = String::new();
1095 let mut saw_dot = false;
1096 if text[i..].starts_with('¯') {
1097 buf.push('-');
1098 i += '¯'.len_utf8();
1099 }
1100 i = take_digits(text, i, &mut buf);
1101 if text[i..].starts_with('.') && text[i + 1..].chars().next().is_some_and(|d| d.is_ascii_digit())
1102 {
1103 saw_dot = true;
1104 buf.push('.');
1105 i += 1;
1106 i = take_digits(text, i, &mut buf);
1107 }
1108 if let Some(c) = text[i..].chars().next() && (c == 'e' || c == 'E') {
1109 let after = i + 1;
1110 let neg = text[after..].starts_with('¯');
1111 let digits_at = if neg { after + '¯'.len_utf8() } else { after };
1112 if text[digits_at..].chars().next().is_some_and(|d| d.is_ascii_digit()) {
1113 buf.push('e');
1114 if neg {
1115 buf.push('-');
1116 }
1117 i = take_digits(text, digits_at, &mut buf);
1118 }
1119 }
1120 let v: f64 = buf.parse().map_err(|_| {
1121 Error::parse(
1122 format!("cannot read the number {}", &text[start..i]),
1123 Span::new(offset + start, offset + i),
1124 )
1125 })?;
1126 let float = saw_dot || v.fract() != 0.0 || v.abs() >= 9.0e18;
1128 Ok((v, float, i))
1129}
1130
1131fn take_digits(text: &str, mut i: usize, buf: &mut String) -> usize {
1132 while let Some(c) = text[i..].chars().next() {
1133 if c.is_ascii_digit() {
1134 buf.push(c);
1135 i += 1;
1136 } else {
1137 break;
1138 }
1139 }
1140 i
1141}
1142
1143fn lex_number_vector(text: &str, start: usize, offset: usize) -> Result<(Token, usize)> {
1146 let mut vals: Vec<crate::complex::Cx> = Vec::new();
1147 let mut any_float = false;
1148 let mut any_complex = false;
1149 let mut i = start;
1150 let mut end;
1151 loop {
1152 let (v, float, mut next) = lex_number(text, i, offset)?;
1153 let mut imag = 0.0;
1154 if let Some(c) = text[next..].chars().next() {
1155 if (c == 'j' || c == 'J') && num_start(text, next + 1) {
1158 let (b, _, imag_end) = lex_number(text, next + 1, offset)?;
1159 imag = b;
1160 next = imag_end;
1161 any_complex = true;
1162 }
1163 }
1164 vals.push([v, imag]);
1165 any_float |= float;
1166 end = next;
1167 i = next;
1168 let mut k = i;
1169 while text[k..].starts_with(' ') || text[k..].starts_with('\t') {
1170 k += 1;
1171 }
1172 if k > i && num_start(text, k) {
1173 i = k;
1174 continue;
1175 }
1176 break;
1177 }
1178 let data = if any_complex {
1179 Data::Complex(vals.into())
1180 } else if any_float {
1181 Data::F64(vals.iter().map(|&v| v[0]).collect())
1182 } else {
1183 Data::I64(vals.iter().map(|&v| v[0] as i64).collect())
1184 };
1185 let shape = if data.len() == 1 { vec![] } else { vec![data.len()] };
1186 let tok = Token {
1187 kind: Tok::Nums(Array::new(shape, data)),
1188 span: Span::new(offset + start, offset + end),
1189 };
1190 Ok((tok, end))
1191}
1192
1193fn fold_operators(toks: Vec<Token>, d: Rules) -> Result<Vec<Token>> {
1201 let mut out: Vec<Token> = Vec::new();
1202 let mut it = toks.into_iter().peekable();
1203 while let Some(t) = it.next() {
1204 if matches!(t.kind, Tok::RParen) {
1209 out.push(t);
1210 close_paren(&mut out, d)?;
1211 continue;
1212 }
1213 if let Tok::UserOp { def, omega } = &t.kind {
1216 let (def, omega) = (def.clone(), *omega);
1217 let right = if omega {
1218 match it.peek() {
1219 Some(tok) if matches!(tok.kind, Tok::Func(_)) => {
1220 let g = it.next().expect("peeked");
1221 let Tok::Func(g) = g.kind else { unreachable!("checked above") };
1222 Some(Box::new(g))
1223 }
1224 _ => {
1225 return Err(Error::parse("⍵⍵ needs a function on the operator's right", t.span));
1226 }
1227 }
1228 } else {
1229 None
1230 };
1231 let Some(Token { kind: Tok::Func(f), span: fspan }) = out.pop() else {
1232 return Err(Error::parse("⍺⍺ needs a function on the operator's left", t.span));
1233 };
1234 let derived = Verb::UserDerived {
1235 def: Box::new(def),
1236 alpha: Box::new(f),
1237 omega: right,
1238 };
1239 out.push(Token { kind: Tok::Func(derived), span: Span::merge(fspan, t.span) });
1240 continue;
1241 }
1242 let op = match t.kind {
1243 Tok::Op(op) => op,
1244 _ => {
1245 out.push(t);
1246 continue;
1247 }
1248 };
1249 if op == OpGlyph::JotDot {
1252 let ftok = match it.peek() {
1253 Some(tok) if matches!(tok.kind, Tok::Func(_)) => it.next().unwrap(),
1254 _ => {
1255 return Err(Error::parse("∘. needs a function on its right", t.span));
1256 }
1257 };
1258 let span = Span::merge(t.span, ftok.span);
1259 let Tok::Func(f) = ftok.kind else { unreachable!("checked above") };
1260 out.push(Token { kind: Tok::Func(Verb::Reduce(Box::new(f))), span });
1261 continue;
1262 }
1263 if matches!(
1266 op,
1267 OpGlyph::Jot | OpGlyph::Over | OpGlyph::Before | OpGlyph::Under | OpGlyph::Dot
1268 ) {
1269 let Some(gtok) = it.peek().filter(|x| matches!(x.kind, Tok::Func(_))) else {
1270 return Err(Error::not_yet(
1271 format!("{} with a value operand", op.glyph()),
1272 t.span,
1273 ));
1274 };
1275 let gspan = gtok.span;
1276 let Some(Token { kind: Tok::Func(g), .. }) = it.next() else {
1277 unreachable!("peeked a function")
1278 };
1279 let Some(Token { kind: Tok::Func(f), span: fspan }) = out.pop() else {
1280 return Err(Error::not_yet(
1281 format!("{} with a value operand", op.glyph()),
1282 t.span,
1283 ));
1284 };
1285 let span = Span::merge(fspan, gspan);
1286 let derived = match op {
1289 OpGlyph::Jot => Verb::Beside(Box::new(f), Box::new(g)),
1290 OpGlyph::Before => Verb::Before(Box::new(f), Box::new(g)),
1291 OpGlyph::Under => {
1295 let back = crate::verb::obverse(&g).ok_or_else(|| {
1296 Error::not_yet(
1297 format!("the obverse of {} (no inverse is known)", g.name()),
1298 gspan,
1299 )
1300 })?;
1301 let composed = Verb::Compose(Box::new(f), Box::new(g));
1302 Verb::Atop(Box::new(back), Box::new(composed))
1303 }
1304 OpGlyph::Dot => Verb::InnerProduct {
1307 u: Box::new(Verb::Reduce(Box::new(f))),
1308 v: Box::new(g),
1309 apl: true,
1310 },
1311 _ => Verb::Compose(Box::new(f), Box::new(g)),
1312 };
1313 out.push(Token { kind: Tok::Func(derived), span });
1314 continue;
1315 }
1316 let left_is_func = matches!(out.last().map(|x| &x.kind), Some(Tok::Func(_)));
1318 if !left_is_func {
1319 if out.last().is_some_and(|x| is_operand_end(&x.kind)) {
1323 let f = match op {
1324 OpGlyph::Slash => copy_verb(false),
1325 OpGlyph::SlashBar => copy_verb(true),
1326 OpGlyph::Backslash => expand_verb(false),
1327 OpGlyph::BackslashBar => expand_verb(true),
1328 OpGlyph::Rank
1329 | OpGlyph::Commute
1330 | OpGlyph::Power
1331 | OpGlyph::JotDot
1332 | OpGlyph::Jot
1333 | OpGlyph::Over
1334 | OpGlyph::Under
1335 | OpGlyph::Stencil
1336 | OpGlyph::Before
1337 | OpGlyph::Key
1338 | OpGlyph::Dot
1339 | OpGlyph::Variant
1340 | OpGlyph::Each => {
1341 return Err(Error::parse(
1342 format!("{} needs a function to its left", op.glyph()),
1343 t.span,
1344 ));
1345 }
1346 };
1347 out.push(Token { kind: Tok::Func(f), span: t.span });
1348 continue;
1349 }
1350 return Err(Error::parse(
1351 format!("{} needs a function to its left", op.glyph()),
1352 t.span,
1353 ));
1354 }
1355 let ftok = out.pop().unwrap();
1356 let f = match ftok.kind {
1357 Tok::Func(f) => f,
1358 _ => unreachable!("checked above"),
1359 };
1360 let span = Span::merge(ftok.span, t.span);
1361 if let Some((k, aspan)) = take_axis(&mut it, d)? {
1365 let inner = match op {
1366 OpGlyph::Slash | OpGlyph::SlashBar => Verb::Reduce(Box::new(f)),
1367 OpGlyph::Backslash | OpGlyph::BackslashBar => {
1368 Verb::Windowed(Box::new(Verb::Reduce(Box::new(f))), WindowKind::Scan)
1369 }
1370 _ => {
1371 return Err(Error::not_yet(
1372 format!("axis specification for {}", op.glyph()),
1373 aspan,
1374 ));
1375 }
1376 };
1377 out.push(Token {
1378 kind: Tok::Func(Verb::AlongAxis(Box::new(inner), k)),
1379 span: Span::merge(span, aspan),
1380 });
1381 continue;
1382 }
1383 let derived = match op {
1384 OpGlyph::Slash => Verb::Rank(Box::new(Verb::Reduce(Box::new(f))), [1, 1, 1]),
1387 OpGlyph::SlashBar => Verb::Reduce(Box::new(f)),
1388 OpGlyph::Backslash => Verb::Rank(
1392 Box::new(Verb::Windowed(Box::new(Verb::Reduce(Box::new(f))), WindowKind::Scan)),
1393 [1, 1, 1],
1394 ),
1395 OpGlyph::BackslashBar => {
1396 Verb::Windowed(Box::new(Verb::Reduce(Box::new(f))), WindowKind::Scan)
1397 }
1398 OpGlyph::Commute => Verb::Commute(Box::new(f)),
1399 OpGlyph::Key => Verb::KeyPairs(Box::new(f)),
1400 OpGlyph::Variant => {
1403 let (options, ospan) = variant_options(&mut it, t.span)?;
1404 let derived = variant(f, &options, Span::merge(span, ospan))?;
1405 out.push(Token { kind: Tok::Func(derived), span: Span::merge(span, ospan) });
1406 continue;
1407 }
1408 OpGlyph::Dot => {
1411 return Err(Error::parse("the inner product . needs a function on its right", t.span));
1412 }
1413 OpGlyph::Each => Verb::Each(Box::new(f), Enclose::ExceptSimpleScalar),
1417 OpGlyph::Power => {
1418 let spec = match it.peek() {
1419 Some(tok) if matches!(tok.kind, Tok::Func(_)) => {
1422 let gtok = it.next().unwrap();
1423 let Tok::Func(g) = gtok.kind else { unreachable!("checked above") };
1424 let v = Verb::PowerUntil(Box::new(f), Box::new(g));
1425 out.push(Token {
1426 kind: Tok::Func(v),
1427 span: Span::merge(span, gtok.span),
1428 });
1429 continue;
1430 }
1431 Some(tok) if literal(&tok.kind).is_some() => it.next().unwrap(),
1432 _ => {
1433 return Err(Error::not_yet("computed power (f⍣n)", t.span));
1434 }
1435 };
1436 let arr = literal(&spec.kind).expect("checked above");
1437 let p = power_spec(arr, spec.span)?;
1438 let f = Verb::PowerN(Box::new(f), p);
1439 out.push(Token { kind: Tok::Func(f), span: Span::merge(span, spec.span) });
1440 continue;
1441 }
1442 OpGlyph::Stencil => {
1446 let Some(spec) = it.peek().filter(|t| literal(&t.kind).is_some()) else {
1447 return Err(Error::parse(
1448 "⌺ needs a window specification on its right",
1449 t.span,
1450 ));
1451 };
1452 let sspan = spec.span;
1453 let spec = it.next().expect("peeked a literal");
1454 let arr = literal(&spec.kind).expect("checked above");
1455 if arr.rank() > 1 {
1456 return Err(Error::not_yet(
1457 "a stencil with a movement row (f⌺(m⍪w))",
1458 sspan,
1459 ));
1460 }
1461 let sizes = arr
1462 .to_i64_vec()
1463 .ok_or_else(|| Error::domain("a stencil window is whole numbers", sspan))?;
1464 let v = Verb::Stencil(Box::new(f), sizes);
1465 out.push(Token { kind: Tok::Func(v), span: Span::merge(span, sspan) });
1466 continue;
1467 }
1468 OpGlyph::Rank => {
1469 let spec = match it.peek() {
1470 Some(tok) if matches!(tok.kind, Tok::Func(_)) => {
1473 let gtok = it.next().unwrap();
1474 let Tok::Func(g) = gtok.kind else { unreachable!("checked above") };
1475 let v = Verb::Atop(Box::new(f), Box::new(g));
1476 out.push(Token {
1477 kind: Tok::Func(v),
1478 span: Span::merge(span, gtok.span),
1479 });
1480 continue;
1481 }
1482 Some(tok) if literal(&tok.kind).is_some() => it.next().unwrap(),
1483 _ => {
1484 return Err(Error::parse(
1485 "⍤ needs a rank specification on its right",
1486 t.span,
1487 ));
1488 }
1489 };
1490 let arr = literal(&spec.kind).expect("checked above");
1491 let ranks = rank_spec(arr, spec.span)?;
1492 let f = Verb::Rank(Box::new(f), ranks);
1493 out.push(Token { kind: Tok::Func(f), span: Span::merge(span, spec.span) });
1494 continue;
1495 }
1496 OpGlyph::JotDot
1498 | OpGlyph::Jot
1499 | OpGlyph::Over
1500 | OpGlyph::Under
1501 | OpGlyph::Before => {
1502 unreachable!("handled above")
1503 }
1504 };
1505 out.push(Token { kind: Tok::Func(derived), span });
1506 }
1507 Ok(out)
1508}
1509
1510fn take_axis(
1513 it: &mut std::iter::Peekable<std::vec::IntoIter<Token>>,
1514 d: Rules,
1515) -> Result<Option<(usize, Span)>> {
1516 if !matches!(it.peek().map(|t| &t.kind), Some(Tok::LBracket)) {
1517 return Ok(None);
1518 }
1519 let open = it.next().expect("peeked");
1520 let spec = match it.next() {
1521 Some(tok) if literal(&tok.kind).is_some() => tok,
1522 Some(tok) => return Err(Error::not_yet("a computed axis (f[k])", tok.span)),
1523 None => return Err(Error::parse("unterminated axis specification", open.span)),
1524 };
1525 let close = match it.next() {
1526 Some(tok) if matches!(tok.kind, Tok::RBracket) => tok,
1527 _ => return Err(Error::parse("unterminated axis specification", open.span)),
1528 };
1529 let span = Span::merge(open.span, close.span);
1530 let arr = literal(&spec.kind).expect("checked above");
1531 let ints = arr
1532 .to_i64_vec()
1533 .ok_or_else(|| Error::parse("an axis must be a whole number", spec.span))?;
1534 let [k] = ints[..] else {
1535 return Err(Error::not_yet("several axes in one specification", spec.span));
1536 };
1537 let origin = d.origin;
1538 let k = k - origin;
1539 if k < 0 {
1540 return Err(Error::domain(format!("axis {} does not exist", k + origin), spec.span));
1541 }
1542 Ok(Some((k as usize, span)))
1543}
1544
1545fn variant_options(
1554 it: &mut std::iter::Peekable<std::vec::IntoIter<Token>>,
1555 span: Span,
1556) -> Result<(Vec<(String, Array)>, Span)> {
1557 if let Some(tok) = it.peek().filter(|t| literal(&t.kind).is_some()) {
1558 let (value, vspan) = (literal(&tok.kind).expect("peeked a literal").clone(), tok.span);
1559 it.next();
1560 return Ok((vec![("CT".to_string(), value)], vspan));
1561 }
1562 let mut options = Vec::new();
1563 let mut last = span;
1564 while it.peek().is_some_and(|t| matches!(t.kind, Tok::LParen)) {
1565 it.next();
1566 let mut inside: Vec<Array> = Vec::new();
1567 loop {
1568 let Some(tok) = it.next() else {
1569 return Err(Error::parse("unmatched ( after ⍠", span));
1570 };
1571 last = tok.span;
1572 if matches!(tok.kind, Tok::RParen) {
1573 break;
1574 }
1575 match literal(&tok.kind) {
1576 Some(a) => inside.push(a.clone()),
1577 None => {
1578 return Err(Error::not_yet(
1579 "a computed variant option (f⍠v with a name or an expression)",
1580 tok.span,
1581 ));
1582 }
1583 }
1584 }
1585 let [name, value] = inside.as_slice() else {
1586 return Err(Error::parse("a variant option is a name and a value", last));
1587 };
1588 let Data::Char(cs) = &name.data else {
1589 return Err(Error::parse("a variant option starts with its name", last));
1590 };
1591 options.push((cs.as_slice().iter().collect::<String>().to_uppercase(), value.clone()));
1592 }
1593 if options.is_empty() {
1594 let where_ = it.peek().map_or(span, |t| t.span);
1595 return Err(Error::not_yet(
1596 "a computed variant option (f⍠v with a name or an expression)",
1597 where_,
1598 ));
1599 }
1600 Ok((options, last))
1601}
1602
1603fn variant(f: Verb, options: &[(String, Array)], span: Span) -> Result<Verb> {
1609 let mut out = f;
1610 for (name, value) in options {
1611 out = match name.as_str() {
1612 "CT" => {
1613 let Some(ct) = value.to_f64_vec().and_then(|v| v.first().copied()) else {
1614 return Err(Error::domain("a comparison tolerance is a number", span));
1615 };
1616 if !out.uses_tolerance() {
1617 return Err(Error::domain(
1618 format!(
1619 "the comparison tolerance is not an option of {}: it consults none",
1620 out.name()
1621 ),
1622 span,
1623 ));
1624 }
1625 if !(0.0..1.0).contains(&ct) {
1626 return Err(Error::domain(
1627 "a comparison tolerance lies between 0 and 1",
1628 span,
1629 ));
1630 }
1631 Verb::Fit(Box::new(out), ct)
1632 }
1633 "IO" => {
1634 let Some(io) = value.to_i64_vec().and_then(|v| v.first().copied()) else {
1635 return Err(Error::domain("an index origin is a whole number", span));
1636 };
1637 if io != 0 && io != 1 {
1638 return Err(Error::domain("an index origin is 0 or 1", span));
1639 }
1640 crate::verb::with_origin(&out, io).ok_or_else(|| {
1641 Error::domain(
1642 format!("the index origin is not an option of {}", out.name()),
1643 span,
1644 )
1645 })?
1646 }
1647 other => {
1648 return Err(Error::not_yet(
1649 format!("the variant option {other} (f⍠v)"),
1650 span,
1651 ));
1652 }
1653 };
1654 }
1655 Ok(out)
1656}
1657
1658fn unwrap_lone_operators(toks: Vec<Token>) -> Vec<Token> {
1663 let mut out: Vec<Token> = Vec::with_capacity(toks.len());
1664 for t in toks {
1665 let n = out.len();
1666 if matches!(t.kind, Tok::RParen)
1667 && n >= 2
1668 && matches!(out[n - 1].kind, Tok::Op(_))
1669 && matches!(out[n - 2].kind, Tok::LParen)
1670 {
1671 let op = out.pop().expect("checked above");
1672 let open = out.pop().expect("checked above");
1673 out.push(Token { kind: op.kind, span: Span::merge(open.span, t.span) });
1674 continue;
1675 }
1676 out.push(t);
1677 }
1678 out
1679}
1680
1681fn close_paren(out: &mut Vec<Token>, d: Rules) -> Result<()> {
1685 let close = out.len() - 1;
1686 let Some(open) = matching_lparen(out, close) else { return Ok(()) };
1687 let span = Span::merge(out[open].span, out[close].span);
1688 let inner = &out[open + 1..close];
1689 if inner.len() == 1 && matches!(inner[0].kind, Tok::Func(_)) {
1690 let Some(Token { kind, .. }) = out.get(open + 1).cloned() else {
1691 unreachable!("checked above")
1692 };
1693 out.truncate(open);
1694 out.push(Token { kind, span });
1695 return Ok(());
1696 }
1697 if !d.trains || inner.len() < 2 || !inner[1..].iter().all(|t| matches!(t.kind, Tok::Func(_))) {
1698 return Ok(());
1699 }
1700 let Some(verb) = train(inner)? else { return Ok(()) };
1701 out.truncate(open);
1702 out.push(Token { kind: Tok::Func(verb), span });
1703 Ok(())
1704}
1705
1706fn matching_lparen(out: &[Token], close: usize) -> Option<usize> {
1708 let mut depth = 0usize;
1709 for i in (0..close).rev() {
1710 match out[i].kind {
1711 Tok::RParen => depth += 1,
1712 Tok::LParen => {
1713 if depth == 0 {
1714 return Some(i);
1715 }
1716 depth -= 1;
1717 }
1718 _ => {}
1719 }
1720 }
1721 None
1722}
1723
1724fn train(tines: &[Token]) -> Result<Option<Verb>> {
1733 debug_assert!(!tines.is_empty());
1734 if tines.len() == 1 {
1735 return Ok(match &tines[0].kind {
1736 Tok::Func(f) => Some(f.clone()),
1737 _ => None,
1738 });
1739 }
1740 if tines.len() == 2 {
1741 let (Tok::Func(g), Tok::Func(h)) = (&tines[0].kind, &tines[1].kind) else {
1742 return Ok(None);
1743 };
1744 return Ok(Some(Verb::Atop(Box::new(g.clone()), Box::new(h.clone()))));
1745 }
1746 let head = &tines[0].kind;
1749 if tines.len() % 2 == 0 {
1750 let Tok::Func(f) = head else {
1751 return Err(Error::parse(
1752 "a value may only be a fork's left tine, and this train has an even number of tines",
1753 tines[0].span,
1754 ));
1755 };
1756 let Some(rest) = train(&tines[1..])? else { return Ok(None) };
1757 return Ok(Some(Verb::Atop(Box::new(f.clone()), Box::new(rest))));
1758 }
1759 let Some(rest) = train(&tines[2..])? else { return Ok(None) };
1760 let Tok::Func(g) = &tines[1].kind else { unreachable!("the tail is all functions") };
1761 match head {
1762 Tok::Func(f) => {
1763 Ok(Some(Verb::Fork(Box::new(f.clone()), Box::new(g.clone()), Box::new(rest))))
1764 }
1765 Tok::Value(n) | Tok::Nums(n) => {
1766 Ok(Some(Verb::NounFork(n.clone(), Box::new(g.clone()), Box::new(rest))))
1767 }
1768 Tok::Name(_) | Tok::Param(_) | Tok::RParen | Tok::RBracket | Tok::Niladic(_) => {
1772 Err(Error::not_yet("a train whose left tine is a computed value", tines[0].span))
1773 }
1774 _ => Ok(None),
1775 }
1776}
1777
1778fn tine_run(toks: &[Token], d: Rules) -> Result<Option<Verb>> {
1784 if !d.trains || toks.is_empty() {
1785 return Ok(None);
1786 }
1787 if !toks[1..].iter().all(|t| matches!(t.kind, Tok::Func(_))) {
1788 return Ok(None);
1789 }
1790 train(toks)
1791}
1792
1793fn fold_axes(toks: Vec<Token>, d: Rules) -> Result<Vec<Token>> {
1795 let mut out: Vec<Token> = Vec::new();
1796 let mut it = toks.into_iter().peekable();
1797 while let Some(t) = it.next() {
1798 let Tok::Func(f) = &t.kind else {
1799 out.push(t);
1800 continue;
1801 };
1802 let Some((k, aspan)) = take_axis(&mut it, d)? else {
1803 out.push(t);
1804 continue;
1805 };
1806 let Some(inner) = leading_axis_form(f) else {
1807 return Err(Error::not_yet(format!("axis specification for {}", f.name()), aspan));
1808 };
1809 out.push(Token {
1810 kind: Tok::Func(Verb::AlongAxis(Box::new(inner), k)),
1811 span: Span::merge(t.span, aspan),
1812 });
1813 }
1814 Ok(out)
1815}
1816
1817fn leading_axis_form(v: &Verb) -> Option<Verb> {
1821 match v {
1822 Verb::Rank(inner, [1, 0, 1]) => leading_axis_form(inner),
1824 Verb::Prim(p) if matches!(p.monad, MonadOp::Reverse) => Some(v.clone()),
1825 _ => None,
1826 }
1827}
1828
1829fn select_axis_verb(axis: usize, rank: usize, d: Rules) -> Verb {
1831 Verb::Prim(Prim {
1832 name: "[…]",
1833 monad: MonadOp::None,
1834 dyad: DyadOp::SelectAxis { axis, rank, origin: d.origin },
1835 ranks: [RANK_INF; 3],
1836 })
1837}
1838
1839fn power_spec(a: &Array, span: Span) -> Result<Power> {
1842 let ints = a
1843 .to_i64_vec()
1844 .ok_or_else(|| Error::parse("⍣ needs a whole number on its right", span))?;
1845 let [n] = ints[..] else {
1846 return Err(Error::not_yet("power over a list of counts (f⍣n)", span));
1847 };
1848 if n < 0 {
1849 return Err(Error::not_yet("inverse power (f⍣¯1 and other negative powers)", span));
1850 }
1851 Ok(Power::Times(n as u64))
1852}
1853
1854fn rank_spec(a: &Array, span: Span) -> Result<[i64; 3]> {
1856 let ints = a
1857 .to_i64_vec()
1858 .ok_or_else(|| Error::parse("⍤ rank specification must be integers", span))?;
1859 match ints.len() {
1860 1 => Ok([ints[0], ints[0], ints[0]]),
1861 2 => Ok([ints[1], ints[0], ints[1]]),
1862 3 => Ok([ints[0], ints[1], ints[2]]),
1863 _ => Err(Error::parse("⍤ rank specification takes 1 to 3 integers", span)),
1864 }
1865}
1866
1867fn parse_range(toks: &[Token], lo: usize, hi: usize, hint: Span, d: Rules) -> Result<Expr> {
1874 let (mut acc, mut start) = parse_operand(toks, lo, hi, hint, d)?;
1875 let end = toks[hi - 1].span.end;
1876 loop {
1877 if start == lo {
1878 return Ok(acc);
1879 }
1880 let left = &toks[start - 1];
1881 match &left.kind {
1882 Tok::Func(f) => {
1883 let dyadic = start >= lo + 2 && is_operand_end(&toks[start - 2].kind);
1885 if dyadic {
1886 let (x, xstart) = parse_operand(toks, lo, start - 1, left.span, d)?;
1887 acc = Expr::Dyad {
1888 verb: f.clone(),
1889 x: Box::new(x),
1890 y: Box::new(acc),
1891 span: Span::new(toks[xstart].span.start, end),
1892 };
1893 start = xstart;
1894 } else {
1895 acc = Expr::Monad {
1896 verb: f.clone(),
1897 y: Box::new(acc),
1898 span: Span::new(left.span.start, end),
1899 };
1900 start -= 1;
1901 }
1902 }
1903 Tok::Assign => {
1904 if start < lo + 2 {
1905 return Err(Error::parse("assignment target must be a name", left.span));
1906 }
1907 let target = &toks[start - 2];
1908 let span = Span::new(target.span.start, end);
1909 match &target.kind {
1910 Tok::Name(n) => {
1911 acc = Expr::Assign {
1912 name: n.clone(),
1913 value: Box::new(acc),
1914 scope: Scope::Local,
1915 span,
1916 };
1917 }
1918 Tok::Quad { quote } => {
1919 acc = Expr::PrintPass { value: Box::new(acc), bare: *quote, span };
1920 }
1921 _ => {
1922 return Err(Error::parse(
1923 "assignment target must be a name",
1924 target.span,
1925 ));
1926 }
1927 }
1928 start -= 2;
1929 }
1930 _ => break,
1933 }
1934 }
1935 let span = Span::new(toks[lo].span.start, toks[start - 1].span.end);
1936 if d.trains && toks[lo..start].iter().all(|t| matches!(t.kind, Tok::Func(_))) {
1940 return Err(Error::parse(
1941 "a train is a function; parenthesise it to apply it to an argument",
1942 span,
1943 ));
1944 }
1945 Err(Error::parse("syntax error", span))
1946}
1947
1948fn parse_operand(
1955 toks: &[Token],
1956 lo: usize,
1957 hi: usize,
1958 hint: Span,
1959 d: Rules,
1960) -> Result<(Expr, usize)> {
1961 let (first, mut start) = parse_primary(toks, lo, hi, hint, d)?;
1962 if start == lo || !is_operand_end(&toks[start - 1].kind) {
1963 return Ok((first, start));
1964 }
1965 let mut items: Vec<Expr> = Vec::new();
1966 let mut cur = first;
1967 loop {
1968 push_items(&mut items, cur, &toks[start]);
1969 if start == lo || !is_operand_end(&toks[start - 1].kind) {
1970 break;
1971 }
1972 let (e, s) = parse_primary(toks, lo, start, toks[start - 1].span, d)?;
1973 cur = e;
1974 start = s;
1975 }
1976 let span = Span::new(toks[start].span.start, toks[hi - 1].span.end);
1977 let mut it = items.into_iter();
1978 let last = it.next().expect("a strand has at least one item");
1979 let mut acc = Expr::Monad { verb: strand_seed(d), y: Box::new(last), span };
1980 for item in it {
1981 acc = Expr::Dyad { verb: strand_verb(), x: Box::new(item), y: Box::new(acc), span };
1982 }
1983 Ok((acc, start))
1984}
1985
1986fn push_items(items: &mut Vec<Expr>, e: Expr, tok: &Token) {
1988 if let Tok::Nums(a) = &tok.kind && a.rank() > 0 {
1989 for i in (0..a.count()).rev() {
1990 let atom = Array::new(Vec::new(), a.data.slice(i, i + 1));
1991 items.push(Expr::Const(atom, tok.span));
1992 }
1993 return;
1994 }
1995 items.push(e);
1996}
1997
1998fn strand_seed(d: Rules) -> Verb {
2001 Verb::Atop(
2002 Box::new(Verb::Prim(prim_for(',', d).expect("`,` is a primitive"))),
2003 Box::new(Verb::Prim(prim_for('⊂', d).expect("`⊂` is a primitive"))),
2004 )
2005}
2006
2007fn strand_verb() -> Verb {
2009 Verb::Prim(Prim {
2010 name: "(vector notation)",
2011 monad: MonadOp::None,
2012 dyad: DyadOp::Strand,
2013 ranks: [RANK_INF; 3],
2014 })
2015}
2016
2017fn parse_primary(
2020 toks: &[Token],
2021 lo: usize,
2022 hi: usize,
2023 hint: Span,
2024 d: Rules,
2025) -> Result<(Expr, usize)> {
2026 if hi == lo {
2027 return Err(Error::parse("empty parentheses", hint));
2028 }
2029 let t = &toks[hi - 1];
2030 match &t.kind {
2031 Tok::Value(a) | Tok::Nums(a) => Ok((Expr::Const(a.clone(), t.span), hi - 1)),
2032 Tok::Param(i) => Ok((Expr::Param(*i, t.span), hi - 1)),
2033 Tok::Name(n) => Ok((Expr::Name(n.clone(), t.span), hi - 1)),
2034 Tok::Niladic(v) => Ok((
2037 Expr::Monad {
2038 verb: v.clone(),
2039 y: Box::new(Expr::Const(Array::empty(crate::dtype::DType::I64), t.span)),
2040 span: t.span,
2041 },
2042 hi - 1,
2043 )),
2044 Tok::RParen => {
2045 let l = match_lparen(toks, lo, hi - 1)?;
2046 let hint = Span::merge(toks[l].span, t.span);
2047 let inner = parse_range(toks, l + 1, hi - 1, hint, d)?;
2048 Ok((inner, l))
2049 }
2050 Tok::RBracket => index_brackets(toks, lo, hi, d),
2051 Tok::Func(_) if hi >= lo + 2 && matches!(toks[hi - 2].kind, Tok::Assign) => {
2055 let from = if hi >= lo + 3 { toks[hi - 3].span } else { toks[hi - 2].span };
2056 let span = Span::merge(from, t.span);
2057 if d.trains {
2058 Err(Error::not_yet("naming a function inside a larger sentence", span))
2059 } else {
2060 Err(Error::not_yet("function assignment (F←+/)", span))
2061 }
2062 }
2063 Tok::Func(_) => Err(Error::parse("missing right argument", t.span)),
2064 Tok::Assign => Err(Error::parse("← needs a value on its right", t.span)),
2065 Tok::Quad { quote } => Ok((Expr::Input { eval: !*quote, span: t.span }, hi - 1)),
2067 Tok::LParen => Err(Error::parse("unmatched (", t.span)),
2068 Tok::LBracket => Err(Error::parse("unmatched [", t.span)),
2069 Tok::Semi => Err(Error::parse("; is only meaningful inside index brackets", t.span)),
2070 Tok::Colon => Err(Error::parse(": is only meaningful in a dfn guard", t.span)),
2071 Tok::UserOp { .. } => Err(Error::parse(
2072 "this dfn mentions ⍺⍺ or ⍵⍵, so it is an operator and needs a function operand",
2073 t.span,
2074 )),
2075 Tok::Arrow => Err(Error::parse(
2076 "→ branches, and only a line of a ∇ definition may begin with it",
2077 t.span,
2078 )),
2079 Tok::Del => Err(Error::parse("∇ opens a definition; it is not a value", t.span)),
2080 Tok::Control(w) => Err(Error::parse(
2081 format!(":{w} is only meaningful inside a ∇ definition"),
2082 t.span,
2083 )),
2084 Tok::LBrace | Tok::RBrace => Err(Error::parse("unmatched {", t.span)),
2085 Tok::Separator => Err(Error::internal("a statement break survived folding")),
2086 Tok::Op(_) => Err(Error::internal("operator survived folding")),
2087 }
2088}
2089
2090fn index_brackets(
2097 toks: &[Token],
2098 lo: usize,
2099 hi: usize,
2100 d: Rules,
2101) -> Result<(Expr, usize)> {
2102 let close = &toks[hi - 1];
2103 let open = match_lbracket(toks, lo, hi - 1)?;
2104 if open == lo || !is_operand_end(&toks[open - 1].kind) {
2105 return Err(Error::parse("[ needs a value on its left", toks[open].span));
2106 }
2107 let (base, start) = parse_primary(toks, lo, open, toks[open].span, d)?;
2108 let slots = index_slots(toks, open + 1, hi - 1, toks[open].span)?;
2109 let span = Span::new(toks[start].span.start, close.span.end);
2110 let rank = slots.len();
2111 let mut acc = base;
2112 let mut first = true;
2113 for (axis, slot) in slots.iter().enumerate().rev() {
2114 let Some((slo, shi)) = *slot else { continue };
2115 let idx = parse_range(toks, slo, shi, toks[open].span, d)?;
2116 let check = if first { rank } else { 0 };
2117 first = false;
2118 acc = Expr::Dyad {
2119 verb: select_axis_verb(axis, check, d),
2120 x: Box::new(idx),
2121 y: Box::new(acc),
2122 span,
2123 };
2124 }
2125 Ok((acc, start))
2126}
2127
2128fn index_slots(
2131 toks: &[Token],
2132 lo: usize,
2133 hi: usize,
2134 hint: Span,
2135) -> Result<Vec<Option<(usize, usize)>>> {
2136 let mut out = Vec::new();
2137 let mut depth = 0usize;
2138 let mut start = lo;
2139 for (i, t) in toks.iter().enumerate().take(hi).skip(lo) {
2140 match t.kind {
2141 Tok::LParen | Tok::LBracket => depth += 1,
2142 Tok::RParen | Tok::RBracket => depth -= 1,
2143 Tok::Semi if depth == 0 => {
2144 out.push((start < i).then_some((start, i)));
2145 start = i + 1;
2146 }
2147 _ => {}
2148 }
2149 }
2150 out.push((start < hi).then_some((start, hi)));
2151 if out.len() == 1 && out[0].is_none() {
2152 return Err(Error::parse("empty index brackets", hint));
2153 }
2154 Ok(out)
2155}
2156
2157fn match_lbracket(toks: &[Token], lo: usize, rbracket: usize) -> Result<usize> {
2158 let mut depth = 0usize;
2159 let mut i = rbracket;
2160 while i > lo {
2161 i -= 1;
2162 match toks[i].kind {
2163 Tok::RBracket => depth += 1,
2164 Tok::LBracket => {
2165 if depth == 0 {
2166 return Ok(i);
2167 }
2168 depth -= 1;
2169 }
2170 _ => {}
2171 }
2172 }
2173 Err(Error::parse("unmatched ]", toks[rbracket].span))
2174}
2175
2176fn match_lparen(toks: &[Token], lo: usize, rparen: usize) -> Result<usize> {
2177 let mut depth = 0usize;
2178 let mut i = rparen;
2179 while i > lo {
2180 i -= 1;
2181 match toks[i].kind {
2182 Tok::RParen => depth += 1,
2183 Tok::LParen => {
2184 if depth == 0 {
2185 return Ok(i);
2186 }
2187 depth -= 1;
2188 }
2189 _ => {}
2190 }
2191 }
2192 Err(Error::parse("unmatched )", toks[rparen].span))
2193}
2194
2195const CONTROL_WORDS: [&str; 18] = [
2207 "If", "ElseIf", "Else", "EndIf", "While", "EndWhile", "Repeat", "Until", "For", "In",
2208 "EndFor", "Select", "Case", "EndSelect", "Return", "Leave", "Continue", "End",
2209];
2210
2211fn control_word(word: &str) -> Option<&'static str> {
2214 CONTROL_WORDS.iter().copied().find(|w| w.eq_ignore_ascii_case(word))
2215}
2216
2217fn match_close(toks: &[Token], open: usize, opener: &Tok, closer: &Tok) -> Option<usize> {
2219 let same = |a: &Tok, b: &Tok| std::mem::discriminant(a) == std::mem::discriminant(b);
2220 let mut depth = 0usize;
2221 for (i, t) in toks.iter().enumerate().skip(open) {
2222 if same(&t.kind, opener) {
2223 depth += 1;
2224 } else if same(&t.kind, closer) {
2225 depth -= 1;
2226 if depth == 0 {
2227 return Some(i);
2228 }
2229 }
2230 }
2231 None
2232}
2233
2234fn fold_dfns(
2236 toks: Vec<Token>,
2237 d: Rules,
2238 verbs: &HashMap<String, Verb>,
2239) -> Result<Vec<Token>> {
2240 let Some(open) = toks.iter().position(|t| matches!(t.kind, Tok::LBrace)) else {
2241 return Ok(toks);
2242 };
2243 let close = match_close(&toks, open, &Tok::LBrace, &Tok::RBrace)
2244 .ok_or_else(|| Error::parse("unmatched {", toks[open].span))?;
2245 let span = Span::merge(toks[open].span, toks[close].span);
2246 let (verb, omega) = build_dfn(&toks[open + 1..close], d, verbs)?;
2247 let mut out: Vec<Token> = toks[..open].to_vec();
2248 let kind = match omega {
2249 Some(omega) => Tok::UserOp { def: verb, omega },
2250 None => Tok::Func(verb),
2251 };
2252 out.push(Token { kind, span });
2253 out.extend_from_slice(&toks[close + 1..]);
2254 fold_dfns(out, d, verbs)
2256}
2257
2258fn split_statements(toks: &[Token]) -> Vec<&[Token]> {
2261 let mut out = Vec::new();
2262 let mut depth = 0usize;
2263 let mut start = 0usize;
2264 for (i, t) in toks.iter().enumerate() {
2265 match t.kind {
2266 Tok::LBrace => depth += 1,
2267 Tok::RBrace => depth = depth.saturating_sub(1),
2268 Tok::Separator if depth == 0 => {
2269 out.push(&toks[start..i]);
2270 start = i + 1;
2271 }
2272 _ => {}
2273 }
2274 }
2275 out.push(&toks[start..]);
2276 out.into_iter().filter(|s| !s.is_empty()).collect()
2277}
2278
2279fn build_dfn(
2284 body: &[Token],
2285 d: Rules,
2286 verbs: &HashMap<String, Verb>,
2287) -> Result<(Verb, Option<bool>)> {
2288 let mut depth = 0usize;
2289 let mut dyadic = false;
2290 let mut alpha_op = false;
2291 let mut omega_op = false;
2292 for t in body {
2293 match &t.kind {
2294 Tok::LBrace => depth += 1,
2295 Tok::RBrace => depth = depth.saturating_sub(1),
2296 Tok::Name(n) if depth == 0 && n == "⍺" => dyadic = true,
2297 Tok::Name(n) if depth == 0 && n == "⍺⍺" => alpha_op = true,
2298 Tok::Name(n) if depth == 0 && n == "⍵⍵" => omega_op = true,
2299 _ => {}
2300 }
2301 }
2302 let mut inner = verbs.clone();
2303 if alpha_op || omega_op {
2306 inner.insert("⍺⍺".to_string(), Verb::Named("⍺⍺".to_string()));
2307 inner.insert("⍵⍵".to_string(), Verb::Named("⍵⍵".to_string()));
2308 }
2309 let stmts = parse_dfn_body(body, d, &mut inner)?;
2310 let span = body.first().map_or(Span::new(0, 0), |t| t.span);
2314 match d.dfn_result {
2315 DfnResult::LastSentence => {}
2316 DfnResult::FirstNonAssignment => {
2317 return Err(Error::not_yet("a dfn that answers with its first value", span))
2318 }
2319 }
2320 let pure = stmts.iter().all(is_pure_stmt);
2321 let operator = (alpha_op || omega_op).then_some(omega_op);
2322 let verb = Verb::Explicit(Arc::new(ExplicitDef {
2323 name: "{…}".to_string(),
2324 left: dyadic.then(|| "⍺".to_string()),
2325 right: "⍵".to_string(),
2326 dyad_only: false,
2329 result: None,
2330 locals: Vec::new(),
2331 body: stmts,
2332 empty: None,
2334 labels: Vec::new(),
2335 pure,
2336 }));
2337 Ok((verb, operator))
2338}
2339
2340fn parse_dfn_body(
2341 body: &[Token],
2342 d: Rules,
2343 verbs: &mut HashMap<String, Verb>,
2344) -> Result<Vec<Expr>> {
2345 let mut stmts = Vec::new();
2346 for stmt in split_statements(body) {
2347 let stmt: Vec<Token> = stmt
2349 .iter()
2350 .map(|t| match t.kind {
2351 Tok::Del => Token { kind: Tok::Func(Verb::SelfRef), span: t.span },
2352 _ => t.clone(),
2353 })
2354 .collect();
2355 stmts.push(parse_guarded(stmt, d, verbs)?);
2356 }
2357 Ok(stmts)
2358}
2359
2360fn parse_guarded(
2362 stmt: Vec<Token>,
2363 d: Rules,
2364 verbs: &mut HashMap<String, Verb>,
2365) -> Result<Expr> {
2366 let mut depth = 0usize;
2367 let mut colon = None;
2368 for (i, t) in stmt.iter().enumerate() {
2369 match t.kind {
2370 Tok::LBrace | Tok::LParen | Tok::LBracket => depth += 1,
2371 Tok::RBrace | Tok::RParen | Tok::RBracket => depth = depth.saturating_sub(1),
2372 Tok::Colon if depth == 0 => {
2373 colon = Some(i);
2374 break;
2375 }
2376 _ => {}
2377 }
2378 }
2379 if let Some(k) = colon {
2380 let span = Span::merge(stmt[0].span, stmt[stmt.len() - 1].span);
2381 let test = one_statement(stmt[..k].to_vec(), d, verbs, stmt[k].span)?;
2382 let body = one_statement(stmt[k + 1..].to_vec(), d, verbs, stmt[k].span)?;
2383 let arm = Branch {
2385 test: Some(vec![test]),
2386 body: vec![body, Expr::Control(Box::new(Control::Return), span)],
2387 fall_through: false,
2388 };
2389 return Ok(Expr::Control(
2390 Box::new(Control::If { arms: vec![arm], otherwise: None }),
2391 span,
2392 ));
2393 }
2394 let default = matches!(
2398 (stmt.first().map(|t| &t.kind), stmt.get(1).map(|t| &t.kind)),
2399 (Some(Tok::Name(n)), Some(Tok::Assign)) if n == "⍺"
2400 );
2401 let span = stmt.first().map_or(Span::new(0, 0), |t| t.span);
2402 let e = one_statement(stmt, d, verbs, span)?;
2403 if default {
2404 let scope = match d.default_arg {
2405 DefaultArg::Eager => Scope::LocalDefault,
2406 DefaultArg::Lazy => return Err(Error::not_yet("a lazy ⍺← default", span)),
2407 };
2408 if let Expr::Assign { name, value, span, .. } = e {
2409 return Ok(Expr::Assign { name, value, scope, span });
2410 }
2411 }
2412 Ok(e)
2413}
2414
2415fn one_statement(
2416 stmt: Vec<Token>,
2417 d: Rules,
2418 verbs: &mut HashMap<String, Verb>,
2419 hint: Span,
2420) -> Result<Expr> {
2421 parse_statement(stmt, d, verbs, true)?
2422 .ok_or_else(|| Error::parse("this needs an expression", hint))
2423}
2424
2425fn is_pure_stmt(e: &Expr) -> bool {
2427 match e {
2428 Expr::Const(..) | Expr::Param(..) | Expr::Name(..) => true,
2429 Expr::Monad { verb, y, .. } => verb.is_pure() && is_pure_stmt(y),
2430 Expr::Dyad { verb, x, y, .. } => verb.is_pure() && is_pure_stmt(x) && is_pure_stmt(y),
2431 Expr::Assign { value, .. } => is_pure_stmt(value),
2432 Expr::Control(c, _) => is_pure_control(c),
2433 _ => false,
2434 }
2435}
2436
2437fn is_pure_control(c: &Control) -> bool {
2438 let all = |b: &Vec<Expr>| b.iter().all(is_pure_stmt);
2439 match c {
2440 Control::Return | Control::Break | Control::Continue => true,
2441 Control::Branch(target) => is_pure_stmt(target),
2442 Control::If { arms, otherwise } => {
2443 arms.iter().all(|a| a.test.as_ref().is_none_or(all) && all(&a.body))
2444 && otherwise.as_ref().is_none_or(all)
2445 }
2446 Control::While { test, body, .. } => all(test) && all(body),
2447 Control::For { source, body, .. } => is_pure_stmt(source) && all(body),
2448 Control::Select { subject, cases } => {
2449 is_pure_stmt(subject)
2450 && cases.iter().all(|c| c.test.as_ref().is_none_or(all) && all(&c.body))
2451 }
2452 Control::Try { body, catch } => all(body) && all(catch),
2453 }
2454}
2455
2456fn parse_tradfn(
2463 sentences: &[Vec<Token>],
2464 i: &mut usize,
2465 d: Rules,
2466 verbs: &mut HashMap<String, Verb>,
2467) -> Result<Expr> {
2468 let header = &sentences[*i];
2469 let open = header[0].span;
2470 *i += 1;
2471 let (name, def_left, def_right, result, locals) = parse_header(&header[1..], open)?;
2472 let mut body_lines: Vec<Vec<Token>> = Vec::new();
2473 loop {
2474 let Some(line) = sentences.get(*i) else {
2475 return Err(Error::parse("this definition has no closing ∇", open));
2476 };
2477 *i += 1;
2478 if line.len() == 1 && matches!(line[0].kind, Tok::Del) {
2479 break;
2480 }
2481 body_lines.push(line.clone());
2482 }
2483 let close = sentences
2484 .get(i.saturating_sub(1))
2485 .and_then(|l| l.first())
2486 .map_or(open, |t| t.span);
2487 let span = Span::merge(open, close);
2488 let mut inner = verbs.clone();
2490 inner.insert(name.clone(), Verb::Named(name.clone()));
2491 let mut items = Vec::new();
2492 let mut labels: Vec<(String, usize)> = Vec::new();
2493 for line in &body_lines {
2494 let mut label = None;
2495 let item = to_item(line.clone(), d, &mut inner, &mut label)?;
2496 if let Some(name) = label {
2497 labels.push((name, items.len()));
2498 }
2499 items.push(item);
2500 }
2501 let item_count = items.len();
2502 let mut cursor = AplCursor { items: &items, at: 0, d };
2503 let mut body = parse_apl_block(&mut cursor, &[])?;
2504 if !labels.is_empty() && body.len() != item_count {
2508 return Err(Error::not_yet("a label and a control structure in one definition", span));
2509 }
2510 if let Some(item) = cursor.peek() {
2511 return Err(Error::parse(
2512 format!(":{} has no matching opening word", item.word().unwrap_or("?")),
2513 item.span(),
2514 ));
2515 }
2516 let mut own: Vec<String> = locals.clone();
2519 own.extend(result.clone());
2520 own.extend(def_left.clone());
2521 own.push(def_right.clone());
2522 for stmt in &mut body {
2523 set_scopes(stmt, &own);
2524 }
2525 let pure = body.iter().all(is_pure_stmt);
2526 let verb = Verb::Explicit(Arc::new(ExplicitDef {
2527 name: format!("∇{name}"),
2528 left: def_left,
2529 right: def_right,
2530 dyad_only: false,
2531 result,
2532 locals,
2533 body,
2534 empty: None,
2535 labels,
2536 pure,
2537 }));
2538 verbs.insert(name.clone(), verb.clone());
2539 Ok(Expr::VerbDef { name, verb, span })
2540}
2541
2542type Header = (String, Option<String>, String, Option<String>, Vec<String>);
2543
2544fn parse_header(toks: &[Token], span: Span) -> Result<Header> {
2546 let mut names: Vec<String> = Vec::new();
2547 let mut locals: Vec<String> = Vec::new();
2548 let mut result = None;
2549 let mut in_locals = false;
2550 let mut k = 0usize;
2551 if let (Some(Tok::Name(z)), Some(Tok::Assign)) =
2553 (toks.first().map(|t| &t.kind), toks.get(1).map(|t| &t.kind))
2554 {
2555 result = Some(z.clone());
2556 k = 2;
2557 }
2558 while k < toks.len() {
2559 match &toks[k].kind {
2560 Tok::Semi => in_locals = true,
2561 Tok::Name(n) if in_locals => locals.push(n.clone()),
2562 Tok::Name(n) => names.push(n.clone()),
2563 _ => {
2564 return Err(Error::parse("this is not a ∇ definition header", toks[k].span));
2565 }
2566 }
2567 k += 1;
2568 }
2569 match names.len() {
2570 3 => Ok((names[1].clone(), Some(names[0].clone()), names[2].clone(), result, locals)),
2571 2 => Ok((names[0].clone(), None, names[1].clone(), result, locals)),
2572 1 => Ok((names[0].clone(), None, crate::ir::NILADIC.to_string(), result, locals)),
2573 _ => Err(Error::parse("a ∇ definition header names a function and its arguments", span)),
2574 }
2575}
2576
2577enum AplItem {
2580 Sentence(Expr),
2581 Word { word: &'static str, rest: Vec<Token>, span: Span },
2582}
2583
2584impl AplItem {
2585 fn word(&self) -> Option<&'static str> {
2586 match self {
2587 AplItem::Word { word, .. } => Some(word),
2588 AplItem::Sentence(_) => None,
2589 }
2590 }
2591
2592 fn span(&self) -> Span {
2593 match self {
2594 AplItem::Word { span, .. } => *span,
2595 AplItem::Sentence(e) => e.span(),
2596 }
2597 }
2598}
2599
2600fn to_item(
2601 line: Vec<Token>,
2602 d: Rules,
2603 verbs: &mut HashMap<String, Verb>,
2604 label: &mut Option<String>,
2605) -> Result<AplItem> {
2606 let mut line = line;
2607 if let (Some(Tok::Name(n)), Some(Tok::Colon)) =
2610 (line.first().map(|t| &t.kind), line.get(1).map(|t| &t.kind))
2611 {
2612 *label = Some(n.clone());
2613 line.drain(..2);
2614 }
2615 if let Some(Tok::Control(word)) = line.first().map(|t| &t.kind) {
2616 let word = *word;
2617 let span = line[0].span;
2618 return Ok(AplItem::Word { word, rest: line[1..].to_vec(), span });
2619 }
2620 if matches!(line.first().map(|t| &t.kind), Some(Tok::Arrow)) {
2621 let span = line[0].span;
2622 let target = parse_statement(line[1..].to_vec(), d, verbs, true)?
2623 .ok_or_else(|| Error::parse("→ needs a line to branch to", span))?;
2624 let span = Span::merge(span, target.span());
2625 return Ok(AplItem::Sentence(Expr::Control(
2626 Box::new(Control::Branch(Box::new(target))),
2627 span,
2628 )));
2629 }
2630 if line.is_empty() {
2634 let span = label.as_ref().map_or(Span::new(0, 0), |_| Span::new(0, 0));
2635 let nowhere = Expr::Const(Array::empty(crate::dtype::DType::I64), span);
2636 return Ok(AplItem::Sentence(Expr::Control(
2637 Box::new(Control::Branch(Box::new(nowhere))),
2638 span,
2639 )));
2640 }
2641 let span = line.first().map_or(Span::new(0, 0), |t| t.span);
2642 let e = parse_statement(line, d, verbs, true)?
2643 .ok_or_else(|| Error::parse("this line has no sentence", span))?;
2644 Ok(AplItem::Sentence(e))
2645}
2646
2647struct AplCursor<'a> {
2648 items: &'a [AplItem],
2649 at: usize,
2650 d: Rules,
2652}
2653
2654impl<'a> AplCursor<'a> {
2655 fn peek(&self) -> Option<&'a AplItem> {
2656 self.items.get(self.at)
2657 }
2658
2659 fn peek_word(&self) -> Option<&'static str> {
2660 self.peek().and_then(AplItem::word)
2661 }
2662
2663 fn last_span(&self) -> Span {
2664 self.items
2665 .get(self.at.saturating_sub(1))
2666 .map_or_else(|| Span::new(0, 0), AplItem::span)
2667 }
2668
2669 fn close(&mut self, want: &str) -> Result<()> {
2671 match self.peek_word() {
2672 Some(w) if w == want || w == "End" => {
2673 self.at += 1;
2674 Ok(())
2675 }
2676 Some(w) => Err(Error::parse(
2677 format!("expected :{want} here, not :{w}"),
2678 self.peek().expect("a word").span(),
2679 )),
2680 None => Err(Error::parse(format!("this block needs a :{want}"), self.last_span())),
2681 }
2682 }
2683}
2684
2685fn parse_apl_block(cur: &mut AplCursor<'_>, stop: &[&str]) -> Result<Vec<Expr>> {
2686 let mut out = Vec::new();
2687 loop {
2688 match cur.peek() {
2689 None => return Ok(out),
2690 Some(AplItem::Word { word, .. }) if stop.contains(word) || *word == "End" => {
2691 return Ok(out);
2692 }
2693 Some(AplItem::Sentence(e)) => {
2694 cur.at += 1;
2695 out.push(e.clone());
2696 }
2697 Some(AplItem::Word { .. }) => out.push(parse_apl_control(cur)?),
2698 }
2699 }
2700}
2701
2702fn parse_apl_control(cur: &mut AplCursor<'_>) -> Result<Expr> {
2703 let Some(AplItem::Word { word, rest, span }) = cur.peek() else {
2704 return Err(Error::internal("expected a control word"));
2705 };
2706 let (word, rest, start) = (*word, rest.clone(), *span);
2707 cur.at += 1;
2708 let control = match word {
2709 "If" => {
2710 let mut arms = Vec::new();
2711 let mut otherwise = None;
2712 let mut test = rest;
2713 loop {
2714 let test_expr = condition(test, start, cur.d)?;
2715 let body = parse_apl_block(cur, &["ElseIf", "Else", "EndIf"])?;
2716 arms.push(Branch { test: Some(vec![test_expr]), body, fall_through: false });
2717 match cur.peek_word() {
2718 Some("ElseIf") => {
2719 let Some(AplItem::Word { rest, .. }) = cur.peek() else { unreachable!() };
2720 test = rest.clone();
2721 cur.at += 1;
2722 }
2723 Some("Else") => {
2724 cur.at += 1;
2725 otherwise = Some(parse_apl_block(cur, &["EndIf"])?);
2726 cur.close("EndIf")?;
2727 break;
2728 }
2729 _ => {
2730 cur.close("EndIf")?;
2731 break;
2732 }
2733 }
2734 }
2735 Control::If { arms, otherwise }
2736 }
2737 "While" => {
2738 let test = condition(rest, start, cur.d)?;
2739 let body = parse_apl_block(cur, &["EndWhile"])?;
2740 cur.close("EndWhile")?;
2741 Control::While { test: vec![test], body, body_first: false, until: false }
2742 }
2743 "Repeat" => {
2744 if !rest.is_empty() {
2745 return Err(Error::parse(":Repeat takes no condition", start));
2746 }
2747 let body = parse_apl_block(cur, &["Until"])?;
2748 let Some(AplItem::Word { rest, span, .. }) = cur.peek() else {
2749 return Err(Error::parse("this :Repeat needs an :Until", cur.last_span()));
2750 };
2751 let test = condition(rest.clone(), *span, cur.d)?;
2752 cur.at += 1;
2753 Control::While { test: vec![test], body, body_first: true, until: true }
2754 }
2755 "For" => {
2756 let (name, source) = for_header(&rest, start, cur.d)?;
2758 let body = parse_apl_block(cur, &["EndFor"])?;
2759 cur.close("EndFor")?;
2760 Control::For { name: Some(name), source: Box::new(source), body }
2761 }
2762 "Select" => {
2763 let subject = condition(rest, start, cur.d)?;
2764 let mut cases = Vec::new();
2765 loop {
2766 match cur.peek() {
2767 Some(AplItem::Word { word: "Case", rest, span }) => {
2768 let test = condition(rest.clone(), *span, cur.d)?;
2769 cur.at += 1;
2770 let body = parse_apl_block(cur, &["Case", "Else", "EndSelect"])?;
2771 cases.push(Branch {
2772 test: Some(vec![test]),
2773 body,
2774 fall_through: false,
2775 });
2776 }
2777 Some(AplItem::Word { word: "Else", .. }) => {
2778 cur.at += 1;
2779 let body = parse_apl_block(cur, &["EndSelect"])?;
2780 cases.push(Branch { test: None, body, fall_through: false });
2781 cur.close("EndSelect")?;
2782 break;
2783 }
2784 _ => {
2785 cur.close("EndSelect")?;
2786 break;
2787 }
2788 }
2789 }
2790 Control::Select { subject: Box::new(subject), cases }
2791 }
2792 "Return" => Control::Return,
2793 "Leave" => Control::Break,
2794 "Continue" => Control::Continue,
2795 other => {
2796 return Err(Error::parse(format!(":{other} has no matching opening word"), start));
2797 }
2798 };
2799 Ok(Expr::Control(Box::new(control), Span::merge(start, cur.last_span())))
2800}
2801
2802fn condition(rest: Vec<Token>, span: Span, d: Rules) -> Result<Expr> {
2804 match rest.first() {
2805 None => Err(Error::parse("this control word needs a condition", span)),
2806 Some(first) => {
2807 let hint = Span::merge(first.span, rest[rest.len() - 1].span);
2808 match &rest[0].kind {
2809 Tok::Control(w) => Err(Error::parse(format!("unexpected :{w}"), rest[0].span)),
2810 _ => Ok(AplItem::Sentence(parse_prepared(&rest, hint, d)?)).map(|it| match it {
2811 AplItem::Sentence(e) => e,
2812 AplItem::Word { .. } => unreachable!(),
2813 }),
2814 }
2815 }
2816 }
2817}
2818
2819fn for_header(rest: &[Token], span: Span, d: Rules) -> Result<(String, Expr)> {
2821 let Some(Tok::Name(name)) = rest.first().map(|t| &t.kind) else {
2822 return Err(Error::parse(":For needs a name to bind", span));
2823 };
2824 let Some(k) = rest.iter().position(|t| matches!(t.kind, Tok::Control("In"))) else {
2825 return Err(Error::parse(":For needs an :In", span));
2826 };
2827 if k != 1 {
2828 return Err(Error::not_yet("several :For names", span));
2829 }
2830 let source = &rest[k + 1..];
2831 let Some(first) = source.first() else {
2832 return Err(Error::parse(":In needs a value", span));
2833 };
2834 let hint = Span::merge(first.span, source[source.len() - 1].span);
2835 Ok((name.clone(), parse_prepared(source, hint, d)?))
2836}
2837
2838fn parse_prepared(toks: &[Token], hint: Span, d: Rules) -> Result<Expr> {
2840 let toks = fold_axes(fold_operators(toks.to_vec(), d)?, d)?;
2841 if toks.is_empty() {
2842 return Err(Error::parse("this needs an expression", hint));
2843 }
2844 parse_range(&toks, 0, toks.len(), hint, d)
2845}
2846
2847fn indexed_assignment(toks: &[Token], d: Rules, hint: Span) -> Result<Option<Expr>> {
2850 let Some(assign) = toks.iter().position(|t| matches!(t.kind, Tok::Assign)) else {
2851 return Ok(None);
2852 };
2853 if assign < 3 || !matches!(toks[assign - 1].kind, Tok::RBracket) {
2854 return Ok(None);
2855 }
2856 let close = assign - 1;
2857 let open = match_lbracket(toks, 0, close)?;
2858 if open == 0 {
2859 return Err(Error::parse("[ needs a value on its left", toks[open].span));
2860 }
2861 let Tok::Name(name) = &toks[open - 1].kind else {
2862 return Err(Error::not_yet("indexed assignment through an expression", hint));
2863 };
2864 if open != 1 {
2865 return Err(Error::not_yet("indexed assignment inside a larger sentence", hint));
2866 }
2867 let ranges = index_slots(toks, open + 1, close, toks[open].span)?;
2868 let mut slots = Vec::with_capacity(ranges.len());
2869 for slot in &ranges {
2870 slots.push(match *slot {
2871 None => None,
2872 Some((lo, hi)) => Some(parse_range(toks, lo, hi, toks[open].span, d)?),
2873 });
2874 }
2875 let value = parse_range(toks, assign + 1, toks.len(), toks[assign].span, d)?;
2876 let span = Span::merge(toks[0].span, toks[toks.len() - 1].span);
2877 Ok(Some(Expr::AmendIndex {
2878 name: name.clone(),
2879 slots,
2880 value: Box::new(value),
2881 origin: d.origin,
2882 scope: Scope::Local,
2883 span,
2884 }))
2885}
2886
2887fn set_scopes(e: &mut Expr, own: &[String]) {
2891 let pick = |name: &str| {
2892 if own.iter().any(|n| n == name) {
2893 Scope::Local
2894 } else {
2895 Scope::Global
2896 }
2897 };
2898 match e {
2899 Expr::Assign { name, value, scope, .. } => {
2900 *scope = pick(name);
2901 set_scopes(value, own);
2902 }
2903 Expr::AmendIndex { name, slots, value, scope, .. } => {
2904 *scope = pick(name);
2905 for slot in slots.iter_mut().flatten() {
2906 set_scopes(slot, own);
2907 }
2908 set_scopes(value, own);
2909 }
2910 Expr::Monad { y, .. } => set_scopes(y, own),
2911 Expr::Dyad { x, y, .. } => {
2912 set_scopes(x, own);
2913 set_scopes(y, own);
2914 }
2915 Expr::PrintPass { value, .. } => set_scopes(value, own),
2916 Expr::Input { .. } => {}
2917 Expr::Control(c, _) => {
2918 let walk = |b: &mut Vec<Expr>| b.iter_mut().for_each(|s| set_scopes(s, own));
2919 match &mut **c {
2920 Control::Branch(target) => set_scopes(target, own),
2921 Control::If { arms, otherwise } => {
2922 for arm in arms {
2923 if let Some(t) = &mut arm.test {
2924 walk(t);
2925 }
2926 walk(&mut arm.body);
2927 }
2928 if let Some(b) = otherwise {
2929 walk(b);
2930 }
2931 }
2932 Control::While { test, body, .. } => {
2933 walk(test);
2934 walk(body);
2935 }
2936 Control::For { source, body, .. } => {
2937 set_scopes(source, own);
2938 walk(body);
2939 }
2940 Control::Select { subject, cases } => {
2941 set_scopes(subject, own);
2942 for case in cases {
2943 if let Some(t) = &mut case.test {
2944 walk(t);
2945 }
2946 walk(&mut case.body);
2947 }
2948 }
2949 Control::Try { body, catch } => {
2950 walk(body);
2951 walk(catch);
2952 }
2953 Control::Return | Control::Break | Control::Continue => {}
2954 }
2955 }
2956 Expr::Const(..)
2957 | Expr::Param(..)
2958 | Expr::Name(..)
2959 | Expr::Fused { .. }
2960 | Expr::Elided { .. }
2961 | Expr::VerbDef { .. }
2962 | Expr::ModDef { .. } => {}
2963 }
2964}
2965
2966#[cfg(test)]
2967mod tests {
2968 use super::*;
2969 use crate::error::ErrorKind;
2970 use rstest::rstest;
2971
2972 fn rules(origin: i64) -> Rules {
2974 crate::Dialect { index_origin: Some(origin), ..crate::Dialect::default() }
2975 .rules(crate::Lang::Apl)
2976 .expect("the shipped dialect is implemented")
2977 }
2978
2979 fn p(src: &str) -> Result<Vec<Expr>> {
2981 parse(&SourceParts::from_source(src).unwrap(), rules(1))
2982 }
2983
2984 fn one(src: &str) -> Expr {
2985 let mut stmts = p(src).unwrap_or_else(|e| panic!("{src}: {e}"));
2986 assert_eq!(stmts.len(), 1, "{src}: expected one sentence");
2987 stmts.pop().unwrap()
2988 }
2989
2990 fn err(src: &str) -> Error {
2991 match p(src) {
2992 Ok(_) => panic!("{src}: expected an error"),
2993 Err(e) => e,
2994 }
2995 }
2996
2997 fn as_const(e: &Expr) -> &Array {
2998 match e {
2999 Expr::Const(a, _) => a,
3000 other => panic!("expected a constant, got {other:?}"),
3001 }
3002 }
3003
3004 fn as_prim(v: &Verb) -> Prim {
3006 match v {
3007 Verb::Prim(p) => *p,
3008 other => panic!("expected a primitive, got {other:?}"),
3009 }
3010 }
3011
3012 fn monad_of<'a>(e: &'a Expr, name: &str) -> &'a Expr {
3013 match e {
3014 Expr::Monad { verb, y, .. } => {
3015 assert_eq!(as_prim(verb).name, name, "monad name");
3016 y.as_ref()
3017 }
3018 other => panic!("expected a monad, got {other:?}"),
3019 }
3020 }
3021
3022 fn dyad_of<'a>(e: &'a Expr, name: &str) -> (&'a Expr, &'a Expr) {
3023 match e {
3024 Expr::Dyad { verb, x, y, .. } => {
3025 assert_eq!(as_prim(verb).name, name, "dyad name");
3026 (x.as_ref(), y.as_ref())
3027 }
3028 other => panic!("expected a dyad, got {other:?}"),
3029 }
3030 }
3031
3032 fn verb_of(e: &Expr) -> &Verb {
3033 match e {
3034 Expr::Monad { verb, .. } | Expr::Dyad { verb, .. } => verb,
3035 other => panic!("expected an application, got {other:?}"),
3036 }
3037 }
3038
3039 #[test]
3042 fn single_number_is_a_scalar() {
3043 let e = one("5");
3044 let a = as_const(&e);
3045 assert_eq!(a.shape, Vec::<usize>::new());
3046 assert_eq!(a.data, Data::I64(vec![5].into()));
3047 }
3048
3049 #[test]
3050 fn adjacent_numbers_merge_into_one_vector() {
3051 let a = as_const(&one("2 3 4")).clone();
3052 assert_eq!(a.shape, vec![3]);
3053 assert_eq!(a.data, Data::I64(vec![2, 3, 4].into()));
3054 }
3055
3056 #[test]
3057 fn one_float_makes_the_whole_vector_float() {
3058 let a = as_const(&one("1 2.5 3")).clone();
3059 assert_eq!(a.shape, vec![3]);
3060 assert_eq!(a.data, Data::F64(vec![1.0, 2.5, 3.0].into()));
3061 }
3062
3063 #[rstest]
3064 #[case("¯3", Data::I64(vec![-3].into()))]
3065 #[case("¯3.5", Data::F64(vec![-3.5].into()))]
3066 #[case("1e3", Data::I64(vec![1000].into()))]
3067 #[case("1e¯3", Data::F64(vec![0.001].into()))]
3068 #[case("2.5e2", Data::F64(vec![250.0].into()))]
3069 #[case("¯1 ¯2", Data::I64(vec![-1, -2].into()))]
3070 fn numeric_literals(#[case] src: &str, #[case] want: Data) {
3071 assert_eq!(as_const(&one(src)).data, want);
3072 }
3073
3074 #[test]
3075 fn single_char_string_is_rank_zero() {
3076 let a = as_const(&one("'a'")).clone();
3077 assert_eq!(a.shape, Vec::<usize>::new());
3078 assert_eq!(a.data, Data::Char(vec!['a'].into()));
3079 }
3080
3081 #[test]
3082 fn string_escape_doubles_the_quote() {
3083 let a = as_const(&one("'don''t'")).clone();
3084 assert_eq!(a.shape, vec![5]);
3085 assert_eq!(a.data, Data::Char("don't".chars().collect()));
3086 }
3087
3088 #[test]
3089 fn empty_string_is_an_empty_char_vector() {
3090 let a = as_const(&one("''")).clone();
3091 assert_eq!(a.shape, vec![0]);
3092 assert_eq!(a.data, Data::Char(vec![].into()));
3093 }
3094
3095 #[test]
3096 fn unterminated_string_is_a_parse_error() {
3097 let e = err("'abc");
3098 assert_eq!(e.kind, ErrorKind::Parse);
3099 assert!(e.msg.contains("unterminated"), "{}", e.msg);
3100 }
3101
3102 #[rstest]
3103 #[case("2j3", vec![[2.0, 3.0]])]
3104 #[case("1J¯1", vec![[1.0, -1.0]])]
3105 #[case("2 1j2", vec![[2.0, 0.0], [1.0, 2.0]])]
3106 fn complex_literals(#[case] src: &str, #[case] want: Vec<[f64; 2]>) {
3107 assert_eq!(as_const(&one(src)).data, Data::Complex(want.into()));
3108 }
3109
3110 #[test]
3113 fn a_comment_runs_to_the_end_of_the_line() {
3114 let stmts = p("2+2 ⍝ a note ⋄ still a note\n3").unwrap();
3115 assert_eq!(stmts.len(), 2);
3116 dyad_of(&stmts[0], "+");
3117 assert_eq!(as_const(&stmts[1]).data, Data::I64(vec![3].into()));
3118 }
3119
3120 #[test]
3121 fn blank_sentences_are_skipped() {
3122 let stmts = p("\n\n2 ⋄ ⋄ 3 ⋄\n").unwrap();
3123 assert_eq!(stmts.len(), 2);
3124 }
3125
3126 #[test]
3127 fn diamond_and_newline_both_separate_sentences() {
3128 let stmts = p("x←3 ⋄ x+1").unwrap();
3129 assert_eq!(stmts.len(), 2);
3130 match &stmts[0] {
3131 Expr::Assign { name, value, .. } => {
3132 assert_eq!(name, "x");
3133 assert_eq!(as_const(value).data, Data::I64(vec![3].into()));
3134 }
3135 other => panic!("expected an assignment, got {other:?}"),
3136 }
3137 let (x, y) = dyad_of(&stmts[1], "+");
3138 assert!(matches!(x, Expr::Name(n, _) if n == "x"));
3139 assert_eq!(as_const(y).data, Data::I64(vec![1].into()));
3140 }
3141
3142 #[rstest]
3143 #[case("x")]
3144 #[case("abc123")]
3145 #[case("∆x")]
3146 #[case("⍙y_2")]
3147 #[case("Σ")]
3148 fn names(#[case] src: &str) {
3149 match one(src) {
3150 Expr::Name(n, _) => assert_eq!(n, src),
3151 other => panic!("expected a name, got {other:?}"),
3152 }
3153 }
3154
3155 #[test]
3156 fn unknown_symbol_is_reported_with_its_position() {
3157 let e = err("2 @ 3");
3158 assert_eq!(e.kind, ErrorKind::Parse);
3159 assert_eq!(e.msg, "unknown symbol: @");
3160 assert_eq!(e.span, Some(Span::new(2, 3)));
3161 }
3162
3163 #[test]
3164 fn system_variables_are_read_only() {
3165 let e = err("⎕IO←0");
3168 assert_eq!(e.kind, ErrorKind::Language);
3169 assert!(e.msg.contains("read-only"), "{}", e.msg);
3170 let e = err("⎕TS");
3173 assert_eq!(e.kind, ErrorKind::Sandbox);
3174 assert!(e.msg.contains("outside the program"), "{}", e.msg);
3175 }
3176
3177 #[rstest]
3180 #[case('+', MonadOp::Scalar(ScalarMonad::Conj), DyadOp::Scalar(ScalarDyad::Add))]
3181 #[case('-', MonadOp::Scalar(ScalarMonad::Neg), DyadOp::Scalar(ScalarDyad::Sub))]
3182 #[case('×', MonadOp::Scalar(ScalarMonad::Signum), DyadOp::Scalar(ScalarDyad::Mul))]
3183 #[case('÷', MonadOp::Scalar(ScalarMonad::Recip), DyadOp::Scalar(ScalarDyad::DivApl))]
3184 #[case('⌈', MonadOp::Scalar(ScalarMonad::Ceil), DyadOp::Scalar(ScalarDyad::Max))]
3185 #[case('⌊', MonadOp::Scalar(ScalarMonad::Floor), DyadOp::Scalar(ScalarDyad::Min))]
3186 #[case('*', MonadOp::Scalar(ScalarMonad::Exp), DyadOp::Scalar(ScalarDyad::Pow))]
3187 #[case('|', MonadOp::Scalar(ScalarMonad::Abs), DyadOp::Scalar(ScalarDyad::Residue))]
3188 #[case('=', MonadOp::None, DyadOp::Scalar(ScalarDyad::Eq))]
3189 #[case('<', MonadOp::None, DyadOp::Scalar(ScalarDyad::Lt))]
3190 #[case('≤', MonadOp::None, DyadOp::Scalar(ScalarDyad::Le))]
3191 #[case('>', MonadOp::None, DyadOp::Scalar(ScalarDyad::Gt))]
3192 #[case('≥', MonadOp::None, DyadOp::Scalar(ScalarDyad::Ge))]
3193 #[case('⍴', MonadOp::ShapeOf, DyadOp::Reshape)]
3194 #[case('⍉', MonadOp::TransposeAxes, DyadOp::TransposeApl)]
3195 #[case(',', MonadOp::Ravel, DyadOp::AppendLast)]
3196 #[case('⍪', MonadOp::TableOf, DyadOp::AppendLeading)]
3197 #[case('!', MonadOp::Scalar(ScalarMonad::Factorial), DyadOp::Scalar(ScalarDyad::Binomial))]
3198 #[case('⍕', MonadOp::Format, DyadOp::FormatSpec)]
3199 #[case('⊥', MonadOp::None, DyadOp::DecodeApl)]
3200 #[case('⊤', MonadOp::None, DyadOp::EncodeApl)]
3201 #[case('≢', MonadOp::Tally, DyadOp::NotMatch)]
3202 #[case('≡', MonadOp::Depth, DyadOp::Match)]
3203 #[case('∊', MonadOp::Enlist, DyadOp::MemberApl)]
3204 #[case('∪', MonadOp::Nub, DyadOp::Union)]
3205 #[case('∧', MonadOp::None, DyadOp::Scalar(ScalarDyad::Lcm))]
3206 #[case('∨', MonadOp::None, DyadOp::Scalar(ScalarDyad::Gcd))]
3207 #[case('⍟', MonadOp::Scalar(ScalarMonad::Ln), DyadOp::Scalar(ScalarDyad::Log))]
3208 #[case('~', MonadOp::Scalar(ScalarMonad::Not), DyadOp::Less)]
3209 #[case('⊖', MonadOp::Reverse, DyadOp::Rotate)]
3210 #[case('⍋', MonadOp::GradeUp { origin: 1 }, DyadOp::CollateGrade { down: false, origin: 1 })]
3211 #[case('⍒', MonadOp::GradeDown { origin: 1 }, DyadOp::CollateGrade { down: true, origin: 1 })]
3212 #[case('⊢', MonadOp::Same, DyadOp::Right)]
3213 #[case('⊣', MonadOp::Same, DyadOp::Left)]
3214 #[case('↑', MonadOp::First, DyadOp::Take)]
3215 #[case('⊂', MonadOp::Enclose(Enclose::ExceptSimpleScalar), DyadOp::PartitionEnclose)]
3216 #[case('⊃', MonadOp::Open, DyadOp::Pick { origin: 1 })]
3217 #[case('↓', MonadOp::Split, DyadOp::Drop)]
3218 fn primitive_meanings(#[case] glyph: char, #[case] monad: MonadOp, #[case] dyad: DyadOp) {
3219 let src = format!("{glyph}1");
3220 let e = one(&src);
3221 match e {
3222 Expr::Monad { verb, .. } => {
3223 let prim = as_prim(&verb);
3224 assert_eq!(prim.monad, monad);
3225 assert_eq!(prim.dyad, dyad);
3226 assert_eq!(prim.name.chars().next(), Some(glyph));
3227 }
3228 other => panic!("expected a monad, got {other:?}"),
3229 }
3230 }
3231
3232 #[test]
3233 fn monadic_not_equal_is_the_nub_sieve() {
3234 let e = one("≠1");
3235 match e {
3236 Expr::Monad { verb, .. } => {
3237 assert_eq!(as_prim(&verb).monad, MonadOp::NubSieve);
3238 }
3239 other => panic!("expected a monad, got {other:?}"),
3240 }
3241 }
3242
3243 #[test]
3244 fn monadic_equals_parses_and_is_left_to_evaluation() {
3245 let e = one("=1");
3247 assert_eq!(as_prim(verb_of(&e)).monad, MonadOp::None);
3248 }
3249
3250 #[rstest]
3251 #[case(0)]
3252 #[case(1)]
3253 fn iota_carries_the_index_origin(#[case] origin: i64) {
3254 let sp = SourceParts::from_source("⍳3").unwrap();
3255 let stmts = parse(&sp, rules(origin)).unwrap();
3256 match &stmts[0] {
3257 Expr::Monad { verb, .. } => {
3258 assert_eq!(as_prim(verb).monad, MonadOp::IotaApl { origin });
3259 assert_eq!(as_prim(verb).dyad, DyadOp::IndexOf { origin });
3260 assert_eq!(as_prim(verb).ranks, [RANK_INF, RANK_INF, RANK_INF]);
3261 }
3262 other => panic!("expected a monad, got {other:?}"),
3263 }
3264 }
3265
3266 #[test]
3267 fn reverse_and_rotate_pick_their_axis() {
3268 let e = one("⌽2 3⍴⍳6");
3270 match verb_of(&e) {
3271 Verb::Rank(f, ranks) => {
3272 assert_eq!(*ranks, [1, 0, 1]);
3273 assert_eq!(as_prim(f).monad, MonadOp::Reverse);
3274 assert_eq!(as_prim(f).dyad, DyadOp::Rotate);
3275 }
3276 other => panic!("expected a ranked verb, got {other:?}"),
3277 }
3278 assert!(matches!(verb_of(&one("⊖2 3⍴⍳6")), Verb::Prim(_)));
3280 }
3281
3282 #[test]
3283 fn reshape_ranks_are_infinite_one_infinite() {
3284 let e = one("2 3⍴⍳6");
3285 assert_eq!(verb_of(&e).ranks(), [RANK_INF, 1, RANK_INF]);
3286 }
3287
3288 #[test]
3291 fn reshape_of_iota() {
3292 let e = one("2 3⍴⍳6");
3293 let (x, y) = dyad_of(&e, "⍴");
3294 assert_eq!(as_const(x).data, Data::I64(vec![2, 3].into()));
3295 let iy = monad_of(y, "⍳");
3296 assert_eq!(as_const(iy).data, Data::I64(vec![6].into()));
3297 }
3298
3299 #[test]
3300 fn leading_minus_is_monadic_and_the_rest_is_evaluated_first() {
3301 let e = one("-3+4");
3303 let inner = monad_of(&e, "-");
3304 let (x, y) = dyad_of(inner, "+");
3305 assert_eq!(as_const(x).data, Data::I64(vec![3].into()));
3306 assert_eq!(as_const(y).data, Data::I64(vec![4].into()));
3307 }
3308
3309 #[test]
3310 fn a_chain_of_dyads_associates_to_the_right() {
3311 let e = one("2×3+4");
3312 let (x, y) = dyad_of(&e, "×");
3313 assert_eq!(as_const(x).data, Data::I64(vec![2].into()));
3314 dyad_of(y, "+");
3315 }
3316
3317 #[test]
3318 fn parentheses_override_the_order() {
3319 let e = one("(2+3)×4");
3320 let (x, y) = dyad_of(&e, "×");
3321 dyad_of(x, "+");
3322 assert_eq!(as_const(y).data, Data::I64(vec![4].into()));
3323 }
3324
3325 #[test]
3326 fn nested_parentheses() {
3327 let e = one("((2+3))×4");
3328 let (x, _) = dyad_of(&e, "×");
3329 dyad_of(x, "+");
3330 }
3331
3332 #[test]
3333 fn a_function_left_of_a_function_is_monadic() {
3334 let e = one("⍴⍳5");
3336 monad_of(monad_of(&e, "⍴"), "⍳");
3337 }
3338
3339 #[test]
3342 fn slash_reduces_the_last_axis() {
3343 let e = one("+/2 3⍴⍳6");
3345 match &e {
3346 Expr::Monad { verb: Verb::Rank(inner, ranks), .. } => {
3347 assert_eq!(*ranks, [1, 1, 1]);
3348 match inner.as_ref() {
3349 Verb::Reduce(f) => assert_eq!(as_prim(f).name, "+"),
3350 other => panic!("expected a reduce, got {other:?}"),
3351 }
3352 }
3353 other => panic!("expected monadic Rank(Reduce(+)), got {other:?}"),
3354 }
3355 }
3356
3357 #[test]
3358 fn slashbar_reduces_the_leading_axis() {
3359 let e = one("+⌿2 3⍴⍳6");
3360 match &e {
3361 Expr::Monad { verb: Verb::Reduce(f), .. } => assert_eq!(as_prim(f).name, "+"),
3362 other => panic!("expected monadic Reduce(+), got {other:?}"),
3363 }
3364 }
3365
3366 #[test]
3367 fn backslash_scans_the_last_axis_and_backslashbar_the_leading_one() {
3368 let inner = |v: &Verb| match v {
3371 Verb::Windowed(g, WindowKind::Scan) => match &**g {
3372 Verb::Reduce(h) => as_prim(h).name,
3373 other => panic!("expected a reduction under the scan, got {other:?}"),
3374 },
3375 other => panic!("expected a scan, got {other:?}"),
3376 };
3377 match &one("+\\1 2 3") {
3378 Expr::Monad { verb: Verb::Rank(f, ranks), .. } => {
3379 assert_eq!(*ranks, [1, 1, 1]);
3380 assert_eq!(inner(f), "+");
3381 }
3382 other => panic!("expected a ranked scan, got {other:?}"),
3383 }
3384 match &one("+⍀1 2 3") {
3385 Expr::Monad { verb, .. } => assert_eq!(inner(verb), "+"),
3386 other => panic!("expected a leading-axis scan, got {other:?}"),
3387 }
3388 }
3389
3390 #[rstest]
3393 #[case("1 0 1/1 2 3", "/")]
3394 #[case("1 0 1⌿1 2 3", "⌿")]
3395 #[case("x/1 2 3", "/")]
3396 #[case("(1 0)/1 2 3", "/")]
3397 fn slash_after_an_operand_is_replicate(#[case] src: &str, #[case] name: &str) {
3398 let e = one(src);
3399 let (_, _) = dyad_of(&e, name);
3400 assert_eq!(as_prim(verb_of(&e)).dyad, DyadOp::Copy);
3401 }
3402
3403 #[rstest]
3404 #[case("1 0 1\\1 2 3")]
3405 #[case("1 0 1⍀1 2 3")]
3406 fn expand_after_a_value_is_a_function(#[case] src: &str) {
3407 let e = one(src);
3408 assert_eq!(as_prim(verb_of(&e)).dyad, DyadOp::Expand);
3409 }
3410
3411 #[test]
3412 fn commute_and_power_are_operators() {
3413 match one("2-⍨5") {
3414 Expr::Dyad { verb: Verb::Commute(f), .. } => assert_eq!(as_prim(&f).name, "-"),
3415 other => panic!("expected a commute, got {other:?}"),
3416 }
3417 match one("+⍣3⊢5") {
3418 Expr::Monad { verb: Verb::PowerN(_, p), .. } => assert_eq!(p, Power::Times(3)),
3419 other => panic!("expected a power, got {other:?}"),
3420 }
3421 match one("+⍣≡⊢5") {
3422 Expr::Monad { verb: Verb::PowerUntil(..), .. } => {}
3423 other => panic!("expected a power until, got {other:?}"),
3424 }
3425 let e = err("+⍣¯1⊢5");
3426 assert_eq!(e.kind, ErrorKind::NotYet);
3427 assert!(e.msg.contains("inverse power"), "{}", e.msg);
3428 }
3429
3430 #[rstest]
3431 #[case("+⍤2⊢5", [2, 2, 2])]
3432 #[case("+⍤1 2⊢5", [2, 1, 2])]
3433 #[case("+⍤0 1 2⊢5", [0, 1, 2])]
3434 #[case("+⍤¯1⊢5", [-1, -1, -1])]
3435 fn rank_operator_spec(#[case] src: &str, #[case] want: [i64; 3]) {
3436 let e = one(src);
3437 match &e {
3438 Expr::Monad { verb: Verb::Rank(f, ranks), .. } => {
3439 assert_eq!(*ranks, want);
3440 assert_eq!(as_prim(f).name, "+");
3441 }
3442 other => panic!("expected monadic Rank(+), got {other:?}"),
3443 }
3444 }
3445
3446 #[test]
3447 fn rank_operator_stacks_on_a_derived_function() {
3448 let e = one("+/⍤1⊢5");
3449 match &e {
3450 Expr::Monad { verb: Verb::Rank(inner, ranks), .. } => {
3451 assert_eq!(*ranks, [1, 1, 1]);
3452 assert!(matches!(inner.as_ref(), Verb::Rank(_, [1, 1, 1])));
3453 }
3454 other => panic!("expected Rank(Rank(Reduce(+))), got {other:?}"),
3455 }
3456 }
3457
3458 #[test]
3459 fn a_function_operand_makes_the_rank_operator_an_atop() {
3460 let e = one("+⍤×5");
3462 let Expr::Monad { verb, .. } = e else { panic!("expected a monad") };
3463 assert!(matches!(verb, Verb::Atop(..)), "{verb:?}");
3464 }
3465
3466 #[rstest]
3467 #[case("+⍤0 1 2 3⊢5", "1 to 3")]
3468 #[case("+⍤", "rank specification")]
3469 #[case("+⍤2.5⊢5", "must be integers")]
3470 #[case("+⍤'a'⊢5", "must be integers")]
3471 fn bad_rank_specifications(#[case] src: &str, #[case] fragment: &str) {
3472 let e = err(src);
3473 assert_eq!(e.kind, ErrorKind::Parse);
3474 assert!(e.msg.contains(fragment), "{}", e.msg);
3475 }
3476
3477 #[test]
3480 fn quad_arrow_is_print_pass() {
3481 let e = one("⎕←2+2");
3482 match &e {
3483 Expr::PrintPass { value, .. } => {
3484 dyad_of(value, "+");
3485 }
3486 other => panic!("expected PrintPass, got {other:?}"),
3487 }
3488 }
3489
3490 #[test]
3491 fn assignment_chains() {
3492 let e = one("a←b←5");
3493 match &e {
3494 Expr::Assign { name, value, .. } => {
3495 assert_eq!(name, "a");
3496 match value.as_ref() {
3497 Expr::Assign { name, value, .. } => {
3498 assert_eq!(name, "b");
3499 assert_eq!(as_const(value).data, Data::I64(vec![5].into()));
3500 }
3501 other => panic!("expected a nested assignment, got {other:?}"),
3502 }
3503 }
3504 other => panic!("expected an assignment, got {other:?}"),
3505 }
3506 }
3507
3508 #[test]
3509 fn assignment_inside_an_expression() {
3510 let e = one("2+a←3");
3511 let (x, y) = dyad_of(&e, "+");
3512 assert_eq!(as_const(x).data, Data::I64(vec![2].into()));
3513 match y {
3514 Expr::Assign { name, value, .. } => {
3515 assert_eq!(name, "a");
3516 assert_eq!(as_const(value).data, Data::I64(vec![3].into()));
3517 }
3518 other => panic!("expected an assignment, got {other:?}"),
3519 }
3520 }
3521
3522 #[rstest]
3523 #[case("2←3")]
3524 #[case("(2+2)←3")]
3525 fn assignment_target_must_be_a_name(#[case] src: &str) {
3526 let e = err(src);
3527 assert_eq!(e.kind, ErrorKind::Parse);
3528 assert_eq!(e.msg, "assignment target must be a name");
3529 }
3530
3531 #[test]
3534 fn a_parameter_hole_is_an_operand() {
3535 let sp = SourceParts::from_parts(&["", "+1"], &["x"]);
3536 let stmts = parse(&sp, rules(1)).unwrap();
3537 let (x, y) = dyad_of(&stmts[0], "+");
3538 assert!(matches!(x, Expr::Param(0, _)));
3539 assert_eq!(as_const(y).data, Data::I64(vec![1].into()));
3540 assert_eq!(x.span(), Span::new(0, 3));
3542 assert_eq!(sp.display, "{x}+1");
3543 }
3544
3545 #[test]
3546 fn a_parameter_can_be_reduced_over() {
3547 let sp = SourceParts::from_parts(&["+/", ""], &["m"]);
3548 let stmts = parse(&sp, rules(1)).unwrap();
3549 match &stmts[0] {
3550 Expr::Monad { verb: Verb::Rank(_, [1, 1, 1]), y, .. } => {
3551 assert!(matches!(y.as_ref(), Expr::Param(0, _)));
3552 }
3553 other => panic!("expected a reduction over a parameter, got {other:?}"),
3554 }
3555 }
3556
3557 #[test]
3558 fn a_parameter_inside_a_comment_is_dropped() {
3559 let sp = SourceParts::from_parts(&["1 ⍝ ", "\n2"], &["x"]);
3560 let stmts = parse(&sp, rules(1)).unwrap();
3561 assert_eq!(stmts.len(), 2);
3562 assert_eq!(as_const(&stmts[0]).data, Data::I64(vec![1].into()));
3563 assert_eq!(as_const(&stmts[1]).data, Data::I64(vec![2].into()));
3564 }
3565
3566 #[test]
3569 fn nodes_cover_their_source_extent() {
3570 let src = "2 3⍴⍳6";
3571 let e = one(src);
3572 assert_eq!(e.span(), Span::new(0, src.len()));
3573 let (x, y) = dyad_of(&e, "⍴");
3574 assert_eq!(x.span(), Span::new(0, 3));
3575 assert_eq!(y.span(), Span::new(6, src.len()));
3577 }
3578
3579 #[test]
3580 fn spans_of_a_later_sentence_are_absolute() {
3581 let src = "x←3 ⋄ x+1";
3582 let stmts = p(src).unwrap();
3583 assert_eq!(&src[10..], "x+1");
3585 assert_eq!(stmts[1].span(), Span::new(10, src.len()));
3586 }
3587
3588 #[test]
3589 fn a_dyad_span_includes_the_parenthesised_left_argument() {
3590 let src = "(2+3)×4";
3591 let e = one(src);
3592 assert_eq!(e.span(), Span::new(0, src.len()));
3593 }
3594
3595 #[rstest]
3600 #[case("(2 3)(4 5)", 2)]
3601 #[case("2 x", 2)]
3602 #[case("x y", 2)]
3603 #[case("2(3)", 2)]
3604 #[case("1 2 (3 4)", 3)]
3605 #[case("'ab' 'cd' 'ef'", 3)]
3606 fn juxtaposition_is_vector_notation(#[case] src: &str, #[case] items: usize) {
3607 let mut e = &one(src);
3610 for _ in 0..items - 1 {
3611 match e {
3612 Expr::Dyad { verb, y, .. } => {
3613 assert_eq!(verb.name(), "(vector notation)", "{src}");
3614 e = y.as_ref();
3615 }
3616 other => panic!("{src}: expected a strand, got {other:?}"),
3617 }
3618 }
3619 assert!(matches!(e, Expr::Monad { .. }), "{src}: {e:?}");
3620 }
3621
3622 #[rstest]
3623 #[case("2+", "missing right argument")]
3624 #[case("x←", "← needs a value")]
3625 #[case("(2+3", "syntax error")]
3626 #[case("2+3)", "unmatched )")]
3627 #[case("()", "empty parentheses")]
3628 #[case("/2 3", "needs a function to its left")]
3629 fn syntax_errors(#[case] src: &str, #[case] fragment: &str) {
3630 let e = err(src);
3631 assert_eq!(e.kind, ErrorKind::Parse);
3632 assert!(e.msg.contains(fragment), "{src}: {}", e.msg);
3633 }
3634
3635 #[test]
3636 fn empty_source_has_no_statements() {
3637 assert!(p("").unwrap().is_empty());
3638 assert!(p(" ⍝ nothing here\n").unwrap().is_empty());
3639 }
3640
3641 #[rstest]
3643 #[case("2+2")]
3644 #[case("¯2×3")]
3645 #[case("-3+4")]
3646 #[case("0÷0")]
3647 #[case("⍳4")]
3648 #[case("⍳0")]
3649 #[case("2 3⍴⍳6")]
3650 #[case("⍴2 3⍴⍳6")]
3651 #[case("⍉2 3⍴⍳6")]
3652 #[case("≢7 8 9")]
3653 #[case("2↑9 8 7")]
3654 #[case("¯2↑9 8 7")]
3655 #[case("1↓3 3⍴⍳9")]
3656 #[case(",2 2⍴⍳4")]
3657 #[case("x←3 ⋄ x+1")]
3658 #[case("2+a←3")]
3659 #[case("⎕←2+2")]
3660 #[case("(2 3⍴⍳6)+10 20")]
3661 #[case("2+3 ⍝ sum")]
3662 #[case("+/2 3⍴⍳6")]
3663 #[case("+⌿2 3⍴⍳6")]
3664 #[case("⎕←'Hello, world!'")]
3665 fn the_evaluation_corpus_parses(#[case] src: &str) {
3666 p(src).unwrap_or_else(|e| panic!("{src}: {e}"));
3667 }
3668
3669 #[test]
3670 fn errors_render_against_the_display_source() {
3671 let src = "2 3⍴⍳6\n2 @ 3";
3672 let e = err(src);
3673 let rendered = e.render(src);
3674 assert!(rendered.contains("unknown symbol: @"), "{rendered}");
3675 assert!(rendered.contains("2 @ 3"), "{rendered}");
3676 }
3677}