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, DepthSign, DfnResult, FirstDisclose, IndexForm, LookupLeft, NestedModel, Partition,
16 Rules, Segment, SourceParts,
17};
18use crate::ir::{Branch, Control, ExplicitDef, Expr, Scope};
19use crate::verb::{
20 BoolDyad, DyadOp, Enclose, MonadOp, OpDef, Operand, Power, Prim, ScalarDyad, ScalarMonad,
21 Verb, WindowKind,
22 RANK_INF,
23};
24
25pub fn parse(src: &SourceParts, d: Rules) -> Result<Vec<Expr>> {
29 let sentences = lex(src, d)?;
30 let mut verbs: HashMap<String, Verb> = HashMap::new();
31 let mut stmts = Vec::with_capacity(sentences.len());
32 let mut i = 0usize;
33 while i < sentences.len() {
34 if matches!(sentences[i].first().map(|t| &t.kind), Some(Tok::Del)) {
35 let stmt = parse_tradfn(&sentences, &mut i, d, &mut verbs)?;
36 stmts.push(stmt);
37 continue;
38 }
39 let mut sentence = sentences[i].clone();
40 i += 1;
41 while let Some(at) = outermost_fx(&sentence) {
44 let mut end = at + 1;
45 while end < sentence.len() && matches!(sentence[end].kind, Tok::Value(_)) {
46 end += 1;
47 }
48 let (def, name) = fix_definition(&sentence[at..end], d, &mut verbs)?;
49 stmts.push(def);
50 let span = Span::merge(sentence[at].span, sentence[end - 1].span);
51 sentence.splice(at..end, [Token { kind: Tok::Value(name), span }]);
52 }
53 if let Some(stmt) = parse_statement(sentence, d, &mut verbs, false)? {
54 stmts.push(stmt);
55 }
56 }
57 Ok(stmts)
58}
59
60fn outermost_fx(sentence: &[Token]) -> Option<usize> {
64 let mut depth = 0usize;
65 for (i, t) in sentence.iter().enumerate() {
66 match t.kind {
67 Tok::LBrace => depth += 1,
68 Tok::RBrace => depth = depth.saturating_sub(1),
69 Tok::QuadFx if depth == 0 => return Some(i),
70 _ => {}
71 }
72 }
73 None
74}
75
76fn fix_definition(
87 toks: &[Token],
88 d: Rules,
89 verbs: &mut HashMap<String, Verb>,
90) -> Result<(Expr, Array)> {
91 let fx = toks[0].span;
92 if toks.len() == 1 {
93 return Err(Error::not_yet(
94 "⎕FX on a definition that is not literal text in the program",
95 fx,
96 ));
97 }
98 let span = Span::merge(fx, toks[toks.len() - 1].span);
99 let mut lines: Vec<(Vec<Token>, Span)> = Vec::new();
100 for t in &toks[1..] {
101 let text = match &t.kind {
102 Tok::Value(a) if a.rank() <= 1 => match &a.data {
103 Data::Char(cs) => Some(cs.iter().collect::<String>()),
104 _ => None,
105 },
106 _ => None,
107 };
108 let Some(text) = text else {
109 return Err(Error::not_yet(
110 "⎕FX on a definition that is not literal text in the program",
111 t.span,
112 ));
113 };
114 let src = SourceParts::from_parts(&[&text], &[]);
115 let mut lexed = lex(&src, d)?;
116 if lexed.len() > 1 {
117 return Err(Error::not_yet("a ⋄ inside a ⎕FX line", t.span));
118 }
119 let mut line = lexed.pop().unwrap_or_default();
122 for tok in &mut line {
123 tok.span = t.span;
124 }
125 lines.push((line, t.span));
126 }
127 let (head, head_span) = lines.remove(0);
128 if head.is_empty() {
129 return Err(Error::parse("⎕FX starts with the definition's header", head_span));
130 }
131 let body: Vec<Vec<Token>> = lines.into_iter().map(|(l, _)| l).collect();
132 let def = build_tradfn(&head, &body, span, d, verbs)?;
133 let Expr::VerbDef { name, .. } = &def else {
134 return Err(Error::internal("⎕FX did not build a definition"));
135 };
136 let answer = Array::from_chars(name.chars().collect());
137 Ok((def, answer))
138}
139
140fn parse_statement(
143 sentence: Vec<Token>,
144 d: Rules,
145 verbs: &mut HashMap<String, Verb>,
146 shared_verbs: bool,
152) -> Result<Option<Expr>> {
153 if let Some(t) = sentence.iter().find(|t| matches!(t.kind, Tok::QuadFx)) {
157 return Err(Error::not_yet("⎕FX inside another definition", t.span));
158 }
159 let sentence = substitute_verbs(sentence, verbs);
160 let sentence = fold_dfns(sentence, d, verbs)?;
161 if let [name, assign, func] = &sentence[..]
164 && let (Tok::Name(n), Tok::Assign) = (&name.kind, &assign.kind)
165 {
166 let named = match &func.kind {
169 Tok::Func(v) => Some(v.clone()),
170 Tok::UserOp { def, omega } => Some(unapplied_op(def.clone(), *omega)),
171 _ => None,
172 };
173 if let Some(v) = named {
174 let span = Span::merge(name.span, func.span);
175 if !shared_verbs {
176 verbs.insert(n.clone(), v.clone());
177 }
178 return Ok(Some(Expr::VerbDef { name: n.clone(), verb: v, span }));
179 }
180 }
181 let toks = fold_axes(fold_operators(unwrap_lone_operators(sentence), d)?, d)?;
182 if toks.is_empty() {
183 return Ok(None);
184 }
185 if let [name, assign, rest @ ..] = &toks[..]
189 && let (Tok::Name(n), Tok::Assign) = (&name.kind, &assign.kind)
190 && let Some(v) = tine_run(rest, d)?
191 {
192 let span = Span::merge(name.span, toks[toks.len() - 1].span);
193 if !shared_verbs {
194 verbs.insert(n.clone(), v.clone());
195 }
196 return Ok(Some(Expr::VerbDef { name: n.clone(), verb: v, span }));
197 }
198 if let Some(t) = toks.iter().find(|t| matches!(t.kind, Tok::Control(_))) {
199 return Err(Error::parse(
200 "control structures are only meaningful inside a ∇ definition",
201 t.span,
202 ));
203 }
204 if let Some(t) = toks.iter().find(|t| matches!(t.kind, Tok::Arrow)) {
205 return Err(Error::parse(
206 "→ branches, and only a line of a ∇ definition may begin with it",
207 t.span,
208 ));
209 }
210 let hint = Span::merge(toks[0].span, toks[toks.len() - 1].span);
211 if let Some(e) = indexed_assignment(&toks, d, hint)? {
214 return Ok(Some(e));
215 }
216 parse_range(&toks, 0, toks.len(), hint, d).map(Some)
217}
218
219fn substitute_verbs(mut toks: Vec<Token>, verbs: &HashMap<String, Verb>) -> Vec<Token> {
222 for i in 0..toks.len() {
223 let Tok::Name(n) = &toks[i].kind else { continue };
224 if matches!(toks.get(i + 1).map(|t| &t.kind), Some(Tok::Assign)) {
225 continue;
226 }
227 if let Some(v) = verbs.get(n) {
228 toks[i].kind = match as_user_op(v) {
231 Some((def, omega)) => Tok::UserOp { def, omega },
232 None if is_niladic(v) => Tok::Niladic(v.clone()),
233 None => Tok::Func(v.clone()),
234 };
235 }
236 }
237 toks
238}
239
240#[derive(Clone, Copy, Debug, PartialEq, Eq)]
245enum OpGlyph {
246 Slash,
248 SlashBar,
250 Backslash,
252 BackslashBar,
254 Rank,
256 Commute,
258 Power,
260 JotDot,
262 Over,
264 Under,
266 Stencil,
268 Jot,
270 Each,
272 Before,
274 Key,
276 Dot,
279 Variant,
281}
282
283impl OpGlyph {
284 fn glyph(self) -> char {
285 match self {
286 OpGlyph::Slash => '/',
287 OpGlyph::SlashBar => '⌿',
288 OpGlyph::Backslash => '\\',
289 OpGlyph::BackslashBar => '⍀',
290 OpGlyph::Rank => '⍤',
291 OpGlyph::Commute => '⍨',
292 OpGlyph::Power => '⍣',
293 OpGlyph::JotDot | OpGlyph::Jot => '∘',
294 OpGlyph::Over => '⍥',
295 OpGlyph::Under => '⍢',
296 OpGlyph::Stencil => '⌺',
297 OpGlyph::Each => '¨',
298 OpGlyph::Before => '⍛',
299 OpGlyph::Key => '⌸',
300 OpGlyph::Dot => '.',
301 OpGlyph::Variant => '⍠',
302 }
303 }
304}
305
306#[derive(Clone, Debug)]
307enum Tok {
308 Value(Array),
310 Nums(Array),
314 Param(usize),
316 Name(String),
317 Func(Verb),
319 Op(OpGlyph),
321 Assign,
322 Quad { quote: bool },
325 LParen,
326 RParen,
327 LBracket,
328 RBracket,
329 Semi,
332 LBrace,
334 RBrace,
335 Separator,
338 Colon,
340 Arrow,
342 Niladic(Verb),
345 UserOp { def: Arc<OpDef>, omega: bool },
348 Del,
350 Control(&'static str),
352 QuadFx,
356}
357
358#[derive(Clone, Debug)]
359struct Token {
360 kind: Tok,
361 span: Span,
362}
363
364fn is_operand_end(k: &Tok) -> bool {
367 matches!(
368 k,
369 Tok::Value(_)
370 | Tok::Nums(_)
371 | Tok::Param(_)
372 | Tok::Name(_)
373 | Tok::Niladic(_)
374 | Tok::Quad { .. }
379 | Tok::RParen
380 | Tok::RBracket
381 )
382}
383
384fn unapplied_op(def: Arc<OpDef>, omega: bool) -> Verb {
388 let named = |n: &str| Operand::Func(Box::new(Verb::Named(n.to_string())));
389 Verb::UserDerived {
390 def,
391 alpha: named("⍺⍺"),
392 omega: omega.then(|| named("⍵⍵")),
393 }
394}
395
396fn as_user_op(v: &Verb) -> Option<(Arc<OpDef>, bool)> {
399 let Verb::UserDerived { def, alpha, omega } = v else { return None };
400 let Operand::Func(f) = alpha else { return None };
403 match &**f {
404 Verb::Named(n) if n == "⍺⍺" => Some((def.clone(), omega.is_some())),
405 _ => None,
406 }
407}
408
409fn is_niladic(v: &Verb) -> bool {
411 matches!(v, Verb::Explicit(d) if d.left.is_none() && d.right == crate::ir::NILADIC)
412}
413
414fn literal(k: &Tok) -> Option<&Array> {
416 match k {
417 Tok::Value(a) | Tok::Nums(a) => Some(a),
418 _ => None,
419 }
420}
421
422fn prim_for(ch: char, d: Rules) -> Option<Prim> {
435 use DyadOp as D;
436 use MonadOp as M;
437 use ScalarDyad as SD;
438 use ScalarMonad as SM;
439 let origin = d.origin;
440 let p = match ch {
441 '+' => Prim {
442 name: "+",
443 monad: M::Scalar(SM::Conj),
444 dyad: D::Scalar(SD::Add),
445 ranks: [0, 0, 0],
446 },
447 '-' => {
448 Prim { name: "-", monad: M::Scalar(SM::Neg), dyad: D::Scalar(SD::Sub), ranks: [0, 0, 0] }
449 }
450 '×' => Prim {
451 name: "×",
452 monad: M::Scalar(SM::Signum),
453 dyad: D::Scalar(SD::Mul),
454 ranks: [0, 0, 0],
455 },
456 '÷' => Prim {
457 name: "÷",
458 monad: M::Scalar(SM::Recip),
459 dyad: D::Scalar(SD::DivApl),
460 ranks: [0, 0, 0],
461 },
462 '⌈' => Prim {
463 name: "⌈",
464 monad: M::Scalar(SM::Ceil),
465 dyad: D::Scalar(SD::Max),
466 ranks: [0, 0, 0],
467 },
468 '⌊' => Prim {
469 name: "⌊",
470 monad: M::Scalar(SM::Floor),
471 dyad: D::Scalar(SD::Min),
472 ranks: [0, 0, 0],
473 },
474 '*' => {
475 Prim { name: "*", monad: M::Scalar(SM::Exp), dyad: D::Scalar(SD::Pow), ranks: [0, 0, 0] }
476 }
477 '|' => Prim {
478 name: "|",
479 monad: M::Scalar(SM::Abs),
480 dyad: D::Scalar(SD::Residue),
481 ranks: [0, 0, 0],
482 },
483 '=' => Prim { name: "=", monad: M::None, dyad: D::Scalar(SD::Eq), ranks: [0, 0, 0] },
484 '≠' => Prim {
485 name: "≠",
486 monad: M::NubSieve,
487 dyad: D::Scalar(SD::Ne),
488 ranks: [RANK_INF, 0, 0],
489 },
490 '<' => Prim { name: "<", monad: M::None, dyad: D::Scalar(SD::Lt), ranks: [0, 0, 0] },
491 '≤' => Prim { name: "≤", monad: M::None, dyad: D::Scalar(SD::Le), ranks: [0, 0, 0] },
492 '>' => Prim { name: ">", monad: M::None, dyad: D::Scalar(SD::Gt), ranks: [0, 0, 0] },
493 '≥' => Prim { name: "≥", monad: M::None, dyad: D::Scalar(SD::Ge), ranks: [0, 0, 0] },
494 '⍴' => Prim {
495 name: "⍴",
496 monad: M::ShapeOf,
497 dyad: D::Reshape,
498 ranks: [RANK_INF, 1, RANK_INF],
499 },
500 '⍳' => Prim {
501 name: "⍳",
502 monad: M::IotaApl { origin },
503 dyad: D::IndexOf { origin, vector_left: d.lookup_left == LookupLeft::VectorOnly },
504 ranks: [RANK_INF, RANK_INF, RANK_INF],
508 },
509 '∊' => Prim {
510 name: "∊",
511 monad: M::Enlist,
512 dyad: D::MemberApl,
513 ranks: [RANK_INF, RANK_INF, RANK_INF],
514 },
515 '∪' => Prim {
516 name: "∪",
517 monad: M::Nub,
518 dyad: D::Union,
519 ranks: [RANK_INF, RANK_INF, RANK_INF],
520 },
521 '∩' => Prim {
522 name: "∩",
523 monad: M::None,
524 dyad: D::Intersect,
525 ranks: [RANK_INF, RANK_INF, RANK_INF],
526 },
527 '∧' => Prim { name: "∧", monad: M::None, dyad: D::Scalar(SD::Lcm), ranks: [0, 0, 0] },
528 '∨' => Prim { name: "∨", monad: M::None, dyad: D::Scalar(SD::Gcd), ranks: [0, 0, 0] },
529 '⍱' => Prim {
530 name: "⍱",
531 monad: M::None,
532 dyad: D::Boolean(BoolDyad::Nor),
533 ranks: [0, 0, 0],
534 },
535 '⍲' => Prim {
536 name: "⍲",
537 monad: M::None,
538 dyad: D::Boolean(BoolDyad::Nand),
539 ranks: [0, 0, 0],
540 },
541 '⍟' => Prim {
542 name: "⍟",
543 monad: M::Scalar(SM::Ln),
544 dyad: D::Scalar(SD::Log),
545 ranks: [0, 0, 0],
546 },
547 '~' => Prim {
548 name: "~",
549 monad: M::Scalar(SM::Not),
550 dyad: D::Less,
551 ranks: [0, RANK_INF, RANK_INF],
552 },
553 '≡' => Prim {
554 name: "≡",
555 monad: M::Depth { signed: d.depth_sign == DepthSign::Signed },
556 dyad: D::Match,
557 ranks: [RANK_INF, RANK_INF, RANK_INF],
558 },
559 '⍋' => Prim {
560 name: "⍋",
561 monad: M::GradeUp { origin },
562 dyad: D::CollateGrade { down: false, origin },
563 ranks: [RANK_INF, RANK_INF, RANK_INF],
564 },
565 '⍒' => Prim {
566 name: "⍒",
567 monad: M::GradeDown { origin },
568 dyad: D::CollateGrade { down: true, origin },
569 ranks: [RANK_INF, RANK_INF, RANK_INF],
570 },
571 '⊖' | '⌽' => Prim {
577 name: if ch == '⊖' { "⊖" } else { "⌽" },
578 monad: M::Reverse,
579 dyad: D::RotateApl { last: ch == '⌽' },
580 ranks: [RANK_INF, RANK_INF, RANK_INF],
581 },
582 '⍪' => Prim {
583 name: "⍪",
584 monad: M::TableOf,
585 dyad: D::AppendLeading,
586 ranks: [RANK_INF, RANK_INF, RANK_INF],
587 },
588 '!' => Prim {
589 name: "!",
590 monad: M::Scalar(SM::Factorial),
591 dyad: D::Scalar(SD::Binomial),
592 ranks: [0, 0, 0],
593 },
594 '⍕' => Prim {
595 name: "⍕",
596 monad: M::Format,
597 dyad: D::FormatSpec,
598 ranks: [RANK_INF, 1, RANK_INF],
599 },
600 '⊥' => Prim {
606 name: "⊥",
607 monad: M::None,
608 dyad: D::DecodeApl,
609 ranks: [RANK_INF, RANK_INF, RANK_INF],
610 },
611 '⊤' => Prim {
612 name: "⊤",
613 monad: M::None,
614 dyad: D::EncodeApl,
615 ranks: [RANK_INF, RANK_INF, RANK_INF],
616 },
617 '⍉' => Prim {
618 name: "⍉",
619 monad: M::TransposeAxes,
620 dyad: D::TransposeApl,
621 ranks: [RANK_INF, RANK_INF, RANK_INF],
622 },
623 '↑' => Prim {
626 name: "↑",
627 monad: match d.first_disclose {
628 FirstDisclose::UpIsFirst => M::First,
629 FirstDisclose::UpIsMix => M::Open,
630 },
631 ranks: match d.first_disclose {
634 FirstDisclose::UpIsFirst => [RANK_INF, 1, RANK_INF],
635 FirstDisclose::UpIsMix => [0, 1, RANK_INF],
636 },
637 dyad: D::Take,
638 },
639 '⊂' => Prim {
640 name: "⊂",
641 monad: match d.nested_model {
644 NestedModel::Floating => M::Enclose(Enclose::ExceptSimpleScalar),
645 NestedModel::Grounded => return None,
646 },
647 dyad: match d.partition {
650 Partition::Flags => D::PartitionEnclose,
651 Partition::Counts => D::PartitionCounts,
652 },
653 ranks: [RANK_INF, RANK_INF, RANK_INF],
654 },
655 '⊆' => Prim {
658 name: "⊆",
659 monad: M::Nest,
660 dyad: D::PartitionEnclose,
661 ranks: [RANK_INF, RANK_INF, RANK_INF],
662 },
663 '⍸' => Prim {
666 name: "⍸",
667 monad: M::Indices { origin, boxed_coords: true },
668 dyad: D::IntervalIndex { offset: origin - 1, closed: true },
669 ranks: [RANK_INF, 1, RANK_INF],
670 },
671 '⌷' => Prim {
674 name: "⌷",
675 monad: M::Same,
678 dyad: D::Squad { origin, leading: d.index_form == IndexForm::AxisVectors },
679 ranks: [RANK_INF, RANK_INF, RANK_INF],
680 },
681 '?' => Prim {
682 name: "?",
683 monad: M::Roll { origin, fixed: false, float_at_zero: false },
684 dyad: D::Deal { origin, fixed: false },
685 ranks: [RANK_INF, 0, 0],
686 },
687 '⌹' => Prim {
688 name: "⌹",
689 monad: M::MatrixInverse,
690 dyad: D::MatrixDivide,
691 ranks: [2, RANK_INF, 2],
692 },
693 '⊃' => Prim {
694 name: "⊃",
695 monad: match d.first_disclose {
696 FirstDisclose::UpIsFirst => M::Open,
697 FirstDisclose::UpIsMix => M::First,
698 },
699 dyad: D::Pick { origin },
700 ranks: match d.first_disclose {
701 FirstDisclose::UpIsFirst => [0, RANK_INF, RANK_INF],
702 FirstDisclose::UpIsMix => [RANK_INF, RANK_INF, RANK_INF],
703 },
704 },
705 '↓' => Prim {
706 name: "↓",
707 monad: M::Split,
708 dyad: D::Drop,
709 ranks: [RANK_INF, 1, RANK_INF],
710 },
711 ',' => Prim {
712 name: ",",
713 monad: M::Ravel,
714 dyad: D::AppendLast,
715 ranks: [RANK_INF, RANK_INF, RANK_INF],
716 },
717 '≢' => Prim {
718 name: "≢",
719 monad: M::Tally,
720 dyad: D::NotMatch,
721 ranks: [RANK_INF, RANK_INF, RANK_INF],
722 },
723 '⊢' => Prim {
724 name: "⊢",
725 monad: M::Same,
726 dyad: D::Right,
727 ranks: [RANK_INF, RANK_INF, RANK_INF],
728 },
729 '⊣' => Prim {
730 name: "⊣",
731 monad: M::Same,
732 dyad: D::Left,
733 ranks: [RANK_INF, RANK_INF, RANK_INF],
734 },
735 '○' => Prim {
736 name: "○",
737 monad: M::Scalar(SM::Pi),
738 dyad: D::Scalar(SD::Circle),
739 ranks: [0, 0, 0],
740 },
741 '⍷' => Prim {
742 name: "⍷",
743 monad: M::None,
744 dyad: D::FindSeq,
745 ranks: [RANK_INF, RANK_INF, RANK_INF],
746 },
747 '⍎' => Prim {
748 name: "⍎",
749 monad: M::Execute { apl: true },
750 dyad: D::None,
751 ranks: [1, RANK_INF, RANK_INF],
752 },
753 _ => return None,
754 };
755 Some(p)
756}
757
758fn verb_for(ch: char, d: Rules) -> Option<Verb> {
762 let p = prim_for(ch, d)?;
763 if ch == '⌽' {
766 return Some(Verb::Rank(Box::new(Verb::Prim(p)), [1, RANK_INF, RANK_INF]));
767 }
768 Some(Verb::Prim(p))
769}
770
771fn quad_name(name: &str, d: Rules, span: Span) -> Result<Tok> {
777 let chars = |s: &str| Tok::Value(Array::from_chars(s.chars().collect()));
778 Ok(match name {
779 "A" => chars("ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
780 "D" => chars("0123456789"),
781 "IO" => Tok::Value(Array::scalar_i64(d.origin)),
782 "CT" => Tok::Value(Array::scalar_f64(d.ct)),
783 "FX" => Tok::QuadFx,
784 "UCS" => Tok::Func(Verb::Prim(Prim {
785 name: "⎕UCS",
786 monad: MonadOp::Unicode { pass_chars: false },
787 dyad: DyadOp::None,
788 ranks: [RANK_INF, RANK_INF, RANK_INF],
789 })),
790 "TS" | "AI" | "TC" | "WA" | "SI" | "LC" | "NL" | "EX" | "FIO" | "NA" | "SH" | "CMD"
794 | "MAP" | "SVO" | "SVQ" | "TZ" | "DL" => {
795 Err(Error::sandbox(format!("⎕{name} reads outside the program"), span))?
796 }
797 other => Err(Error::not_yet(format!("the system name ⎕{other}"), span))?,
798 })
799}
800
801fn queued_glyph(ch: char) -> Option<&'static str> {
804 Some(match ch {
805 '⌶' => "I-beam (⌶)",
806 '&' => "the spawn operator (f&y)",
810 _ => return None,
811 })
812}
813
814fn op_for(ch: char) -> Option<OpGlyph> {
815 match ch {
816 '/' => Some(OpGlyph::Slash),
817 '⌿' => Some(OpGlyph::SlashBar),
818 '\\' => Some(OpGlyph::Backslash),
819 '⍀' => Some(OpGlyph::BackslashBar),
820 '⍤' => Some(OpGlyph::Rank),
821 '⍨' => Some(OpGlyph::Commute),
822 '⍣' => Some(OpGlyph::Power),
823 '∘' => Some(OpGlyph::Jot),
824 '⍥' => Some(OpGlyph::Over),
825 '⍢' => Some(OpGlyph::Under),
826 '⌺' => Some(OpGlyph::Stencil),
827 '¨' => Some(OpGlyph::Each),
828 '⍛' => Some(OpGlyph::Before),
829 '⌸' => Some(OpGlyph::Key),
830 '⍠' => Some(OpGlyph::Variant),
831 '.' => Some(OpGlyph::Dot),
834 _ => None,
835 }
836}
837
838fn expand_verb(leading: bool) -> Verb {
841 let p = Prim {
842 name: if leading { "⍀" } else { "\\" },
843 monad: MonadOp::None,
844 dyad: DyadOp::Expand,
845 ranks: if leading { [RANK_INF, 1, RANK_INF] } else { [RANK_INF, 1, 1] },
846 };
847 Verb::Prim(p)
848}
849
850fn copy_verb(leading: bool) -> Verb {
854 let p = Prim {
855 name: if leading { "⌿" } else { "/" },
856 monad: MonadOp::None,
857 dyad: DyadOp::Copy,
858 ranks: if leading { [RANK_INF, 1, RANK_INF] } else { [RANK_INF, 1, 1] },
859 };
860 Verb::Prim(p)
861}
862
863fn lex(src: &SourceParts, d: Rules) -> Result<Vec<Vec<Token>>> {
870 let mut out: Vec<Vec<Token>> = Vec::new();
871 let mut cur: Vec<Token> = Vec::new();
872 let mut in_comment = false;
874 let mut braces = 0usize;
875 for seg in &src.segments {
876 match seg {
877 Segment::Text { text, offset } => {
878 lex_text(text, *offset, d, &mut out, &mut cur, &mut in_comment, &mut braces)?;
879 }
880 Segment::Param { index, offset, len } => {
881 if !in_comment {
882 cur.push(Token {
883 kind: Tok::Param(*index),
884 span: Span::new(*offset, offset + len),
885 });
886 }
887 }
888 }
889 }
890 if !cur.is_empty() {
891 out.push(cur);
892 }
893 Ok(out)
894}
895
896#[allow(clippy::too_many_arguments)]
897fn lex_text(
898 text: &str,
899 offset: usize,
900 d: Rules,
901 out: &mut Vec<Vec<Token>>,
902 cur: &mut Vec<Token>,
903 in_comment: &mut bool,
904 braces: &mut usize,
905) -> Result<()> {
906 let mut i = 0usize;
907 while i < text.len() {
908 let ch = text[i..].chars().next().unwrap();
909 let clen = ch.len_utf8();
910 if *in_comment {
911 if ch == '\n' {
912 *in_comment = false;
913 end_sentence(out, cur);
914 }
915 i += clen;
916 continue;
917 }
918 match ch {
919 '\n' | '⋄' => {
922 if *braces > 0 {
923 cur.push(Token {
924 kind: Tok::Separator,
925 span: Span::new(offset + i, offset + i + clen),
926 });
927 } else {
928 end_sentence(out, cur);
929 }
930 i += clen;
931 }
932 ' ' | '\t' | '\r' => i += clen,
933 '⍝' => {
934 *in_comment = true;
935 i += clen;
936 }
937 '\'' => {
938 let (arr, next) = lex_string(text, i, offset)?;
939 cur.push(Token {
940 kind: Tok::Value(arr),
941 span: Span::new(offset + i, offset + next),
942 });
943 i = next;
944 }
945 '{' => {
946 *braces += 1;
947 cur.push(Token { kind: Tok::LBrace, span: Span::new(offset + i, offset + i + 1) });
948 i += 1;
949 }
950 '}' => {
951 *braces = braces.saturating_sub(1);
952 cur.push(Token { kind: Tok::RBrace, span: Span::new(offset + i, offset + i + 1) });
953 i += 1;
954 }
955 '∇' => {
956 cur.push(Token { kind: Tok::Del, span: Span::new(offset + i, offset + i + clen) });
957 i += clen;
958 }
959 '⍺' | '⍵' => {
961 let mut end = i + clen;
962 if text[end..].starts_with(ch) {
963 end += clen;
964 }
965 cur.push(Token {
966 kind: Tok::Name(text[i..end].to_string()),
967 span: Span::new(offset + i, offset + end),
968 });
969 i = end;
970 }
971 ':' if *braces > 0 => {
976 cur.push(Token {
977 kind: Tok::Colon,
978 span: Span::new(offset + i, offset + i + 1),
979 });
980 i += 1;
981 }
982 ':' => {
983 let mut j = i + 1;
984 while let Some(c) = text[j..].chars().next() {
985 if c.is_ascii_alphabetic() {
986 j += c.len_utf8();
987 } else {
988 break;
989 }
990 }
991 let span = Span::new(offset + i, offset + j);
992 match control_word(&text[i + 1..j]) {
993 Some(word) => cur.push(Token { kind: Tok::Control(word), span }),
994 None if j > i + 1 => {
995 return Err(Error::parse(
996 format!("unknown control word: {}", &text[i..j]),
997 span,
998 ));
999 }
1000 None => cur.push(Token {
1001 kind: Tok::Colon,
1002 span: Span::new(offset + i, offset + i + 1),
1003 }),
1004 }
1005 i = j;
1006 }
1007 '→' => {
1008 cur.push(Token {
1009 kind: Tok::Arrow,
1010 span: Span::new(offset + i, offset + i + clen),
1011 });
1012 i += clen;
1013 }
1014 '⍬' => {
1016 cur.push(Token {
1017 kind: Tok::Value(Array::empty(crate::dtype::DType::I64)),
1018 span: Span::new(offset + i, offset + i + clen),
1019 });
1020 i += clen;
1021 }
1022 '(' => {
1023 cur.push(Token { kind: Tok::LParen, span: Span::new(offset + i, offset + i + 1) });
1024 i += 1;
1025 }
1026 ')' => {
1027 cur.push(Token { kind: Tok::RParen, span: Span::new(offset + i, offset + i + 1) });
1028 i += 1;
1029 }
1030 '[' => {
1031 cur.push(Token {
1032 kind: Tok::LBracket,
1033 span: Span::new(offset + i, offset + i + 1),
1034 });
1035 i += 1;
1036 }
1037 ']' => {
1038 cur.push(Token {
1039 kind: Tok::RBracket,
1040 span: Span::new(offset + i, offset + i + 1),
1041 });
1042 i += 1;
1043 }
1044 ';' => {
1045 cur.push(Token { kind: Tok::Semi, span: Span::new(offset + i, offset + i + 1) });
1046 i += 1;
1047 }
1048 '←' => {
1049 cur.push(Token {
1050 kind: Tok::Assign,
1051 span: Span::new(offset + i, offset + i + clen),
1052 });
1053 i += clen;
1054 }
1055 '⍞' => {
1057 cur.push(Token {
1058 kind: Tok::Quad { quote: true },
1059 span: Span::new(offset + i, offset + i + clen),
1060 });
1061 i += clen;
1062 }
1063 '⎕' => {
1064 let after = i + clen;
1065 let mut j = after;
1066 while let Some(c) = text[j..].chars().next() {
1067 if c.is_alphabetic() {
1068 j += c.len_utf8();
1069 } else {
1070 break;
1071 }
1072 }
1073 if j > after {
1074 let span = Span::new(offset + i, offset + j);
1075 let name = text[after..j].to_uppercase();
1076 if text[j..].trim_start().starts_with('←') {
1082 quad_name(&name, d, span)?;
1083 return Err(Error::language(
1084 format!(
1085 "⎕{name} is read-only: libjay's system names are \
1086 fixed before the program runs"
1087 ),
1088 span,
1089 ));
1090 }
1091 cur.push(Token { kind: quad_name(&name, d, span)?, span });
1092 i = j;
1093 continue;
1094 }
1095 cur.push(Token {
1096 kind: Tok::Quad { quote: false },
1097 span: Span::new(offset + i, offset + after),
1098 });
1099 i = after;
1100 }
1101 _ if num_start(text, i) => {
1102 let (tok, next) = lex_number_vector(text, i, offset)?;
1103 cur.push(tok);
1104 i = next;
1105 }
1106 _ if is_name_start(ch) => {
1107 let start = i;
1108 i += clen;
1109 while let Some(c) = text[i..].chars().next() {
1110 if is_name_body(c) {
1111 i += c.len_utf8();
1112 } else {
1113 break;
1114 }
1115 }
1116 cur.push(Token {
1117 kind: Tok::Name(text[start..i].to_string()),
1118 span: Span::new(offset + start, offset + i),
1119 });
1120 }
1121 _ => {
1122 let mut end = i + clen;
1123 if let Some(v) = verb_for(ch, d) {
1124 cur.push(Token {
1125 kind: Tok::Func(v),
1126 span: Span::new(offset + i, offset + end),
1127 });
1128 } else if let Some(mut op) = op_for(ch) {
1129 if op == OpGlyph::Jot && text[end..].starts_with('.') {
1132 op = OpGlyph::JotDot;
1133 end += 1;
1134 }
1135 cur.push(Token {
1136 kind: Tok::Op(op),
1137 span: Span::new(offset + i, offset + end),
1138 });
1139 } else if let Some(what) = queued_glyph(ch) {
1140 return Err(Error::not_yet(what, Span::new(offset + i, offset + end)));
1143 } else {
1144 return Err(Error::parse(
1145 format!("unknown symbol: {ch}"),
1146 Span::new(offset + i, offset + end),
1147 ));
1148 }
1149 i = end;
1150 }
1151 }
1152 }
1153 Ok(())
1154}
1155
1156fn end_sentence(out: &mut Vec<Vec<Token>>, cur: &mut Vec<Token>) {
1157 if !cur.is_empty() {
1158 out.push(std::mem::take(cur));
1159 }
1160}
1161
1162fn is_name_start(c: char) -> bool {
1163 c.is_alphabetic() || c == '∆' || c == '⍙'
1164}
1165
1166fn is_name_body(c: char) -> bool {
1167 c.is_alphanumeric() || c == '_' || c == '∆' || c == '⍙'
1168}
1169
1170fn lex_string(text: &str, start: usize, offset: usize) -> Result<(Array, usize)> {
1173 let mut chars: Vec<char> = Vec::new();
1174 let mut i = start + 1;
1175 loop {
1176 let c = match text[i..].chars().next() {
1177 Some(c) => c,
1178 None => {
1179 return Err(Error::parse(
1180 "unterminated string",
1181 Span::new(offset + start, offset + text.len()),
1182 ));
1183 }
1184 };
1185 if c == '\'' {
1186 if text[i + 1..].starts_with('\'') {
1187 chars.push('\'');
1188 i += 2;
1189 continue;
1190 }
1191 i += 1;
1192 break;
1193 }
1194 chars.push(c);
1195 i += c.len_utf8();
1196 }
1197 let shape = if chars.len() == 1 { vec![] } else { vec![chars.len()] };
1198 Ok((Array::new(shape, Data::Char(chars.into())), i))
1199}
1200
1201fn num_start(text: &str, i: usize) -> bool {
1203 let s = match text.get(i..) {
1204 Some(s) => s,
1205 None => return false,
1206 };
1207 let mut cs = s.chars();
1208 let c0 = match cs.next() {
1209 Some(c) => c,
1210 None => return false,
1211 };
1212 if c0.is_ascii_digit() {
1213 return true;
1214 }
1215 if c0 == '.' {
1216 return cs.next().is_some_and(|d| d.is_ascii_digit());
1217 }
1218 if c0 == '¯' {
1219 return match cs.next() {
1220 Some(d) if d.is_ascii_digit() => true,
1221 Some('.') => cs.next().is_some_and(|d| d.is_ascii_digit()),
1222 _ => false,
1223 };
1224 }
1225 false
1226}
1227
1228fn lex_number(text: &str, start: usize, offset: usize) -> Result<(f64, Option<i64>, usize)> {
1236 let mut i = start;
1237 let mut buf = String::new();
1238 let mut saw_dot = false;
1239 if text[i..].starts_with('¯') {
1240 buf.push('-');
1241 i += '¯'.len_utf8();
1242 }
1243 i = take_digits(text, i, &mut buf);
1244 if text[i..].starts_with('.') && text[i + 1..].chars().next().is_some_and(|d| d.is_ascii_digit())
1245 {
1246 saw_dot = true;
1247 buf.push('.');
1248 i += 1;
1249 i = take_digits(text, i, &mut buf);
1250 }
1251 if let Some(c) = text[i..].chars().next() && (c == 'e' || c == 'E') {
1252 let after = i + 1;
1253 let neg = text[after..].starts_with('¯');
1254 let digits_at = if neg { after + '¯'.len_utf8() } else { after };
1255 if text[digits_at..].chars().next().is_some_and(|d| d.is_ascii_digit()) {
1256 buf.push('e');
1257 if neg {
1258 buf.push('-');
1259 }
1260 i = take_digits(text, digits_at, &mut buf);
1261 }
1262 }
1263 let v: f64 = buf.parse().map_err(|_| {
1264 Error::parse(
1265 format!("cannot read the number {}", &text[start..i]),
1266 Span::new(offset + start, offset + i),
1267 )
1268 })?;
1269 let exact = if saw_dot {
1273 None
1274 } else if let Ok(k) = buf.parse::<i64>() {
1275 Some(k)
1276 } else if v.fract() == 0.0 && v.abs() < 9.0e18 {
1277 Some(v as i64)
1278 } else {
1279 None
1280 };
1281 Ok((v, exact, i))
1282}
1283
1284fn take_digits(text: &str, mut i: usize, buf: &mut String) -> usize {
1285 while let Some(c) = text[i..].chars().next() {
1286 if c.is_ascii_digit() {
1287 buf.push(c);
1288 i += 1;
1289 } else {
1290 break;
1291 }
1292 }
1293 i
1294}
1295
1296fn lex_number_vector(text: &str, start: usize, offset: usize) -> Result<(Token, usize)> {
1299 let mut vals: Vec<crate::complex::Cx> = Vec::new();
1300 let mut exacts: Vec<i64> = Vec::new();
1301 let mut any_float = false;
1302 let mut any_complex = false;
1303 let mut i = start;
1304 let mut end;
1305 loop {
1306 let (v, exact, mut next) = lex_number(text, i, offset)?;
1307 let mut imag = 0.0;
1308 if let Some(c) = text[next..].chars().next() {
1309 if (c == 'j' || c == 'J') && num_start(text, next + 1) {
1312 let (b, _, imag_end) = lex_number(text, next + 1, offset)?;
1313 imag = b;
1314 next = imag_end;
1315 any_complex = true;
1316 }
1317 }
1318 vals.push([v, imag]);
1319 match exact {
1320 Some(k) => exacts.push(k),
1321 None => any_float = true,
1322 }
1323 end = next;
1324 i = next;
1325 let mut k = i;
1326 while text[k..].starts_with(' ') || text[k..].starts_with('\t') {
1327 k += 1;
1328 }
1329 if k > i && num_start(text, k) {
1330 i = k;
1331 continue;
1332 }
1333 break;
1334 }
1335 let data = if any_complex {
1336 Data::Complex(vals.into())
1337 } else if any_float {
1338 Data::F64(vals.iter().map(|&v| v[0]).collect())
1339 } else {
1340 Data::I64(exacts.into())
1341 };
1342 let shape = if data.len() == 1 { vec![] } else { vec![data.len()] };
1343 let tok = Token {
1344 kind: Tok::Nums(Array::new(shape, data)),
1345 span: Span::new(offset + start, offset + end),
1346 };
1347 Ok((tok, end))
1348}
1349
1350fn bind_value(
1362 it: &mut std::iter::Peekable<std::vec::IntoIter<Token>>,
1363 out: &mut Vec<Token>,
1364) -> Option<Token> {
1365 let left_is_value = out.last().map(|t| &t.kind).and_then(literal).is_some();
1366 let left_is_func = matches!(out.last().map(|t| &t.kind), Some(Tok::Func(_)));
1367 let right_is_value = it.peek().map(|t| &t.kind).and_then(literal).is_some();
1368 let right_is_func = matches!(it.peek().map(|t| &t.kind), Some(Tok::Func(_)));
1369 if !((left_is_value && right_is_func) || (left_is_func && right_is_value)) {
1370 return None;
1371 }
1372 let ltok = out.pop().expect("checked above");
1373 let rtok = it.next().expect("peeked");
1374 let span = Span::merge(ltok.span, rtok.span);
1375 let derived = if left_is_value {
1377 let Tok::Func(g) = rtok.kind else { unreachable!("checked above") };
1378 Verb::BondLeft(literal(<ok.kind).expect("checked above").clone(), Box::new(g))
1379 } else {
1380 let Tok::Func(f) = ltok.kind else { unreachable!("checked above") };
1381 Verb::BondRight(Box::new(f), literal(&rtok.kind).expect("checked above").clone())
1382 };
1383 Some(Token { kind: Tok::Func(derived), span })
1384}
1385
1386fn fold_operators(toks: Vec<Token>, d: Rules) -> Result<Vec<Token>> {
1390 let mut out: Vec<Token> = Vec::new();
1391 let mut it = toks.into_iter().peekable();
1392 while let Some(t) = it.next() {
1393 if matches!(t.kind, Tok::RParen) {
1398 out.push(t);
1399 close_paren(&mut out, d)?;
1400 continue;
1401 }
1402 if let Tok::UserOp { def, omega } = &t.kind {
1405 let (def, omega) = (def.clone(), *omega);
1406 let right = if omega {
1407 match it.peek().map(|tok| &tok.kind) {
1408 Some(Tok::Func(_)) => {
1409 let g = it.next().expect("peeked");
1410 let Tok::Func(g) = g.kind else { unreachable!("checked above") };
1411 Some(Operand::Func(Box::new(g)))
1412 }
1413 Some(k) if literal(k).is_some() => {
1416 let a = it.next().expect("peeked");
1417 Some(Operand::Value(Box::new(literal(&a.kind).expect("checked").clone())))
1418 }
1419 _ => {
1420 return Err(Error::parse(
1421 "⍵⍵ needs an operand on the operator's right",
1422 t.span,
1423 ));
1424 }
1425 }
1426 } else {
1427 None
1428 };
1429 let alpha = match out.pop() {
1430 Some(Token { kind: Tok::Func(f), span }) => (Operand::Func(Box::new(f)), span),
1431 Some(tok) if literal(&tok.kind).is_some() => {
1432 (Operand::Value(Box::new(literal(&tok.kind).expect("checked").clone())), tok.span)
1433 }
1434 _ => {
1435 return Err(Error::parse(
1436 "⍺⍺ needs an operand on the operator's left",
1437 t.span,
1438 ));
1439 }
1440 };
1441 let (alpha, fspan) = alpha;
1442 let derived = Verb::UserDerived { def, alpha, omega: right };
1443 out.push(Token { kind: Tok::Func(derived), span: Span::merge(fspan, t.span) });
1444 continue;
1445 }
1446 let op = match t.kind {
1447 Tok::Op(op) => op,
1448 _ => {
1449 out.push(t);
1450 continue;
1451 }
1452 };
1453 if op == OpGlyph::JotDot {
1456 let ftok = match it.peek() {
1457 Some(tok) if matches!(tok.kind, Tok::Func(_)) => it.next().unwrap(),
1458 _ => {
1459 return Err(Error::parse("∘. needs a function on its right", t.span));
1460 }
1461 };
1462 let span = Span::merge(t.span, ftok.span);
1463 let Tok::Func(f) = ftok.kind else { unreachable!("checked above") };
1464 out.push(Token { kind: Tok::Func(Verb::Reduce(Box::new(f))), span });
1465 continue;
1466 }
1467 if op == OpGlyph::Jot
1472 && let Some(bound) = bind_value(&mut it, &mut out)
1473 {
1474 out.push(bound);
1475 continue;
1476 }
1477 if matches!(
1480 op,
1481 OpGlyph::Jot | OpGlyph::Over | OpGlyph::Before | OpGlyph::Under | OpGlyph::Dot
1482 ) {
1483 let Some(gtok) = it.peek().filter(|x| matches!(x.kind, Tok::Func(_))) else {
1484 return Err(Error::not_yet(
1485 format!("{} with a value operand", op.glyph()),
1486 t.span,
1487 ));
1488 };
1489 let gspan = gtok.span;
1490 let Some(Token { kind: Tok::Func(g), .. }) = it.next() else {
1491 unreachable!("peeked a function")
1492 };
1493 let Some(Token { kind: Tok::Func(f), span: fspan }) = out.pop() else {
1494 return Err(Error::not_yet(
1495 format!("{} with a value operand", op.glyph()),
1496 t.span,
1497 ));
1498 };
1499 let span = Span::merge(fspan, gspan);
1500 let derived = match op {
1503 OpGlyph::Jot => Verb::Beside(Box::new(f), Box::new(g)),
1504 OpGlyph::Before => Verb::Before(Box::new(f), Box::new(g)),
1505 OpGlyph::Under => {
1509 let back = crate::verb::obverse(&g).ok_or_else(|| {
1510 Error::not_yet(
1511 format!("the obverse of {} (no inverse is known)", g.name()),
1512 gspan,
1513 )
1514 })?;
1515 let composed = Verb::Compose(Box::new(f), Box::new(g));
1516 Verb::Atop(Box::new(back), Box::new(composed))
1517 }
1518 OpGlyph::Dot => Verb::InnerProduct {
1521 u: Box::new(Verb::Reduce(Box::new(f))),
1522 v: Box::new(g),
1523 apl: true,
1524 },
1525 _ => Verb::Compose(Box::new(f), Box::new(g)),
1526 };
1527 out.push(Token { kind: Tok::Func(derived), span });
1528 continue;
1529 }
1530 let left_is_func = matches!(out.last().map(|x| &x.kind), Some(Tok::Func(_)));
1532 if !left_is_func {
1533 if out.last().is_some_and(|x| is_operand_end(&x.kind)) {
1537 let f = match op {
1538 OpGlyph::Slash => copy_verb(false),
1539 OpGlyph::SlashBar => copy_verb(true),
1540 OpGlyph::Backslash => expand_verb(false),
1541 OpGlyph::BackslashBar => expand_verb(true),
1542 OpGlyph::Rank
1543 | OpGlyph::Commute
1544 | OpGlyph::Power
1545 | OpGlyph::JotDot
1546 | OpGlyph::Jot
1547 | OpGlyph::Over
1548 | OpGlyph::Under
1549 | OpGlyph::Stencil
1550 | OpGlyph::Before
1551 | OpGlyph::Key
1552 | OpGlyph::Dot
1553 | OpGlyph::Variant
1554 | OpGlyph::Each => {
1555 return Err(Error::parse(
1556 format!("{} needs a function to its left", op.glyph()),
1557 t.span,
1558 ));
1559 }
1560 };
1561 out.push(Token { kind: Tok::Func(f), span: t.span });
1562 continue;
1563 }
1564 return Err(Error::parse(
1565 format!("{} needs a function to its left", op.glyph()),
1566 t.span,
1567 ));
1568 }
1569 let ftok = out.pop().unwrap();
1570 let f = match ftok.kind {
1571 Tok::Func(f) => f,
1572 _ => unreachable!("checked above"),
1573 };
1574 let span = Span::merge(ftok.span, t.span);
1575 if let Some((k, aspan)) = take_axis(&mut it, d)? {
1579 let inner = match op {
1580 OpGlyph::Slash | OpGlyph::SlashBar => Verb::NWise(Box::new(f)),
1581 OpGlyph::Backslash | OpGlyph::BackslashBar => {
1582 Verb::Windowed(Box::new(Verb::Reduce(Box::new(f))), WindowKind::Scan)
1583 }
1584 _ => {
1585 return Err(Error::not_yet(
1586 format!("axis specification for {}", op.glyph()),
1587 aspan,
1588 ));
1589 }
1590 };
1591 out.push(Token {
1592 kind: Tok::Func(Verb::AlongAxis(Box::new(inner), k)),
1593 span: Span::merge(span, aspan),
1594 });
1595 continue;
1596 }
1597 let derived = match op {
1598 OpGlyph::Slash => Verb::Rank(Box::new(Verb::NWise(Box::new(f))), [1, RANK_INF, 1]),
1604 OpGlyph::SlashBar => Verb::NWise(Box::new(f)),
1605 OpGlyph::Backslash => Verb::Rank(
1609 Box::new(Verb::Windowed(Box::new(Verb::Reduce(Box::new(f))), WindowKind::Scan)),
1610 [1, 1, 1],
1611 ),
1612 OpGlyph::BackslashBar => {
1613 Verb::Windowed(Box::new(Verb::Reduce(Box::new(f))), WindowKind::Scan)
1614 }
1615 OpGlyph::Commute => Verb::Commute(Box::new(f)),
1616 OpGlyph::Key => Verb::KeyPairs(Box::new(f)),
1617 OpGlyph::Variant => {
1620 let (options, ospan) = variant_options(&mut it, t.span)?;
1621 let derived = variant(f, &options, Span::merge(span, ospan))?;
1622 out.push(Token { kind: Tok::Func(derived), span: Span::merge(span, ospan) });
1623 continue;
1624 }
1625 OpGlyph::Dot => {
1628 return Err(Error::parse("the inner product . needs a function on its right", t.span));
1629 }
1630 OpGlyph::Each => Verb::Each(Box::new(f), Enclose::ExceptSimpleScalar),
1634 OpGlyph::Power => {
1635 let spec = match it.peek() {
1636 Some(tok) if matches!(tok.kind, Tok::Func(_)) => {
1639 let gtok = it.next().unwrap();
1640 let Tok::Func(g) = gtok.kind else { unreachable!("checked above") };
1641 let v = Verb::PowerUntil(Box::new(f), Box::new(g));
1642 out.push(Token {
1643 kind: Tok::Func(v),
1644 span: Span::merge(span, gtok.span),
1645 });
1646 continue;
1647 }
1648 Some(tok) if literal(&tok.kind).is_some() => it.next().unwrap(),
1649 _ => {
1650 return Err(Error::not_yet("computed power (f⍣n)", t.span));
1651 }
1652 };
1653 let arr = literal(&spec.kind).expect("checked above");
1654 let (p, inverse) = power_spec(arr, spec.span)?;
1655 let f = if inverse { crate::frontend::j::obverse_of(&f, span)? } else { f };
1658 let f = Verb::PowerN(Box::new(f), p);
1659 out.push(Token { kind: Tok::Func(f), span: Span::merge(span, spec.span) });
1660 continue;
1661 }
1662 OpGlyph::Stencil => {
1666 let Some(spec) = it.peek().filter(|t| literal(&t.kind).is_some()) else {
1667 return Err(Error::parse(
1668 "⌺ needs a window specification on its right",
1669 t.span,
1670 ));
1671 };
1672 let sspan = spec.span;
1673 let spec = it.next().expect("peeked a literal");
1674 let arr = literal(&spec.kind).expect("checked above");
1675 if arr.rank() > 1 {
1676 return Err(Error::not_yet(
1677 "a stencil with a movement row (f⌺(m⍪w))",
1678 sspan,
1679 ));
1680 }
1681 let sizes = arr
1682 .to_i64_vec()
1683 .ok_or_else(|| Error::domain("a stencil window is whole numbers", sspan))?;
1684 let v = Verb::Stencil(Box::new(f), sizes);
1685 out.push(Token { kind: Tok::Func(v), span: Span::merge(span, sspan) });
1686 continue;
1687 }
1688 OpGlyph::Rank => {
1689 let spec = match it.peek() {
1690 Some(tok) if matches!(tok.kind, Tok::Func(_)) => {
1693 let gtok = it.next().unwrap();
1694 let Tok::Func(g) = gtok.kind else { unreachable!("checked above") };
1695 let v = Verb::Atop(Box::new(f), Box::new(g));
1696 out.push(Token {
1697 kind: Tok::Func(v),
1698 span: Span::merge(span, gtok.span),
1699 });
1700 continue;
1701 }
1702 Some(tok) if literal(&tok.kind).is_some() => it.next().unwrap(),
1703 _ => {
1704 return Err(Error::parse(
1705 "⍤ needs a rank specification on its right",
1706 t.span,
1707 ));
1708 }
1709 };
1710 let arr = literal(&spec.kind).expect("checked above");
1711 let ranks = rank_spec(arr, spec.span)?;
1712 let f = Verb::Rank(Box::new(f), ranks);
1713 out.push(Token { kind: Tok::Func(f), span: Span::merge(span, spec.span) });
1714 continue;
1715 }
1716 OpGlyph::JotDot
1718 | OpGlyph::Jot
1719 | OpGlyph::Over
1720 | OpGlyph::Under
1721 | OpGlyph::Before => {
1722 unreachable!("handled above")
1723 }
1724 };
1725 out.push(Token { kind: Tok::Func(derived), span });
1726 }
1727 Ok(out)
1728}
1729
1730fn take_axis(
1733 it: &mut std::iter::Peekable<std::vec::IntoIter<Token>>,
1734 d: Rules,
1735) -> Result<Option<(usize, Span)>> {
1736 if !matches!(it.peek().map(|t| &t.kind), Some(Tok::LBracket)) {
1737 return Ok(None);
1738 }
1739 let open = it.next().expect("peeked");
1740 let spec = match it.next() {
1741 Some(tok) if literal(&tok.kind).is_some() => tok,
1742 Some(tok) => return Err(Error::not_yet("a computed axis (f[k])", tok.span)),
1743 None => return Err(Error::parse("unterminated axis specification", open.span)),
1744 };
1745 let close = match it.next() {
1746 Some(tok) if matches!(tok.kind, Tok::RBracket) => tok,
1747 _ => return Err(Error::parse("unterminated axis specification", open.span)),
1748 };
1749 let span = Span::merge(open.span, close.span);
1750 let arr = literal(&spec.kind).expect("checked above");
1751 let ints = arr
1752 .to_i64_vec()
1753 .ok_or_else(|| Error::parse("an axis must be a whole number", spec.span))?;
1754 let [k] = ints[..] else {
1755 return Err(Error::not_yet("several axes in one specification", spec.span));
1756 };
1757 let origin = d.origin;
1758 let k = k - origin;
1759 if k < 0 {
1760 return Err(Error::domain(format!("axis {} does not exist", k + origin), spec.span));
1761 }
1762 Ok(Some((k as usize, span)))
1763}
1764
1765fn variant_options(
1774 it: &mut std::iter::Peekable<std::vec::IntoIter<Token>>,
1775 span: Span,
1776) -> Result<(Vec<(String, Array)>, Span)> {
1777 if let Some(tok) = it.peek().filter(|t| literal(&t.kind).is_some()) {
1778 let (value, vspan) = (literal(&tok.kind).expect("peeked a literal").clone(), tok.span);
1779 it.next();
1780 return Ok((vec![("CT".to_string(), value)], vspan));
1781 }
1782 let mut options = Vec::new();
1783 let mut last = span;
1784 while it.peek().is_some_and(|t| matches!(t.kind, Tok::LParen)) {
1785 it.next();
1786 let mut inside: Vec<Array> = Vec::new();
1787 loop {
1788 let Some(tok) = it.next() else {
1789 return Err(Error::parse("unmatched ( after ⍠", span));
1790 };
1791 last = tok.span;
1792 if matches!(tok.kind, Tok::RParen) {
1793 break;
1794 }
1795 match literal(&tok.kind) {
1796 Some(a) => inside.push(a.clone()),
1797 None => {
1798 return Err(Error::not_yet(
1799 "a computed variant option (f⍠v with a name or an expression)",
1800 tok.span,
1801 ));
1802 }
1803 }
1804 }
1805 let [name, value] = inside.as_slice() else {
1806 return Err(Error::parse("a variant option is a name and a value", last));
1807 };
1808 let Data::Char(cs) = &name.data else {
1809 return Err(Error::parse("a variant option starts with its name", last));
1810 };
1811 options.push((cs.as_slice().iter().collect::<String>().to_uppercase(), value.clone()));
1812 }
1813 if options.is_empty() {
1814 let where_ = it.peek().map_or(span, |t| t.span);
1815 return Err(Error::not_yet(
1816 "a computed variant option (f⍠v with a name or an expression)",
1817 where_,
1818 ));
1819 }
1820 Ok((options, last))
1821}
1822
1823fn variant(f: Verb, options: &[(String, Array)], span: Span) -> Result<Verb> {
1829 let mut out = f;
1830 for (name, value) in options {
1831 out = match name.as_str() {
1832 "CT" => {
1833 let Some(ct) = value.to_f64_vec().and_then(|v| v.first().copied()) else {
1834 return Err(Error::domain("a comparison tolerance is a number", span));
1835 };
1836 if !out.uses_tolerance() {
1837 return Err(Error::domain(
1838 format!(
1839 "the comparison tolerance is not an option of {}: it consults none",
1840 out.name()
1841 ),
1842 span,
1843 ));
1844 }
1845 if !(0.0..1.0).contains(&ct) {
1846 return Err(Error::domain(
1847 "a comparison tolerance lies between 0 and 1",
1848 span,
1849 ));
1850 }
1851 Verb::Fit(Box::new(out), ct)
1852 }
1853 "IO" => {
1854 let Some(io) = value.to_i64_vec().and_then(|v| v.first().copied()) else {
1855 return Err(Error::domain("an index origin is a whole number", span));
1856 };
1857 if io != 0 && io != 1 {
1858 return Err(Error::domain("an index origin is 0 or 1", span));
1859 }
1860 crate::verb::with_origin(&out, io).ok_or_else(|| {
1861 Error::domain(
1862 format!("the index origin is not an option of {}", out.name()),
1863 span,
1864 )
1865 })?
1866 }
1867 other => {
1868 return Err(Error::not_yet(
1869 format!("the variant option {other} (f⍠v)"),
1870 span,
1871 ));
1872 }
1873 };
1874 }
1875 Ok(out)
1876}
1877
1878fn unwrap_lone_operators(toks: Vec<Token>) -> Vec<Token> {
1883 let mut out: Vec<Token> = Vec::with_capacity(toks.len());
1884 for t in toks {
1885 let n = out.len();
1886 if matches!(t.kind, Tok::RParen)
1887 && n >= 2
1888 && matches!(out[n - 1].kind, Tok::Op(_))
1889 && matches!(out[n - 2].kind, Tok::LParen)
1890 {
1891 let op = out.pop().expect("checked above");
1892 let open = out.pop().expect("checked above");
1893 out.push(Token { kind: op.kind, span: Span::merge(open.span, t.span) });
1894 continue;
1895 }
1896 out.push(t);
1897 }
1898 out
1899}
1900
1901fn close_paren(out: &mut Vec<Token>, d: Rules) -> Result<()> {
1905 let close = out.len() - 1;
1906 let Some(open) = matching_lparen(out, close) else { return Ok(()) };
1907 let span = Span::merge(out[open].span, out[close].span);
1908 let inner = &out[open + 1..close];
1909 if inner.len() == 1 && matches!(inner[0].kind, Tok::Func(_)) {
1910 let Some(Token { kind, .. }) = out.get(open + 1).cloned() else {
1911 unreachable!("checked above")
1912 };
1913 out.truncate(open);
1914 out.push(Token { kind, span });
1915 return Ok(());
1916 }
1917 if !d.trains || inner.len() < 2 || !inner[1..].iter().all(|t| matches!(t.kind, Tok::Func(_))) {
1918 return Ok(());
1919 }
1920 let Some(verb) = train(inner)? else { return Ok(()) };
1921 out.truncate(open);
1922 out.push(Token { kind: Tok::Func(verb), span });
1923 Ok(())
1924}
1925
1926fn matching_lparen(out: &[Token], close: usize) -> Option<usize> {
1928 let mut depth = 0usize;
1929 for i in (0..close).rev() {
1930 match out[i].kind {
1931 Tok::RParen => depth += 1,
1932 Tok::LParen => {
1933 if depth == 0 {
1934 return Some(i);
1935 }
1936 depth -= 1;
1937 }
1938 _ => {}
1939 }
1940 }
1941 None
1942}
1943
1944fn train(tines: &[Token]) -> Result<Option<Verb>> {
1953 debug_assert!(!tines.is_empty());
1954 if tines.len() == 1 {
1955 return Ok(match &tines[0].kind {
1956 Tok::Func(f) => Some(f.clone()),
1957 _ => None,
1958 });
1959 }
1960 if tines.len() == 2 {
1961 let (Tok::Func(g), Tok::Func(h)) = (&tines[0].kind, &tines[1].kind) else {
1962 return Ok(None);
1963 };
1964 return Ok(Some(Verb::Atop(Box::new(g.clone()), Box::new(h.clone()))));
1965 }
1966 let head = &tines[0].kind;
1969 if tines.len() % 2 == 0 {
1970 let Tok::Func(f) = head else {
1971 return Err(Error::parse(
1972 "a value may only be a fork's left tine, and this train has an even number of tines",
1973 tines[0].span,
1974 ));
1975 };
1976 let Some(rest) = train(&tines[1..])? else { return Ok(None) };
1977 return Ok(Some(Verb::Atop(Box::new(f.clone()), Box::new(rest))));
1978 }
1979 let Some(rest) = train(&tines[2..])? else { return Ok(None) };
1980 let Tok::Func(g) = &tines[1].kind else { unreachable!("the tail is all functions") };
1981 match head {
1982 Tok::Func(f) => {
1983 Ok(Some(Verb::Fork(Box::new(f.clone()), Box::new(g.clone()), Box::new(rest))))
1984 }
1985 Tok::Value(n) | Tok::Nums(n) => {
1986 Ok(Some(Verb::NounFork(n.clone(), Box::new(g.clone()), Box::new(rest))))
1987 }
1988 Tok::Name(_) | Tok::Param(_) | Tok::RParen | Tok::RBracket | Tok::Niladic(_) => {
1992 Err(Error::not_yet("a train whose left tine is a computed value", tines[0].span))
1993 }
1994 _ => Ok(None),
1995 }
1996}
1997
1998fn tine_run(toks: &[Token], d: Rules) -> Result<Option<Verb>> {
2004 if !d.trains || toks.is_empty() {
2005 return Ok(None);
2006 }
2007 if !toks[1..].iter().all(|t| matches!(t.kind, Tok::Func(_))) {
2008 return Ok(None);
2009 }
2010 train(toks)
2011}
2012
2013fn fold_axes(toks: Vec<Token>, d: Rules) -> Result<Vec<Token>> {
2015 let mut out: Vec<Token> = Vec::new();
2016 let mut it = toks.into_iter().peekable();
2017 while let Some(t) = it.next() {
2018 let Tok::Func(f) = &t.kind else {
2019 out.push(t);
2020 continue;
2021 };
2022 let Some((k, aspan)) = take_axis(&mut it, d)? else {
2023 out.push(t);
2024 continue;
2025 };
2026 let Some(inner) = leading_axis_form(f) else {
2027 return Err(Error::not_yet(format!("axis specification for {}", f.name()), aspan));
2028 };
2029 out.push(Token {
2030 kind: Tok::Func(Verb::AlongAxis(Box::new(inner), k)),
2031 span: Span::merge(t.span, aspan),
2032 });
2033 }
2034 Ok(out)
2035}
2036
2037fn leading_axis_form(v: &Verb) -> Option<Verb> {
2041 match v {
2042 Verb::Rank(inner, [1, RANK_INF, RANK_INF]) => leading_axis_form(inner),
2044 Verb::Prim(p) if matches!(p.monad, MonadOp::Reverse) => {
2047 let mut p = *p;
2048 if matches!(p.dyad, DyadOp::RotateApl { .. }) {
2049 p.dyad = DyadOp::RotateApl { last: false };
2050 }
2051 Some(Verb::Prim(p))
2052 }
2053 _ => None,
2054 }
2055}
2056
2057fn select_axis_verb(axis: usize, rank: usize, d: Rules) -> Verb {
2059 Verb::Prim(Prim {
2060 name: "[…]",
2061 monad: MonadOp::None,
2062 dyad: DyadOp::SelectAxis { axis, rank, origin: d.origin },
2063 ranks: [RANK_INF; 3],
2064 })
2065}
2066
2067fn power_spec(a: &Array, span: Span) -> Result<(Power, bool)> {
2071 let ints = a
2072 .to_i64_vec()
2073 .ok_or_else(|| Error::parse("⍣ needs a whole number on its right", span))?;
2074 let [n] = ints[..] else {
2075 return Err(Error::not_yet("power over a list of counts (f⍣n)", span));
2076 };
2077 Ok((Power::Times(n.unsigned_abs()), n < 0))
2078}
2079
2080fn rank_spec(a: &Array, span: Span) -> Result<[i64; 3]> {
2082 let ints = a
2083 .to_i64_vec()
2084 .ok_or_else(|| Error::parse("⍤ rank specification must be integers", span))?;
2085 match ints.len() {
2086 1 => Ok([ints[0], ints[0], ints[0]]),
2087 2 => Ok([ints[1], ints[0], ints[1]]),
2088 3 => Ok([ints[0], ints[1], ints[2]]),
2089 _ => Err(Error::parse("⍤ rank specification takes 1 to 3 integers", span)),
2090 }
2091}
2092
2093fn parse_range(toks: &[Token], lo: usize, hi: usize, hint: Span, d: Rules) -> Result<Expr> {
2100 let (mut acc, mut start) = parse_operand(toks, lo, hi, hint, d)?;
2101 let end = toks[hi - 1].span.end;
2102 loop {
2103 if start == lo {
2104 return Ok(acc);
2105 }
2106 let left = &toks[start - 1];
2107 match &left.kind {
2108 Tok::Func(f) => {
2109 let dyadic = start >= lo + 2 && is_operand_end(&toks[start - 2].kind);
2111 if dyadic {
2112 let (x, xstart) = parse_operand(toks, lo, start - 1, left.span, d)?;
2113 acc = Expr::Dyad {
2114 verb: f.clone(),
2115 x: Box::new(x),
2116 y: Box::new(acc),
2117 span: Span::new(toks[xstart].span.start, end),
2118 };
2119 start = xstart;
2120 } else {
2121 acc = Expr::Monad {
2122 verb: f.clone(),
2123 y: Box::new(acc),
2124 span: Span::new(left.span.start, end),
2125 };
2126 start -= 1;
2127 }
2128 }
2129 Tok::Assign => {
2130 if start < lo + 2 {
2131 return Err(Error::parse("assignment target must be a name", left.span));
2132 }
2133 let target = &toks[start - 2];
2134 let span = Span::new(target.span.start, end);
2135 match &target.kind {
2136 Tok::Name(n) => {
2137 acc = Expr::Assign {
2138 name: n.clone(),
2139 value: Box::new(acc),
2140 scope: Scope::Local,
2141 span,
2142 };
2143 }
2144 Tok::Quad { quote } => {
2145 acc = Expr::PrintPass { value: Box::new(acc), bare: *quote, span };
2146 }
2147 _ => {
2148 return Err(Error::parse(
2149 "assignment target must be a name",
2150 target.span,
2151 ));
2152 }
2153 }
2154 start -= 2;
2155 }
2156 _ => break,
2159 }
2160 }
2161 let span = Span::new(toks[lo].span.start, toks[start - 1].span.end);
2162 if d.trains && toks[lo..start].iter().all(|t| matches!(t.kind, Tok::Func(_))) {
2166 return Err(Error::parse(
2167 "a train is a function; parenthesise it to apply it to an argument",
2168 span,
2169 ));
2170 }
2171 Err(Error::parse("syntax error", span))
2172}
2173
2174fn parse_operand(
2181 toks: &[Token],
2182 lo: usize,
2183 hi: usize,
2184 hint: Span,
2185 d: Rules,
2186) -> Result<(Expr, usize)> {
2187 let (first, mut start) = parse_primary(toks, lo, hi, hint, d)?;
2188 if start == lo || !is_operand_end(&toks[start - 1].kind) {
2189 return Ok((first, start));
2190 }
2191 let mut items: Vec<Expr> = Vec::new();
2192 let mut cur = first;
2193 loop {
2194 push_items(&mut items, cur, &toks[start]);
2195 if start == lo || !is_operand_end(&toks[start - 1].kind) {
2196 break;
2197 }
2198 let (e, s) = parse_primary(toks, lo, start, toks[start - 1].span, d)?;
2199 cur = e;
2200 start = s;
2201 }
2202 let span = Span::new(toks[start].span.start, toks[hi - 1].span.end);
2203 let mut it = items.into_iter();
2204 let last = it.next().expect("a strand has at least one item");
2205 let mut acc = Expr::Monad { verb: strand_seed(d), y: Box::new(last), span };
2206 for item in it {
2207 acc = Expr::Dyad { verb: strand_verb(), x: Box::new(item), y: Box::new(acc), span };
2208 }
2209 Ok((acc, start))
2210}
2211
2212fn push_items(items: &mut Vec<Expr>, e: Expr, tok: &Token) {
2214 if let Tok::Nums(a) = &tok.kind && a.rank() > 0 {
2215 for i in (0..a.count()).rev() {
2216 let atom = Array::new(Vec::new(), a.data.slice(i, i + 1));
2217 items.push(Expr::Const(atom, tok.span));
2218 }
2219 return;
2220 }
2221 items.push(e);
2222}
2223
2224fn strand_seed(d: Rules) -> Verb {
2227 Verb::Atop(
2228 Box::new(Verb::Prim(prim_for(',', d).expect("`,` is a primitive"))),
2229 Box::new(Verb::Prim(prim_for('⊂', d).expect("`⊂` is a primitive"))),
2230 )
2231}
2232
2233fn strand_verb() -> Verb {
2235 Verb::Prim(Prim {
2236 name: "(vector notation)",
2237 monad: MonadOp::None,
2238 dyad: DyadOp::Strand,
2239 ranks: [RANK_INF; 3],
2240 })
2241}
2242
2243fn parse_primary(
2246 toks: &[Token],
2247 lo: usize,
2248 hi: usize,
2249 hint: Span,
2250 d: Rules,
2251) -> Result<(Expr, usize)> {
2252 if hi == lo {
2253 return Err(Error::parse("empty parentheses", hint));
2254 }
2255 let t = &toks[hi - 1];
2256 match &t.kind {
2257 Tok::Value(a) | Tok::Nums(a) => Ok((Expr::Const(a.clone(), t.span), hi - 1)),
2258 Tok::Param(i) => Ok((Expr::Param(*i, t.span), hi - 1)),
2259 Tok::Name(n) => Ok((Expr::Name(n.clone(), t.span), hi - 1)),
2260 Tok::Niladic(v) => Ok((
2263 Expr::Monad {
2264 verb: v.clone(),
2265 y: Box::new(Expr::Const(Array::empty(crate::dtype::DType::I64), t.span)),
2266 span: t.span,
2267 },
2268 hi - 1,
2269 )),
2270 Tok::RParen => {
2271 let l = match_lparen(toks, lo, hi - 1)?;
2272 let hint = Span::merge(toks[l].span, t.span);
2273 let inner = parse_range(toks, l + 1, hi - 1, hint, d)?;
2274 Ok((inner, l))
2275 }
2276 Tok::RBracket => index_brackets(toks, lo, hi, d),
2277 Tok::Func(_) if hi >= lo + 2 && matches!(toks[hi - 2].kind, Tok::Assign) => {
2281 let from = if hi >= lo + 3 { toks[hi - 3].span } else { toks[hi - 2].span };
2282 let span = Span::merge(from, t.span);
2283 if d.trains {
2284 Err(Error::not_yet("naming a function inside a larger sentence", span))
2285 } else {
2286 Err(Error::not_yet("function assignment (F←+/)", span))
2287 }
2288 }
2289 Tok::Func(_) => Err(Error::parse("missing right argument", t.span)),
2290 Tok::Assign => Err(Error::parse("← needs a value on its right", t.span)),
2291 Tok::Quad { quote } => Ok((Expr::Input { eval: !*quote, span: t.span }, hi - 1)),
2293 Tok::LParen => Err(Error::parse("unmatched (", t.span)),
2294 Tok::LBracket => Err(Error::parse("unmatched [", t.span)),
2295 Tok::Semi => Err(Error::parse("; is only meaningful inside index brackets", t.span)),
2296 Tok::Colon => Err(Error::parse(": is only meaningful in a dfn guard", t.span)),
2297 Tok::UserOp { .. } => Err(Error::parse(
2298 "this dfn mentions ⍺⍺ or ⍵⍵, so it is an operator and needs a function operand",
2299 t.span,
2300 )),
2301 Tok::Arrow => Err(Error::parse(
2302 "→ branches, and only a line of a ∇ definition may begin with it",
2303 t.span,
2304 )),
2305 Tok::Del => Err(Error::parse("∇ opens a definition; it is not a value", t.span)),
2306 Tok::Control(w) => Err(Error::parse(
2307 format!(":{w} is only meaningful inside a ∇ definition"),
2308 t.span,
2309 )),
2310 Tok::LBrace | Tok::RBrace => Err(Error::parse("unmatched {", t.span)),
2311 Tok::Separator => Err(Error::internal("a statement break survived folding")),
2312 Tok::Op(_) => Err(Error::internal("operator survived folding")),
2313 Tok::QuadFx => Err(Error::not_yet("⎕FX inside another definition", t.span)),
2318 }
2319}
2320
2321fn index_brackets(
2328 toks: &[Token],
2329 lo: usize,
2330 hi: usize,
2331 d: Rules,
2332) -> Result<(Expr, usize)> {
2333 let close = &toks[hi - 1];
2334 let open = match_lbracket(toks, lo, hi - 1)?;
2335 if open == lo || !is_operand_end(&toks[open - 1].kind) {
2336 return Err(Error::parse("[ needs a value on its left", toks[open].span));
2337 }
2338 let (base, start) = parse_primary(toks, lo, open, toks[open].span, d)?;
2339 let slots = index_slots(toks, open + 1, hi - 1, toks[open].span)?;
2340 let span = Span::new(toks[start].span.start, close.span.end);
2341 let rank = slots.len();
2342 let mut acc = base;
2343 let mut first = true;
2344 for (axis, slot) in slots.iter().enumerate().rev() {
2345 let Some((slo, shi)) = *slot else { continue };
2346 let idx = parse_range(toks, slo, shi, toks[open].span, d)?;
2347 let check = if first { rank } else { 0 };
2348 first = false;
2349 acc = Expr::Dyad {
2350 verb: select_axis_verb(axis, check, d),
2351 x: Box::new(idx),
2352 y: Box::new(acc),
2353 span,
2354 };
2355 }
2356 Ok((acc, start))
2357}
2358
2359fn index_slots(
2362 toks: &[Token],
2363 lo: usize,
2364 hi: usize,
2365 hint: Span,
2366) -> Result<Vec<Option<(usize, usize)>>> {
2367 let mut out = Vec::new();
2368 let mut depth = 0usize;
2369 let mut start = lo;
2370 for (i, t) in toks.iter().enumerate().take(hi).skip(lo) {
2371 match t.kind {
2372 Tok::LParen | Tok::LBracket => depth += 1,
2373 Tok::RParen | Tok::RBracket => depth -= 1,
2374 Tok::Semi if depth == 0 => {
2375 out.push((start < i).then_some((start, i)));
2376 start = i + 1;
2377 }
2378 _ => {}
2379 }
2380 }
2381 out.push((start < hi).then_some((start, hi)));
2382 if out.len() == 1 && out[0].is_none() {
2383 return Err(Error::parse("empty index brackets", hint));
2384 }
2385 Ok(out)
2386}
2387
2388fn match_lbracket(toks: &[Token], lo: usize, rbracket: usize) -> Result<usize> {
2389 let mut depth = 0usize;
2390 let mut i = rbracket;
2391 while i > lo {
2392 i -= 1;
2393 match toks[i].kind {
2394 Tok::RBracket => depth += 1,
2395 Tok::LBracket => {
2396 if depth == 0 {
2397 return Ok(i);
2398 }
2399 depth -= 1;
2400 }
2401 _ => {}
2402 }
2403 }
2404 Err(Error::parse("unmatched ]", toks[rbracket].span))
2405}
2406
2407fn match_lparen(toks: &[Token], lo: usize, rparen: usize) -> Result<usize> {
2408 let mut depth = 0usize;
2409 let mut i = rparen;
2410 while i > lo {
2411 i -= 1;
2412 match toks[i].kind {
2413 Tok::RParen => depth += 1,
2414 Tok::LParen => {
2415 if depth == 0 {
2416 return Ok(i);
2417 }
2418 depth -= 1;
2419 }
2420 _ => {}
2421 }
2422 }
2423 Err(Error::parse("unmatched )", toks[rparen].span))
2424}
2425
2426const CONTROL_WORDS: [&str; 18] = [
2438 "If", "ElseIf", "Else", "EndIf", "While", "EndWhile", "Repeat", "Until", "For", "In",
2439 "EndFor", "Select", "Case", "EndSelect", "Return", "Leave", "Continue", "End",
2440];
2441
2442fn control_word(word: &str) -> Option<&'static str> {
2445 CONTROL_WORDS.iter().copied().find(|w| w.eq_ignore_ascii_case(word))
2446}
2447
2448fn match_close(toks: &[Token], open: usize, opener: &Tok, closer: &Tok) -> Option<usize> {
2450 let same = |a: &Tok, b: &Tok| std::mem::discriminant(a) == std::mem::discriminant(b);
2451 let mut depth = 0usize;
2452 for (i, t) in toks.iter().enumerate().skip(open) {
2453 if same(&t.kind, opener) {
2454 depth += 1;
2455 } else if same(&t.kind, closer) {
2456 depth -= 1;
2457 if depth == 0 {
2458 return Some(i);
2459 }
2460 }
2461 }
2462 None
2463}
2464
2465fn fold_dfns(
2467 toks: Vec<Token>,
2468 d: Rules,
2469 verbs: &HashMap<String, Verb>,
2470) -> Result<Vec<Token>> {
2471 let Some(open) = toks.iter().position(|t| matches!(t.kind, Tok::LBrace)) else {
2472 return Ok(toks);
2473 };
2474 let close = match_close(&toks, open, &Tok::LBrace, &Tok::RBrace)
2475 .ok_or_else(|| Error::parse("unmatched {", toks[open].span))?;
2476 let span = Span::merge(toks[open].span, toks[close].span);
2477 let mut out: Vec<Token> = toks[..open].to_vec();
2478 let kind = match build_dfn(&toks[open + 1..close], d, verbs)? {
2479 Dfn::Func(verb) => Tok::Func(*verb),
2480 Dfn::Op { def, omega } => Tok::UserOp { def, omega },
2481 };
2482 out.push(Token { kind, span });
2483 out.extend_from_slice(&toks[close + 1..]);
2484 fold_dfns(out, d, verbs)
2486}
2487
2488fn split_statements(toks: &[Token]) -> Vec<&[Token]> {
2491 let mut out = Vec::new();
2492 let mut depth = 0usize;
2493 let mut start = 0usize;
2494 for (i, t) in toks.iter().enumerate() {
2495 match t.kind {
2496 Tok::LBrace => depth += 1,
2497 Tok::RBrace => depth = depth.saturating_sub(1),
2498 Tok::Separator if depth == 0 => {
2499 out.push(&toks[start..i]);
2500 start = i + 1;
2501 }
2502 _ => {}
2503 }
2504 }
2505 out.push(&toks[start..]);
2506 out.into_iter().filter(|s| !s.is_empty()).collect()
2507}
2508
2509enum Dfn {
2512 Func(Box<Verb>),
2513 Op { def: Arc<OpDef>, omega: bool },
2514}
2515
2516thread_local! {
2525 static ENCLOSING: std::cell::RefCell<Vec<u64>> =
2526 const { std::cell::RefCell::new(Vec::new()) };
2527 static NEXT_DFN_ID: std::cell::Cell<u64> = const { std::cell::Cell::new(1) };
2528}
2529
2530fn build_dfn(body: &[Token], d: Rules, verbs: &HashMap<String, Verb>) -> Result<Dfn> {
2535 let mut depth = 0usize;
2536 let mut dyadic = false;
2537 let mut alpha_op = false;
2538 let mut omega_op = false;
2539 for t in body {
2540 match &t.kind {
2541 Tok::LBrace => depth += 1,
2542 Tok::RBrace => depth = depth.saturating_sub(1),
2543 Tok::Name(n) if depth == 0 && n == "⍺" => dyadic = true,
2544 Tok::Name(n) if depth == 0 && n == "⍺⍺" => alpha_op = true,
2545 Tok::Name(n) if depth == 0 && n == "⍵⍵" => omega_op = true,
2546 _ => {}
2547 }
2548 }
2549 let reading = |alpha_value: bool, omega_value: bool| -> Result<Verb> {
2553 let mut inner = verbs.clone();
2554 if alpha_op && !alpha_value {
2555 inner.insert("⍺⍺".to_string(), Verb::Named("⍺⍺".to_string()));
2556 }
2557 if (alpha_op || omega_op) && !omega_value {
2558 inner.insert("⍵⍵".to_string(), Verb::Named("⍵⍵".to_string()));
2559 }
2560 let id = NEXT_DFN_ID.with(|c| {
2563 let id = c.get();
2564 c.set(id.wrapping_add(1));
2565 id
2566 });
2567 let enclosing = ENCLOSING.with(|e| e.borrow().clone());
2568 ENCLOSING.with(|e| e.borrow_mut().push(id));
2569 let parsed = parse_dfn_body(body, d, &mut inner);
2570 ENCLOSING.with(|e| {
2571 e.borrow_mut().pop();
2572 });
2573 let mut stmts = parsed?;
2574 match d.dfn_result {
2579 DfnResult::LastSentence => {}
2580 DfnResult::FirstNonAssignment => {
2581 let plain = |e: &Expr| {
2585 !matches!(
2586 e,
2587 Expr::Assign { .. }
2588 | Expr::AmendIndex { .. }
2589 | Expr::Control(..)
2590 | Expr::VerbDef { .. }
2591 | Expr::ModDef { .. }
2592 )
2593 };
2594 if let Some(k) = stmts.iter().position(plain) {
2595 stmts.truncate(k + 1);
2596 }
2597 }
2598 }
2599 let pure = stmts.iter().all(is_pure_stmt);
2600 Ok(Verb::Explicit(Arc::new(ExplicitDef {
2601 name: "{…}".to_string(),
2602 left: dyadic.then(|| "⍺".to_string()),
2603 right: "⍵".to_string(),
2604 dyad_only: false,
2608 spare_left: true,
2609 result: None,
2610 locals: Vec::new(),
2611 body: stmts,
2612 empty: None,
2614 labels: Vec::new(),
2615 enclosing,
2616 id,
2617 pure,
2618 })))
2619 };
2620 if !(alpha_op || omega_op) {
2621 return Ok(Dfn::Func(Box::new(reading(false, false)?)));
2622 }
2623 let mut readings: [std::result::Result<Verb, String>; 4] =
2628 [const { Err(String::new()) }; 4];
2629 for (i, slot) in readings.iter_mut().enumerate() {
2630 *slot = reading(i & 1 != 0, i & 2 != 0).map_err(|e| e.msg);
2631 }
2632 if let Err(msg) = &readings[0]
2635 && readings.iter().all(|r| r.is_err())
2636 {
2637 return Err(Error::parse(msg.clone(), body.first().map_or(Span::new(0, 0), |t| t.span)));
2638 }
2639 Ok(Dfn::Op { def: Arc::new(OpDef { readings }), omega: omega_op })
2640}
2641
2642fn parse_dfn_body(
2643 body: &[Token],
2644 d: Rules,
2645 verbs: &mut HashMap<String, Verb>,
2646) -> Result<Vec<Expr>> {
2647 let mut stmts = Vec::new();
2648 for stmt in split_statements(body) {
2649 let stmt: Vec<Token> = stmt
2651 .iter()
2652 .map(|t| match t.kind {
2653 Tok::Del => Token { kind: Tok::Func(Verb::SelfRef), span: t.span },
2654 _ => t.clone(),
2655 })
2656 .collect();
2657 stmts.push(parse_guarded(stmt, d, verbs)?);
2658 }
2659 Ok(stmts)
2660}
2661
2662fn parse_guarded(
2664 stmt: Vec<Token>,
2665 d: Rules,
2666 verbs: &mut HashMap<String, Verb>,
2667) -> Result<Expr> {
2668 let mut depth = 0usize;
2669 let mut colon = None;
2670 for (i, t) in stmt.iter().enumerate() {
2671 match t.kind {
2672 Tok::LBrace | Tok::LParen | Tok::LBracket => depth += 1,
2673 Tok::RBrace | Tok::RParen | Tok::RBracket => depth = depth.saturating_sub(1),
2674 Tok::Colon if depth == 0 => {
2675 colon = Some(i);
2676 break;
2677 }
2678 _ => {}
2679 }
2680 }
2681 if let Some(k) = colon {
2682 let span = Span::merge(stmt[0].span, stmt[stmt.len() - 1].span);
2683 let test = one_statement(stmt[..k].to_vec(), d, verbs, stmt[k].span)?;
2684 let body = one_statement(stmt[k + 1..].to_vec(), d, verbs, stmt[k].span)?;
2685 return Ok(Expr::Control(
2687 Box::new(Control::Guard {
2688 test: vec![test],
2689 body: vec![body, Expr::Control(Box::new(Control::Return), span)],
2690 }),
2691 span,
2692 ));
2693 }
2694 let default = matches!(
2698 (stmt.first().map(|t| &t.kind), stmt.get(1).map(|t| &t.kind)),
2699 (Some(Tok::Name(n)), Some(Tok::Assign)) if n == "⍺"
2700 );
2701 let span = stmt.first().map_or(Span::new(0, 0), |t| t.span);
2702 let e = one_statement(stmt, d, verbs, span)?;
2703 if default {
2704 let scope = match d.default_arg {
2705 DefaultArg::Eager => Scope::LocalDefault,
2706 DefaultArg::Lazy => return Err(Error::not_yet("a lazy ⍺← default", span)),
2707 };
2708 if let Expr::Assign { name, value, span, .. } = e {
2709 return Ok(Expr::Assign { name, value, scope, span });
2710 }
2711 }
2712 Ok(e)
2713}
2714
2715fn one_statement(
2716 stmt: Vec<Token>,
2717 d: Rules,
2718 verbs: &mut HashMap<String, Verb>,
2719 hint: Span,
2720) -> Result<Expr> {
2721 parse_statement(stmt, d, verbs, false)?
2722 .ok_or_else(|| Error::parse("this needs an expression", hint))
2723}
2724
2725fn is_pure_stmt(e: &Expr) -> bool {
2727 match e {
2728 Expr::Const(..) | Expr::Param(..) | Expr::Name(..) => true,
2729 Expr::Monad { verb, y, .. } => verb.is_pure() && is_pure_stmt(y),
2730 Expr::Dyad { verb, x, y, .. } => verb.is_pure() && is_pure_stmt(x) && is_pure_stmt(y),
2731 Expr::Assign { value, .. } => is_pure_stmt(value),
2732 Expr::Control(c, _) => is_pure_control(c),
2733 _ => false,
2734 }
2735}
2736
2737fn is_pure_control(c: &Control) -> bool {
2738 let all = |b: &Vec<Expr>| b.iter().all(is_pure_stmt);
2739 match c {
2740 Control::Return | Control::Break | Control::Continue => true,
2741 Control::Branch(target) => is_pure_stmt(target),
2742 Control::If { arms, otherwise } => {
2743 arms.iter().all(|a| a.test.as_ref().is_none_or(all) && all(&a.body))
2744 && otherwise.as_ref().is_none_or(all)
2745 }
2746 Control::While { test, body, .. } => all(test) && all(body),
2747 Control::For { source, body, .. } => is_pure_stmt(source) && all(body),
2748 Control::Select { subject, cases } => {
2749 is_pure_stmt(subject)
2750 && cases.iter().all(|c| c.test.as_ref().is_none_or(all) && all(&c.body))
2751 }
2752 Control::Try { body, catch } => all(body) && all(catch),
2753 Control::Guard { test, body } => all(test) && all(body),
2754 }
2755}
2756
2757fn parse_tradfn(
2764 sentences: &[Vec<Token>],
2765 i: &mut usize,
2766 d: Rules,
2767 verbs: &mut HashMap<String, Verb>,
2768) -> Result<Expr> {
2769 let header = &sentences[*i];
2770 let open = header[0].span;
2771 *i += 1;
2772 let head = header[1..].to_vec();
2773 let mut body_lines: Vec<Vec<Token>> = Vec::new();
2774 loop {
2775 let Some(line) = sentences.get(*i) else {
2776 return Err(Error::parse("this definition has no closing ∇", open));
2777 };
2778 *i += 1;
2779 if line.len() == 1 && matches!(line[0].kind, Tok::Del) {
2780 break;
2781 }
2782 body_lines.push(line.clone());
2783 }
2784 let close = sentences
2785 .get(i.saturating_sub(1))
2786 .and_then(|l| l.first())
2787 .map_or(open, |t| t.span);
2788 build_tradfn(&head, &body_lines, Span::merge(open, close), d, verbs)
2789}
2790
2791fn build_tradfn(
2794 head: &[Token],
2795 body_lines: &[Vec<Token>],
2796 span: Span,
2797 d: Rules,
2798 verbs: &mut HashMap<String, Verb>,
2799) -> Result<Expr> {
2800 let (name, def_left, def_right, result, locals) = parse_header(head, span)?;
2801 let mut inner = verbs.clone();
2803 inner.insert(name.clone(), Verb::Named(name.clone()));
2804 let mut items = Vec::new();
2805 let mut labels: Vec<(String, usize)> = Vec::new();
2806 for line in body_lines {
2807 let mut label = None;
2808 let item = to_item(line.clone(), d, &mut inner, &mut label)?;
2809 if let Some(name) = label {
2810 labels.push((name, items.len()));
2811 }
2812 items.push(item);
2813 }
2814 let item_count = items.len();
2815 let mut cursor = AplCursor { items: &items, at: 0, d };
2816 let mut body = parse_apl_block(&mut cursor, &[])?;
2817 if !labels.is_empty() && body.len() != item_count {
2821 return Err(Error::not_yet("a label and a control structure in one definition", span));
2822 }
2823 if let Some(item) = cursor.peek() {
2824 return Err(Error::parse(
2825 format!(":{} has no matching opening word", item.word().unwrap_or("?")),
2826 item.span(),
2827 ));
2828 }
2829 let mut own: Vec<String> = locals.clone();
2832 own.extend(result.clone());
2833 own.extend(def_left.clone());
2834 own.push(def_right.clone());
2835 for stmt in &mut body {
2836 set_scopes(stmt, &own);
2837 }
2838 let pure = body.iter().all(is_pure_stmt);
2839 let verb = Verb::Explicit(Arc::new(ExplicitDef {
2840 name: format!("∇{name}"),
2841 left: def_left,
2842 right: def_right,
2843 dyad_only: false,
2844 spare_left: false,
2848 enclosing: Vec::new(),
2849 id: 0,
2850 result,
2851 locals,
2852 body,
2853 empty: None,
2854 labels,
2855 pure,
2856 }));
2857 verbs.insert(name.clone(), verb.clone());
2858 Ok(Expr::VerbDef { name, verb, span })
2859}
2860
2861type Header = (String, Option<String>, String, Option<String>, Vec<String>);
2862
2863fn parse_header(toks: &[Token], span: Span) -> Result<Header> {
2865 let mut names: Vec<String> = Vec::new();
2866 let mut locals: Vec<String> = Vec::new();
2867 let mut result = None;
2868 let mut in_locals = false;
2869 let mut k = 0usize;
2870 if let (Some(Tok::Name(z)), Some(Tok::Assign)) =
2872 (toks.first().map(|t| &t.kind), toks.get(1).map(|t| &t.kind))
2873 {
2874 result = Some(z.clone());
2875 k = 2;
2876 }
2877 while k < toks.len() {
2878 match &toks[k].kind {
2879 Tok::Semi => in_locals = true,
2880 Tok::Name(n) if in_locals => locals.push(n.clone()),
2881 Tok::Name(n) => names.push(n.clone()),
2882 _ => {
2883 return Err(Error::parse("this is not a ∇ definition header", toks[k].span));
2884 }
2885 }
2886 k += 1;
2887 }
2888 match names.len() {
2889 3 => Ok((names[1].clone(), Some(names[0].clone()), names[2].clone(), result, locals)),
2890 2 => Ok((names[0].clone(), None, names[1].clone(), result, locals)),
2891 1 => Ok((names[0].clone(), None, crate::ir::NILADIC.to_string(), result, locals)),
2892 _ => Err(Error::parse("a ∇ definition header names a function and its arguments", span)),
2893 }
2894}
2895
2896enum AplItem {
2899 Sentence(Expr),
2900 Word { word: &'static str, rest: Vec<Token>, span: Span },
2901}
2902
2903impl AplItem {
2904 fn word(&self) -> Option<&'static str> {
2905 match self {
2906 AplItem::Word { word, .. } => Some(word),
2907 AplItem::Sentence(_) => None,
2908 }
2909 }
2910
2911 fn span(&self) -> Span {
2912 match self {
2913 AplItem::Word { span, .. } => *span,
2914 AplItem::Sentence(e) => e.span(),
2915 }
2916 }
2917}
2918
2919fn to_item(
2920 line: Vec<Token>,
2921 d: Rules,
2922 verbs: &mut HashMap<String, Verb>,
2923 label: &mut Option<String>,
2924) -> Result<AplItem> {
2925 let mut line = line;
2926 if let (Some(Tok::Name(n)), Some(Tok::Colon)) =
2929 (line.first().map(|t| &t.kind), line.get(1).map(|t| &t.kind))
2930 {
2931 *label = Some(n.clone());
2932 line.drain(..2);
2933 }
2934 if let Some(Tok::Control(word)) = line.first().map(|t| &t.kind) {
2935 let word = *word;
2936 let span = line[0].span;
2937 return Ok(AplItem::Word { word, rest: line[1..].to_vec(), span });
2938 }
2939 if matches!(line.first().map(|t| &t.kind), Some(Tok::Arrow)) {
2940 let span = line[0].span;
2941 let target = parse_statement(line[1..].to_vec(), d, verbs, true)?
2942 .ok_or_else(|| Error::parse("→ needs a line to branch to", span))?;
2943 let span = Span::merge(span, target.span());
2944 return Ok(AplItem::Sentence(Expr::Control(
2945 Box::new(Control::Branch(Box::new(target))),
2946 span,
2947 )));
2948 }
2949 if line.is_empty() {
2953 let span = label.as_ref().map_or(Span::new(0, 0), |_| Span::new(0, 0));
2954 let nowhere = Expr::Const(Array::empty(crate::dtype::DType::I64), span);
2955 return Ok(AplItem::Sentence(Expr::Control(
2956 Box::new(Control::Branch(Box::new(nowhere))),
2957 span,
2958 )));
2959 }
2960 let span = line.first().map_or(Span::new(0, 0), |t| t.span);
2961 let e = parse_statement(line, d, verbs, true)?
2962 .ok_or_else(|| Error::parse("this line has no sentence", span))?;
2963 Ok(AplItem::Sentence(e))
2964}
2965
2966struct AplCursor<'a> {
2967 items: &'a [AplItem],
2968 at: usize,
2969 d: Rules,
2971}
2972
2973impl<'a> AplCursor<'a> {
2974 fn peek(&self) -> Option<&'a AplItem> {
2975 self.items.get(self.at)
2976 }
2977
2978 fn peek_word(&self) -> Option<&'static str> {
2979 self.peek().and_then(AplItem::word)
2980 }
2981
2982 fn last_span(&self) -> Span {
2983 self.items
2984 .get(self.at.saturating_sub(1))
2985 .map_or_else(|| Span::new(0, 0), AplItem::span)
2986 }
2987
2988 fn close(&mut self, want: &str) -> Result<()> {
2990 match self.peek_word() {
2991 Some(w) if w == want || w == "End" => {
2992 self.at += 1;
2993 Ok(())
2994 }
2995 Some(w) => Err(Error::parse(
2996 format!("expected :{want} here, not :{w}"),
2997 self.peek().expect("a word").span(),
2998 )),
2999 None => Err(Error::parse(format!("this block needs a :{want}"), self.last_span())),
3000 }
3001 }
3002}
3003
3004fn parse_apl_block(cur: &mut AplCursor<'_>, stop: &[&str]) -> Result<Vec<Expr>> {
3005 let mut out = Vec::new();
3006 loop {
3007 match cur.peek() {
3008 None => return Ok(out),
3009 Some(AplItem::Word { word, .. }) if stop.contains(word) || *word == "End" => {
3010 return Ok(out);
3011 }
3012 Some(AplItem::Sentence(e)) => {
3013 cur.at += 1;
3014 out.push(e.clone());
3015 }
3016 Some(AplItem::Word { .. }) => out.push(parse_apl_control(cur)?),
3017 }
3018 }
3019}
3020
3021fn parse_apl_control(cur: &mut AplCursor<'_>) -> Result<Expr> {
3022 let Some(AplItem::Word { word, rest, span }) = cur.peek() else {
3023 return Err(Error::internal("expected a control word"));
3024 };
3025 let (word, rest, start) = (*word, rest.clone(), *span);
3026 cur.at += 1;
3027 let control = match word {
3028 "If" => {
3029 let mut arms = Vec::new();
3030 let mut otherwise = None;
3031 let mut test = rest;
3032 loop {
3033 let test_expr = condition(test, start, cur.d)?;
3034 let body = parse_apl_block(cur, &["ElseIf", "Else", "EndIf"])?;
3035 arms.push(Branch { test: Some(vec![test_expr]), body, fall_through: false });
3036 match cur.peek_word() {
3037 Some("ElseIf") => {
3038 let Some(AplItem::Word { rest, .. }) = cur.peek() else { unreachable!() };
3039 test = rest.clone();
3040 cur.at += 1;
3041 }
3042 Some("Else") => {
3043 cur.at += 1;
3044 otherwise = Some(parse_apl_block(cur, &["EndIf"])?);
3045 cur.close("EndIf")?;
3046 break;
3047 }
3048 _ => {
3049 cur.close("EndIf")?;
3050 break;
3051 }
3052 }
3053 }
3054 Control::If { arms, otherwise }
3055 }
3056 "While" => {
3057 let test = condition(rest, start, cur.d)?;
3058 let body = parse_apl_block(cur, &["EndWhile"])?;
3059 cur.close("EndWhile")?;
3060 Control::While { test: vec![test], body, body_first: false, until: false }
3061 }
3062 "Repeat" => {
3063 if !rest.is_empty() {
3064 return Err(Error::parse(":Repeat takes no condition", start));
3065 }
3066 let body = parse_apl_block(cur, &["Until"])?;
3067 let Some(AplItem::Word { rest, span, .. }) = cur.peek() else {
3068 return Err(Error::parse("this :Repeat needs an :Until", cur.last_span()));
3069 };
3070 let test = condition(rest.clone(), *span, cur.d)?;
3071 cur.at += 1;
3072 Control::While { test: vec![test], body, body_first: true, until: true }
3073 }
3074 "For" => {
3075 let (name, source) = for_header(&rest, start, cur.d)?;
3077 let body = parse_apl_block(cur, &["EndFor"])?;
3078 cur.close("EndFor")?;
3079 Control::For { name: Some(name), source: Box::new(source), body }
3080 }
3081 "Select" => {
3082 let subject = condition(rest, start, cur.d)?;
3083 let mut cases = Vec::new();
3084 loop {
3085 match cur.peek() {
3086 Some(AplItem::Word { word: "Case", rest, span }) => {
3087 let test = condition(rest.clone(), *span, cur.d)?;
3088 cur.at += 1;
3089 let body = parse_apl_block(cur, &["Case", "Else", "EndSelect"])?;
3090 cases.push(Branch {
3091 test: Some(vec![test]),
3092 body,
3093 fall_through: false,
3094 });
3095 }
3096 Some(AplItem::Word { word: "Else", .. }) => {
3097 cur.at += 1;
3098 let body = parse_apl_block(cur, &["EndSelect"])?;
3099 cases.push(Branch { test: None, body, fall_through: false });
3100 cur.close("EndSelect")?;
3101 break;
3102 }
3103 _ => {
3104 cur.close("EndSelect")?;
3105 break;
3106 }
3107 }
3108 }
3109 Control::Select { subject: Box::new(subject), cases }
3110 }
3111 "Return" => Control::Return,
3112 "Leave" => Control::Break,
3113 "Continue" => Control::Continue,
3114 other => {
3115 return Err(Error::parse(format!(":{other} has no matching opening word"), start));
3116 }
3117 };
3118 Ok(Expr::Control(Box::new(control), Span::merge(start, cur.last_span())))
3119}
3120
3121fn condition(rest: Vec<Token>, span: Span, d: Rules) -> Result<Expr> {
3123 match rest.first() {
3124 None => Err(Error::parse("this control word needs a condition", span)),
3125 Some(first) => {
3126 let hint = Span::merge(first.span, rest[rest.len() - 1].span);
3127 match &rest[0].kind {
3128 Tok::Control(w) => Err(Error::parse(format!("unexpected :{w}"), rest[0].span)),
3129 _ => Ok(AplItem::Sentence(parse_prepared(&rest, hint, d)?)).map(|it| match it {
3130 AplItem::Sentence(e) => e,
3131 AplItem::Word { .. } => unreachable!(),
3132 }),
3133 }
3134 }
3135 }
3136}
3137
3138fn for_header(rest: &[Token], span: Span, d: Rules) -> Result<(String, Expr)> {
3140 let Some(Tok::Name(name)) = rest.first().map(|t| &t.kind) else {
3141 return Err(Error::parse(":For needs a name to bind", span));
3142 };
3143 let Some(k) = rest.iter().position(|t| matches!(t.kind, Tok::Control("In"))) else {
3144 return Err(Error::parse(":For needs an :In", span));
3145 };
3146 if k != 1 {
3147 return Err(Error::not_yet("several :For names", span));
3148 }
3149 let source = &rest[k + 1..];
3150 let Some(first) = source.first() else {
3151 return Err(Error::parse(":In needs a value", span));
3152 };
3153 let hint = Span::merge(first.span, source[source.len() - 1].span);
3154 Ok((name.clone(), parse_prepared(source, hint, d)?))
3155}
3156
3157fn parse_prepared(toks: &[Token], hint: Span, d: Rules) -> Result<Expr> {
3159 let toks = fold_axes(fold_operators(toks.to_vec(), d)?, d)?;
3160 if toks.is_empty() {
3161 return Err(Error::parse("this needs an expression", hint));
3162 }
3163 parse_range(&toks, 0, toks.len(), hint, d)
3164}
3165
3166fn indexed_assignment(toks: &[Token], d: Rules, hint: Span) -> Result<Option<Expr>> {
3169 let Some(assign) = toks.iter().position(|t| matches!(t.kind, Tok::Assign)) else {
3170 return Ok(None);
3171 };
3172 if assign < 3 || !matches!(toks[assign - 1].kind, Tok::RBracket) {
3173 return Ok(None);
3174 }
3175 let close = assign - 1;
3176 let open = match_lbracket(toks, 0, close)?;
3177 if open == 0 {
3178 return Err(Error::parse("[ needs a value on its left", toks[open].span));
3179 }
3180 let Tok::Name(name) = &toks[open - 1].kind else {
3181 return Err(Error::not_yet("indexed assignment through an expression", hint));
3182 };
3183 if open != 1 {
3184 return Err(Error::not_yet("indexed assignment inside a larger sentence", hint));
3185 }
3186 let ranges = index_slots(toks, open + 1, close, toks[open].span)?;
3187 let mut slots = Vec::with_capacity(ranges.len());
3188 for slot in &ranges {
3189 slots.push(match *slot {
3190 None => None,
3191 Some((lo, hi)) => Some(parse_range(toks, lo, hi, toks[open].span, d)?),
3192 });
3193 }
3194 let value = parse_range(toks, assign + 1, toks.len(), toks[assign].span, d)?;
3195 let span = Span::merge(toks[0].span, toks[toks.len() - 1].span);
3196 Ok(Some(Expr::AmendIndex {
3197 name: name.clone(),
3198 slots,
3199 value: Box::new(value),
3200 origin: d.origin,
3201 scope: Scope::Local,
3202 span,
3203 }))
3204}
3205
3206fn set_scopes(e: &mut Expr, own: &[String]) {
3210 let pick = |name: &str| {
3211 if own.iter().any(|n| n == name) {
3212 Scope::Local
3213 } else {
3214 Scope::Global
3215 }
3216 };
3217 match e {
3218 Expr::Assign { name, value, scope, .. } => {
3219 *scope = pick(name);
3220 set_scopes(value, own);
3221 }
3222 Expr::AmendIndex { name, slots, value, scope, .. } => {
3223 *scope = pick(name);
3224 for slot in slots.iter_mut().flatten() {
3225 set_scopes(slot, own);
3226 }
3227 set_scopes(value, own);
3228 }
3229 Expr::Monad { y, .. } => set_scopes(y, own),
3230 Expr::Dyad { x, y, .. } => {
3231 set_scopes(x, own);
3232 set_scopes(y, own);
3233 }
3234 Expr::PrintPass { value, .. } => set_scopes(value, own),
3235 Expr::Input { .. } => {}
3236 Expr::Control(c, _) => {
3237 let walk = |b: &mut Vec<Expr>| b.iter_mut().for_each(|s| set_scopes(s, own));
3238 match &mut **c {
3239 Control::Branch(target) => set_scopes(target, own),
3240 Control::If { arms, otherwise } => {
3241 for arm in arms {
3242 if let Some(t) = &mut arm.test {
3243 walk(t);
3244 }
3245 walk(&mut arm.body);
3246 }
3247 if let Some(b) = otherwise {
3248 walk(b);
3249 }
3250 }
3251 Control::While { test, body, .. } | Control::Guard { test, body } => {
3252 walk(test);
3253 walk(body);
3254 }
3255 Control::For { source, body, .. } => {
3256 set_scopes(source, own);
3257 walk(body);
3258 }
3259 Control::Select { subject, cases } => {
3260 set_scopes(subject, own);
3261 for case in cases {
3262 if let Some(t) = &mut case.test {
3263 walk(t);
3264 }
3265 walk(&mut case.body);
3266 }
3267 }
3268 Control::Try { body, catch } => {
3269 walk(body);
3270 walk(catch);
3271 }
3272 Control::Return | Control::Break | Control::Continue => {}
3273 }
3274 }
3275 Expr::Const(..)
3276 | Expr::Param(..)
3277 | Expr::Name(..)
3278 | Expr::Fused { .. }
3279 | Expr::Elided { .. }
3280 | Expr::VerbDef { .. }
3281 | Expr::ModDef { .. } => {}
3282 }
3283}
3284
3285#[cfg(test)]
3286mod tests {
3287 use super::*;
3288 use crate::error::ErrorKind;
3289 use rstest::rstest;
3290
3291 fn rules(origin: i64) -> Rules {
3293 crate::Dialect { index_origin: Some(origin), ..crate::Dialect::default() }
3294 .rules(crate::Lang::Apl)
3295 .expect("the shipped dialect is implemented")
3296 }
3297
3298 fn p(src: &str) -> Result<Vec<Expr>> {
3300 parse(&SourceParts::from_source(src).unwrap(), rules(1))
3301 }
3302
3303 fn one(src: &str) -> Expr {
3304 let mut stmts = p(src).unwrap_or_else(|e| panic!("{src}: {e}"));
3305 assert_eq!(stmts.len(), 1, "{src}: expected one sentence");
3306 stmts.pop().unwrap()
3307 }
3308
3309 fn err(src: &str) -> Error {
3310 match p(src) {
3311 Ok(_) => panic!("{src}: expected an error"),
3312 Err(e) => e,
3313 }
3314 }
3315
3316 fn as_const(e: &Expr) -> &Array {
3317 match e {
3318 Expr::Const(a, _) => a,
3319 other => panic!("expected a constant, got {other:?}"),
3320 }
3321 }
3322
3323 fn as_prim(v: &Verb) -> Prim {
3325 match v {
3326 Verb::Prim(p) => *p,
3327 other => panic!("expected a primitive, got {other:?}"),
3328 }
3329 }
3330
3331 fn monad_of<'a>(e: &'a Expr, name: &str) -> &'a Expr {
3332 match e {
3333 Expr::Monad { verb, y, .. } => {
3334 assert_eq!(as_prim(verb).name, name, "monad name");
3335 y.as_ref()
3336 }
3337 other => panic!("expected a monad, got {other:?}"),
3338 }
3339 }
3340
3341 fn dyad_of<'a>(e: &'a Expr, name: &str) -> (&'a Expr, &'a Expr) {
3342 match e {
3343 Expr::Dyad { verb, x, y, .. } => {
3344 assert_eq!(as_prim(verb).name, name, "dyad name");
3345 (x.as_ref(), y.as_ref())
3346 }
3347 other => panic!("expected a dyad, got {other:?}"),
3348 }
3349 }
3350
3351 fn verb_of(e: &Expr) -> &Verb {
3352 match e {
3353 Expr::Monad { verb, .. } | Expr::Dyad { verb, .. } => verb,
3354 other => panic!("expected an application, got {other:?}"),
3355 }
3356 }
3357
3358 #[test]
3361 fn single_number_is_a_scalar() {
3362 let e = one("5");
3363 let a = as_const(&e);
3364 assert_eq!(a.shape, Vec::<usize>::new());
3365 assert_eq!(a.data, Data::I64(vec![5].into()));
3366 }
3367
3368 #[test]
3369 fn adjacent_numbers_merge_into_one_vector() {
3370 let a = as_const(&one("2 3 4")).clone();
3371 assert_eq!(a.shape, vec![3]);
3372 assert_eq!(a.data, Data::I64(vec![2, 3, 4].into()));
3373 }
3374
3375 #[test]
3376 fn one_float_makes_the_whole_vector_float() {
3377 let a = as_const(&one("1 2.5 3")).clone();
3378 assert_eq!(a.shape, vec![3]);
3379 assert_eq!(a.data, Data::F64(vec![1.0, 2.5, 3.0].into()));
3380 }
3381
3382 #[rstest]
3383 #[case("¯3", Data::I64(vec![-3].into()))]
3384 #[case("¯3.5", Data::F64(vec![-3.5].into()))]
3385 #[case("1e3", Data::I64(vec![1000].into()))]
3386 #[case("1e¯3", Data::F64(vec![0.001].into()))]
3387 #[case("2.5e2", Data::F64(vec![250.0].into()))]
3388 #[case("¯1 ¯2", Data::I64(vec![-1, -2].into()))]
3389 fn numeric_literals(#[case] src: &str, #[case] want: Data) {
3390 assert_eq!(as_const(&one(src)).data, want);
3391 }
3392
3393 #[test]
3394 fn single_char_string_is_rank_zero() {
3395 let a = as_const(&one("'a'")).clone();
3396 assert_eq!(a.shape, Vec::<usize>::new());
3397 assert_eq!(a.data, Data::Char(vec!['a'].into()));
3398 }
3399
3400 #[test]
3401 fn string_escape_doubles_the_quote() {
3402 let a = as_const(&one("'don''t'")).clone();
3403 assert_eq!(a.shape, vec![5]);
3404 assert_eq!(a.data, Data::Char("don't".chars().collect()));
3405 }
3406
3407 #[test]
3408 fn empty_string_is_an_empty_char_vector() {
3409 let a = as_const(&one("''")).clone();
3410 assert_eq!(a.shape, vec![0]);
3411 assert_eq!(a.data, Data::Char(vec![].into()));
3412 }
3413
3414 #[test]
3415 fn unterminated_string_is_a_parse_error() {
3416 let e = err("'abc");
3417 assert_eq!(e.kind, ErrorKind::Parse);
3418 assert!(e.msg.contains("unterminated"), "{}", e.msg);
3419 }
3420
3421 #[rstest]
3422 #[case("2j3", vec![[2.0, 3.0]])]
3423 #[case("1J¯1", vec![[1.0, -1.0]])]
3424 #[case("2 1j2", vec![[2.0, 0.0], [1.0, 2.0]])]
3425 fn complex_literals(#[case] src: &str, #[case] want: Vec<[f64; 2]>) {
3426 assert_eq!(as_const(&one(src)).data, Data::Complex(want.into()));
3427 }
3428
3429 #[test]
3432 fn a_comment_runs_to_the_end_of_the_line() {
3433 let stmts = p("2+2 ⍝ a note ⋄ still a note\n3").unwrap();
3434 assert_eq!(stmts.len(), 2);
3435 dyad_of(&stmts[0], "+");
3436 assert_eq!(as_const(&stmts[1]).data, Data::I64(vec![3].into()));
3437 }
3438
3439 #[test]
3440 fn blank_sentences_are_skipped() {
3441 let stmts = p("\n\n2 ⋄ ⋄ 3 ⋄\n").unwrap();
3442 assert_eq!(stmts.len(), 2);
3443 }
3444
3445 #[test]
3446 fn diamond_and_newline_both_separate_sentences() {
3447 let stmts = p("x←3 ⋄ x+1").unwrap();
3448 assert_eq!(stmts.len(), 2);
3449 match &stmts[0] {
3450 Expr::Assign { name, value, .. } => {
3451 assert_eq!(name, "x");
3452 assert_eq!(as_const(value).data, Data::I64(vec![3].into()));
3453 }
3454 other => panic!("expected an assignment, got {other:?}"),
3455 }
3456 let (x, y) = dyad_of(&stmts[1], "+");
3457 assert!(matches!(x, Expr::Name(n, _) if n == "x"));
3458 assert_eq!(as_const(y).data, Data::I64(vec![1].into()));
3459 }
3460
3461 #[rstest]
3462 #[case("x")]
3463 #[case("abc123")]
3464 #[case("∆x")]
3465 #[case("⍙y_2")]
3466 #[case("Σ")]
3467 fn names(#[case] src: &str) {
3468 match one(src) {
3469 Expr::Name(n, _) => assert_eq!(n, src),
3470 other => panic!("expected a name, got {other:?}"),
3471 }
3472 }
3473
3474 #[test]
3475 fn unknown_symbol_is_reported_with_its_position() {
3476 let e = err("2 @ 3");
3477 assert_eq!(e.kind, ErrorKind::Parse);
3478 assert_eq!(e.msg, "unknown symbol: @");
3479 assert_eq!(e.span, Some(Span::new(2, 3)));
3480 }
3481
3482 #[test]
3483 fn system_variables_are_read_only() {
3484 let e = err("⎕IO←0");
3487 assert_eq!(e.kind, ErrorKind::Language);
3488 assert!(e.msg.contains("read-only"), "{}", e.msg);
3489 let e = err("⎕TS");
3492 assert_eq!(e.kind, ErrorKind::Sandbox);
3493 assert!(e.msg.contains("outside the program"), "{}", e.msg);
3494 }
3495
3496 #[rstest]
3499 #[case('+', MonadOp::Scalar(ScalarMonad::Conj), DyadOp::Scalar(ScalarDyad::Add))]
3500 #[case('-', MonadOp::Scalar(ScalarMonad::Neg), DyadOp::Scalar(ScalarDyad::Sub))]
3501 #[case('×', MonadOp::Scalar(ScalarMonad::Signum), DyadOp::Scalar(ScalarDyad::Mul))]
3502 #[case('÷', MonadOp::Scalar(ScalarMonad::Recip), DyadOp::Scalar(ScalarDyad::DivApl))]
3503 #[case('⌈', MonadOp::Scalar(ScalarMonad::Ceil), DyadOp::Scalar(ScalarDyad::Max))]
3504 #[case('⌊', MonadOp::Scalar(ScalarMonad::Floor), DyadOp::Scalar(ScalarDyad::Min))]
3505 #[case('*', MonadOp::Scalar(ScalarMonad::Exp), DyadOp::Scalar(ScalarDyad::Pow))]
3506 #[case('|', MonadOp::Scalar(ScalarMonad::Abs), DyadOp::Scalar(ScalarDyad::Residue))]
3507 #[case('=', MonadOp::None, DyadOp::Scalar(ScalarDyad::Eq))]
3508 #[case('<', MonadOp::None, DyadOp::Scalar(ScalarDyad::Lt))]
3509 #[case('≤', MonadOp::None, DyadOp::Scalar(ScalarDyad::Le))]
3510 #[case('>', MonadOp::None, DyadOp::Scalar(ScalarDyad::Gt))]
3511 #[case('≥', MonadOp::None, DyadOp::Scalar(ScalarDyad::Ge))]
3512 #[case('⍴', MonadOp::ShapeOf, DyadOp::Reshape)]
3513 #[case('⍉', MonadOp::TransposeAxes, DyadOp::TransposeApl)]
3514 #[case(',', MonadOp::Ravel, DyadOp::AppendLast)]
3515 #[case('⍪', MonadOp::TableOf, DyadOp::AppendLeading)]
3516 #[case('!', MonadOp::Scalar(ScalarMonad::Factorial), DyadOp::Scalar(ScalarDyad::Binomial))]
3517 #[case('⍕', MonadOp::Format, DyadOp::FormatSpec)]
3518 #[case('⊥', MonadOp::None, DyadOp::DecodeApl)]
3519 #[case('⊤', MonadOp::None, DyadOp::EncodeApl)]
3520 #[case('≢', MonadOp::Tally, DyadOp::NotMatch)]
3521 #[case('≡', MonadOp::Depth { signed: false }, DyadOp::Match)]
3522 #[case('∊', MonadOp::Enlist, DyadOp::MemberApl)]
3523 #[case('∪', MonadOp::Nub, DyadOp::Union)]
3524 #[case('∧', MonadOp::None, DyadOp::Scalar(ScalarDyad::Lcm))]
3525 #[case('∨', MonadOp::None, DyadOp::Scalar(ScalarDyad::Gcd))]
3526 #[case('⍟', MonadOp::Scalar(ScalarMonad::Ln), DyadOp::Scalar(ScalarDyad::Log))]
3527 #[case('~', MonadOp::Scalar(ScalarMonad::Not), DyadOp::Less)]
3528 #[case('⊖', MonadOp::Reverse, DyadOp::RotateApl { last: false })]
3529 #[case('⍋', MonadOp::GradeUp { origin: 1 }, DyadOp::CollateGrade { down: false, origin: 1 })]
3530 #[case('⍒', MonadOp::GradeDown { origin: 1 }, DyadOp::CollateGrade { down: true, origin: 1 })]
3531 #[case('⊢', MonadOp::Same, DyadOp::Right)]
3532 #[case('⊣', MonadOp::Same, DyadOp::Left)]
3533 #[case('↑', MonadOp::First, DyadOp::Take)]
3534 #[case('⊂', MonadOp::Enclose(Enclose::ExceptSimpleScalar), DyadOp::PartitionEnclose)]
3535 #[case('⊃', MonadOp::Open, DyadOp::Pick { origin: 1 })]
3536 #[case('↓', MonadOp::Split, DyadOp::Drop)]
3537 fn primitive_meanings(#[case] glyph: char, #[case] monad: MonadOp, #[case] dyad: DyadOp) {
3538 let src = format!("{glyph}1");
3539 let e = one(&src);
3540 match e {
3541 Expr::Monad { verb, .. } => {
3542 let prim = as_prim(&verb);
3543 assert_eq!(prim.monad, monad);
3544 assert_eq!(prim.dyad, dyad);
3545 assert_eq!(prim.name.chars().next(), Some(glyph));
3546 }
3547 other => panic!("expected a monad, got {other:?}"),
3548 }
3549 }
3550
3551 #[test]
3552 fn monadic_not_equal_is_the_nub_sieve() {
3553 let e = one("≠1");
3554 match e {
3555 Expr::Monad { verb, .. } => {
3556 assert_eq!(as_prim(&verb).monad, MonadOp::NubSieve);
3557 }
3558 other => panic!("expected a monad, got {other:?}"),
3559 }
3560 }
3561
3562 #[test]
3563 fn monadic_equals_parses_and_is_left_to_evaluation() {
3564 let e = one("=1");
3566 assert_eq!(as_prim(verb_of(&e)).monad, MonadOp::None);
3567 }
3568
3569 #[rstest]
3570 #[case(0)]
3571 #[case(1)]
3572 fn iota_carries_the_index_origin(#[case] origin: i64) {
3573 let sp = SourceParts::from_source("⍳3").unwrap();
3574 let stmts = parse(&sp, rules(origin)).unwrap();
3575 match &stmts[0] {
3576 Expr::Monad { verb, .. } => {
3577 assert_eq!(as_prim(verb).monad, MonadOp::IotaApl { origin });
3578 assert_eq!(as_prim(verb).dyad, DyadOp::IndexOf { origin, vector_left: false });
3579 assert_eq!(as_prim(verb).ranks, [RANK_INF, RANK_INF, RANK_INF]);
3580 }
3581 other => panic!("expected a monad, got {other:?}"),
3582 }
3583 }
3584
3585 #[test]
3586 fn reverse_and_rotate_pick_their_axis() {
3587 let e = one("⌽2 3⍴⍳6");
3590 match verb_of(&e) {
3591 Verb::Rank(f, ranks) => {
3592 assert_eq!(*ranks, [1, RANK_INF, RANK_INF]);
3593 assert_eq!(as_prim(f).monad, MonadOp::Reverse);
3594 assert_eq!(as_prim(f).dyad, DyadOp::RotateApl { last: true });
3595 }
3596 other => panic!("expected a ranked verb, got {other:?}"),
3597 }
3598 assert!(matches!(verb_of(&one("⊖2 3⍴⍳6")), Verb::Prim(_)));
3600 }
3601
3602 #[test]
3603 fn reshape_ranks_are_infinite_one_infinite() {
3604 let e = one("2 3⍴⍳6");
3605 assert_eq!(verb_of(&e).ranks(), [RANK_INF, 1, RANK_INF]);
3606 }
3607
3608 #[test]
3611 fn reshape_of_iota() {
3612 let e = one("2 3⍴⍳6");
3613 let (x, y) = dyad_of(&e, "⍴");
3614 assert_eq!(as_const(x).data, Data::I64(vec![2, 3].into()));
3615 let iy = monad_of(y, "⍳");
3616 assert_eq!(as_const(iy).data, Data::I64(vec![6].into()));
3617 }
3618
3619 #[test]
3620 fn leading_minus_is_monadic_and_the_rest_is_evaluated_first() {
3621 let e = one("-3+4");
3623 let inner = monad_of(&e, "-");
3624 let (x, y) = dyad_of(inner, "+");
3625 assert_eq!(as_const(x).data, Data::I64(vec![3].into()));
3626 assert_eq!(as_const(y).data, Data::I64(vec![4].into()));
3627 }
3628
3629 #[test]
3630 fn a_chain_of_dyads_associates_to_the_right() {
3631 let e = one("2×3+4");
3632 let (x, y) = dyad_of(&e, "×");
3633 assert_eq!(as_const(x).data, Data::I64(vec![2].into()));
3634 dyad_of(y, "+");
3635 }
3636
3637 #[test]
3638 fn parentheses_override_the_order() {
3639 let e = one("(2+3)×4");
3640 let (x, y) = dyad_of(&e, "×");
3641 dyad_of(x, "+");
3642 assert_eq!(as_const(y).data, Data::I64(vec![4].into()));
3643 }
3644
3645 #[test]
3646 fn nested_parentheses() {
3647 let e = one("((2+3))×4");
3648 let (x, _) = dyad_of(&e, "×");
3649 dyad_of(x, "+");
3650 }
3651
3652 #[test]
3653 fn a_function_left_of_a_function_is_monadic() {
3654 let e = one("⍴⍳5");
3656 monad_of(monad_of(&e, "⍴"), "⍳");
3657 }
3658
3659 #[test]
3662 fn slash_reduces_the_last_axis() {
3663 let e = one("+/2 3⍴⍳6");
3667 match &e {
3668 Expr::Monad { verb: Verb::Rank(inner, ranks), .. } => {
3669 assert_eq!(*ranks, [1, RANK_INF, 1]);
3670 match inner.as_ref() {
3671 Verb::NWise(f) => assert_eq!(as_prim(f).name, "+"),
3672 other => panic!("expected a reduce, got {other:?}"),
3673 }
3674 }
3675 other => panic!("expected monadic Rank(NWise(+)), got {other:?}"),
3676 }
3677 }
3678
3679 #[test]
3680 fn slashbar_reduces_the_leading_axis() {
3681 let e = one("+⌿2 3⍴⍳6");
3682 match &e {
3683 Expr::Monad { verb: Verb::NWise(f), .. } => assert_eq!(as_prim(f).name, "+"),
3684 other => panic!("expected monadic NWise(+), got {other:?}"),
3685 }
3686 }
3687
3688 #[test]
3689 fn backslash_scans_the_last_axis_and_backslashbar_the_leading_one() {
3690 let inner = |v: &Verb| match v {
3693 Verb::Windowed(g, WindowKind::Scan) => match &**g {
3694 Verb::Reduce(h) => as_prim(h).name,
3695 other => panic!("expected a reduction under the scan, got {other:?}"),
3696 },
3697 other => panic!("expected a scan, got {other:?}"),
3698 };
3699 match &one("+\\1 2 3") {
3700 Expr::Monad { verb: Verb::Rank(f, ranks), .. } => {
3701 assert_eq!(*ranks, [1, 1, 1]);
3702 assert_eq!(inner(f), "+");
3703 }
3704 other => panic!("expected a ranked scan, got {other:?}"),
3705 }
3706 match &one("+⍀1 2 3") {
3707 Expr::Monad { verb, .. } => assert_eq!(inner(verb), "+"),
3708 other => panic!("expected a leading-axis scan, got {other:?}"),
3709 }
3710 }
3711
3712 #[rstest]
3715 #[case("1 0 1/1 2 3", "/")]
3716 #[case("1 0 1⌿1 2 3", "⌿")]
3717 #[case("x/1 2 3", "/")]
3718 #[case("(1 0)/1 2 3", "/")]
3719 fn slash_after_an_operand_is_replicate(#[case] src: &str, #[case] name: &str) {
3720 let e = one(src);
3721 let (_, _) = dyad_of(&e, name);
3722 assert_eq!(as_prim(verb_of(&e)).dyad, DyadOp::Copy);
3723 }
3724
3725 #[rstest]
3726 #[case("1 0 1\\1 2 3")]
3727 #[case("1 0 1⍀1 2 3")]
3728 fn expand_after_a_value_is_a_function(#[case] src: &str) {
3729 let e = one(src);
3730 assert_eq!(as_prim(verb_of(&e)).dyad, DyadOp::Expand);
3731 }
3732
3733 #[test]
3734 fn commute_and_power_are_operators() {
3735 match one("2-⍨5") {
3736 Expr::Dyad { verb: Verb::Commute(f), .. } => assert_eq!(as_prim(&f).name, "-"),
3737 other => panic!("expected a commute, got {other:?}"),
3738 }
3739 match one("+⍣3⊢5") {
3740 Expr::Monad { verb: Verb::PowerN(_, p), .. } => assert_eq!(p, Power::Times(3)),
3741 other => panic!("expected a power, got {other:?}"),
3742 }
3743 match one("+⍣≡⊢5") {
3744 Expr::Monad { verb: Verb::PowerUntil(..), .. } => {}
3745 other => panic!("expected a power until, got {other:?}"),
3746 }
3747 match one("⌽⍣¯1⊢5") {
3750 Expr::Monad { verb: Verb::PowerN(_, p), .. } => assert_eq!(p, Power::Times(1)),
3751 other => panic!("expected a power, got {other:?}"),
3752 }
3753 let e = err("⍴⍣¯1⊢5");
3754 assert_eq!(e.kind, ErrorKind::NotYet);
3755 assert!(e.msg.contains("obverse"), "{}", e.msg);
3756 }
3757
3758 #[rstest]
3759 #[case("+⍤2⊢5", [2, 2, 2])]
3760 #[case("+⍤1 2⊢5", [2, 1, 2])]
3761 #[case("+⍤0 1 2⊢5", [0, 1, 2])]
3762 #[case("+⍤¯1⊢5", [-1, -1, -1])]
3763 fn rank_operator_spec(#[case] src: &str, #[case] want: [i64; 3]) {
3764 let e = one(src);
3765 match &e {
3766 Expr::Monad { verb: Verb::Rank(f, ranks), .. } => {
3767 assert_eq!(*ranks, want);
3768 assert_eq!(as_prim(f).name, "+");
3769 }
3770 other => panic!("expected monadic Rank(+), got {other:?}"),
3771 }
3772 }
3773
3774 #[test]
3775 fn rank_operator_stacks_on_a_derived_function() {
3776 let e = one("+/⍤1⊢5");
3777 match &e {
3778 Expr::Monad { verb: Verb::Rank(inner, ranks), .. } => {
3779 assert_eq!(*ranks, [1, 1, 1]);
3780 assert!(matches!(inner.as_ref(), Verb::Rank(_, [1, RANK_INF, 1])));
3781 }
3782 other => panic!("expected Rank(Rank(NWise(+))), got {other:?}"),
3783 }
3784 }
3785
3786 #[test]
3787 fn a_function_operand_makes_the_rank_operator_an_atop() {
3788 let e = one("+⍤×5");
3790 let Expr::Monad { verb, .. } = e else { panic!("expected a monad") };
3791 assert!(matches!(verb, Verb::Atop(..)), "{verb:?}");
3792 }
3793
3794 #[rstest]
3795 #[case("+⍤0 1 2 3⊢5", "1 to 3")]
3796 #[case("+⍤", "rank specification")]
3797 #[case("+⍤2.5⊢5", "must be integers")]
3798 #[case("+⍤'a'⊢5", "must be integers")]
3799 fn bad_rank_specifications(#[case] src: &str, #[case] fragment: &str) {
3800 let e = err(src);
3801 assert_eq!(e.kind, ErrorKind::Parse);
3802 assert!(e.msg.contains(fragment), "{}", e.msg);
3803 }
3804
3805 #[test]
3808 fn quad_arrow_is_print_pass() {
3809 let e = one("⎕←2+2");
3810 match &e {
3811 Expr::PrintPass { value, .. } => {
3812 dyad_of(value, "+");
3813 }
3814 other => panic!("expected PrintPass, got {other:?}"),
3815 }
3816 }
3817
3818 #[test]
3819 fn assignment_chains() {
3820 let e = one("a←b←5");
3821 match &e {
3822 Expr::Assign { name, value, .. } => {
3823 assert_eq!(name, "a");
3824 match value.as_ref() {
3825 Expr::Assign { name, value, .. } => {
3826 assert_eq!(name, "b");
3827 assert_eq!(as_const(value).data, Data::I64(vec![5].into()));
3828 }
3829 other => panic!("expected a nested assignment, got {other:?}"),
3830 }
3831 }
3832 other => panic!("expected an assignment, got {other:?}"),
3833 }
3834 }
3835
3836 #[test]
3837 fn assignment_inside_an_expression() {
3838 let e = one("2+a←3");
3839 let (x, y) = dyad_of(&e, "+");
3840 assert_eq!(as_const(x).data, Data::I64(vec![2].into()));
3841 match y {
3842 Expr::Assign { name, value, .. } => {
3843 assert_eq!(name, "a");
3844 assert_eq!(as_const(value).data, Data::I64(vec![3].into()));
3845 }
3846 other => panic!("expected an assignment, got {other:?}"),
3847 }
3848 }
3849
3850 #[rstest]
3851 #[case("2←3")]
3852 #[case("(2+2)←3")]
3853 fn assignment_target_must_be_a_name(#[case] src: &str) {
3854 let e = err(src);
3855 assert_eq!(e.kind, ErrorKind::Parse);
3856 assert_eq!(e.msg, "assignment target must be a name");
3857 }
3858
3859 #[test]
3862 fn a_parameter_hole_is_an_operand() {
3863 let sp = SourceParts::from_parts(&["", "+1"], &["x"]);
3864 let stmts = parse(&sp, rules(1)).unwrap();
3865 let (x, y) = dyad_of(&stmts[0], "+");
3866 assert!(matches!(x, Expr::Param(0, _)));
3867 assert_eq!(as_const(y).data, Data::I64(vec![1].into()));
3868 assert_eq!(x.span(), Span::new(0, 3));
3870 assert_eq!(sp.display, "{x}+1");
3871 }
3872
3873 #[test]
3874 fn a_parameter_can_be_reduced_over() {
3875 let sp = SourceParts::from_parts(&["+/", ""], &["m"]);
3876 let stmts = parse(&sp, rules(1)).unwrap();
3877 match &stmts[0] {
3878 Expr::Monad { verb: Verb::Rank(_, [1, RANK_INF, 1]), y, .. } => {
3879 assert!(matches!(y.as_ref(), Expr::Param(0, _)));
3880 }
3881 other => panic!("expected a reduction over a parameter, got {other:?}"),
3882 }
3883 }
3884
3885 #[test]
3886 fn a_parameter_inside_a_comment_is_dropped() {
3887 let sp = SourceParts::from_parts(&["1 ⍝ ", "\n2"], &["x"]);
3888 let stmts = parse(&sp, rules(1)).unwrap();
3889 assert_eq!(stmts.len(), 2);
3890 assert_eq!(as_const(&stmts[0]).data, Data::I64(vec![1].into()));
3891 assert_eq!(as_const(&stmts[1]).data, Data::I64(vec![2].into()));
3892 }
3893
3894 #[test]
3897 fn nodes_cover_their_source_extent() {
3898 let src = "2 3⍴⍳6";
3899 let e = one(src);
3900 assert_eq!(e.span(), Span::new(0, src.len()));
3901 let (x, y) = dyad_of(&e, "⍴");
3902 assert_eq!(x.span(), Span::new(0, 3));
3903 assert_eq!(y.span(), Span::new(6, src.len()));
3905 }
3906
3907 #[test]
3908 fn spans_of_a_later_sentence_are_absolute() {
3909 let src = "x←3 ⋄ x+1";
3910 let stmts = p(src).unwrap();
3911 assert_eq!(&src[10..], "x+1");
3913 assert_eq!(stmts[1].span(), Span::new(10, src.len()));
3914 }
3915
3916 #[test]
3917 fn a_dyad_span_includes_the_parenthesised_left_argument() {
3918 let src = "(2+3)×4";
3919 let e = one(src);
3920 assert_eq!(e.span(), Span::new(0, src.len()));
3921 }
3922
3923 #[rstest]
3928 #[case("(2 3)(4 5)", 2)]
3929 #[case("2 x", 2)]
3930 #[case("x y", 2)]
3931 #[case("2(3)", 2)]
3932 #[case("1 2 (3 4)", 3)]
3933 #[case("'ab' 'cd' 'ef'", 3)]
3934 fn juxtaposition_is_vector_notation(#[case] src: &str, #[case] items: usize) {
3935 let mut e = &one(src);
3938 for _ in 0..items - 1 {
3939 match e {
3940 Expr::Dyad { verb, y, .. } => {
3941 assert_eq!(verb.name(), "(vector notation)", "{src}");
3942 e = y.as_ref();
3943 }
3944 other => panic!("{src}: expected a strand, got {other:?}"),
3945 }
3946 }
3947 assert!(matches!(e, Expr::Monad { .. }), "{src}: {e:?}");
3948 }
3949
3950 #[rstest]
3951 #[case("2+", "missing right argument")]
3952 #[case("x←", "← needs a value")]
3953 #[case("(2+3", "syntax error")]
3954 #[case("2+3)", "unmatched )")]
3955 #[case("()", "empty parentheses")]
3956 #[case("/2 3", "needs a function to its left")]
3957 fn syntax_errors(#[case] src: &str, #[case] fragment: &str) {
3958 let e = err(src);
3959 assert_eq!(e.kind, ErrorKind::Parse);
3960 assert!(e.msg.contains(fragment), "{src}: {}", e.msg);
3961 }
3962
3963 #[test]
3964 fn empty_source_has_no_statements() {
3965 assert!(p("").unwrap().is_empty());
3966 assert!(p(" ⍝ nothing here\n").unwrap().is_empty());
3967 }
3968
3969 #[rstest]
3971 #[case("2+2")]
3972 #[case("¯2×3")]
3973 #[case("-3+4")]
3974 #[case("0÷0")]
3975 #[case("⍳4")]
3976 #[case("⍳0")]
3977 #[case("2 3⍴⍳6")]
3978 #[case("⍴2 3⍴⍳6")]
3979 #[case("⍉2 3⍴⍳6")]
3980 #[case("≢7 8 9")]
3981 #[case("2↑9 8 7")]
3982 #[case("¯2↑9 8 7")]
3983 #[case("1↓3 3⍴⍳9")]
3984 #[case(",2 2⍴⍳4")]
3985 #[case("x←3 ⋄ x+1")]
3986 #[case("2+a←3")]
3987 #[case("⎕←2+2")]
3988 #[case("(2 3⍴⍳6)+10 20")]
3989 #[case("2+3 ⍝ sum")]
3990 #[case("+/2 3⍴⍳6")]
3991 #[case("+⌿2 3⍴⍳6")]
3992 #[case("⎕←'Hello, world!'")]
3993 fn the_evaluation_corpus_parses(#[case] src: &str) {
3994 p(src).unwrap_or_else(|e| panic!("{src}: {e}"));
3995 }
3996
3997 #[test]
3998 fn errors_render_against_the_display_source() {
3999 let src = "2 3⍴⍳6\n2 @ 3";
4000 let e = err(src);
4001 let rendered = e.render(src);
4002 assert!(rendered.contains("unknown symbol: @"), "{rendered}");
4003 assert!(rendered.contains("2 @ 3"), "{rendered}");
4004 }
4005}