1use std::collections::HashMap;
10use std::sync::Arc;
11
12use crate::array::{Array, Data};
13use crate::error::{Error, Result, Span};
14use crate::frontend::{
15 ControlStrictness, DefaultArg, DepthSign, DfnResult, FirstDisclose, IndexForm, LookupLeft,
16 NestedModel, Partition, 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 for name in fixed_names(&sentences, d) {
32 verbs.entry(name.clone()).or_insert_with(|| Verb::Named(name.clone()));
33 }
34 let mut stmts = Vec::with_capacity(sentences.len());
35 let mut i = 0usize;
36 while i < sentences.len() {
37 if matches!(sentences[i].first().map(|t| &t.kind), Some(Tok::Del)) {
38 let stmt = parse_tradfn(&sentences, &mut i, d, &mut verbs)?;
39 stmts.push(stmt);
40 continue;
41 }
42 if let Some(Tok::Control(w)) = sentences[i].first().map(|t| &t.kind)
45 && matches!(*w, "If" | "While" | "Repeat" | "For" | "Select")
46 {
47 let end = control_block_end(&sentences, i).unwrap_or(sentences.len());
48 let mut items = Vec::with_capacity(end - i);
49 for line in &sentences[i..end] {
50 let mut label = None;
51 items.push(to_item(line.clone(), d, &mut verbs, &mut label)?);
52 }
53 let mut cursor = AplCursor { items: &items, at: 0, d, loops: 0 };
54 stmts.push(parse_apl_control(&mut cursor)?);
55 i = end;
56 continue;
57 }
58 let mut sentence = sentences[i].clone();
59 i += 1;
60 while let Some(at) = outermost_fx(&sentence) {
63 let mut end = at + 1;
64 while end < sentence.len() && matches!(sentence[end].kind, Tok::Value(_)) {
65 end += 1;
66 }
67 let (def, name) = fix_definition(&sentence[at..end], d, &mut verbs)?;
68 stmts.push(def);
69 let span = Span::merge(sentence[at].span, sentence[end - 1].span);
70 sentence.splice(at..end, [Token { kind: Tok::Value(name), span }]);
71 }
72 if let Some(stmt) = parse_statement(sentence, d, &mut verbs, false)? {
73 stmts.push(stmt);
74 }
75 }
76 Ok(stmts)
77}
78
79fn fixed_names(sentences: &[Vec<Token>], d: Rules) -> Vec<String> {
89 let mut out = Vec::new();
90 for line in sentences {
91 let head = match line.first().map(|t| &t.kind) {
92 Some(Tok::Del) => line[1..].to_vec(),
93 _ => {
94 let Some(at) = line.iter().position(|t| matches!(t.kind, Tok::QuadFx)) else {
95 continue;
96 };
97 let Some(Tok::Value(a)) = line.get(at + 1).map(|t| &t.kind) else {
98 continue;
99 };
100 let Data::Char(cs) = &a.data else { continue };
101 if a.rank() > 1 {
102 continue;
103 }
104 let text: String = cs.iter().collect();
105 let src = SourceParts::from_parts(&[&text], &[]);
106 match lex(&src, d) {
107 Ok(mut lexed) if lexed.len() == 1 => lexed.pop().unwrap_or_default(),
108 _ => continue,
109 }
110 }
111 };
112 let Some(first) = head.first() else { continue };
113 if let Ok((name, ..)) = parse_header(&head, first.span) {
114 out.push(name);
115 }
116 }
117 out
118}
119
120fn control_block_end(sentences: &[Vec<Token>], at: usize) -> Option<usize> {
123 let mut depth = 0usize;
124 for (k, line) in sentences.iter().enumerate().skip(at) {
125 let Some(Tok::Control(w)) = line.first().map(|t| &t.kind) else { continue };
126 match *w {
127 "If" | "While" | "Repeat" | "For" | "Select" => depth += 1,
128 "EndIf" | "EndWhile" | "EndFor" | "EndSelect" | "Until" | "End" => {
129 depth -= 1;
130 if depth == 0 {
131 return Some(k + 1);
132 }
133 }
134 _ => {}
135 }
136 }
137 None
138}
139
140fn outermost_fx(sentence: &[Token]) -> Option<usize> {
144 let mut depth = 0usize;
145 for (i, t) in sentence.iter().enumerate() {
146 match t.kind {
147 Tok::LBrace => depth += 1,
148 Tok::RBrace => depth = depth.saturating_sub(1),
149 Tok::QuadFx if depth == 0 => return Some(i),
150 _ => {}
151 }
152 }
153 None
154}
155
156fn fix_definition(
167 toks: &[Token],
168 d: Rules,
169 verbs: &mut HashMap<String, Verb>,
170) -> Result<(Expr, Array)> {
171 let fx = toks[0].span;
172 if toks.len() == 1 {
173 return Err(Error::not_yet(
174 "⎕FX on a definition that is not literal text in the program",
175 fx,
176 ));
177 }
178 let span = Span::merge(fx, toks[toks.len() - 1].span);
179 let mut lines: Vec<(Vec<Token>, Span)> = Vec::new();
180 for t in &toks[1..] {
181 let text = match &t.kind {
182 Tok::Value(a) if a.rank() <= 1 => match &a.data {
183 Data::Char(cs) => Some(cs.iter().collect::<String>()),
184 _ => None,
185 },
186 _ => None,
187 };
188 let Some(text) = text else {
189 return Err(Error::not_yet(
190 "⎕FX on a definition that is not literal text in the program",
191 t.span,
192 ));
193 };
194 let src = SourceParts::from_parts(&[&text], &[]);
195 let mut lexed = lex(&src, d)?;
196 if lexed.len() > 1 {
197 return Err(Error::not_yet("a ⋄ inside a ⎕FX line", t.span));
198 }
199 let mut line = lexed.pop().unwrap_or_default();
202 for tok in &mut line {
203 tok.span = t.span;
204 }
205 lines.push((line, t.span));
206 }
207 let (head, head_span) = lines.remove(0);
208 if head.is_empty() {
209 return Err(Error::parse("⎕FX starts with the definition's header", head_span));
210 }
211 let body: Vec<Vec<Token>> = lines.into_iter().map(|(l, _)| l).collect();
212 let def = build_tradfn(&head, &body, span, d, verbs)?;
213 let Expr::VerbDef { name, .. } = &def else {
214 return Err(Error::internal("⎕FX did not build a definition"));
215 };
216 let answer = Array::from_chars(name.chars().collect());
217 Ok((def, answer))
218}
219
220fn parse_statement(
223 sentence: Vec<Token>,
224 d: Rules,
225 verbs: &mut HashMap<String, Verb>,
226 shared_verbs: bool,
232) -> Result<Option<Expr>> {
233 if let Some(t) = sentence.iter().find(|t| matches!(t.kind, Tok::QuadFx)) {
237 return Err(Error::not_yet("⎕FX inside another definition", t.span));
238 }
239 let sentence = substitute_verbs(sentence, verbs);
240 let sentence = fold_dfns(sentence, d, verbs)?;
241 if let [name, assign, func] = &sentence[..]
244 && let (Tok::Name(n), Tok::Assign) = (&name.kind, &assign.kind)
245 {
246 let named = match &func.kind {
249 Tok::Func(v) => Some(v.clone()),
250 Tok::UserOp { def, omega } => Some(unapplied_op(def.clone(), *omega)),
251 _ => None,
252 };
253 if let Some(v) = named {
254 let span = Span::merge(name.span, func.span);
255 if !shared_verbs {
256 verbs.insert(n.clone(), v.clone());
257 }
258 return Ok(Some(Expr::VerbDef { name: n.clone(), verb: v, span }));
259 }
260 }
261 let toks = fold_axes(fold_operators(unwrap_lone_operators(sentence), d)?, d)?;
262 if toks.is_empty() {
263 return Ok(None);
264 }
265 if let [name, assign, rest @ ..] = &toks[..]
269 && let (Tok::Name(n), Tok::Assign) = (&name.kind, &assign.kind)
270 && let Some(v) = tine_run(rest, d)?
271 {
272 let span = Span::merge(name.span, toks[toks.len() - 1].span);
273 if !shared_verbs {
274 verbs.insert(n.clone(), v.clone());
275 }
276 return Ok(Some(Expr::VerbDef { name: n.clone(), verb: v, span }));
277 }
278 if let Some(t) = toks.iter().find(|t| matches!(t.kind, Tok::Control(_))) {
281 let Tok::Control(w) = t.kind else { unreachable!() };
282 return Err(Error::parse(format!(":{w} has no matching opening word"), t.span));
283 }
284 if let Some(t) = toks.iter().find(|t| matches!(t.kind, Tok::Arrow)) {
285 return Err(Error::parse(
286 "→ branches, and only a line of a ∇ definition may begin with it",
287 t.span,
288 ));
289 }
290 let hint = Span::merge(toks[0].span, toks[toks.len() - 1].span);
291 if let Some(e) = indexed_assignment(&toks, d, hint)? {
294 return Ok(Some(e));
295 }
296 parse_range(&toks, 0, toks.len(), hint, d).map(Some)
297}
298
299fn substitute_verbs(mut toks: Vec<Token>, verbs: &HashMap<String, Verb>) -> Vec<Token> {
302 for i in 0..toks.len() {
303 let Tok::Name(n) = &toks[i].kind else { continue };
304 if matches!(toks.get(i + 1).map(|t| &t.kind), Some(Tok::Assign)) {
305 continue;
306 }
307 if let Some(v) = verbs.get(n) {
308 toks[i].kind = match as_user_op(v) {
311 Some((def, omega)) => Tok::UserOp { def, omega },
312 None if is_niladic(v) => Tok::Niladic(v.clone()),
313 None => Tok::Func(v.clone()),
314 };
315 }
316 }
317 toks
318}
319
320#[derive(Clone, Copy, Debug, PartialEq, Eq)]
325enum OpGlyph {
326 Slash,
328 SlashBar,
330 Backslash,
332 BackslashBar,
334 Rank,
336 Commute,
338 Power,
340 JotDot,
342 Over,
344 Under,
346 Stencil,
348 Jot,
350 Each,
352 Before,
354 Key,
356 Dot,
359 Variant,
361}
362
363impl OpGlyph {
364 fn glyph(self) -> char {
365 match self {
366 OpGlyph::Slash => '/',
367 OpGlyph::SlashBar => '⌿',
368 OpGlyph::Backslash => '\\',
369 OpGlyph::BackslashBar => '⍀',
370 OpGlyph::Rank => '⍤',
371 OpGlyph::Commute => '⍨',
372 OpGlyph::Power => '⍣',
373 OpGlyph::JotDot | OpGlyph::Jot => '∘',
374 OpGlyph::Over => '⍥',
375 OpGlyph::Under => '⍢',
376 OpGlyph::Stencil => '⌺',
377 OpGlyph::Each => '¨',
378 OpGlyph::Before => '⍛',
379 OpGlyph::Key => '⌸',
380 OpGlyph::Dot => '.',
381 OpGlyph::Variant => '⍠',
382 }
383 }
384}
385
386#[derive(Clone, Debug)]
387enum Tok {
388 Value(Array),
390 Nums(Array),
394 Param(usize),
396 Name(String),
397 Func(Verb),
399 Op(OpGlyph),
401 Assign,
402 Quad { quote: bool },
405 LParen,
406 RParen,
407 LBracket,
408 RBracket,
409 Semi,
412 LBrace,
414 RBrace,
415 Separator,
418 Colon,
420 Arrow,
422 Niladic(Verb),
425 UserOp { def: Arc<OpDef>, omega: bool },
428 Del,
430 Control(&'static str),
432 QuadFx,
436}
437
438#[derive(Clone, Debug)]
439struct Token {
440 kind: Tok,
441 span: Span,
442}
443
444fn is_operand_end(k: &Tok) -> bool {
447 matches!(
448 k,
449 Tok::Value(_)
450 | Tok::Nums(_)
451 | Tok::Param(_)
452 | Tok::Name(_)
453 | Tok::Niladic(_)
454 | Tok::Quad { .. }
459 | Tok::RParen
460 | Tok::RBracket
461 )
462}
463
464fn unapplied_op(def: Arc<OpDef>, omega: bool) -> Verb {
468 let named = |n: &str| Operand::Func(Box::new(Verb::Named(n.to_string())));
469 Verb::UserDerived {
470 def,
471 alpha: named("⍺⍺"),
472 omega: omega.then(|| named("⍵⍵")),
473 }
474}
475
476fn as_user_op(v: &Verb) -> Option<(Arc<OpDef>, bool)> {
479 let Verb::UserDerived { def, alpha, omega } = v else { return None };
480 let Operand::Func(f) = alpha else { return None };
483 match &**f {
484 Verb::Named(n) if n == "⍺⍺" => Some((def.clone(), omega.is_some())),
485 _ => None,
486 }
487}
488
489fn is_niladic(v: &Verb) -> bool {
491 matches!(v, Verb::Explicit(d) if d.left.is_none() && d.right == crate::ir::NILADIC)
492}
493
494fn literal(k: &Tok) -> Option<&Array> {
496 match k {
497 Tok::Value(a) | Tok::Nums(a) => Some(a),
498 _ => None,
499 }
500}
501
502fn prim_for(ch: char, d: Rules) -> Option<Prim> {
515 use DyadOp as D;
516 use MonadOp as M;
517 use ScalarDyad as SD;
518 use ScalarMonad as SM;
519 let origin = d.origin;
520 let p = match ch {
521 '+' => Prim {
522 name: "+",
523 monad: M::Scalar(SM::Conj),
524 dyad: D::Scalar(SD::Add),
525 ranks: [0, 0, 0],
526 },
527 '-' => {
528 Prim { name: "-", monad: M::Scalar(SM::Neg), dyad: D::Scalar(SD::Sub), ranks: [0, 0, 0] }
529 }
530 '×' => Prim {
531 name: "×",
532 monad: M::Scalar(SM::Signum),
533 dyad: D::Scalar(SD::Mul),
534 ranks: [0, 0, 0],
535 },
536 '÷' => Prim {
537 name: "÷",
538 monad: M::Scalar(SM::Recip),
539 dyad: D::Scalar(SD::DivApl),
540 ranks: [0, 0, 0],
541 },
542 '⌈' => Prim {
543 name: "⌈",
544 monad: M::Scalar(SM::Ceil),
545 dyad: D::Scalar(SD::Max),
546 ranks: [0, 0, 0],
547 },
548 '⌊' => Prim {
549 name: "⌊",
550 monad: M::Scalar(SM::Floor),
551 dyad: D::Scalar(SD::Min),
552 ranks: [0, 0, 0],
553 },
554 '*' => {
555 Prim { name: "*", monad: M::Scalar(SM::Exp), dyad: D::Scalar(SD::Pow), ranks: [0, 0, 0] }
556 }
557 '|' => Prim {
558 name: "|",
559 monad: M::Scalar(SM::Abs),
560 dyad: D::Scalar(SD::Residue),
561 ranks: [0, 0, 0],
562 },
563 '=' => Prim { name: "=", monad: M::None, dyad: D::Scalar(SD::Eq), ranks: [0, 0, 0] },
564 '≠' => Prim {
565 name: "≠",
566 monad: M::NubSieve,
567 dyad: D::Scalar(SD::Ne),
568 ranks: [RANK_INF, 0, 0],
569 },
570 '<' => Prim { name: "<", monad: M::None, dyad: D::Scalar(SD::Lt), ranks: [0, 0, 0] },
571 '≤' => Prim { name: "≤", monad: M::None, dyad: D::Scalar(SD::Le), ranks: [0, 0, 0] },
572 '>' => Prim { name: ">", monad: M::None, dyad: D::Scalar(SD::Gt), ranks: [0, 0, 0] },
573 '≥' => Prim { name: "≥", monad: M::None, dyad: D::Scalar(SD::Ge), ranks: [0, 0, 0] },
574 '⍴' => Prim {
575 name: "⍴",
576 monad: M::ShapeOf,
577 dyad: D::Reshape,
578 ranks: [RANK_INF, 1, RANK_INF],
579 },
580 '⍳' => Prim {
581 name: "⍳",
582 monad: M::IotaApl { origin },
583 dyad: D::IndexOf { origin, vector_left: d.lookup_left == LookupLeft::VectorOnly },
584 ranks: [RANK_INF, RANK_INF, RANK_INF],
588 },
589 '∊' => Prim {
590 name: "∊",
591 monad: M::Enlist,
592 dyad: D::MemberApl,
593 ranks: [RANK_INF, RANK_INF, RANK_INF],
594 },
595 '∪' => Prim {
596 name: "∪",
597 monad: M::Nub,
598 dyad: D::Union,
599 ranks: [RANK_INF, RANK_INF, RANK_INF],
600 },
601 '∩' => Prim {
602 name: "∩",
603 monad: M::None,
604 dyad: D::Intersect,
605 ranks: [RANK_INF, RANK_INF, RANK_INF],
606 },
607 '∧' => Prim { name: "∧", monad: M::None, dyad: D::Scalar(SD::Lcm), ranks: [0, 0, 0] },
608 '∨' => Prim { name: "∨", monad: M::None, dyad: D::Scalar(SD::Gcd), ranks: [0, 0, 0] },
609 '⍱' => Prim {
610 name: "⍱",
611 monad: M::None,
612 dyad: D::Boolean(BoolDyad::Nor),
613 ranks: [0, 0, 0],
614 },
615 '⍲' => Prim {
616 name: "⍲",
617 monad: M::None,
618 dyad: D::Boolean(BoolDyad::Nand),
619 ranks: [0, 0, 0],
620 },
621 '⍟' => Prim {
622 name: "⍟",
623 monad: M::Scalar(SM::Ln),
624 dyad: D::Scalar(SD::Log),
625 ranks: [0, 0, 0],
626 },
627 '~' => Prim {
628 name: "~",
629 monad: M::Scalar(SM::Not),
630 dyad: D::Less,
631 ranks: [0, RANK_INF, RANK_INF],
632 },
633 '≡' => Prim {
634 name: "≡",
635 monad: M::Depth { signed: d.depth_sign == DepthSign::Signed },
636 dyad: D::Match,
637 ranks: [RANK_INF, RANK_INF, RANK_INF],
638 },
639 '⍋' => Prim {
640 name: "⍋",
641 monad: M::GradeUp { origin },
642 dyad: D::CollateGrade { down: false, origin },
643 ranks: [RANK_INF, RANK_INF, RANK_INF],
644 },
645 '⍒' => Prim {
646 name: "⍒",
647 monad: M::GradeDown { origin },
648 dyad: D::CollateGrade { down: true, origin },
649 ranks: [RANK_INF, RANK_INF, RANK_INF],
650 },
651 '⊖' | '⌽' => Prim {
657 name: if ch == '⊖' { "⊖" } else { "⌽" },
658 monad: M::Reverse,
659 dyad: D::RotateApl { last: ch == '⌽' },
660 ranks: [RANK_INF, RANK_INF, RANK_INF],
661 },
662 '⍪' => Prim {
663 name: "⍪",
664 monad: M::TableOf,
665 dyad: D::AppendLeading,
666 ranks: [RANK_INF, RANK_INF, RANK_INF],
667 },
668 '!' => Prim {
669 name: "!",
670 monad: M::Scalar(SM::Factorial),
671 dyad: D::Scalar(SD::Binomial),
672 ranks: [0, 0, 0],
673 },
674 '⍕' => Prim {
675 name: "⍕",
676 monad: M::Format,
677 dyad: D::FormatSpec,
678 ranks: [RANK_INF, 1, RANK_INF],
679 },
680 '⊥' => Prim {
686 name: "⊥",
687 monad: M::None,
688 dyad: D::DecodeApl,
689 ranks: [RANK_INF, RANK_INF, RANK_INF],
690 },
691 '⊤' => Prim {
692 name: "⊤",
693 monad: M::None,
694 dyad: D::EncodeApl,
695 ranks: [RANK_INF, RANK_INF, RANK_INF],
696 },
697 '⍉' => Prim {
698 name: "⍉",
699 monad: M::TransposeAxes,
700 dyad: D::TransposeApl,
701 ranks: [RANK_INF, RANK_INF, RANK_INF],
702 },
703 '↑' => Prim {
706 name: "↑",
707 monad: match d.first_disclose {
708 FirstDisclose::UpIsFirst => M::First,
709 FirstDisclose::UpIsMix => M::Open,
710 },
711 ranks: match d.first_disclose {
714 FirstDisclose::UpIsFirst => [RANK_INF, 1, RANK_INF],
715 FirstDisclose::UpIsMix => [0, 1, RANK_INF],
716 },
717 dyad: D::Take,
718 },
719 '⊂' => Prim {
720 name: "⊂",
721 monad: match d.nested_model {
724 NestedModel::Floating => M::Enclose(Enclose::ExceptSimpleScalar),
725 NestedModel::Grounded => return None,
726 },
727 dyad: match d.partition {
730 Partition::Flags => D::PartitionEnclose,
731 Partition::Counts => D::PartitionCounts,
732 },
733 ranks: [RANK_INF, RANK_INF, RANK_INF],
734 },
735 '⊆' => Prim {
738 name: "⊆",
739 monad: M::Nest,
740 dyad: D::PartitionEnclose,
741 ranks: [RANK_INF, RANK_INF, RANK_INF],
742 },
743 '⍸' => Prim {
746 name: "⍸",
747 monad: M::Indices { origin, boxed_coords: true },
748 dyad: D::IntervalIndex { offset: origin - 1, closed: true },
749 ranks: [RANK_INF, 1, RANK_INF],
750 },
751 '⌷' => Prim {
754 name: "⌷",
755 monad: M::Same,
758 dyad: D::Squad { origin, leading: d.index_form == IndexForm::AxisVectors },
759 ranks: [RANK_INF, RANK_INF, RANK_INF],
760 },
761 '?' => Prim {
762 name: "?",
763 monad: M::Roll { origin, fixed: false, float_at_zero: false },
764 dyad: D::Deal { origin, fixed: false },
765 ranks: [RANK_INF, 0, 0],
766 },
767 '⌹' => Prim {
768 name: "⌹",
769 monad: M::MatrixInverse,
770 dyad: D::MatrixDivide,
771 ranks: [2, RANK_INF, 2],
772 },
773 '⊃' => Prim {
774 name: "⊃",
775 monad: match d.first_disclose {
776 FirstDisclose::UpIsFirst => M::Open,
777 FirstDisclose::UpIsMix => M::First,
778 },
779 dyad: D::Pick { origin },
780 ranks: match d.first_disclose {
781 FirstDisclose::UpIsFirst => [0, RANK_INF, RANK_INF],
782 FirstDisclose::UpIsMix => [RANK_INF, RANK_INF, RANK_INF],
783 },
784 },
785 '↓' => Prim {
786 name: "↓",
787 monad: M::Split,
788 dyad: D::Drop,
789 ranks: [RANK_INF, 1, RANK_INF],
790 },
791 ',' => Prim {
792 name: ",",
793 monad: M::Ravel,
794 dyad: D::AppendLast,
795 ranks: [RANK_INF, RANK_INF, RANK_INF],
796 },
797 '≢' => Prim {
798 name: "≢",
799 monad: M::Tally,
800 dyad: D::NotMatch,
801 ranks: [RANK_INF, RANK_INF, RANK_INF],
802 },
803 '⊢' => Prim {
804 name: "⊢",
805 monad: M::Same,
806 dyad: D::Right,
807 ranks: [RANK_INF, RANK_INF, RANK_INF],
808 },
809 '⊣' => Prim {
810 name: "⊣",
811 monad: M::Same,
812 dyad: D::Left,
813 ranks: [RANK_INF, RANK_INF, RANK_INF],
814 },
815 '○' => Prim {
816 name: "○",
817 monad: M::Scalar(SM::Pi),
818 dyad: D::Scalar(SD::Circle),
819 ranks: [0, 0, 0],
820 },
821 '⍷' => Prim {
822 name: "⍷",
823 monad: M::None,
824 dyad: D::FindSeq,
825 ranks: [RANK_INF, RANK_INF, RANK_INF],
826 },
827 '⍎' => Prim {
828 name: "⍎",
829 monad: M::Execute { apl: true },
830 dyad: D::None,
831 ranks: [1, RANK_INF, RANK_INF],
832 },
833 _ => return None,
834 };
835 Some(p)
836}
837
838fn verb_for(ch: char, d: Rules) -> Option<Verb> {
842 let p = prim_for(ch, d)?;
843 if ch == '⌽' {
846 return Some(Verb::Rank(Box::new(Verb::Prim(p)), [1, RANK_INF, RANK_INF]));
847 }
848 Some(Verb::Prim(p))
849}
850
851fn quad_name(name: &str, d: Rules, span: Span) -> Result<Tok> {
857 let chars = |s: &str| Tok::Value(Array::from_chars(s.chars().collect()));
858 Ok(match name {
859 "A" => chars("ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
860 "D" => chars("0123456789"),
861 "IO" => Tok::Value(Array::scalar_i64(d.origin)),
862 "CT" => Tok::Value(Array::scalar_f64(d.ct)),
863 "FX" => Tok::QuadFx,
864 "UCS" => Tok::Func(Verb::Prim(Prim {
865 name: "⎕UCS",
866 monad: MonadOp::Unicode { pass_chars: false },
867 dyad: DyadOp::None,
868 ranks: [RANK_INF, RANK_INF, RANK_INF],
869 })),
870 "TS" | "AI" | "TC" | "WA" | "SI" | "LC" | "NL" | "EX" | "FIO" | "NA" | "SH" | "CMD"
874 | "MAP" | "SVO" | "SVQ" | "TZ" | "DL" => {
875 Err(Error::sandbox(format!("⎕{name} reads outside the program"), span))?
876 }
877 other => Err(Error::not_yet(format!("the system name ⎕{other}"), span))?,
878 })
879}
880
881fn queued_glyph(ch: char) -> Option<&'static str> {
884 Some(match ch {
885 '⌶' => "I-beam (⌶)",
886 '&' => "the spawn operator (f&y)",
890 _ => return None,
891 })
892}
893
894fn op_for(ch: char) -> Option<OpGlyph> {
895 match ch {
896 '/' => Some(OpGlyph::Slash),
897 '⌿' => Some(OpGlyph::SlashBar),
898 '\\' => Some(OpGlyph::Backslash),
899 '⍀' => Some(OpGlyph::BackslashBar),
900 '⍤' => Some(OpGlyph::Rank),
901 '⍨' => Some(OpGlyph::Commute),
902 '⍣' => Some(OpGlyph::Power),
903 '∘' => Some(OpGlyph::Jot),
904 '⍥' => Some(OpGlyph::Over),
905 '⍢' => Some(OpGlyph::Under),
906 '⌺' => Some(OpGlyph::Stencil),
907 '¨' => Some(OpGlyph::Each),
908 '⍛' => Some(OpGlyph::Before),
909 '⌸' => Some(OpGlyph::Key),
910 '⍠' => Some(OpGlyph::Variant),
911 '.' => Some(OpGlyph::Dot),
914 _ => None,
915 }
916}
917
918fn expand_verb(leading: bool) -> Verb {
921 let p = Prim {
922 name: if leading { "⍀" } else { "\\" },
923 monad: MonadOp::None,
924 dyad: DyadOp::Expand,
925 ranks: if leading { [RANK_INF, 1, RANK_INF] } else { [RANK_INF, 1, 1] },
926 };
927 Verb::Prim(p)
928}
929
930fn copy_verb(leading: bool) -> Verb {
934 let p = Prim {
935 name: if leading { "⌿" } else { "/" },
936 monad: MonadOp::None,
937 dyad: DyadOp::Copy,
938 ranks: if leading { [RANK_INF, 1, RANK_INF] } else { [RANK_INF, 1, 1] },
939 };
940 Verb::Prim(p)
941}
942
943fn lex(src: &SourceParts, d: Rules) -> Result<Vec<Vec<Token>>> {
950 let mut out: Vec<Vec<Token>> = Vec::new();
951 let mut cur: Vec<Token> = Vec::new();
952 let mut in_comment = false;
954 let mut braces = 0usize;
955 for seg in &src.segments {
956 match seg {
957 Segment::Text { text, offset } => {
958 lex_text(text, *offset, d, &mut out, &mut cur, &mut in_comment, &mut braces)?;
959 }
960 Segment::Param { index, offset, len } => {
961 if !in_comment {
962 cur.push(Token {
963 kind: Tok::Param(*index),
964 span: Span::new(*offset, offset + len),
965 });
966 }
967 }
968 }
969 }
970 if !cur.is_empty() {
971 out.push(cur);
972 }
973 Ok(out)
974}
975
976#[allow(clippy::too_many_arguments)]
977fn lex_text(
978 text: &str,
979 offset: usize,
980 d: Rules,
981 out: &mut Vec<Vec<Token>>,
982 cur: &mut Vec<Token>,
983 in_comment: &mut bool,
984 braces: &mut usize,
985) -> Result<()> {
986 let mut i = 0usize;
987 while i < text.len() {
988 let ch = text[i..].chars().next().unwrap();
989 let clen = ch.len_utf8();
990 if *in_comment {
991 if ch == '\n' {
992 *in_comment = false;
993 end_sentence(out, cur);
994 }
995 i += clen;
996 continue;
997 }
998 match ch {
999 '\n' | '⋄' => {
1002 if *braces > 0 {
1003 cur.push(Token {
1004 kind: Tok::Separator,
1005 span: Span::new(offset + i, offset + i + clen),
1006 });
1007 } else {
1008 end_sentence(out, cur);
1009 }
1010 i += clen;
1011 }
1012 ' ' | '\t' | '\r' => i += clen,
1013 '⍝' => {
1014 *in_comment = true;
1015 i += clen;
1016 }
1017 '\'' => {
1018 let (arr, next) = lex_string(text, i, offset)?;
1019 cur.push(Token {
1020 kind: Tok::Value(arr),
1021 span: Span::new(offset + i, offset + next),
1022 });
1023 i = next;
1024 }
1025 '{' => {
1026 *braces += 1;
1027 cur.push(Token { kind: Tok::LBrace, span: Span::new(offset + i, offset + i + 1) });
1028 i += 1;
1029 }
1030 '}' => {
1031 *braces = braces.saturating_sub(1);
1032 cur.push(Token { kind: Tok::RBrace, span: Span::new(offset + i, offset + i + 1) });
1033 i += 1;
1034 }
1035 '∇' => {
1036 cur.push(Token { kind: Tok::Del, span: Span::new(offset + i, offset + i + clen) });
1037 i += clen;
1038 }
1039 '⍺' | '⍵' => {
1041 let mut end = i + clen;
1042 if text[end..].starts_with(ch) {
1043 end += clen;
1044 }
1045 cur.push(Token {
1046 kind: Tok::Name(text[i..end].to_string()),
1047 span: Span::new(offset + i, offset + end),
1048 });
1049 i = end;
1050 }
1051 ':' if *braces > 0 => {
1056 cur.push(Token {
1057 kind: Tok::Colon,
1058 span: Span::new(offset + i, offset + i + 1),
1059 });
1060 i += 1;
1061 }
1062 ':' => {
1063 let mut j = i + 1;
1064 while let Some(c) = text[j..].chars().next() {
1065 if c.is_ascii_alphabetic() {
1066 j += c.len_utf8();
1067 } else {
1068 break;
1069 }
1070 }
1071 let span = Span::new(offset + i, offset + j);
1072 match control_word(&text[i + 1..j]) {
1073 Some(word) => cur.push(Token { kind: Tok::Control(word), span }),
1074 None if j > i + 1 => {
1075 return Err(Error::parse(
1076 format!("unknown control word: {}", &text[i..j]),
1077 span,
1078 ));
1079 }
1080 None => cur.push(Token {
1081 kind: Tok::Colon,
1082 span: Span::new(offset + i, offset + i + 1),
1083 }),
1084 }
1085 i = j;
1086 }
1087 '→' => {
1088 cur.push(Token {
1089 kind: Tok::Arrow,
1090 span: Span::new(offset + i, offset + i + clen),
1091 });
1092 i += clen;
1093 }
1094 '⍬' => {
1096 cur.push(Token {
1097 kind: Tok::Value(Array::empty(crate::dtype::DType::I64)),
1098 span: Span::new(offset + i, offset + i + clen),
1099 });
1100 i += clen;
1101 }
1102 '(' => {
1103 cur.push(Token { kind: Tok::LParen, span: Span::new(offset + i, offset + i + 1) });
1104 i += 1;
1105 }
1106 ')' => {
1107 cur.push(Token { kind: Tok::RParen, span: Span::new(offset + i, offset + i + 1) });
1108 i += 1;
1109 }
1110 '[' => {
1111 cur.push(Token {
1112 kind: Tok::LBracket,
1113 span: Span::new(offset + i, offset + i + 1),
1114 });
1115 i += 1;
1116 }
1117 ']' => {
1118 cur.push(Token {
1119 kind: Tok::RBracket,
1120 span: Span::new(offset + i, offset + i + 1),
1121 });
1122 i += 1;
1123 }
1124 ';' => {
1125 cur.push(Token { kind: Tok::Semi, span: Span::new(offset + i, offset + i + 1) });
1126 i += 1;
1127 }
1128 '←' => {
1129 cur.push(Token {
1130 kind: Tok::Assign,
1131 span: Span::new(offset + i, offset + i + clen),
1132 });
1133 i += clen;
1134 }
1135 '⍞' => {
1137 cur.push(Token {
1138 kind: Tok::Quad { quote: true },
1139 span: Span::new(offset + i, offset + i + clen),
1140 });
1141 i += clen;
1142 }
1143 '⎕' => {
1144 let after = i + clen;
1145 let mut j = after;
1146 while let Some(c) = text[j..].chars().next() {
1147 if c.is_alphabetic() {
1148 j += c.len_utf8();
1149 } else {
1150 break;
1151 }
1152 }
1153 if j > after {
1154 let span = Span::new(offset + i, offset + j);
1155 let name = text[after..j].to_uppercase();
1156 if text[j..].trim_start().starts_with('←') {
1162 quad_name(&name, d, span)?;
1163 return Err(Error::language(
1164 format!(
1165 "⎕{name} is read-only: libjay's system names are \
1166 fixed before the program runs"
1167 ),
1168 span,
1169 ));
1170 }
1171 cur.push(Token { kind: quad_name(&name, d, span)?, span });
1172 i = j;
1173 continue;
1174 }
1175 cur.push(Token {
1176 kind: Tok::Quad { quote: false },
1177 span: Span::new(offset + i, offset + after),
1178 });
1179 i = after;
1180 }
1181 _ if num_start(text, i) => {
1182 let (tok, next) = lex_number_vector(text, i, offset)?;
1183 cur.push(tok);
1184 i = next;
1185 }
1186 _ if is_name_start(ch) => {
1187 let start = i;
1188 i += clen;
1189 while let Some(c) = text[i..].chars().next() {
1190 if is_name_body(c) {
1191 i += c.len_utf8();
1192 } else {
1193 break;
1194 }
1195 }
1196 cur.push(Token {
1197 kind: Tok::Name(text[start..i].to_string()),
1198 span: Span::new(offset + start, offset + i),
1199 });
1200 }
1201 _ => {
1202 let mut end = i + clen;
1203 if let Some(v) = verb_for(ch, d) {
1204 cur.push(Token {
1205 kind: Tok::Func(v),
1206 span: Span::new(offset + i, offset + end),
1207 });
1208 } else if let Some(mut op) = op_for(ch) {
1209 if op == OpGlyph::Jot && text[end..].starts_with('.') {
1212 op = OpGlyph::JotDot;
1213 end += 1;
1214 }
1215 cur.push(Token {
1216 kind: Tok::Op(op),
1217 span: Span::new(offset + i, offset + end),
1218 });
1219 } else if let Some(what) = queued_glyph(ch) {
1220 return Err(Error::not_yet(what, Span::new(offset + i, offset + end)));
1223 } else {
1224 return Err(Error::parse(
1225 format!("unknown symbol: {ch}"),
1226 Span::new(offset + i, offset + end),
1227 ));
1228 }
1229 i = end;
1230 }
1231 }
1232 }
1233 Ok(())
1234}
1235
1236fn end_sentence(out: &mut Vec<Vec<Token>>, cur: &mut Vec<Token>) {
1237 if !cur.is_empty() {
1238 out.push(std::mem::take(cur));
1239 }
1240}
1241
1242fn is_name_start(c: char) -> bool {
1243 c.is_alphabetic() || c == '∆' || c == '⍙'
1244}
1245
1246fn is_name_body(c: char) -> bool {
1247 c.is_alphanumeric() || c == '_' || c == '∆' || c == '⍙'
1248}
1249
1250fn lex_string(text: &str, start: usize, offset: usize) -> Result<(Array, usize)> {
1253 let mut chars: Vec<char> = Vec::new();
1254 let mut i = start + 1;
1255 loop {
1256 let c = match text[i..].chars().next() {
1257 Some(c) => c,
1258 None => {
1259 return Err(Error::parse(
1260 "unterminated string",
1261 Span::new(offset + start, offset + text.len()),
1262 ));
1263 }
1264 };
1265 if c == '\'' {
1266 if text[i + 1..].starts_with('\'') {
1267 chars.push('\'');
1268 i += 2;
1269 continue;
1270 }
1271 i += 1;
1272 break;
1273 }
1274 chars.push(c);
1275 i += c.len_utf8();
1276 }
1277 let shape = if chars.len() == 1 { vec![] } else { vec![chars.len()] };
1278 Ok((Array::new(shape, Data::Char(chars.into())), i))
1279}
1280
1281fn num_start(text: &str, i: usize) -> bool {
1283 let s = match text.get(i..) {
1284 Some(s) => s,
1285 None => return false,
1286 };
1287 let mut cs = s.chars();
1288 let c0 = match cs.next() {
1289 Some(c) => c,
1290 None => return false,
1291 };
1292 if c0.is_ascii_digit() {
1293 return true;
1294 }
1295 if c0 == '.' {
1296 return cs.next().is_some_and(|d| d.is_ascii_digit());
1297 }
1298 if c0 == '¯' {
1299 return match cs.next() {
1300 Some(d) if d.is_ascii_digit() => true,
1301 Some('.') => cs.next().is_some_and(|d| d.is_ascii_digit()),
1302 _ => false,
1303 };
1304 }
1305 false
1306}
1307
1308fn lex_number(text: &str, start: usize, offset: usize) -> Result<(f64, Option<i64>, usize)> {
1316 let mut i = start;
1317 let mut buf = String::new();
1318 let mut saw_dot = false;
1319 if text[i..].starts_with('¯') {
1320 buf.push('-');
1321 i += '¯'.len_utf8();
1322 }
1323 i = take_digits(text, i, &mut buf);
1324 if text[i..].starts_with('.') && text[i + 1..].chars().next().is_some_and(|d| d.is_ascii_digit())
1325 {
1326 saw_dot = true;
1327 buf.push('.');
1328 i += 1;
1329 i = take_digits(text, i, &mut buf);
1330 }
1331 if let Some(c) = text[i..].chars().next() && (c == 'e' || c == 'E') {
1332 let after = i + 1;
1333 let neg = text[after..].starts_with('¯');
1334 let digits_at = if neg { after + '¯'.len_utf8() } else { after };
1335 if text[digits_at..].chars().next().is_some_and(|d| d.is_ascii_digit()) {
1336 buf.push('e');
1337 if neg {
1338 buf.push('-');
1339 }
1340 i = take_digits(text, digits_at, &mut buf);
1341 }
1342 }
1343 let v: f64 = buf.parse().map_err(|_| {
1344 Error::parse(
1345 format!("cannot read the number {}", &text[start..i]),
1346 Span::new(offset + start, offset + i),
1347 )
1348 })?;
1349 let exact = if saw_dot {
1353 None
1354 } else if let Ok(k) = buf.parse::<i64>() {
1355 Some(k)
1356 } else if v.fract() == 0.0 && v.abs() < 9.0e18 {
1357 Some(v as i64)
1358 } else {
1359 None
1360 };
1361 Ok((v, exact, i))
1362}
1363
1364fn take_digits(text: &str, mut i: usize, buf: &mut String) -> usize {
1365 while let Some(c) = text[i..].chars().next() {
1366 if c.is_ascii_digit() {
1367 buf.push(c);
1368 i += 1;
1369 } else {
1370 break;
1371 }
1372 }
1373 i
1374}
1375
1376fn lex_number_vector(text: &str, start: usize, offset: usize) -> Result<(Token, usize)> {
1379 let mut vals: Vec<crate::complex::Cx> = Vec::new();
1380 let mut exacts: Vec<i64> = Vec::new();
1381 let mut any_float = false;
1382 let mut any_complex = false;
1383 let mut i = start;
1384 let mut end;
1385 loop {
1386 let (v, exact, mut next) = lex_number(text, i, offset)?;
1387 let mut imag = 0.0;
1388 if let Some(c) = text[next..].chars().next() {
1389 if (c == 'j' || c == 'J') && num_start(text, next + 1) {
1392 let (b, _, imag_end) = lex_number(text, next + 1, offset)?;
1393 imag = b;
1394 next = imag_end;
1395 any_complex = true;
1396 }
1397 }
1398 vals.push([v, imag]);
1399 match exact {
1400 Some(k) => exacts.push(k),
1401 None => any_float = true,
1402 }
1403 end = next;
1404 i = next;
1405 let mut k = i;
1406 while text[k..].starts_with(' ') || text[k..].starts_with('\t') {
1407 k += 1;
1408 }
1409 if k > i && num_start(text, k) {
1410 i = k;
1411 continue;
1412 }
1413 break;
1414 }
1415 let data = if any_complex {
1416 Data::Complex(vals.into())
1417 } else if any_float {
1418 Data::F64(vals.iter().map(|&v| v[0]).collect())
1419 } else {
1420 Data::I64(exacts.into())
1421 };
1422 let shape = if data.len() == 1 { vec![] } else { vec![data.len()] };
1423 let tok = Token {
1424 kind: Tok::Nums(Array::new(shape, data)),
1425 span: Span::new(offset + start, offset + end),
1426 };
1427 Ok((tok, end))
1428}
1429
1430fn bind_value(
1442 it: &mut std::iter::Peekable<std::vec::IntoIter<Token>>,
1443 out: &mut Vec<Token>,
1444) -> Option<Token> {
1445 let left_is_value = out.last().map(|t| &t.kind).and_then(literal).is_some();
1446 let left_is_func = matches!(out.last().map(|t| &t.kind), Some(Tok::Func(_)));
1447 let right_is_value = it.peek().map(|t| &t.kind).and_then(literal).is_some();
1448 let right_is_func = matches!(it.peek().map(|t| &t.kind), Some(Tok::Func(_)));
1449 if !((left_is_value && right_is_func) || (left_is_func && right_is_value)) {
1450 return None;
1451 }
1452 let ltok = out.pop().expect("checked above");
1453 let rtok = it.next().expect("peeked");
1454 let span = Span::merge(ltok.span, rtok.span);
1455 let derived = if left_is_value {
1457 let Tok::Func(g) = rtok.kind else { unreachable!("checked above") };
1458 Verb::BondLeft(literal(<ok.kind).expect("checked above").clone(), Box::new(g))
1459 } else {
1460 let Tok::Func(f) = ltok.kind else { unreachable!("checked above") };
1461 Verb::BondRight(Box::new(f), literal(&rtok.kind).expect("checked above").clone())
1462 };
1463 Some(Token { kind: Tok::Func(derived), span })
1464}
1465
1466fn fold_operators(toks: Vec<Token>, d: Rules) -> Result<Vec<Token>> {
1470 let mut out: Vec<Token> = Vec::new();
1471 let mut it = toks.into_iter().peekable();
1472 while let Some(t) = it.next() {
1473 if matches!(t.kind, Tok::RParen) {
1478 out.push(t);
1479 close_paren(&mut out, d)?;
1480 continue;
1481 }
1482 if let Tok::UserOp { def, omega } = &t.kind {
1485 let (def, omega) = (def.clone(), *omega);
1486 let right = if omega {
1487 match it.peek().map(|tok| &tok.kind) {
1488 Some(Tok::Func(_)) => {
1489 let g = it.next().expect("peeked");
1490 let Tok::Func(g) = g.kind else { unreachable!("checked above") };
1491 Some(Operand::Func(Box::new(g)))
1492 }
1493 Some(k) if literal(k).is_some() => {
1496 let a = it.next().expect("peeked");
1497 Some(Operand::Value(Box::new(literal(&a.kind).expect("checked").clone())))
1498 }
1499 _ => {
1500 return Err(Error::parse(
1501 "⍵⍵ needs an operand on the operator's right",
1502 t.span,
1503 ));
1504 }
1505 }
1506 } else {
1507 None
1508 };
1509 let alpha = match out.pop() {
1510 Some(Token { kind: Tok::Func(f), span }) => (Operand::Func(Box::new(f)), span),
1511 Some(tok) if literal(&tok.kind).is_some() => {
1512 (Operand::Value(Box::new(literal(&tok.kind).expect("checked").clone())), tok.span)
1513 }
1514 _ => {
1515 return Err(Error::parse(
1516 "⍺⍺ needs an operand on the operator's left",
1517 t.span,
1518 ));
1519 }
1520 };
1521 let (alpha, fspan) = alpha;
1522 let derived = Verb::UserDerived { def, alpha, omega: right };
1523 out.push(Token { kind: Tok::Func(derived), span: Span::merge(fspan, t.span) });
1524 continue;
1525 }
1526 let op = match t.kind {
1527 Tok::Op(op) => op,
1528 _ => {
1529 out.push(t);
1530 continue;
1531 }
1532 };
1533 if op == OpGlyph::JotDot {
1536 let ftok = match it.peek() {
1537 Some(tok) if matches!(tok.kind, Tok::Func(_)) => it.next().unwrap(),
1538 _ => {
1539 return Err(Error::parse("∘. needs a function on its right", t.span));
1540 }
1541 };
1542 let span = Span::merge(t.span, ftok.span);
1543 let Tok::Func(f) = ftok.kind else { unreachable!("checked above") };
1544 out.push(Token { kind: Tok::Func(Verb::Reduce(Box::new(f))), span });
1545 continue;
1546 }
1547 if op == OpGlyph::Jot
1552 && let Some(bound) = bind_value(&mut it, &mut out)
1553 {
1554 out.push(bound);
1555 continue;
1556 }
1557 if matches!(
1560 op,
1561 OpGlyph::Jot | OpGlyph::Over | OpGlyph::Before | OpGlyph::Under | OpGlyph::Dot
1562 ) {
1563 let Some(gtok) = it.peek().filter(|x| matches!(x.kind, Tok::Func(_))) else {
1564 return Err(Error::not_yet(
1565 format!("{} with a value operand", op.glyph()),
1566 t.span,
1567 ));
1568 };
1569 let gspan = gtok.span;
1570 let Some(Token { kind: Tok::Func(g), .. }) = it.next() else {
1571 unreachable!("peeked a function")
1572 };
1573 let Some(Token { kind: Tok::Func(f), span: fspan }) = out.pop() else {
1574 return Err(Error::not_yet(
1575 format!("{} with a value operand", op.glyph()),
1576 t.span,
1577 ));
1578 };
1579 let span = Span::merge(fspan, gspan);
1580 let derived = match op {
1583 OpGlyph::Jot => Verb::Beside(Box::new(f), Box::new(g)),
1584 OpGlyph::Before => Verb::Before(Box::new(f), Box::new(g)),
1585 OpGlyph::Under => {
1589 let back = crate::verb::obverse(&g).ok_or_else(|| {
1590 Error::not_yet(
1591 format!("the obverse of {} (no inverse is known)", g.name()),
1592 gspan,
1593 )
1594 })?;
1595 let composed = Verb::Compose(Box::new(f), Box::new(g));
1596 Verb::Atop(Box::new(back), Box::new(composed))
1597 }
1598 OpGlyph::Dot => Verb::InnerProduct {
1601 u: Box::new(Verb::Reduce(Box::new(f))),
1602 v: Box::new(g),
1603 apl: true,
1604 },
1605 _ => Verb::Compose(Box::new(f), Box::new(g)),
1606 };
1607 out.push(Token { kind: Tok::Func(derived), span });
1608 continue;
1609 }
1610 let left_is_func = matches!(out.last().map(|x| &x.kind), Some(Tok::Func(_)));
1612 if !left_is_func {
1613 if out.last().is_some_and(|x| is_operand_end(&x.kind)) {
1617 let f = match op {
1618 OpGlyph::Slash => copy_verb(false),
1619 OpGlyph::SlashBar => copy_verb(true),
1620 OpGlyph::Backslash => expand_verb(false),
1621 OpGlyph::BackslashBar => expand_verb(true),
1622 OpGlyph::Rank
1623 | OpGlyph::Commute
1624 | OpGlyph::Power
1625 | OpGlyph::JotDot
1626 | OpGlyph::Jot
1627 | OpGlyph::Over
1628 | OpGlyph::Under
1629 | OpGlyph::Stencil
1630 | OpGlyph::Before
1631 | OpGlyph::Key
1632 | OpGlyph::Dot
1633 | OpGlyph::Variant
1634 | OpGlyph::Each => {
1635 return Err(Error::parse(
1636 format!("{} needs a function to its left", op.glyph()),
1637 t.span,
1638 ));
1639 }
1640 };
1641 out.push(Token { kind: Tok::Func(f), span: t.span });
1642 continue;
1643 }
1644 return Err(Error::parse(
1645 format!("{} needs a function to its left", op.glyph()),
1646 t.span,
1647 ));
1648 }
1649 let ftok = out.pop().unwrap();
1650 let f = match ftok.kind {
1651 Tok::Func(f) => f,
1652 _ => unreachable!("checked above"),
1653 };
1654 let span = Span::merge(ftok.span, t.span);
1655 if let Some((k, aspan)) = take_axis(&mut it, d)? {
1659 let inner = match op {
1660 OpGlyph::Slash | OpGlyph::SlashBar => Verb::NWise(Box::new(f)),
1661 OpGlyph::Backslash | OpGlyph::BackslashBar => {
1662 Verb::Windowed(Box::new(Verb::Reduce(Box::new(f))), WindowKind::Scan)
1663 }
1664 _ => {
1665 return Err(Error::not_yet(
1666 format!("axis specification for {}", op.glyph()),
1667 aspan,
1668 ));
1669 }
1670 };
1671 out.push(Token {
1672 kind: Tok::Func(Verb::AlongAxis(Box::new(inner), k)),
1673 span: Span::merge(span, aspan),
1674 });
1675 continue;
1676 }
1677 let derived = match op {
1678 OpGlyph::Slash => Verb::Rank(Box::new(Verb::NWise(Box::new(f))), [1, RANK_INF, 1]),
1684 OpGlyph::SlashBar => Verb::NWise(Box::new(f)),
1685 OpGlyph::Backslash => Verb::Rank(
1689 Box::new(Verb::Windowed(Box::new(Verb::Reduce(Box::new(f))), WindowKind::Scan)),
1690 [1, 1, 1],
1691 ),
1692 OpGlyph::BackslashBar => {
1693 Verb::Windowed(Box::new(Verb::Reduce(Box::new(f))), WindowKind::Scan)
1694 }
1695 OpGlyph::Commute => Verb::Commute(Box::new(f)),
1696 OpGlyph::Key => Verb::KeyPairs(Box::new(f)),
1697 OpGlyph::Variant => {
1700 let (options, ospan) = variant_options(&mut it, t.span)?;
1701 let derived = variant(f, &options, Span::merge(span, ospan))?;
1702 out.push(Token { kind: Tok::Func(derived), span: Span::merge(span, ospan) });
1703 continue;
1704 }
1705 OpGlyph::Dot => {
1708 return Err(Error::parse("the inner product . needs a function on its right", t.span));
1709 }
1710 OpGlyph::Each => Verb::Each(Box::new(f), Enclose::ExceptSimpleScalar),
1714 OpGlyph::Power => {
1715 let spec = match it.peek() {
1716 Some(tok) if matches!(tok.kind, Tok::Func(_)) => {
1719 let gtok = it.next().unwrap();
1720 let Tok::Func(g) = gtok.kind else { unreachable!("checked above") };
1721 let v = Verb::PowerUntil(Box::new(f), Box::new(g));
1722 out.push(Token {
1723 kind: Tok::Func(v),
1724 span: Span::merge(span, gtok.span),
1725 });
1726 continue;
1727 }
1728 Some(tok) if literal(&tok.kind).is_some() => it.next().unwrap(),
1729 _ => {
1730 return Err(Error::not_yet("computed power (f⍣n)", t.span));
1731 }
1732 };
1733 let arr = literal(&spec.kind).expect("checked above");
1734 let (p, inverse) = power_spec(arr, spec.span)?;
1735 let f = if inverse { crate::frontend::j::obverse_of(&f, span)? } else { f };
1738 let f = Verb::PowerN(Box::new(f), p);
1739 out.push(Token { kind: Tok::Func(f), span: Span::merge(span, spec.span) });
1740 continue;
1741 }
1742 OpGlyph::Stencil => {
1746 let Some(spec) = it.peek().filter(|t| literal(&t.kind).is_some()) else {
1747 return Err(Error::parse(
1748 "⌺ needs a window specification on its right",
1749 t.span,
1750 ));
1751 };
1752 let sspan = spec.span;
1753 let spec = it.next().expect("peeked a literal");
1754 let arr = literal(&spec.kind).expect("checked above");
1755 if arr.rank() > 1 {
1756 return Err(Error::not_yet(
1757 "a stencil with a movement row (f⌺(m⍪w))",
1758 sspan,
1759 ));
1760 }
1761 let sizes = arr
1762 .to_i64_vec()
1763 .ok_or_else(|| Error::domain("a stencil window is whole numbers", sspan))?;
1764 let v = Verb::Stencil(Box::new(f), sizes);
1765 out.push(Token { kind: Tok::Func(v), span: Span::merge(span, sspan) });
1766 continue;
1767 }
1768 OpGlyph::Rank => {
1769 let spec = match it.peek() {
1770 Some(tok) if matches!(tok.kind, Tok::Func(_)) => {
1773 let gtok = it.next().unwrap();
1774 let Tok::Func(g) = gtok.kind else { unreachable!("checked above") };
1775 let v = Verb::Atop(Box::new(f), Box::new(g));
1776 out.push(Token {
1777 kind: Tok::Func(v),
1778 span: Span::merge(span, gtok.span),
1779 });
1780 continue;
1781 }
1782 Some(tok) if literal(&tok.kind).is_some() => it.next().unwrap(),
1783 _ => {
1784 return Err(Error::parse(
1785 "⍤ needs a rank specification on its right",
1786 t.span,
1787 ));
1788 }
1789 };
1790 let arr = literal(&spec.kind).expect("checked above");
1791 let ranks = rank_spec(arr, spec.span)?;
1792 let f = Verb::Rank(Box::new(f), ranks);
1793 out.push(Token { kind: Tok::Func(f), span: Span::merge(span, spec.span) });
1794 continue;
1795 }
1796 OpGlyph::JotDot
1798 | OpGlyph::Jot
1799 | OpGlyph::Over
1800 | OpGlyph::Under
1801 | OpGlyph::Before => {
1802 unreachable!("handled above")
1803 }
1804 };
1805 out.push(Token { kind: Tok::Func(derived), span });
1806 }
1807 Ok(out)
1808}
1809
1810fn take_axis(
1813 it: &mut std::iter::Peekable<std::vec::IntoIter<Token>>,
1814 d: Rules,
1815) -> Result<Option<(usize, Span)>> {
1816 if !matches!(it.peek().map(|t| &t.kind), Some(Tok::LBracket)) {
1817 return Ok(None);
1818 }
1819 let open = it.next().expect("peeked");
1820 let spec = match it.next() {
1821 Some(tok) if literal(&tok.kind).is_some() => tok,
1822 Some(tok) => return Err(Error::not_yet("a computed axis (f[k])", tok.span)),
1823 None => return Err(Error::parse("unterminated axis specification", open.span)),
1824 };
1825 let close = match it.next() {
1826 Some(tok) if matches!(tok.kind, Tok::RBracket) => tok,
1827 _ => return Err(Error::parse("unterminated axis specification", open.span)),
1828 };
1829 let span = Span::merge(open.span, close.span);
1830 let arr = literal(&spec.kind).expect("checked above");
1831 let ints = arr
1832 .to_i64_vec()
1833 .ok_or_else(|| Error::parse("an axis must be a whole number", spec.span))?;
1834 let [k] = ints[..] else {
1835 return Err(Error::not_yet("several axes in one specification", spec.span));
1836 };
1837 let origin = d.origin;
1838 let k = k - origin;
1839 if k < 0 {
1840 return Err(Error::domain(format!("axis {} does not exist", k + origin), spec.span));
1841 }
1842 Ok(Some((k as usize, span)))
1843}
1844
1845fn variant_options(
1854 it: &mut std::iter::Peekable<std::vec::IntoIter<Token>>,
1855 span: Span,
1856) -> Result<(Vec<(String, Array)>, Span)> {
1857 if let Some(tok) = it.peek().filter(|t| literal(&t.kind).is_some()) {
1858 let (value, vspan) = (literal(&tok.kind).expect("peeked a literal").clone(), tok.span);
1859 it.next();
1860 return Ok((vec![("CT".to_string(), value)], vspan));
1861 }
1862 let mut options = Vec::new();
1863 let mut last = span;
1864 while it.peek().is_some_and(|t| matches!(t.kind, Tok::LParen)) {
1865 it.next();
1866 let mut inside: Vec<Array> = Vec::new();
1867 loop {
1868 let Some(tok) = it.next() else {
1869 return Err(Error::parse("unmatched ( after ⍠", span));
1870 };
1871 last = tok.span;
1872 if matches!(tok.kind, Tok::RParen) {
1873 break;
1874 }
1875 match literal(&tok.kind) {
1876 Some(a) => inside.push(a.clone()),
1877 None => {
1878 return Err(Error::not_yet(
1879 "a computed variant option (f⍠v with a name or an expression)",
1880 tok.span,
1881 ));
1882 }
1883 }
1884 }
1885 let [name, value] = inside.as_slice() else {
1886 return Err(Error::parse("a variant option is a name and a value", last));
1887 };
1888 let Data::Char(cs) = &name.data else {
1889 return Err(Error::parse("a variant option starts with its name", last));
1890 };
1891 options.push((cs.as_slice().iter().collect::<String>().to_uppercase(), value.clone()));
1892 }
1893 if options.is_empty() {
1894 let where_ = it.peek().map_or(span, |t| t.span);
1895 return Err(Error::not_yet(
1896 "a computed variant option (f⍠v with a name or an expression)",
1897 where_,
1898 ));
1899 }
1900 Ok((options, last))
1901}
1902
1903fn variant(f: Verb, options: &[(String, Array)], span: Span) -> Result<Verb> {
1909 let mut out = f;
1910 for (name, value) in options {
1911 out = match name.as_str() {
1912 "CT" => {
1913 let Some(ct) = value.to_f64_vec().and_then(|v| v.first().copied()) else {
1914 return Err(Error::domain("a comparison tolerance is a number", span));
1915 };
1916 if !out.uses_tolerance() {
1917 return Err(Error::domain(
1918 format!(
1919 "the comparison tolerance is not an option of {}: it consults none",
1920 out.name()
1921 ),
1922 span,
1923 ));
1924 }
1925 if !(0.0..1.0).contains(&ct) {
1926 return Err(Error::domain(
1927 "a comparison tolerance lies between 0 and 1",
1928 span,
1929 ));
1930 }
1931 Verb::Fit(Box::new(out), ct)
1932 }
1933 "IO" => {
1934 let Some(io) = value.to_i64_vec().and_then(|v| v.first().copied()) else {
1935 return Err(Error::domain("an index origin is a whole number", span));
1936 };
1937 if io != 0 && io != 1 {
1938 return Err(Error::domain("an index origin is 0 or 1", span));
1939 }
1940 crate::verb::with_origin(&out, io).ok_or_else(|| {
1941 Error::domain(
1942 format!("the index origin is not an option of {}", out.name()),
1943 span,
1944 )
1945 })?
1946 }
1947 other => {
1948 return Err(Error::not_yet(
1949 format!("the variant option {other} (f⍠v)"),
1950 span,
1951 ));
1952 }
1953 };
1954 }
1955 Ok(out)
1956}
1957
1958fn unwrap_lone_operators(toks: Vec<Token>) -> Vec<Token> {
1963 let mut out: Vec<Token> = Vec::with_capacity(toks.len());
1964 for t in toks {
1965 let n = out.len();
1966 if matches!(t.kind, Tok::RParen)
1967 && n >= 2
1968 && matches!(out[n - 1].kind, Tok::Op(_))
1969 && matches!(out[n - 2].kind, Tok::LParen)
1970 {
1971 let op = out.pop().expect("checked above");
1972 let open = out.pop().expect("checked above");
1973 out.push(Token { kind: op.kind, span: Span::merge(open.span, t.span) });
1974 continue;
1975 }
1976 out.push(t);
1977 }
1978 out
1979}
1980
1981fn close_paren(out: &mut Vec<Token>, d: Rules) -> Result<()> {
1985 let close = out.len() - 1;
1986 let Some(open) = matching_lparen(out, close) else { return Ok(()) };
1987 let span = Span::merge(out[open].span, out[close].span);
1988 let inner = &out[open + 1..close];
1989 if inner.len() == 1 && matches!(inner[0].kind, Tok::Func(_)) {
1990 let Some(Token { kind, .. }) = out.get(open + 1).cloned() else {
1991 unreachable!("checked above")
1992 };
1993 out.truncate(open);
1994 out.push(Token { kind, span });
1995 return Ok(());
1996 }
1997 if !d.trains || inner.len() < 2 || !inner[1..].iter().all(|t| matches!(t.kind, Tok::Func(_))) {
1998 return Ok(());
1999 }
2000 let Some(verb) = train(inner)? else { return Ok(()) };
2001 out.truncate(open);
2002 out.push(Token { kind: Tok::Func(verb), span });
2003 Ok(())
2004}
2005
2006fn matching_lparen(out: &[Token], close: usize) -> Option<usize> {
2008 let mut depth = 0usize;
2009 for i in (0..close).rev() {
2010 match out[i].kind {
2011 Tok::RParen => depth += 1,
2012 Tok::LParen => {
2013 if depth == 0 {
2014 return Some(i);
2015 }
2016 depth -= 1;
2017 }
2018 _ => {}
2019 }
2020 }
2021 None
2022}
2023
2024fn train(tines: &[Token]) -> Result<Option<Verb>> {
2033 debug_assert!(!tines.is_empty());
2034 if tines.len() == 1 {
2035 return Ok(match &tines[0].kind {
2036 Tok::Func(f) => Some(f.clone()),
2037 _ => None,
2038 });
2039 }
2040 if tines.len() == 2 {
2041 let (Tok::Func(g), Tok::Func(h)) = (&tines[0].kind, &tines[1].kind) else {
2042 return Ok(None);
2043 };
2044 return Ok(Some(Verb::Atop(Box::new(g.clone()), Box::new(h.clone()))));
2045 }
2046 let head = &tines[0].kind;
2049 if tines.len() % 2 == 0 {
2050 let Tok::Func(f) = head else {
2051 return Err(Error::parse(
2052 "a value may only be a fork's left tine, and this train has an even number of tines",
2053 tines[0].span,
2054 ));
2055 };
2056 let Some(rest) = train(&tines[1..])? else { return Ok(None) };
2057 return Ok(Some(Verb::Atop(Box::new(f.clone()), Box::new(rest))));
2058 }
2059 let Some(rest) = train(&tines[2..])? else { return Ok(None) };
2060 let Tok::Func(g) = &tines[1].kind else { unreachable!("the tail is all functions") };
2061 match head {
2062 Tok::Func(f) => {
2063 Ok(Some(Verb::Fork(Box::new(f.clone()), Box::new(g.clone()), Box::new(rest))))
2064 }
2065 Tok::Value(n) | Tok::Nums(n) => {
2066 Ok(Some(Verb::NounFork(n.clone(), Box::new(g.clone()), Box::new(rest))))
2067 }
2068 Tok::Name(_) | Tok::Param(_) | Tok::RParen | Tok::RBracket | Tok::Niladic(_) => {
2072 Err(Error::not_yet("a train whose left tine is a computed value", tines[0].span))
2073 }
2074 _ => Ok(None),
2075 }
2076}
2077
2078fn tine_run(toks: &[Token], d: Rules) -> Result<Option<Verb>> {
2084 if !d.trains || toks.is_empty() {
2085 return Ok(None);
2086 }
2087 if !toks[1..].iter().all(|t| matches!(t.kind, Tok::Func(_))) {
2088 return Ok(None);
2089 }
2090 train(toks)
2091}
2092
2093fn fold_axes(toks: Vec<Token>, d: Rules) -> Result<Vec<Token>> {
2095 let mut out: Vec<Token> = Vec::new();
2096 let mut it = toks.into_iter().peekable();
2097 while let Some(t) = it.next() {
2098 let Tok::Func(f) = &t.kind else {
2099 out.push(t);
2100 continue;
2101 };
2102 let Some((k, aspan)) = take_axis(&mut it, d)? else {
2103 out.push(t);
2104 continue;
2105 };
2106 let Some(inner) = leading_axis_form(f) else {
2107 return Err(Error::not_yet(format!("axis specification for {}", f.name()), aspan));
2108 };
2109 out.push(Token {
2110 kind: Tok::Func(Verb::AlongAxis(Box::new(inner), k)),
2111 span: Span::merge(t.span, aspan),
2112 });
2113 }
2114 Ok(out)
2115}
2116
2117fn leading_axis_form(v: &Verb) -> Option<Verb> {
2121 match v {
2122 Verb::Rank(inner, [1, RANK_INF, RANK_INF]) => leading_axis_form(inner),
2124 Verb::Prim(p) if matches!(p.monad, MonadOp::Reverse) => {
2127 let mut p = *p;
2128 if matches!(p.dyad, DyadOp::RotateApl { .. }) {
2129 p.dyad = DyadOp::RotateApl { last: false };
2130 }
2131 Some(Verb::Prim(p))
2132 }
2133 _ => None,
2134 }
2135}
2136
2137fn select_axis_verb(axis: usize, rank: usize, d: Rules) -> Verb {
2139 Verb::Prim(Prim {
2140 name: "[…]",
2141 monad: MonadOp::None,
2142 dyad: DyadOp::SelectAxis { axis, rank, origin: d.origin },
2143 ranks: [RANK_INF; 3],
2144 })
2145}
2146
2147fn power_spec(a: &Array, span: Span) -> Result<(Power, bool)> {
2151 let ints = a
2152 .to_i64_vec()
2153 .ok_or_else(|| Error::parse("⍣ needs a whole number on its right", span))?;
2154 let [n] = ints[..] else {
2155 return Err(Error::not_yet("power over a list of counts (f⍣n)", span));
2156 };
2157 Ok((Power::Times(n.unsigned_abs()), n < 0))
2158}
2159
2160fn rank_spec(a: &Array, span: Span) -> Result<[i64; 3]> {
2162 let ints = a
2163 .to_i64_vec()
2164 .ok_or_else(|| Error::parse("⍤ rank specification must be integers", span))?;
2165 match ints.len() {
2166 1 => Ok([ints[0], ints[0], ints[0]]),
2167 2 => Ok([ints[1], ints[0], ints[1]]),
2168 3 => Ok([ints[0], ints[1], ints[2]]),
2169 _ => Err(Error::parse("⍤ rank specification takes 1 to 3 integers", span)),
2170 }
2171}
2172
2173fn parse_range(toks: &[Token], lo: usize, hi: usize, hint: Span, d: Rules) -> Result<Expr> {
2180 let (mut acc, mut start) = parse_operand(toks, lo, hi, hint, d)?;
2181 let end = toks[hi - 1].span.end;
2182 loop {
2183 if start == lo {
2184 return Ok(acc);
2185 }
2186 let left = &toks[start - 1];
2187 match &left.kind {
2188 Tok::Func(f) => {
2189 let dyadic = start >= lo + 2 && is_operand_end(&toks[start - 2].kind);
2191 if dyadic {
2192 let (x, xstart) = parse_operand(toks, lo, start - 1, left.span, d)?;
2193 acc = Expr::Dyad {
2194 verb: f.clone(),
2195 x: Box::new(x),
2196 y: Box::new(acc),
2197 span: Span::new(toks[xstart].span.start, end),
2198 };
2199 start = xstart;
2200 } else {
2201 acc = Expr::Monad {
2202 verb: f.clone(),
2203 y: Box::new(acc),
2204 span: Span::new(left.span.start, end),
2205 };
2206 start -= 1;
2207 }
2208 }
2209 Tok::Assign => {
2210 if start < lo + 2 {
2211 return Err(Error::parse("assignment target must be a name", left.span));
2212 }
2213 let target = &toks[start - 2];
2214 let span = Span::new(target.span.start, end);
2215 match &target.kind {
2216 Tok::Name(n) => {
2217 acc = Expr::Assign {
2218 name: n.clone(),
2219 value: Box::new(acc),
2220 scope: Scope::Local,
2221 span,
2222 };
2223 }
2224 Tok::Quad { quote } => {
2225 acc = Expr::PrintPass { value: Box::new(acc), bare: *quote, span };
2226 }
2227 _ => {
2228 return Err(Error::parse(
2229 "assignment target must be a name",
2230 target.span,
2231 ));
2232 }
2233 }
2234 start -= 2;
2235 }
2236 _ => break,
2239 }
2240 }
2241 let span = Span::new(toks[lo].span.start, toks[start - 1].span.end);
2242 if d.trains && toks[lo..start].iter().all(|t| matches!(t.kind, Tok::Func(_))) {
2246 return Err(Error::parse(
2247 "a train is a function; parenthesise it to apply it to an argument",
2248 span,
2249 ));
2250 }
2251 Err(Error::parse("syntax error", span))
2252}
2253
2254fn parse_operand(
2261 toks: &[Token],
2262 lo: usize,
2263 hi: usize,
2264 hint: Span,
2265 d: Rules,
2266) -> Result<(Expr, usize)> {
2267 let (first, mut start) = parse_primary(toks, lo, hi, hint, d)?;
2268 if start == lo || !is_operand_end(&toks[start - 1].kind) {
2269 return Ok((first, start));
2270 }
2271 let mut items: Vec<Expr> = Vec::new();
2272 let mut cur = first;
2273 loop {
2274 push_items(&mut items, cur, &toks[start]);
2275 if start == lo || !is_operand_end(&toks[start - 1].kind) {
2276 break;
2277 }
2278 let (e, s) = parse_primary(toks, lo, start, toks[start - 1].span, d)?;
2279 cur = e;
2280 start = s;
2281 }
2282 let span = Span::new(toks[start].span.start, toks[hi - 1].span.end);
2283 let mut it = items.into_iter();
2284 let last = it.next().expect("a strand has at least one item");
2285 let mut acc = Expr::Monad { verb: strand_seed(d), y: Box::new(last), span };
2286 for item in it {
2287 acc = Expr::Dyad { verb: strand_verb(), x: Box::new(item), y: Box::new(acc), span };
2288 }
2289 Ok((acc, start))
2290}
2291
2292fn push_items(items: &mut Vec<Expr>, e: Expr, tok: &Token) {
2294 if let Tok::Nums(a) = &tok.kind && a.rank() > 0 {
2295 for i in (0..a.count()).rev() {
2296 let atom = Array::new(Vec::new(), a.data.slice(i, i + 1));
2297 items.push(Expr::Const(atom, tok.span));
2298 }
2299 return;
2300 }
2301 items.push(e);
2302}
2303
2304fn strand_seed(d: Rules) -> Verb {
2307 Verb::Atop(
2308 Box::new(Verb::Prim(prim_for(',', d).expect("`,` is a primitive"))),
2309 Box::new(Verb::Prim(prim_for('⊂', d).expect("`⊂` is a primitive"))),
2310 )
2311}
2312
2313fn strand_verb() -> Verb {
2315 Verb::Prim(Prim {
2316 name: "(vector notation)",
2317 monad: MonadOp::None,
2318 dyad: DyadOp::Strand,
2319 ranks: [RANK_INF; 3],
2320 })
2321}
2322
2323fn parse_primary(
2326 toks: &[Token],
2327 lo: usize,
2328 hi: usize,
2329 hint: Span,
2330 d: Rules,
2331) -> Result<(Expr, usize)> {
2332 if hi == lo {
2333 return Err(Error::parse("empty parentheses", hint));
2334 }
2335 let t = &toks[hi - 1];
2336 match &t.kind {
2337 Tok::Value(a) | Tok::Nums(a) => Ok((Expr::Const(a.clone(), t.span), hi - 1)),
2338 Tok::Param(i) => Ok((Expr::Param(*i, t.span), hi - 1)),
2339 Tok::Name(n) => Ok((Expr::Name(n.clone(), t.span), hi - 1)),
2340 Tok::Niladic(v) => Ok((
2343 Expr::Monad {
2344 verb: v.clone(),
2345 y: Box::new(Expr::Const(Array::empty(crate::dtype::DType::I64), t.span)),
2346 span: t.span,
2347 },
2348 hi - 1,
2349 )),
2350 Tok::RParen => {
2351 let l = match_lparen(toks, lo, hi - 1)?;
2352 let hint = Span::merge(toks[l].span, t.span);
2353 let inner = parse_range(toks, l + 1, hi - 1, hint, d)?;
2354 Ok((inner, l))
2355 }
2356 Tok::RBracket => index_brackets(toks, lo, hi, d),
2357 Tok::Func(_) if hi >= lo + 2 && matches!(toks[hi - 2].kind, Tok::Assign) => {
2361 let from = if hi >= lo + 3 { toks[hi - 3].span } else { toks[hi - 2].span };
2362 let span = Span::merge(from, t.span);
2363 if d.trains {
2364 Err(Error::not_yet("naming a function inside a larger sentence", span))
2365 } else {
2366 Err(Error::not_yet("function assignment (F←+/)", span))
2367 }
2368 }
2369 Tok::Func(_) => Err(Error::parse("missing right argument", t.span)),
2370 Tok::Assign => Err(Error::parse("← needs a value on its right", t.span)),
2371 Tok::Quad { quote } => Ok((Expr::Input { eval: !*quote, span: t.span }, hi - 1)),
2373 Tok::LParen => Err(Error::parse("unmatched (", t.span)),
2374 Tok::LBracket => Err(Error::parse("unmatched [", t.span)),
2375 Tok::Semi => Err(Error::parse("; is only meaningful inside index brackets", t.span)),
2376 Tok::Colon => Err(Error::parse(": is only meaningful in a dfn guard", t.span)),
2377 Tok::UserOp { .. } => Err(Error::parse(
2378 "this dfn mentions ⍺⍺ or ⍵⍵, so it is an operator and needs a function operand",
2379 t.span,
2380 )),
2381 Tok::Arrow => Err(Error::parse(
2382 "→ branches, and only a line of a ∇ definition may begin with it",
2383 t.span,
2384 )),
2385 Tok::Del => Err(Error::parse("∇ opens a definition; it is not a value", t.span)),
2386 Tok::Control(w) => Err(Error::parse(
2387 format!(":{w} is only meaningful inside a ∇ definition"),
2388 t.span,
2389 )),
2390 Tok::LBrace | Tok::RBrace => Err(Error::parse("unmatched {", t.span)),
2391 Tok::Separator => Err(Error::internal("a statement break survived folding")),
2392 Tok::Op(_) => Err(Error::internal("operator survived folding")),
2393 Tok::QuadFx => Err(Error::not_yet("⎕FX inside another definition", t.span)),
2398 }
2399}
2400
2401fn index_brackets(
2408 toks: &[Token],
2409 lo: usize,
2410 hi: usize,
2411 d: Rules,
2412) -> Result<(Expr, usize)> {
2413 let close = &toks[hi - 1];
2414 let open = match_lbracket(toks, lo, hi - 1)?;
2415 if open == lo || !is_operand_end(&toks[open - 1].kind) {
2416 return Err(Error::parse("[ needs a value on its left", toks[open].span));
2417 }
2418 let (base, start) = parse_primary(toks, lo, open, toks[open].span, d)?;
2419 let slots = index_slots(toks, open + 1, hi - 1, toks[open].span)?;
2420 let span = Span::new(toks[start].span.start, close.span.end);
2421 let rank = slots.len();
2422 let mut acc = base;
2423 let mut first = true;
2424 for (axis, slot) in slots.iter().enumerate().rev() {
2425 let Some((slo, shi)) = *slot else { continue };
2426 let idx = parse_range(toks, slo, shi, toks[open].span, d)?;
2427 let check = if first { rank } else { 0 };
2428 first = false;
2429 acc = Expr::Dyad {
2430 verb: select_axis_verb(axis, check, d),
2431 x: Box::new(idx),
2432 y: Box::new(acc),
2433 span,
2434 };
2435 }
2436 Ok((acc, start))
2437}
2438
2439fn index_slots(
2442 toks: &[Token],
2443 lo: usize,
2444 hi: usize,
2445 hint: Span,
2446) -> Result<Vec<Option<(usize, usize)>>> {
2447 let mut out = Vec::new();
2448 let mut depth = 0usize;
2449 let mut start = lo;
2450 for (i, t) in toks.iter().enumerate().take(hi).skip(lo) {
2451 match t.kind {
2452 Tok::LParen | Tok::LBracket => depth += 1,
2453 Tok::RParen | Tok::RBracket => depth -= 1,
2454 Tok::Semi if depth == 0 => {
2455 out.push((start < i).then_some((start, i)));
2456 start = i + 1;
2457 }
2458 _ => {}
2459 }
2460 }
2461 out.push((start < hi).then_some((start, hi)));
2462 if out.len() == 1 && out[0].is_none() {
2463 return Err(Error::parse("empty index brackets", hint));
2464 }
2465 Ok(out)
2466}
2467
2468fn match_lbracket(toks: &[Token], lo: usize, rbracket: usize) -> Result<usize> {
2469 let mut depth = 0usize;
2470 let mut i = rbracket;
2471 while i > lo {
2472 i -= 1;
2473 match toks[i].kind {
2474 Tok::RBracket => depth += 1,
2475 Tok::LBracket => {
2476 if depth == 0 {
2477 return Ok(i);
2478 }
2479 depth -= 1;
2480 }
2481 _ => {}
2482 }
2483 }
2484 Err(Error::parse("unmatched ]", toks[rbracket].span))
2485}
2486
2487fn match_lparen(toks: &[Token], lo: usize, rparen: usize) -> Result<usize> {
2488 let mut depth = 0usize;
2489 let mut i = rparen;
2490 while i > lo {
2491 i -= 1;
2492 match toks[i].kind {
2493 Tok::RParen => depth += 1,
2494 Tok::LParen => {
2495 if depth == 0 {
2496 return Ok(i);
2497 }
2498 depth -= 1;
2499 }
2500 _ => {}
2501 }
2502 }
2503 Err(Error::parse("unmatched )", toks[rparen].span))
2504}
2505
2506const CONTROL_WORDS: [&str; 21] = [
2518 "If", "ElseIf", "Else", "EndIf", "AndIf", "OrIf", "While", "EndWhile", "Repeat", "Until",
2519 "For", "In", "EndFor", "Select", "Case", "CaseList", "EndSelect", "Return", "Leave",
2520 "Continue", "End",
2521];
2522
2523fn control_word(word: &str) -> Option<&'static str> {
2526 CONTROL_WORDS.iter().copied().find(|w| w.eq_ignore_ascii_case(word))
2527}
2528
2529fn match_close(toks: &[Token], open: usize, opener: &Tok, closer: &Tok) -> Option<usize> {
2531 let same = |a: &Tok, b: &Tok| std::mem::discriminant(a) == std::mem::discriminant(b);
2532 let mut depth = 0usize;
2533 for (i, t) in toks.iter().enumerate().skip(open) {
2534 if same(&t.kind, opener) {
2535 depth += 1;
2536 } else if same(&t.kind, closer) {
2537 depth -= 1;
2538 if depth == 0 {
2539 return Some(i);
2540 }
2541 }
2542 }
2543 None
2544}
2545
2546fn fold_dfns(
2548 toks: Vec<Token>,
2549 d: Rules,
2550 verbs: &HashMap<String, Verb>,
2551) -> Result<Vec<Token>> {
2552 let Some(open) = toks.iter().position(|t| matches!(t.kind, Tok::LBrace)) else {
2553 return Ok(toks);
2554 };
2555 let close = match_close(&toks, open, &Tok::LBrace, &Tok::RBrace)
2556 .ok_or_else(|| Error::parse("unmatched {", toks[open].span))?;
2557 let span = Span::merge(toks[open].span, toks[close].span);
2558 let mut out: Vec<Token> = toks[..open].to_vec();
2559 let kind = match build_dfn(&toks[open + 1..close], d, verbs)? {
2560 Dfn::Func(verb) => Tok::Func(*verb),
2561 Dfn::Op { def, omega } => Tok::UserOp { def, omega },
2562 };
2563 out.push(Token { kind, span });
2564 out.extend_from_slice(&toks[close + 1..]);
2565 fold_dfns(out, d, verbs)
2567}
2568
2569fn split_statements(toks: &[Token]) -> Vec<&[Token]> {
2572 let mut out = Vec::new();
2573 let mut depth = 0usize;
2574 let mut start = 0usize;
2575 for (i, t) in toks.iter().enumerate() {
2576 match t.kind {
2577 Tok::LBrace => depth += 1,
2578 Tok::RBrace => depth = depth.saturating_sub(1),
2579 Tok::Separator if depth == 0 => {
2580 out.push(&toks[start..i]);
2581 start = i + 1;
2582 }
2583 _ => {}
2584 }
2585 }
2586 out.push(&toks[start..]);
2587 out.into_iter().filter(|s| !s.is_empty()).collect()
2588}
2589
2590enum Dfn {
2593 Func(Box<Verb>),
2594 Op { def: Arc<OpDef>, omega: bool },
2595}
2596
2597thread_local! {
2606 static ENCLOSING: std::cell::RefCell<Vec<u64>> =
2607 const { std::cell::RefCell::new(Vec::new()) };
2608 static NEXT_DFN_ID: std::cell::Cell<u64> = const { std::cell::Cell::new(1) };
2609}
2610
2611fn build_dfn(body: &[Token], d: Rules, verbs: &HashMap<String, Verb>) -> Result<Dfn> {
2616 let mut depth = 0usize;
2617 let mut dyadic = false;
2618 let mut alpha_op = false;
2619 let mut omega_op = false;
2620 for t in body {
2621 match &t.kind {
2622 Tok::LBrace => depth += 1,
2623 Tok::RBrace => depth = depth.saturating_sub(1),
2624 Tok::Name(n) if depth == 0 && n == "⍺" => dyadic = true,
2625 Tok::Name(n) if depth == 0 && n == "⍺⍺" => alpha_op = true,
2626 Tok::Name(n) if depth == 0 && n == "⍵⍵" => omega_op = true,
2627 _ => {}
2628 }
2629 }
2630 let reading = |alpha_value: bool, omega_value: bool| -> Result<Verb> {
2634 let mut inner = verbs.clone();
2635 if alpha_op && !alpha_value {
2636 inner.insert("⍺⍺".to_string(), Verb::Named("⍺⍺".to_string()));
2637 }
2638 if (alpha_op || omega_op) && !omega_value {
2639 inner.insert("⍵⍵".to_string(), Verb::Named("⍵⍵".to_string()));
2640 }
2641 let id = NEXT_DFN_ID.with(|c| {
2644 let id = c.get();
2645 c.set(id.wrapping_add(1));
2646 id
2647 });
2648 let enclosing = ENCLOSING.with(|e| e.borrow().clone());
2649 ENCLOSING.with(|e| e.borrow_mut().push(id));
2650 let parsed = parse_dfn_body(body, d, &mut inner);
2651 ENCLOSING.with(|e| {
2652 e.borrow_mut().pop();
2653 });
2654 let mut stmts = parsed?;
2655 match d.dfn_result {
2660 DfnResult::LastSentence => {}
2661 DfnResult::FirstNonAssignment => {
2662 let plain = |e: &Expr| {
2666 !matches!(
2667 e,
2668 Expr::Assign { .. }
2669 | Expr::AmendIndex { .. }
2670 | Expr::Control(..)
2671 | Expr::VerbDef { .. }
2672 | Expr::ModDef { .. }
2673 )
2674 };
2675 if let Some(k) = stmts.iter().position(plain) {
2676 stmts.truncate(k + 1);
2677 }
2678 }
2679 }
2680 let pure = stmts.iter().all(is_pure_stmt);
2681 Ok(Verb::Explicit(Arc::new(ExplicitDef {
2682 name: "{…}".to_string(),
2683 left: dyadic.then(|| "⍺".to_string()),
2684 right: "⍵".to_string(),
2685 dyad_only: false,
2689 spare_left: true,
2690 result: None,
2691 locals: Vec::new(),
2692 body: stmts,
2693 empty: None,
2695 labels: Vec::new(),
2696 enclosing,
2697 id,
2698 pure,
2699 })))
2700 };
2701 if !(alpha_op || omega_op) {
2702 return Ok(Dfn::Func(Box::new(reading(false, false)?)));
2703 }
2704 let mut readings: [std::result::Result<Verb, String>; 4] =
2709 [const { Err(String::new()) }; 4];
2710 for (i, slot) in readings.iter_mut().enumerate() {
2711 *slot = reading(i & 1 != 0, i & 2 != 0).map_err(|e| e.msg);
2712 }
2713 if let Err(msg) = &readings[0]
2716 && readings.iter().all(|r| r.is_err())
2717 {
2718 return Err(Error::parse(msg.clone(), body.first().map_or(Span::new(0, 0), |t| t.span)));
2719 }
2720 Ok(Dfn::Op { def: Arc::new(OpDef { readings }), omega: omega_op })
2721}
2722
2723fn parse_dfn_body(
2724 body: &[Token],
2725 d: Rules,
2726 verbs: &mut HashMap<String, Verb>,
2727) -> Result<Vec<Expr>> {
2728 let mut stmts = Vec::new();
2729 for stmt in split_statements(body) {
2730 let stmt: Vec<Token> = stmt
2732 .iter()
2733 .map(|t| match t.kind {
2734 Tok::Del => Token { kind: Tok::Func(Verb::SelfRef), span: t.span },
2735 _ => t.clone(),
2736 })
2737 .collect();
2738 stmts.push(parse_guarded(stmt, d, verbs)?);
2739 }
2740 Ok(stmts)
2741}
2742
2743fn parse_guarded(
2745 stmt: Vec<Token>,
2746 d: Rules,
2747 verbs: &mut HashMap<String, Verb>,
2748) -> Result<Expr> {
2749 let mut depth = 0usize;
2750 let mut colon = None;
2751 for (i, t) in stmt.iter().enumerate() {
2752 match t.kind {
2753 Tok::LBrace | Tok::LParen | Tok::LBracket => depth += 1,
2754 Tok::RBrace | Tok::RParen | Tok::RBracket => depth = depth.saturating_sub(1),
2755 Tok::Colon if depth == 0 => {
2756 colon = Some(i);
2757 break;
2758 }
2759 _ => {}
2760 }
2761 }
2762 if let Some(k) = colon {
2763 let span = Span::merge(stmt[0].span, stmt[stmt.len() - 1].span);
2764 let test = one_statement(stmt[..k].to_vec(), d, verbs, stmt[k].span)?;
2765 let body = one_statement(stmt[k + 1..].to_vec(), d, verbs, stmt[k].span)?;
2766 return Ok(Expr::Control(
2768 Box::new(Control::Guard {
2769 test: vec![test],
2770 body: vec![body, Expr::Control(Box::new(Control::Return), span)],
2771 }),
2772 span,
2773 ));
2774 }
2775 let default = matches!(
2779 (stmt.first().map(|t| &t.kind), stmt.get(1).map(|t| &t.kind)),
2780 (Some(Tok::Name(n)), Some(Tok::Assign)) if n == "⍺"
2781 );
2782 let span = stmt.first().map_or(Span::new(0, 0), |t| t.span);
2783 let e = one_statement(stmt, d, verbs, span)?;
2784 if default {
2785 let scope = match d.default_arg {
2786 DefaultArg::Eager => Scope::LocalDefault,
2787 DefaultArg::Lazy => return Err(Error::not_yet("a lazy ⍺← default", span)),
2788 };
2789 if let Expr::Assign { name, value, span, .. } = e {
2790 return Ok(Expr::Assign { name, value, scope, span });
2791 }
2792 }
2793 Ok(e)
2794}
2795
2796fn one_statement(
2797 stmt: Vec<Token>,
2798 d: Rules,
2799 verbs: &mut HashMap<String, Verb>,
2800 hint: Span,
2801) -> Result<Expr> {
2802 parse_statement(stmt, d, verbs, false)?
2803 .ok_or_else(|| Error::parse("this needs an expression", hint))
2804}
2805
2806fn is_pure_stmt(e: &Expr) -> bool {
2808 match e {
2809 Expr::Const(..) | Expr::Param(..) | Expr::Name(..) => true,
2810 Expr::Monad { verb, y, .. } => verb.is_pure() && is_pure_stmt(y),
2811 Expr::Dyad { verb, x, y, .. } => verb.is_pure() && is_pure_stmt(x) && is_pure_stmt(y),
2812 Expr::Assign { value, .. } => is_pure_stmt(value),
2813 Expr::Control(c, _) => is_pure_control(c),
2814 _ => false,
2815 }
2816}
2817
2818fn is_pure_control(c: &Control) -> bool {
2819 let all = |b: &Vec<Expr>| b.iter().all(is_pure_stmt);
2820 match c {
2821 Control::Return | Control::Break | Control::Continue => true,
2822 Control::Branch(target) => is_pure_stmt(target),
2823 Control::If { arms, otherwise } => {
2824 arms.iter().all(|a| a.test.as_ref().is_none_or(all) && all(&a.body))
2825 && otherwise.as_ref().is_none_or(all)
2826 }
2827 Control::While { test, body, .. } => all(test) && all(body),
2828 Control::For { source, body, .. } => is_pure_stmt(source) && all(body),
2829 Control::Select { subject, cases } => {
2830 is_pure_stmt(subject)
2831 && cases.iter().all(|c| c.test.as_ref().is_none_or(all) && all(&c.body))
2832 }
2833 Control::Try { body, catch } => all(body) && all(catch),
2834 Control::Guard { test, body } => all(test) && all(body),
2835 }
2836}
2837
2838fn parse_tradfn(
2845 sentences: &[Vec<Token>],
2846 i: &mut usize,
2847 d: Rules,
2848 verbs: &mut HashMap<String, Verb>,
2849) -> Result<Expr> {
2850 let header = &sentences[*i];
2851 let open = header[0].span;
2852 *i += 1;
2853 let head = header[1..].to_vec();
2854 let mut body_lines: Vec<Vec<Token>> = Vec::new();
2855 loop {
2856 let Some(line) = sentences.get(*i) else {
2857 return Err(Error::parse("this definition has no closing ∇", open));
2858 };
2859 *i += 1;
2860 if line.len() == 1 && matches!(line[0].kind, Tok::Del) {
2861 break;
2862 }
2863 body_lines.push(line.clone());
2864 }
2865 let close = sentences
2866 .get(i.saturating_sub(1))
2867 .and_then(|l| l.first())
2868 .map_or(open, |t| t.span);
2869 build_tradfn(&head, &body_lines, Span::merge(open, close), d, verbs)
2870}
2871
2872fn build_tradfn(
2875 head: &[Token],
2876 body_lines: &[Vec<Token>],
2877 span: Span,
2878 d: Rules,
2879 verbs: &mut HashMap<String, Verb>,
2880) -> Result<Expr> {
2881 let (name, def_left, def_right, result, locals) = parse_header(head, span)?;
2882 let mut inner = verbs.clone();
2884 inner.insert(name.clone(), Verb::Named(name.clone()));
2885 let mut items = Vec::new();
2886 let mut labels: Vec<(String, usize)> = Vec::new();
2887 for line in body_lines {
2888 let mut label = None;
2889 let item = to_item(line.clone(), d, &mut inner, &mut label)?;
2890 if let Some(name) = label {
2891 labels.push((name, items.len()));
2892 }
2893 items.push(item);
2894 }
2895 let item_count = items.len();
2896 let mut cursor = AplCursor { items: &items, at: 0, d, loops: 0 };
2897 let mut body = parse_apl_block(&mut cursor, &[])?;
2898 if !labels.is_empty() && body.len() != item_count {
2902 return Err(Error::not_yet("a label and a control structure in one definition", span));
2903 }
2904 if let Some(item) = cursor.peek() {
2905 return Err(Error::parse(
2906 format!(":{} has no matching opening word", item.word().unwrap_or("?")),
2907 item.span(),
2908 ));
2909 }
2910 let mut own: Vec<String> = locals.clone();
2913 own.extend(result.clone());
2914 own.extend(def_left.clone());
2915 own.push(def_right.clone());
2916 for stmt in &mut body {
2917 set_scopes(stmt, &own);
2918 }
2919 let pure = body.iter().all(is_pure_stmt);
2920 let verb = Verb::Explicit(Arc::new(ExplicitDef {
2921 name: format!("∇{name}"),
2922 left: def_left,
2923 right: def_right,
2924 dyad_only: false,
2925 spare_left: false,
2929 enclosing: Vec::new(),
2930 id: 0,
2931 result,
2932 locals,
2933 body,
2934 empty: None,
2935 labels,
2936 pure,
2937 }));
2938 verbs.insert(name.clone(), verb.clone());
2939 Ok(Expr::VerbDef { name, verb, span })
2940}
2941
2942type Header = (String, Option<String>, String, Option<String>, Vec<String>);
2943
2944fn parse_header(toks: &[Token], span: Span) -> Result<Header> {
2946 let mut names: Vec<String> = Vec::new();
2947 let mut locals: Vec<String> = Vec::new();
2948 let mut result = None;
2949 let mut in_locals = false;
2950 let mut k = 0usize;
2951 if let (Some(Tok::Name(z)), Some(Tok::Assign)) =
2953 (toks.first().map(|t| &t.kind), toks.get(1).map(|t| &t.kind))
2954 {
2955 result = Some(z.clone());
2956 k = 2;
2957 }
2958 while k < toks.len() {
2959 match &toks[k].kind {
2960 Tok::Semi => in_locals = true,
2961 Tok::Name(n) if in_locals => locals.push(n.clone()),
2962 Tok::Name(n) => names.push(n.clone()),
2963 _ => {
2964 return Err(Error::parse("this is not a ∇ definition header", toks[k].span));
2965 }
2966 }
2967 k += 1;
2968 }
2969 match names.len() {
2970 3 => Ok((names[1].clone(), Some(names[0].clone()), names[2].clone(), result, locals)),
2971 2 => Ok((names[0].clone(), None, names[1].clone(), result, locals)),
2972 1 => Ok((names[0].clone(), None, crate::ir::NILADIC.to_string(), result, locals)),
2973 _ => Err(Error::parse("a ∇ definition header names a function and its arguments", span)),
2974 }
2975}
2976
2977enum AplItem {
2980 Sentence(Expr),
2981 Word { word: &'static str, rest: Vec<Token>, span: Span },
2982}
2983
2984impl AplItem {
2985 fn word(&self) -> Option<&'static str> {
2986 match self {
2987 AplItem::Word { word, .. } => Some(word),
2988 AplItem::Sentence(_) => None,
2989 }
2990 }
2991
2992 fn span(&self) -> Span {
2993 match self {
2994 AplItem::Word { span, .. } => *span,
2995 AplItem::Sentence(e) => e.span(),
2996 }
2997 }
2998}
2999
3000fn to_item(
3001 line: Vec<Token>,
3002 d: Rules,
3003 verbs: &mut HashMap<String, Verb>,
3004 label: &mut Option<String>,
3005) -> Result<AplItem> {
3006 let mut line = line;
3007 if let (Some(Tok::Name(n)), Some(Tok::Colon)) =
3010 (line.first().map(|t| &t.kind), line.get(1).map(|t| &t.kind))
3011 {
3012 *label = Some(n.clone());
3013 line.drain(..2);
3014 }
3015 if let Some(Tok::Control(word)) = line.first().map(|t| &t.kind) {
3016 let word = *word;
3017 let span = line[0].span;
3018 return Ok(AplItem::Word { word, rest: line[1..].to_vec(), span });
3019 }
3020 if matches!(line.first().map(|t| &t.kind), Some(Tok::Arrow)) {
3021 let span = line[0].span;
3022 let target = parse_statement(line[1..].to_vec(), d, verbs, true)?
3023 .ok_or_else(|| Error::parse("→ needs a line to branch to", span))?;
3024 let span = Span::merge(span, target.span());
3025 return Ok(AplItem::Sentence(Expr::Control(
3026 Box::new(Control::Branch(Box::new(target))),
3027 span,
3028 )));
3029 }
3030 if line.is_empty() {
3034 let span = label.as_ref().map_or(Span::new(0, 0), |_| Span::new(0, 0));
3035 let nowhere = Expr::Const(Array::empty(crate::dtype::DType::I64), span);
3036 return Ok(AplItem::Sentence(Expr::Control(
3037 Box::new(Control::Branch(Box::new(nowhere))),
3038 span,
3039 )));
3040 }
3041 let span = line.first().map_or(Span::new(0, 0), |t| t.span);
3042 let e = parse_statement(line, d, verbs, true)?
3043 .ok_or_else(|| Error::parse("this line has no sentence", span))?;
3044 Ok(AplItem::Sentence(e))
3045}
3046
3047struct AplCursor<'a> {
3048 items: &'a [AplItem],
3049 at: usize,
3050 d: Rules,
3052 loops: usize,
3055}
3056
3057impl<'a> AplCursor<'a> {
3058 fn peek(&self) -> Option<&'a AplItem> {
3059 self.items.get(self.at)
3060 }
3061
3062 fn peek_word(&self) -> Option<&'static str> {
3063 self.peek().and_then(AplItem::word)
3064 }
3065
3066 fn last_span(&self) -> Span {
3067 self.items
3068 .get(self.at.saturating_sub(1))
3069 .map_or_else(|| Span::new(0, 0), AplItem::span)
3070 }
3071
3072 fn in_loop<T>(&mut self, f: impl FnOnce(&mut Self) -> Result<T>) -> Result<T> {
3074 self.loops += 1;
3075 let out = f(self);
3076 self.loops -= 1;
3077 out
3078 }
3079
3080 fn close(&mut self, want: &str) -> Result<()> {
3082 match self.peek_word() {
3083 Some(w) if w == want || w == "End" => {
3084 self.at += 1;
3085 Ok(())
3086 }
3087 Some(w) => Err(Error::parse(
3088 format!("expected :{want} here, not :{w}"),
3089 self.peek().expect("a word").span(),
3090 )),
3091 None => Err(Error::parse(format!("this block needs a :{want}"), self.last_span())),
3092 }
3093 }
3094}
3095
3096fn parse_apl_block(cur: &mut AplCursor<'_>, stop: &[&str]) -> Result<Vec<Expr>> {
3097 let mut out = Vec::new();
3098 loop {
3099 match cur.peek() {
3100 None => return Ok(out),
3101 Some(AplItem::Word { word, .. }) if stop.contains(word) || *word == "End" => {
3102 return Ok(out);
3103 }
3104 Some(AplItem::Sentence(e)) => {
3105 cur.at += 1;
3106 out.push(e.clone());
3107 }
3108 Some(AplItem::Word { .. }) => out.push(parse_apl_control(cur)?),
3109 }
3110 }
3111}
3112
3113fn parse_apl_control(cur: &mut AplCursor<'_>) -> Result<Expr> {
3114 let Some(AplItem::Word { word, rest, span }) = cur.peek() else {
3115 return Err(Error::internal("expected a control word"));
3116 };
3117 let (word, rest, start) = (*word, rest.clone(), *span);
3118 cur.at += 1;
3119 let control = match word {
3120 "If" => {
3121 let mut arms = Vec::new();
3122 let mut otherwise = None;
3123 let mut test = rest;
3124 loop {
3125 let test_expr = condition(test, start, cur.d)?;
3126 let test_expr = continued_condition(cur, test_expr)?;
3127 let body = parse_apl_block(cur, &["ElseIf", "Else", "EndIf"])?;
3128 arms.push(Branch {
3129 test: Some(vec![test_expr]),
3130 body,
3131 fall_through: false,
3132 list: false,
3133 });
3134 match cur.peek_word() {
3135 Some("ElseIf") => {
3136 let Some(AplItem::Word { rest, .. }) = cur.peek() else { unreachable!() };
3137 test = rest.clone();
3138 cur.at += 1;
3139 }
3140 Some("Else") => {
3141 cur.at += 1;
3142 otherwise = Some(parse_apl_block(cur, &["EndIf"])?);
3143 cur.close("EndIf")?;
3144 break;
3145 }
3146 _ => {
3147 cur.close("EndIf")?;
3148 break;
3149 }
3150 }
3151 }
3152 Control::If { arms, otherwise }
3153 }
3154 "While" => {
3155 let test = condition(rest, start, cur.d)?;
3156 let test = continued_condition(cur, test)?;
3157 let body = cur.in_loop(|cur| parse_apl_block(cur, &["EndWhile"]))?;
3158 cur.close("EndWhile")?;
3159 Control::While { test: vec![test], body, body_first: false, until: false }
3160 }
3161 "Repeat" => {
3162 if !rest.is_empty() {
3163 return Err(Error::parse(":Repeat takes no condition", start));
3164 }
3165 let body = cur.in_loop(|cur| parse_apl_block(cur, &["Until"]))?;
3166 let Some(AplItem::Word { rest, span, .. }) = cur.peek() else {
3167 return Err(Error::parse("this :Repeat needs an :Until", cur.last_span()));
3168 };
3169 let (rest, span) = (rest.clone(), *span);
3170 let test = condition(rest, span, cur.d)?;
3171 cur.at += 1;
3172 let test = continued_condition(cur, test)?;
3173 Control::While { test: vec![test], body, body_first: true, until: true }
3174 }
3175 "For" => {
3176 let (names, source) = for_header(&rest, start, cur.d)?;
3178 let body = cur.in_loop(|cur| parse_apl_block(cur, &["EndFor"]))?;
3179 cur.close("EndFor")?;
3180 Control::For { names, source: Box::new(source), body }
3181 }
3182 "Select" => {
3183 let subject = condition(rest, start, cur.d)?;
3184 let mut cases = Vec::new();
3185 loop {
3186 match cur.peek() {
3187 Some(AplItem::Word { word: w @ ("Case" | "CaseList"), rest, span }) => {
3188 let list = *w == "CaseList";
3189 let test = condition(rest.clone(), *span, cur.d)?;
3190 cur.at += 1;
3191 let body =
3192 parse_apl_block(cur, &["Case", "CaseList", "Else", "EndSelect"])?;
3193 cases.push(Branch {
3194 test: Some(vec![test]),
3195 body,
3196 fall_through: false,
3197 list,
3198 });
3199 }
3200 Some(AplItem::Word { word: "Else", .. }) => {
3201 cur.at += 1;
3202 let body = parse_apl_block(cur, &["EndSelect"])?;
3203 cases.push(Branch {
3204 test: None,
3205 body,
3206 fall_through: false,
3207 list: false,
3208 });
3209 cur.close("EndSelect")?;
3210 break;
3211 }
3212 _ => {
3213 cur.close("EndSelect")?;
3214 break;
3215 }
3216 }
3217 }
3218 Control::Select { subject: Box::new(subject), cases }
3219 }
3220 "Return" => Control::Return,
3221 "Leave" | "Continue" => {
3224 if cur.loops == 0 && cur.d.control_strictness == ControlStrictness::Strict {
3225 return Err(Error::parse(format!(":{word} belongs inside a loop"), start));
3226 }
3227 if word == "Leave" { Control::Break } else { Control::Continue }
3228 }
3229 other => {
3230 return Err(Error::parse(format!(":{other} has no matching opening word"), start));
3231 }
3232 };
3233 Ok(Expr::Control(Box::new(control), Span::merge(start, cur.last_span())))
3234}
3235
3236fn continued_condition(cur: &mut AplCursor<'_>, mut test: Expr) -> Result<Expr> {
3245 loop {
3246 let (word, rest, span) = match cur.peek() {
3247 Some(AplItem::Word { word: w @ ("AndIf" | "OrIf"), rest, span }) => {
3248 (*w, rest.clone(), *span)
3249 }
3250 _ => return Ok(test),
3251 };
3252 cur.at += 1;
3253 let next = condition(rest, span, cur.d)?;
3254 let settled = Expr::Const(Array::scalar_bool(word == "OrIf"), span);
3255 let (body, otherwise) = if word == "AndIf" {
3256 (vec![next], vec![settled])
3257 } else {
3258 (vec![settled], vec![next])
3259 };
3260 let whole = Span::merge(test.span(), span);
3261 test = Expr::Control(
3262 Box::new(Control::If {
3263 arms: vec![Branch {
3264 test: Some(vec![test]),
3265 body,
3266 fall_through: false,
3267 list: false,
3268 }],
3269 otherwise: Some(otherwise),
3270 }),
3271 whole,
3272 );
3273 }
3274}
3275
3276fn condition(rest: Vec<Token>, span: Span, d: Rules) -> Result<Expr> {
3278 match rest.first() {
3279 None => Err(Error::parse("this control word needs a condition", span)),
3280 Some(first) => {
3281 let hint = Span::merge(first.span, rest[rest.len() - 1].span);
3282 match &rest[0].kind {
3283 Tok::Control(w) => Err(Error::parse(format!("unexpected :{w}"), rest[0].span)),
3284 _ => Ok(AplItem::Sentence(parse_prepared(&rest, hint, d)?)).map(|it| match it {
3285 AplItem::Sentence(e) => e,
3286 AplItem::Word { .. } => unreachable!(),
3287 }),
3288 }
3289 }
3290 }
3291}
3292
3293fn for_header(rest: &[Token], span: Span, d: Rules) -> Result<(Vec<String>, Expr)> {
3296 let Some(k) = rest.iter().position(|t| matches!(t.kind, Tok::Control("In"))) else {
3297 return Err(Error::parse(":For needs an :In", span));
3298 };
3299 let mut names = Vec::with_capacity(k);
3300 for t in &rest[..k] {
3301 match &t.kind {
3302 Tok::Name(n) => names.push(n.clone()),
3303 _ => return Err(Error::parse(":For binds names, one per item", t.span)),
3304 }
3305 }
3306 if names.is_empty() {
3307 return Err(Error::parse(":For needs a name to bind", span));
3308 }
3309 let source = &rest[k + 1..];
3310 let Some(first) = source.first() else {
3311 return Err(Error::parse(":In needs a value", span));
3312 };
3313 let hint = Span::merge(first.span, source[source.len() - 1].span);
3314 Ok((names, parse_prepared(source, hint, d)?))
3315}
3316
3317fn parse_prepared(toks: &[Token], hint: Span, d: Rules) -> Result<Expr> {
3319 let toks = fold_axes(fold_operators(toks.to_vec(), d)?, d)?;
3320 if toks.is_empty() {
3321 return Err(Error::parse("this needs an expression", hint));
3322 }
3323 parse_range(&toks, 0, toks.len(), hint, d)
3324}
3325
3326fn indexed_assignment(toks: &[Token], d: Rules, hint: Span) -> Result<Option<Expr>> {
3329 let Some(assign) = toks.iter().position(|t| matches!(t.kind, Tok::Assign)) else {
3330 return Ok(None);
3331 };
3332 if assign < 3 || !matches!(toks[assign - 1].kind, Tok::RBracket) {
3333 return Ok(None);
3334 }
3335 let close = assign - 1;
3336 let open = match_lbracket(toks, 0, close)?;
3337 if open == 0 {
3338 return Err(Error::parse("[ needs a value on its left", toks[open].span));
3339 }
3340 let Tok::Name(name) = &toks[open - 1].kind else {
3341 return Err(Error::not_yet("indexed assignment through an expression", hint));
3342 };
3343 if open != 1 {
3344 return Err(Error::not_yet("indexed assignment inside a larger sentence", hint));
3345 }
3346 let ranges = index_slots(toks, open + 1, close, toks[open].span)?;
3347 let mut slots = Vec::with_capacity(ranges.len());
3348 for slot in &ranges {
3349 slots.push(match *slot {
3350 None => None,
3351 Some((lo, hi)) => Some(parse_range(toks, lo, hi, toks[open].span, d)?),
3352 });
3353 }
3354 let value = parse_range(toks, assign + 1, toks.len(), toks[assign].span, d)?;
3355 let span = Span::merge(toks[0].span, toks[toks.len() - 1].span);
3356 Ok(Some(Expr::AmendIndex {
3357 name: name.clone(),
3358 slots,
3359 value: Box::new(value),
3360 origin: d.origin,
3361 scope: Scope::Local,
3362 span,
3363 }))
3364}
3365
3366fn set_scopes(e: &mut Expr, own: &[String]) {
3370 let pick = |name: &str| {
3371 if own.iter().any(|n| n == name) {
3372 Scope::Local
3373 } else {
3374 Scope::Global
3375 }
3376 };
3377 match e {
3378 Expr::Assign { name, value, scope, .. } => {
3379 *scope = pick(name);
3380 set_scopes(value, own);
3381 }
3382 Expr::AmendIndex { name, slots, value, scope, .. } => {
3383 *scope = pick(name);
3384 for slot in slots.iter_mut().flatten() {
3385 set_scopes(slot, own);
3386 }
3387 set_scopes(value, own);
3388 }
3389 Expr::Monad { y, .. } => set_scopes(y, own),
3390 Expr::Dyad { x, y, .. } => {
3391 set_scopes(x, own);
3392 set_scopes(y, own);
3393 }
3394 Expr::PrintPass { value, .. } => set_scopes(value, own),
3395 Expr::Input { .. } => {}
3396 Expr::Control(c, _) => {
3397 let walk = |b: &mut Vec<Expr>| b.iter_mut().for_each(|s| set_scopes(s, own));
3398 match &mut **c {
3399 Control::Branch(target) => set_scopes(target, own),
3400 Control::If { arms, otherwise } => {
3401 for arm in arms {
3402 if let Some(t) = &mut arm.test {
3403 walk(t);
3404 }
3405 walk(&mut arm.body);
3406 }
3407 if let Some(b) = otherwise {
3408 walk(b);
3409 }
3410 }
3411 Control::While { test, body, .. } | Control::Guard { test, body } => {
3412 walk(test);
3413 walk(body);
3414 }
3415 Control::For { source, body, .. } => {
3416 set_scopes(source, own);
3417 walk(body);
3418 }
3419 Control::Select { subject, cases } => {
3420 set_scopes(subject, own);
3421 for case in cases {
3422 if let Some(t) = &mut case.test {
3423 walk(t);
3424 }
3425 walk(&mut case.body);
3426 }
3427 }
3428 Control::Try { body, catch } => {
3429 walk(body);
3430 walk(catch);
3431 }
3432 Control::Return | Control::Break | Control::Continue => {}
3433 }
3434 }
3435 Expr::Const(..)
3436 | Expr::Param(..)
3437 | Expr::Name(..)
3438 | Expr::Fused { .. }
3439 | Expr::Elided { .. }
3440 | Expr::VerbDef { .. }
3441 | Expr::ModDef { .. } => {}
3442 }
3443}
3444
3445#[cfg(test)]
3446mod tests {
3447 use super::*;
3448 use crate::error::ErrorKind;
3449 use rstest::rstest;
3450
3451 fn rules(origin: i64) -> Rules {
3453 crate::Dialect { index_origin: Some(origin), ..crate::Dialect::default() }
3454 .rules(crate::Lang::Apl)
3455 .expect("the shipped dialect is implemented")
3456 }
3457
3458 fn p(src: &str) -> Result<Vec<Expr>> {
3460 parse(&SourceParts::from_source(src).unwrap(), rules(1))
3461 }
3462
3463 fn one(src: &str) -> Expr {
3464 let mut stmts = p(src).unwrap_or_else(|e| panic!("{src}: {e}"));
3465 assert_eq!(stmts.len(), 1, "{src}: expected one sentence");
3466 stmts.pop().unwrap()
3467 }
3468
3469 fn err(src: &str) -> Error {
3470 match p(src) {
3471 Ok(_) => panic!("{src}: expected an error"),
3472 Err(e) => e,
3473 }
3474 }
3475
3476 fn as_const(e: &Expr) -> &Array {
3477 match e {
3478 Expr::Const(a, _) => a,
3479 other => panic!("expected a constant, got {other:?}"),
3480 }
3481 }
3482
3483 fn as_prim(v: &Verb) -> Prim {
3485 match v {
3486 Verb::Prim(p) => *p,
3487 other => panic!("expected a primitive, got {other:?}"),
3488 }
3489 }
3490
3491 fn monad_of<'a>(e: &'a Expr, name: &str) -> &'a Expr {
3492 match e {
3493 Expr::Monad { verb, y, .. } => {
3494 assert_eq!(as_prim(verb).name, name, "monad name");
3495 y.as_ref()
3496 }
3497 other => panic!("expected a monad, got {other:?}"),
3498 }
3499 }
3500
3501 fn dyad_of<'a>(e: &'a Expr, name: &str) -> (&'a Expr, &'a Expr) {
3502 match e {
3503 Expr::Dyad { verb, x, y, .. } => {
3504 assert_eq!(as_prim(verb).name, name, "dyad name");
3505 (x.as_ref(), y.as_ref())
3506 }
3507 other => panic!("expected a dyad, got {other:?}"),
3508 }
3509 }
3510
3511 fn verb_of(e: &Expr) -> &Verb {
3512 match e {
3513 Expr::Monad { verb, .. } | Expr::Dyad { verb, .. } => verb,
3514 other => panic!("expected an application, got {other:?}"),
3515 }
3516 }
3517
3518 #[test]
3521 fn single_number_is_a_scalar() {
3522 let e = one("5");
3523 let a = as_const(&e);
3524 assert_eq!(a.shape, Vec::<usize>::new());
3525 assert_eq!(a.data, Data::I64(vec![5].into()));
3526 }
3527
3528 #[test]
3529 fn adjacent_numbers_merge_into_one_vector() {
3530 let a = as_const(&one("2 3 4")).clone();
3531 assert_eq!(a.shape, vec![3]);
3532 assert_eq!(a.data, Data::I64(vec![2, 3, 4].into()));
3533 }
3534
3535 #[test]
3536 fn one_float_makes_the_whole_vector_float() {
3537 let a = as_const(&one("1 2.5 3")).clone();
3538 assert_eq!(a.shape, vec![3]);
3539 assert_eq!(a.data, Data::F64(vec![1.0, 2.5, 3.0].into()));
3540 }
3541
3542 #[rstest]
3543 #[case("¯3", Data::I64(vec![-3].into()))]
3544 #[case("¯3.5", Data::F64(vec![-3.5].into()))]
3545 #[case("1e3", Data::I64(vec![1000].into()))]
3546 #[case("1e¯3", Data::F64(vec![0.001].into()))]
3547 #[case("2.5e2", Data::F64(vec![250.0].into()))]
3548 #[case("¯1 ¯2", Data::I64(vec![-1, -2].into()))]
3549 fn numeric_literals(#[case] src: &str, #[case] want: Data) {
3550 assert_eq!(as_const(&one(src)).data, want);
3551 }
3552
3553 #[test]
3554 fn single_char_string_is_rank_zero() {
3555 let a = as_const(&one("'a'")).clone();
3556 assert_eq!(a.shape, Vec::<usize>::new());
3557 assert_eq!(a.data, Data::Char(vec!['a'].into()));
3558 }
3559
3560 #[test]
3561 fn string_escape_doubles_the_quote() {
3562 let a = as_const(&one("'don''t'")).clone();
3563 assert_eq!(a.shape, vec![5]);
3564 assert_eq!(a.data, Data::Char("don't".chars().collect()));
3565 }
3566
3567 #[test]
3568 fn empty_string_is_an_empty_char_vector() {
3569 let a = as_const(&one("''")).clone();
3570 assert_eq!(a.shape, vec![0]);
3571 assert_eq!(a.data, Data::Char(vec![].into()));
3572 }
3573
3574 #[test]
3575 fn unterminated_string_is_a_parse_error() {
3576 let e = err("'abc");
3577 assert_eq!(e.kind, ErrorKind::Parse);
3578 assert!(e.msg.contains("unterminated"), "{}", e.msg);
3579 }
3580
3581 #[rstest]
3582 #[case("2j3", vec![[2.0, 3.0]])]
3583 #[case("1J¯1", vec![[1.0, -1.0]])]
3584 #[case("2 1j2", vec![[2.0, 0.0], [1.0, 2.0]])]
3585 fn complex_literals(#[case] src: &str, #[case] want: Vec<[f64; 2]>) {
3586 assert_eq!(as_const(&one(src)).data, Data::Complex(want.into()));
3587 }
3588
3589 #[test]
3592 fn a_comment_runs_to_the_end_of_the_line() {
3593 let stmts = p("2+2 ⍝ a note ⋄ still a note\n3").unwrap();
3594 assert_eq!(stmts.len(), 2);
3595 dyad_of(&stmts[0], "+");
3596 assert_eq!(as_const(&stmts[1]).data, Data::I64(vec![3].into()));
3597 }
3598
3599 #[test]
3600 fn blank_sentences_are_skipped() {
3601 let stmts = p("\n\n2 ⋄ ⋄ 3 ⋄\n").unwrap();
3602 assert_eq!(stmts.len(), 2);
3603 }
3604
3605 #[test]
3606 fn diamond_and_newline_both_separate_sentences() {
3607 let stmts = p("x←3 ⋄ x+1").unwrap();
3608 assert_eq!(stmts.len(), 2);
3609 match &stmts[0] {
3610 Expr::Assign { name, value, .. } => {
3611 assert_eq!(name, "x");
3612 assert_eq!(as_const(value).data, Data::I64(vec![3].into()));
3613 }
3614 other => panic!("expected an assignment, got {other:?}"),
3615 }
3616 let (x, y) = dyad_of(&stmts[1], "+");
3617 assert!(matches!(x, Expr::Name(n, _) if n == "x"));
3618 assert_eq!(as_const(y).data, Data::I64(vec![1].into()));
3619 }
3620
3621 #[rstest]
3622 #[case("x")]
3623 #[case("abc123")]
3624 #[case("∆x")]
3625 #[case("⍙y_2")]
3626 #[case("Σ")]
3627 fn names(#[case] src: &str) {
3628 match one(src) {
3629 Expr::Name(n, _) => assert_eq!(n, src),
3630 other => panic!("expected a name, got {other:?}"),
3631 }
3632 }
3633
3634 #[test]
3635 fn unknown_symbol_is_reported_with_its_position() {
3636 let e = err("2 @ 3");
3637 assert_eq!(e.kind, ErrorKind::Parse);
3638 assert_eq!(e.msg, "unknown symbol: @");
3639 assert_eq!(e.span, Some(Span::new(2, 3)));
3640 }
3641
3642 #[test]
3643 fn system_variables_are_read_only() {
3644 let e = err("⎕IO←0");
3647 assert_eq!(e.kind, ErrorKind::Language);
3648 assert!(e.msg.contains("read-only"), "{}", e.msg);
3649 let e = err("⎕TS");
3652 assert_eq!(e.kind, ErrorKind::Sandbox);
3653 assert!(e.msg.contains("outside the program"), "{}", e.msg);
3654 }
3655
3656 #[rstest]
3659 #[case('+', MonadOp::Scalar(ScalarMonad::Conj), DyadOp::Scalar(ScalarDyad::Add))]
3660 #[case('-', MonadOp::Scalar(ScalarMonad::Neg), DyadOp::Scalar(ScalarDyad::Sub))]
3661 #[case('×', MonadOp::Scalar(ScalarMonad::Signum), DyadOp::Scalar(ScalarDyad::Mul))]
3662 #[case('÷', MonadOp::Scalar(ScalarMonad::Recip), DyadOp::Scalar(ScalarDyad::DivApl))]
3663 #[case('⌈', MonadOp::Scalar(ScalarMonad::Ceil), DyadOp::Scalar(ScalarDyad::Max))]
3664 #[case('⌊', MonadOp::Scalar(ScalarMonad::Floor), DyadOp::Scalar(ScalarDyad::Min))]
3665 #[case('*', MonadOp::Scalar(ScalarMonad::Exp), DyadOp::Scalar(ScalarDyad::Pow))]
3666 #[case('|', MonadOp::Scalar(ScalarMonad::Abs), DyadOp::Scalar(ScalarDyad::Residue))]
3667 #[case('=', MonadOp::None, DyadOp::Scalar(ScalarDyad::Eq))]
3668 #[case('<', MonadOp::None, DyadOp::Scalar(ScalarDyad::Lt))]
3669 #[case('≤', MonadOp::None, DyadOp::Scalar(ScalarDyad::Le))]
3670 #[case('>', MonadOp::None, DyadOp::Scalar(ScalarDyad::Gt))]
3671 #[case('≥', MonadOp::None, DyadOp::Scalar(ScalarDyad::Ge))]
3672 #[case('⍴', MonadOp::ShapeOf, DyadOp::Reshape)]
3673 #[case('⍉', MonadOp::TransposeAxes, DyadOp::TransposeApl)]
3674 #[case(',', MonadOp::Ravel, DyadOp::AppendLast)]
3675 #[case('⍪', MonadOp::TableOf, DyadOp::AppendLeading)]
3676 #[case('!', MonadOp::Scalar(ScalarMonad::Factorial), DyadOp::Scalar(ScalarDyad::Binomial))]
3677 #[case('⍕', MonadOp::Format, DyadOp::FormatSpec)]
3678 #[case('⊥', MonadOp::None, DyadOp::DecodeApl)]
3679 #[case('⊤', MonadOp::None, DyadOp::EncodeApl)]
3680 #[case('≢', MonadOp::Tally, DyadOp::NotMatch)]
3681 #[case('≡', MonadOp::Depth { signed: false }, DyadOp::Match)]
3682 #[case('∊', MonadOp::Enlist, DyadOp::MemberApl)]
3683 #[case('∪', MonadOp::Nub, DyadOp::Union)]
3684 #[case('∧', MonadOp::None, DyadOp::Scalar(ScalarDyad::Lcm))]
3685 #[case('∨', MonadOp::None, DyadOp::Scalar(ScalarDyad::Gcd))]
3686 #[case('⍟', MonadOp::Scalar(ScalarMonad::Ln), DyadOp::Scalar(ScalarDyad::Log))]
3687 #[case('~', MonadOp::Scalar(ScalarMonad::Not), DyadOp::Less)]
3688 #[case('⊖', MonadOp::Reverse, DyadOp::RotateApl { last: false })]
3689 #[case('⍋', MonadOp::GradeUp { origin: 1 }, DyadOp::CollateGrade { down: false, origin: 1 })]
3690 #[case('⍒', MonadOp::GradeDown { origin: 1 }, DyadOp::CollateGrade { down: true, origin: 1 })]
3691 #[case('⊢', MonadOp::Same, DyadOp::Right)]
3692 #[case('⊣', MonadOp::Same, DyadOp::Left)]
3693 #[case('↑', MonadOp::First, DyadOp::Take)]
3694 #[case('⊂', MonadOp::Enclose(Enclose::ExceptSimpleScalar), DyadOp::PartitionEnclose)]
3695 #[case('⊃', MonadOp::Open, DyadOp::Pick { origin: 1 })]
3696 #[case('↓', MonadOp::Split, DyadOp::Drop)]
3697 fn primitive_meanings(#[case] glyph: char, #[case] monad: MonadOp, #[case] dyad: DyadOp) {
3698 let src = format!("{glyph}1");
3699 let e = one(&src);
3700 match e {
3701 Expr::Monad { verb, .. } => {
3702 let prim = as_prim(&verb);
3703 assert_eq!(prim.monad, monad);
3704 assert_eq!(prim.dyad, dyad);
3705 assert_eq!(prim.name.chars().next(), Some(glyph));
3706 }
3707 other => panic!("expected a monad, got {other:?}"),
3708 }
3709 }
3710
3711 #[test]
3712 fn monadic_not_equal_is_the_nub_sieve() {
3713 let e = one("≠1");
3714 match e {
3715 Expr::Monad { verb, .. } => {
3716 assert_eq!(as_prim(&verb).monad, MonadOp::NubSieve);
3717 }
3718 other => panic!("expected a monad, got {other:?}"),
3719 }
3720 }
3721
3722 #[test]
3723 fn monadic_equals_parses_and_is_left_to_evaluation() {
3724 let e = one("=1");
3726 assert_eq!(as_prim(verb_of(&e)).monad, MonadOp::None);
3727 }
3728
3729 #[rstest]
3730 #[case(0)]
3731 #[case(1)]
3732 fn iota_carries_the_index_origin(#[case] origin: i64) {
3733 let sp = SourceParts::from_source("⍳3").unwrap();
3734 let stmts = parse(&sp, rules(origin)).unwrap();
3735 match &stmts[0] {
3736 Expr::Monad { verb, .. } => {
3737 assert_eq!(as_prim(verb).monad, MonadOp::IotaApl { origin });
3738 assert_eq!(as_prim(verb).dyad, DyadOp::IndexOf { origin, vector_left: false });
3739 assert_eq!(as_prim(verb).ranks, [RANK_INF, RANK_INF, RANK_INF]);
3740 }
3741 other => panic!("expected a monad, got {other:?}"),
3742 }
3743 }
3744
3745 #[test]
3746 fn reverse_and_rotate_pick_their_axis() {
3747 let e = one("⌽2 3⍴⍳6");
3750 match verb_of(&e) {
3751 Verb::Rank(f, ranks) => {
3752 assert_eq!(*ranks, [1, RANK_INF, RANK_INF]);
3753 assert_eq!(as_prim(f).monad, MonadOp::Reverse);
3754 assert_eq!(as_prim(f).dyad, DyadOp::RotateApl { last: true });
3755 }
3756 other => panic!("expected a ranked verb, got {other:?}"),
3757 }
3758 assert!(matches!(verb_of(&one("⊖2 3⍴⍳6")), Verb::Prim(_)));
3760 }
3761
3762 #[test]
3763 fn reshape_ranks_are_infinite_one_infinite() {
3764 let e = one("2 3⍴⍳6");
3765 assert_eq!(verb_of(&e).ranks(), [RANK_INF, 1, RANK_INF]);
3766 }
3767
3768 #[test]
3771 fn reshape_of_iota() {
3772 let e = one("2 3⍴⍳6");
3773 let (x, y) = dyad_of(&e, "⍴");
3774 assert_eq!(as_const(x).data, Data::I64(vec![2, 3].into()));
3775 let iy = monad_of(y, "⍳");
3776 assert_eq!(as_const(iy).data, Data::I64(vec![6].into()));
3777 }
3778
3779 #[test]
3780 fn leading_minus_is_monadic_and_the_rest_is_evaluated_first() {
3781 let e = one("-3+4");
3783 let inner = monad_of(&e, "-");
3784 let (x, y) = dyad_of(inner, "+");
3785 assert_eq!(as_const(x).data, Data::I64(vec![3].into()));
3786 assert_eq!(as_const(y).data, Data::I64(vec![4].into()));
3787 }
3788
3789 #[test]
3790 fn a_chain_of_dyads_associates_to_the_right() {
3791 let e = one("2×3+4");
3792 let (x, y) = dyad_of(&e, "×");
3793 assert_eq!(as_const(x).data, Data::I64(vec![2].into()));
3794 dyad_of(y, "+");
3795 }
3796
3797 #[test]
3798 fn parentheses_override_the_order() {
3799 let e = one("(2+3)×4");
3800 let (x, y) = dyad_of(&e, "×");
3801 dyad_of(x, "+");
3802 assert_eq!(as_const(y).data, Data::I64(vec![4].into()));
3803 }
3804
3805 #[test]
3806 fn nested_parentheses() {
3807 let e = one("((2+3))×4");
3808 let (x, _) = dyad_of(&e, "×");
3809 dyad_of(x, "+");
3810 }
3811
3812 #[test]
3813 fn a_function_left_of_a_function_is_monadic() {
3814 let e = one("⍴⍳5");
3816 monad_of(monad_of(&e, "⍴"), "⍳");
3817 }
3818
3819 #[test]
3822 fn slash_reduces_the_last_axis() {
3823 let e = one("+/2 3⍴⍳6");
3827 match &e {
3828 Expr::Monad { verb: Verb::Rank(inner, ranks), .. } => {
3829 assert_eq!(*ranks, [1, RANK_INF, 1]);
3830 match inner.as_ref() {
3831 Verb::NWise(f) => assert_eq!(as_prim(f).name, "+"),
3832 other => panic!("expected a reduce, got {other:?}"),
3833 }
3834 }
3835 other => panic!("expected monadic Rank(NWise(+)), got {other:?}"),
3836 }
3837 }
3838
3839 #[test]
3840 fn slashbar_reduces_the_leading_axis() {
3841 let e = one("+⌿2 3⍴⍳6");
3842 match &e {
3843 Expr::Monad { verb: Verb::NWise(f), .. } => assert_eq!(as_prim(f).name, "+"),
3844 other => panic!("expected monadic NWise(+), got {other:?}"),
3845 }
3846 }
3847
3848 #[test]
3849 fn backslash_scans_the_last_axis_and_backslashbar_the_leading_one() {
3850 let inner = |v: &Verb| match v {
3853 Verb::Windowed(g, WindowKind::Scan) => match &**g {
3854 Verb::Reduce(h) => as_prim(h).name,
3855 other => panic!("expected a reduction under the scan, got {other:?}"),
3856 },
3857 other => panic!("expected a scan, got {other:?}"),
3858 };
3859 match &one("+\\1 2 3") {
3860 Expr::Monad { verb: Verb::Rank(f, ranks), .. } => {
3861 assert_eq!(*ranks, [1, 1, 1]);
3862 assert_eq!(inner(f), "+");
3863 }
3864 other => panic!("expected a ranked scan, got {other:?}"),
3865 }
3866 match &one("+⍀1 2 3") {
3867 Expr::Monad { verb, .. } => assert_eq!(inner(verb), "+"),
3868 other => panic!("expected a leading-axis scan, got {other:?}"),
3869 }
3870 }
3871
3872 #[rstest]
3875 #[case("1 0 1/1 2 3", "/")]
3876 #[case("1 0 1⌿1 2 3", "⌿")]
3877 #[case("x/1 2 3", "/")]
3878 #[case("(1 0)/1 2 3", "/")]
3879 fn slash_after_an_operand_is_replicate(#[case] src: &str, #[case] name: &str) {
3880 let e = one(src);
3881 let (_, _) = dyad_of(&e, name);
3882 assert_eq!(as_prim(verb_of(&e)).dyad, DyadOp::Copy);
3883 }
3884
3885 #[rstest]
3886 #[case("1 0 1\\1 2 3")]
3887 #[case("1 0 1⍀1 2 3")]
3888 fn expand_after_a_value_is_a_function(#[case] src: &str) {
3889 let e = one(src);
3890 assert_eq!(as_prim(verb_of(&e)).dyad, DyadOp::Expand);
3891 }
3892
3893 #[test]
3894 fn commute_and_power_are_operators() {
3895 match one("2-⍨5") {
3896 Expr::Dyad { verb: Verb::Commute(f), .. } => assert_eq!(as_prim(&f).name, "-"),
3897 other => panic!("expected a commute, got {other:?}"),
3898 }
3899 match one("+⍣3⊢5") {
3900 Expr::Monad { verb: Verb::PowerN(_, p), .. } => assert_eq!(p, Power::Times(3)),
3901 other => panic!("expected a power, got {other:?}"),
3902 }
3903 match one("+⍣≡⊢5") {
3904 Expr::Monad { verb: Verb::PowerUntil(..), .. } => {}
3905 other => panic!("expected a power until, got {other:?}"),
3906 }
3907 match one("⌽⍣¯1⊢5") {
3910 Expr::Monad { verb: Verb::PowerN(_, p), .. } => assert_eq!(p, Power::Times(1)),
3911 other => panic!("expected a power, got {other:?}"),
3912 }
3913 let e = err("⍴⍣¯1⊢5");
3914 assert_eq!(e.kind, ErrorKind::NotYet);
3915 assert!(e.msg.contains("obverse"), "{}", e.msg);
3916 }
3917
3918 #[rstest]
3919 #[case("+⍤2⊢5", [2, 2, 2])]
3920 #[case("+⍤1 2⊢5", [2, 1, 2])]
3921 #[case("+⍤0 1 2⊢5", [0, 1, 2])]
3922 #[case("+⍤¯1⊢5", [-1, -1, -1])]
3923 fn rank_operator_spec(#[case] src: &str, #[case] want: [i64; 3]) {
3924 let e = one(src);
3925 match &e {
3926 Expr::Monad { verb: Verb::Rank(f, ranks), .. } => {
3927 assert_eq!(*ranks, want);
3928 assert_eq!(as_prim(f).name, "+");
3929 }
3930 other => panic!("expected monadic Rank(+), got {other:?}"),
3931 }
3932 }
3933
3934 #[test]
3935 fn rank_operator_stacks_on_a_derived_function() {
3936 let e = one("+/⍤1⊢5");
3937 match &e {
3938 Expr::Monad { verb: Verb::Rank(inner, ranks), .. } => {
3939 assert_eq!(*ranks, [1, 1, 1]);
3940 assert!(matches!(inner.as_ref(), Verb::Rank(_, [1, RANK_INF, 1])));
3941 }
3942 other => panic!("expected Rank(Rank(NWise(+))), got {other:?}"),
3943 }
3944 }
3945
3946 #[test]
3947 fn a_function_operand_makes_the_rank_operator_an_atop() {
3948 let e = one("+⍤×5");
3950 let Expr::Monad { verb, .. } = e else { panic!("expected a monad") };
3951 assert!(matches!(verb, Verb::Atop(..)), "{verb:?}");
3952 }
3953
3954 #[rstest]
3955 #[case("+⍤0 1 2 3⊢5", "1 to 3")]
3956 #[case("+⍤", "rank specification")]
3957 #[case("+⍤2.5⊢5", "must be integers")]
3958 #[case("+⍤'a'⊢5", "must be integers")]
3959 fn bad_rank_specifications(#[case] src: &str, #[case] fragment: &str) {
3960 let e = err(src);
3961 assert_eq!(e.kind, ErrorKind::Parse);
3962 assert!(e.msg.contains(fragment), "{}", e.msg);
3963 }
3964
3965 #[test]
3968 fn quad_arrow_is_print_pass() {
3969 let e = one("⎕←2+2");
3970 match &e {
3971 Expr::PrintPass { value, .. } => {
3972 dyad_of(value, "+");
3973 }
3974 other => panic!("expected PrintPass, got {other:?}"),
3975 }
3976 }
3977
3978 #[test]
3979 fn assignment_chains() {
3980 let e = one("a←b←5");
3981 match &e {
3982 Expr::Assign { name, value, .. } => {
3983 assert_eq!(name, "a");
3984 match value.as_ref() {
3985 Expr::Assign { name, value, .. } => {
3986 assert_eq!(name, "b");
3987 assert_eq!(as_const(value).data, Data::I64(vec![5].into()));
3988 }
3989 other => panic!("expected a nested assignment, got {other:?}"),
3990 }
3991 }
3992 other => panic!("expected an assignment, got {other:?}"),
3993 }
3994 }
3995
3996 #[test]
3997 fn assignment_inside_an_expression() {
3998 let e = one("2+a←3");
3999 let (x, y) = dyad_of(&e, "+");
4000 assert_eq!(as_const(x).data, Data::I64(vec![2].into()));
4001 match y {
4002 Expr::Assign { name, value, .. } => {
4003 assert_eq!(name, "a");
4004 assert_eq!(as_const(value).data, Data::I64(vec![3].into()));
4005 }
4006 other => panic!("expected an assignment, got {other:?}"),
4007 }
4008 }
4009
4010 #[rstest]
4011 #[case("2←3")]
4012 #[case("(2+2)←3")]
4013 fn assignment_target_must_be_a_name(#[case] src: &str) {
4014 let e = err(src);
4015 assert_eq!(e.kind, ErrorKind::Parse);
4016 assert_eq!(e.msg, "assignment target must be a name");
4017 }
4018
4019 #[test]
4022 fn a_parameter_hole_is_an_operand() {
4023 let sp = SourceParts::from_parts(&["", "+1"], &["x"]);
4024 let stmts = parse(&sp, rules(1)).unwrap();
4025 let (x, y) = dyad_of(&stmts[0], "+");
4026 assert!(matches!(x, Expr::Param(0, _)));
4027 assert_eq!(as_const(y).data, Data::I64(vec![1].into()));
4028 assert_eq!(x.span(), Span::new(0, 3));
4030 assert_eq!(sp.display, "{x}+1");
4031 }
4032
4033 #[test]
4034 fn a_parameter_can_be_reduced_over() {
4035 let sp = SourceParts::from_parts(&["+/", ""], &["m"]);
4036 let stmts = parse(&sp, rules(1)).unwrap();
4037 match &stmts[0] {
4038 Expr::Monad { verb: Verb::Rank(_, [1, RANK_INF, 1]), y, .. } => {
4039 assert!(matches!(y.as_ref(), Expr::Param(0, _)));
4040 }
4041 other => panic!("expected a reduction over a parameter, got {other:?}"),
4042 }
4043 }
4044
4045 #[test]
4046 fn a_parameter_inside_a_comment_is_dropped() {
4047 let sp = SourceParts::from_parts(&["1 ⍝ ", "\n2"], &["x"]);
4048 let stmts = parse(&sp, rules(1)).unwrap();
4049 assert_eq!(stmts.len(), 2);
4050 assert_eq!(as_const(&stmts[0]).data, Data::I64(vec![1].into()));
4051 assert_eq!(as_const(&stmts[1]).data, Data::I64(vec![2].into()));
4052 }
4053
4054 #[test]
4057 fn nodes_cover_their_source_extent() {
4058 let src = "2 3⍴⍳6";
4059 let e = one(src);
4060 assert_eq!(e.span(), Span::new(0, src.len()));
4061 let (x, y) = dyad_of(&e, "⍴");
4062 assert_eq!(x.span(), Span::new(0, 3));
4063 assert_eq!(y.span(), Span::new(6, src.len()));
4065 }
4066
4067 #[test]
4068 fn spans_of_a_later_sentence_are_absolute() {
4069 let src = "x←3 ⋄ x+1";
4070 let stmts = p(src).unwrap();
4071 assert_eq!(&src[10..], "x+1");
4073 assert_eq!(stmts[1].span(), Span::new(10, src.len()));
4074 }
4075
4076 #[test]
4077 fn a_dyad_span_includes_the_parenthesised_left_argument() {
4078 let src = "(2+3)×4";
4079 let e = one(src);
4080 assert_eq!(e.span(), Span::new(0, src.len()));
4081 }
4082
4083 #[rstest]
4088 #[case("(2 3)(4 5)", 2)]
4089 #[case("2 x", 2)]
4090 #[case("x y", 2)]
4091 #[case("2(3)", 2)]
4092 #[case("1 2 (3 4)", 3)]
4093 #[case("'ab' 'cd' 'ef'", 3)]
4094 fn juxtaposition_is_vector_notation(#[case] src: &str, #[case] items: usize) {
4095 let mut e = &one(src);
4098 for _ in 0..items - 1 {
4099 match e {
4100 Expr::Dyad { verb, y, .. } => {
4101 assert_eq!(verb.name(), "(vector notation)", "{src}");
4102 e = y.as_ref();
4103 }
4104 other => panic!("{src}: expected a strand, got {other:?}"),
4105 }
4106 }
4107 assert!(matches!(e, Expr::Monad { .. }), "{src}: {e:?}");
4108 }
4109
4110 #[rstest]
4111 #[case("2+", "missing right argument")]
4112 #[case("x←", "← needs a value")]
4113 #[case("(2+3", "syntax error")]
4114 #[case("2+3)", "unmatched )")]
4115 #[case("()", "empty parentheses")]
4116 #[case("/2 3", "needs a function to its left")]
4117 fn syntax_errors(#[case] src: &str, #[case] fragment: &str) {
4118 let e = err(src);
4119 assert_eq!(e.kind, ErrorKind::Parse);
4120 assert!(e.msg.contains(fragment), "{src}: {}", e.msg);
4121 }
4122
4123 #[test]
4124 fn empty_source_has_no_statements() {
4125 assert!(p("").unwrap().is_empty());
4126 assert!(p(" ⍝ nothing here\n").unwrap().is_empty());
4127 }
4128
4129 #[rstest]
4131 #[case("2+2")]
4132 #[case("¯2×3")]
4133 #[case("-3+4")]
4134 #[case("0÷0")]
4135 #[case("⍳4")]
4136 #[case("⍳0")]
4137 #[case("2 3⍴⍳6")]
4138 #[case("⍴2 3⍴⍳6")]
4139 #[case("⍉2 3⍴⍳6")]
4140 #[case("≢7 8 9")]
4141 #[case("2↑9 8 7")]
4142 #[case("¯2↑9 8 7")]
4143 #[case("1↓3 3⍴⍳9")]
4144 #[case(",2 2⍴⍳4")]
4145 #[case("x←3 ⋄ x+1")]
4146 #[case("2+a←3")]
4147 #[case("⎕←2+2")]
4148 #[case("(2 3⍴⍳6)+10 20")]
4149 #[case("2+3 ⍝ sum")]
4150 #[case("+/2 3⍴⍳6")]
4151 #[case("+⌿2 3⍴⍳6")]
4152 #[case("⎕←'Hello, world!'")]
4153 fn the_evaluation_corpus_parses(#[case] src: &str) {
4154 p(src).unwrap_or_else(|e| panic!("{src}: {e}"));
4155 }
4156
4157 #[test]
4158 fn errors_render_against_the_display_source() {
4159 let src = "2 3⍴⍳6\n2 @ 3";
4160 let e = err(src);
4161 let rendered = e.render(src);
4162 assert!(rendered.contains("unknown symbol: @"), "{rendered}");
4163 assert!(rendered.contains("2 @ 3"), "{rendered}");
4164 }
4165}