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