1use std::collections::HashMap;
10use std::sync::Arc;
11
12use crate::array::{Array, Data};
13use crate::error::{Error, Result, Span};
14use crate::frontend::{
15 DefaultArg, DfnResult, FirstDisclose, IndexForm, NestedModel, Rules, Segment, SourceParts,
16};
17use crate::ir::{Branch, Control, ExplicitDef, Expr, Scope};
18use crate::verb::{
19 BoolDyad, DyadOp, Enclose, MonadOp, Power, Prim, ScalarDyad, ScalarMonad, Verb, WindowKind,
20 RANK_INF,
21};
22
23pub fn parse(src: &SourceParts, d: Rules) -> Result<Vec<Expr>> {
27 let sentences = lex(src, d)?;
28 let mut verbs: HashMap<String, Verb> = HashMap::new();
29 let mut stmts = Vec::with_capacity(sentences.len());
30 let mut i = 0usize;
31 while i < sentences.len() {
32 if matches!(sentences[i].first().map(|t| &t.kind), Some(Tok::Del)) {
33 let stmt = parse_tradfn(&sentences, &mut i, d, &mut verbs)?;
34 stmts.push(stmt);
35 continue;
36 }
37 let sentence = sentences[i].clone();
38 i += 1;
39 if let Some(stmt) = parse_statement(sentence, d, &mut verbs, false)? {
40 stmts.push(stmt);
41 }
42 }
43 Ok(stmts)
44}
45
46fn parse_statement(
49 sentence: Vec<Token>,
50 d: Rules,
51 verbs: &mut HashMap<String, Verb>,
52 in_def: bool,
53) -> Result<Option<Expr>> {
54 let sentence = substitute_verbs(sentence, verbs);
55 let sentence = fold_dfns(sentence, d, verbs)?;
56 if let [name, assign, func] = &sentence[..] {
59 if let (Tok::Name(n), Tok::Assign) = (&name.kind, &assign.kind) {
60 let named = match &func.kind {
63 Tok::Func(v) => Some(v.clone()),
64 Tok::UserOp { def, omega } => Some(unapplied_op(def.clone(), *omega)),
65 _ => None,
66 };
67 if let Some(v) = named {
68 let span = Span::merge(name.span, func.span);
69 if !in_def {
70 verbs.insert(n.clone(), v.clone());
71 }
72 return Ok(Some(Expr::VerbDef { name: n.clone(), verb: v, span }));
73 }
74 }
75 }
76 let toks = fold_paren_funcs(fold_axes(fold_operators(sentence, d)?, d)?);
77 if toks.is_empty() {
78 return Ok(None);
79 }
80 if let Some(t) = toks.iter().find(|t| matches!(t.kind, Tok::Control(_))) {
81 return Err(Error::parse(
82 "control structures are only meaningful inside a ∇ definition",
83 t.span,
84 ));
85 }
86 if let Some(t) = toks.iter().find(|t| matches!(t.kind, Tok::Arrow)) {
87 return Err(Error::parse(
88 "→ branches, and only a line of a ∇ definition may begin with it",
89 t.span,
90 ));
91 }
92 let hint = Span::merge(toks[0].span, toks[toks.len() - 1].span);
93 if let Some(e) = indexed_assignment(&toks, d, hint)? {
96 return Ok(Some(e));
97 }
98 parse_range(&toks, 0, toks.len(), hint, d).map(Some)
99}
100
101fn substitute_verbs(mut toks: Vec<Token>, verbs: &HashMap<String, Verb>) -> Vec<Token> {
104 for i in 0..toks.len() {
105 let Tok::Name(n) = &toks[i].kind else { continue };
106 if matches!(toks.get(i + 1).map(|t| &t.kind), Some(Tok::Assign)) {
107 continue;
108 }
109 if let Some(v) = verbs.get(n) {
110 toks[i].kind = match as_user_op(v) {
113 Some((def, omega)) => Tok::UserOp { def, omega },
114 None if is_niladic(v) => Tok::Niladic(v.clone()),
115 None => Tok::Func(v.clone()),
116 };
117 }
118 }
119 toks
120}
121
122#[derive(Clone, Copy, Debug, PartialEq, Eq)]
127enum OpGlyph {
128 Slash,
130 SlashBar,
132 Backslash,
134 BackslashBar,
136 Rank,
138 Commute,
140 Power,
142 JotDot,
144 Over,
146 Jot,
148 Each,
150 Before,
152 Key,
154}
155
156impl OpGlyph {
157 fn glyph(self) -> char {
158 match self {
159 OpGlyph::Slash => '/',
160 OpGlyph::SlashBar => '⌿',
161 OpGlyph::Backslash => '\\',
162 OpGlyph::BackslashBar => '⍀',
163 OpGlyph::Rank => '⍤',
164 OpGlyph::Commute => '⍨',
165 OpGlyph::Power => '⍣',
166 OpGlyph::JotDot | OpGlyph::Jot => '∘',
167 OpGlyph::Over => '⍥',
168 OpGlyph::Each => '¨',
169 OpGlyph::Before => '⍛',
170 OpGlyph::Key => '⌸',
171 }
172 }
173}
174
175#[derive(Clone, Debug)]
176enum Tok {
177 Value(Array),
179 Nums(Array),
183 Param(usize),
185 Name(String),
186 Func(Verb),
188 Op(OpGlyph),
190 Assign,
191 Quad,
192 LParen,
193 RParen,
194 LBracket,
195 RBracket,
196 Semi,
199 LBrace,
201 RBrace,
202 Separator,
205 Colon,
207 Arrow,
209 Niladic(Verb),
212 UserOp { def: Verb, omega: bool },
215 Del,
217 Control(&'static str),
219}
220
221#[derive(Clone, Debug)]
222struct Token {
223 kind: Tok,
224 span: Span,
225}
226
227fn is_operand_end(k: &Tok) -> bool {
230 matches!(
231 k,
232 Tok::Value(_)
233 | Tok::Nums(_)
234 | Tok::Param(_)
235 | Tok::Name(_)
236 | Tok::Niladic(_)
237 | Tok::RParen
238 | Tok::RBracket
239 )
240}
241
242fn unapplied_op(def: Verb, omega: bool) -> Verb {
246 Verb::UserDerived {
247 def: Box::new(def),
248 alpha: Box::new(Verb::Named("⍺⍺".to_string())),
249 omega: omega.then(|| Box::new(Verb::Named("⍵⍵".to_string()))),
250 }
251}
252
253fn as_user_op(v: &Verb) -> Option<(Verb, bool)> {
256 match v {
257 Verb::UserDerived { def, alpha, omega }
258 if matches!(&**alpha, Verb::Named(n) if n == "⍺⍺") =>
259 {
260 Some(((**def).clone(), omega.is_some()))
261 }
262 _ => None,
263 }
264}
265
266fn is_niladic(v: &Verb) -> bool {
268 matches!(v, Verb::Explicit(d) if d.left.is_none() && d.right == crate::ir::NILADIC)
269}
270
271fn literal(k: &Tok) -> Option<&Array> {
273 match k {
274 Tok::Value(a) | Tok::Nums(a) => Some(a),
275 _ => None,
276 }
277}
278
279fn prim_for(ch: char, d: Rules) -> Option<Prim> {
292 use DyadOp as D;
293 use MonadOp as M;
294 use ScalarDyad as SD;
295 use ScalarMonad as SM;
296 let origin = d.origin;
297 let p = match ch {
298 '+' => Prim {
299 name: "+",
300 monad: M::Scalar(SM::Conj),
301 dyad: D::Scalar(SD::Add),
302 ranks: [0, 0, 0],
303 },
304 '-' => {
305 Prim { name: "-", monad: M::Scalar(SM::Neg), dyad: D::Scalar(SD::Sub), ranks: [0, 0, 0] }
306 }
307 '×' => Prim {
308 name: "×",
309 monad: M::Scalar(SM::Signum),
310 dyad: D::Scalar(SD::Mul),
311 ranks: [0, 0, 0],
312 },
313 '÷' => Prim {
314 name: "÷",
315 monad: M::Scalar(SM::Recip),
316 dyad: D::Scalar(SD::DivApl),
317 ranks: [0, 0, 0],
318 },
319 '⌈' => Prim {
320 name: "⌈",
321 monad: M::Scalar(SM::Ceil),
322 dyad: D::Scalar(SD::Max),
323 ranks: [0, 0, 0],
324 },
325 '⌊' => Prim {
326 name: "⌊",
327 monad: M::Scalar(SM::Floor),
328 dyad: D::Scalar(SD::Min),
329 ranks: [0, 0, 0],
330 },
331 '*' => {
332 Prim { name: "*", monad: M::Scalar(SM::Exp), dyad: D::Scalar(SD::Pow), ranks: [0, 0, 0] }
333 }
334 '|' => Prim {
335 name: "|",
336 monad: M::Scalar(SM::Abs),
337 dyad: D::Scalar(SD::Residue),
338 ranks: [0, 0, 0],
339 },
340 '=' => Prim { name: "=", monad: M::None, dyad: D::Scalar(SD::Eq), ranks: [0, 0, 0] },
341 '≠' => Prim {
342 name: "≠",
343 monad: M::NubSieve,
344 dyad: D::Scalar(SD::Ne),
345 ranks: [RANK_INF, 0, 0],
346 },
347 '<' => Prim { name: "<", monad: M::None, dyad: D::Scalar(SD::Lt), ranks: [0, 0, 0] },
348 '≤' => Prim { name: "≤", monad: M::None, dyad: D::Scalar(SD::Le), ranks: [0, 0, 0] },
349 '>' => Prim { name: ">", monad: M::None, dyad: D::Scalar(SD::Gt), ranks: [0, 0, 0] },
350 '≥' => Prim { name: "≥", monad: M::None, dyad: D::Scalar(SD::Ge), ranks: [0, 0, 0] },
351 '⍴' => Prim {
352 name: "⍴",
353 monad: M::ShapeOf,
354 dyad: D::Reshape,
355 ranks: [RANK_INF, 1, RANK_INF],
356 },
357 '⍳' => Prim {
358 name: "⍳",
359 monad: M::IotaApl { origin },
360 dyad: D::IndexOf { origin },
361 ranks: [RANK_INF, RANK_INF, RANK_INF],
365 },
366 '∊' => Prim {
367 name: "∊",
368 monad: M::Enlist,
369 dyad: D::MemberApl,
370 ranks: [RANK_INF, RANK_INF, RANK_INF],
371 },
372 '∪' => Prim {
373 name: "∪",
374 monad: M::Nub,
375 dyad: D::Union,
376 ranks: [RANK_INF, RANK_INF, RANK_INF],
377 },
378 '∩' => Prim {
379 name: "∩",
380 monad: M::None,
381 dyad: D::Intersect,
382 ranks: [RANK_INF, RANK_INF, RANK_INF],
383 },
384 '∧' => Prim { name: "∧", monad: M::None, dyad: D::Scalar(SD::Lcm), ranks: [0, 0, 0] },
385 '∨' => Prim { name: "∨", monad: M::None, dyad: D::Scalar(SD::Gcd), ranks: [0, 0, 0] },
386 '⍱' => Prim {
387 name: "⍱",
388 monad: M::None,
389 dyad: D::Boolean(BoolDyad::Nor),
390 ranks: [0, 0, 0],
391 },
392 '⍲' => Prim {
393 name: "⍲",
394 monad: M::None,
395 dyad: D::Boolean(BoolDyad::Nand),
396 ranks: [0, 0, 0],
397 },
398 '⍟' => Prim {
399 name: "⍟",
400 monad: M::Scalar(SM::Ln),
401 dyad: D::Scalar(SD::Log),
402 ranks: [0, 0, 0],
403 },
404 '~' => Prim {
405 name: "~",
406 monad: M::Scalar(SM::Not),
407 dyad: D::Less,
408 ranks: [0, RANK_INF, RANK_INF],
409 },
410 '≡' => Prim {
411 name: "≡",
412 monad: M::Depth,
413 dyad: D::Match,
414 ranks: [RANK_INF, RANK_INF, RANK_INF],
415 },
416 '⍋' => Prim {
417 name: "⍋",
418 monad: M::GradeUp { origin },
419 dyad: D::NotYet("dyadic grade (collation)"),
420 ranks: [RANK_INF, RANK_INF, RANK_INF],
421 },
422 '⍒' => Prim {
423 name: "⍒",
424 monad: M::GradeDown { origin },
425 dyad: D::NotYet("dyadic grade (collation)"),
426 ranks: [RANK_INF, RANK_INF, RANK_INF],
427 },
428 '⊖' | '⌽' => Prim {
431 name: if ch == '⊖' { "⊖" } else { "⌽" },
432 monad: M::Reverse,
433 dyad: D::Rotate,
434 ranks: [RANK_INF, 1, RANK_INF],
435 },
436 '⍪' => Prim {
437 name: "⍪",
438 monad: M::TableOf,
439 dyad: D::AppendLeading,
440 ranks: [RANK_INF, RANK_INF, RANK_INF],
441 },
442 '!' => Prim {
443 name: "!",
444 monad: M::Scalar(SM::Factorial),
445 dyad: D::Scalar(SD::Binomial),
446 ranks: [0, 0, 0],
447 },
448 '⍕' => Prim {
449 name: "⍕",
450 monad: M::Format,
451 dyad: D::FormatSpec,
452 ranks: [RANK_INF, 1, RANK_INF],
453 },
454 '⊥' => Prim {
458 name: "⊥",
459 monad: M::None,
460 dyad: D::Decode,
461 ranks: [RANK_INF, 1, 1],
462 },
463 '⊤' => Prim {
464 name: "⊤",
465 monad: M::None,
466 dyad: D::Encode,
467 ranks: [RANK_INF, 1, RANK_INF],
468 },
469 '⍉' => Prim {
470 name: "⍉",
471 monad: M::TransposeAxes,
472 dyad: D::NotYet("dyadic transpose"),
473 ranks: [RANK_INF, 1, RANK_INF],
474 },
475 '↑' => Prim {
478 name: "↑",
479 monad: match d.first_disclose {
480 FirstDisclose::UpIsFirst => M::First,
481 FirstDisclose::UpIsMix => return None,
482 },
483 dyad: D::Take,
484 ranks: [RANK_INF, 1, RANK_INF],
485 },
486 '⊂' => Prim {
487 name: "⊂",
488 monad: match d.nested_model {
491 NestedModel::Floating => M::Enclose(Enclose::ExceptSimpleScalar),
492 NestedModel::Grounded => return None,
493 },
494 dyad: D::PartitionEnclose,
495 ranks: [RANK_INF, RANK_INF, RANK_INF],
496 },
497 '⊆' => Prim {
500 name: "⊆",
501 monad: M::Nest,
502 dyad: D::PartitionEnclose,
503 ranks: [RANK_INF, RANK_INF, RANK_INF],
504 },
505 '⍸' => Prim {
508 name: "⍸",
509 monad: M::Indices { origin, boxed_coords: true },
510 dyad: D::IntervalIndex { offset: origin - 1 },
511 ranks: [RANK_INF, 1, RANK_INF],
512 },
513 '⌷' => Prim {
516 name: "⌷",
517 monad: match d.index_form {
518 IndexForm::ScalarPerAxis => M::Same,
519 IndexForm::AxisVectors => return None,
520 },
521 dyad: D::Squad { origin },
522 ranks: [RANK_INF, RANK_INF, RANK_INF],
523 },
524 '?' => Prim {
525 name: "?",
526 monad: M::Roll { origin, fixed: false, float_at_zero: false },
527 dyad: D::Deal { origin, fixed: false },
528 ranks: [RANK_INF, 0, 0],
529 },
530 '⌹' => Prim {
531 name: "⌹",
532 monad: M::MatrixInverse,
533 dyad: D::MatrixDivide,
534 ranks: [2, RANK_INF, 2],
535 },
536 '⊃' => Prim {
537 name: "⊃",
538 monad: match d.first_disclose {
539 FirstDisclose::UpIsFirst => M::Open,
540 FirstDisclose::UpIsMix => return None,
541 },
542 dyad: D::Pick { origin },
543 ranks: [0, RANK_INF, RANK_INF],
544 },
545 '↓' => Prim {
546 name: "↓",
547 monad: M::Split,
548 dyad: D::Drop,
549 ranks: [RANK_INF, 1, RANK_INF],
550 },
551 ',' => Prim {
552 name: ",",
553 monad: M::Ravel,
554 dyad: D::AppendLast,
555 ranks: [RANK_INF, RANK_INF, RANK_INF],
556 },
557 '≢' => Prim {
558 name: "≢",
559 monad: M::Tally,
560 dyad: D::NotMatch,
561 ranks: [RANK_INF, RANK_INF, RANK_INF],
562 },
563 '⊢' => Prim {
564 name: "⊢",
565 monad: M::Same,
566 dyad: D::Right,
567 ranks: [RANK_INF, RANK_INF, RANK_INF],
568 },
569 '⊣' => Prim {
570 name: "⊣",
571 monad: M::Same,
572 dyad: D::Left,
573 ranks: [RANK_INF, RANK_INF, RANK_INF],
574 },
575 '○' => Prim {
576 name: "○",
577 monad: M::Scalar(SM::Pi),
578 dyad: D::Scalar(SD::Circle),
579 ranks: [0, 0, 0],
580 },
581 '⍷' => Prim {
582 name: "⍷",
583 monad: M::None,
584 dyad: D::FindSeq,
585 ranks: [RANK_INF, RANK_INF, RANK_INF],
586 },
587 '⍎' => Prim {
588 name: "⍎",
589 monad: M::Execute { apl: true },
590 dyad: D::None,
591 ranks: [1, RANK_INF, RANK_INF],
592 },
593 _ => return None,
594 };
595 Some(p)
596}
597
598fn verb_for(ch: char, d: Rules) -> Option<Verb> {
602 let p = prim_for(ch, d)?;
603 if ch == '⌽' {
604 return Some(Verb::Rank(Box::new(Verb::Prim(p)), [1, 0, 1]));
605 }
606 Some(Verb::Prim(p))
607}
608
609fn quad_name(name: &str, d: Rules, span: Span) -> Result<Tok> {
615 let chars = |s: &str| Tok::Value(Array::from_chars(s.chars().collect()));
616 Ok(match name {
617 "A" => chars("ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
618 "D" => chars("0123456789"),
619 "IO" => Tok::Value(Array::scalar_i64(d.origin)),
620 "CT" => Tok::Value(Array::scalar_f64(d.ct)),
621 "UCS" => Tok::Func(Verb::Prim(Prim {
622 name: "⎕UCS",
623 monad: MonadOp::Unicode { pass_chars: false },
624 dyad: DyadOp::None,
625 ranks: [RANK_INF, RANK_INF, RANK_INF],
626 })),
627 "TS" | "AI" | "TC" | "WA" | "SI" | "LC" | "NL" | "EX" | "FIO" | "NA" | "SH" | "CMD"
631 | "MAP" | "SVO" | "SVQ" | "TZ" | "DL" => Err(Error::language(
632 format!("⎕{name} is closed by the sandbox: it reads outside the program"),
633 span,
634 ))?,
635 other => Err(Error::not_yet(format!("the system name ⎕{other}"), span))?,
636 })
637}
638
639fn op_for(ch: char) -> Option<OpGlyph> {
640 match ch {
641 '/' => Some(OpGlyph::Slash),
642 '⌿' => Some(OpGlyph::SlashBar),
643 '\\' => Some(OpGlyph::Backslash),
644 '⍀' => Some(OpGlyph::BackslashBar),
645 '⍤' => Some(OpGlyph::Rank),
646 '⍨' => Some(OpGlyph::Commute),
647 '⍣' => Some(OpGlyph::Power),
648 '∘' => Some(OpGlyph::Jot),
649 '⍥' => Some(OpGlyph::Over),
650 '¨' => Some(OpGlyph::Each),
651 '⍛' => Some(OpGlyph::Before),
652 '⌸' => Some(OpGlyph::Key),
653 _ => None,
654 }
655}
656
657fn expand_verb(leading: bool) -> Verb {
660 let p = Prim {
661 name: if leading { "⍀" } else { "\\" },
662 monad: MonadOp::None,
663 dyad: DyadOp::Expand,
664 ranks: if leading { [RANK_INF, 1, RANK_INF] } else { [RANK_INF, 1, 1] },
665 };
666 Verb::Prim(p)
667}
668
669fn copy_verb(leading: bool) -> Verb {
673 let p = Prim {
674 name: if leading { "⌿" } else { "/" },
675 monad: MonadOp::None,
676 dyad: DyadOp::Copy,
677 ranks: if leading { [RANK_INF, 1, RANK_INF] } else { [RANK_INF, 1, 1] },
678 };
679 Verb::Prim(p)
680}
681
682fn lex(src: &SourceParts, d: Rules) -> Result<Vec<Vec<Token>>> {
689 let mut out: Vec<Vec<Token>> = Vec::new();
690 let mut cur: Vec<Token> = Vec::new();
691 let mut in_comment = false;
693 let mut braces = 0usize;
694 for seg in &src.segments {
695 match seg {
696 Segment::Text { text, offset } => {
697 lex_text(text, *offset, d, &mut out, &mut cur, &mut in_comment, &mut braces)?;
698 }
699 Segment::Param { index, offset, len } => {
700 if !in_comment {
701 cur.push(Token {
702 kind: Tok::Param(*index),
703 span: Span::new(*offset, offset + len),
704 });
705 }
706 }
707 }
708 }
709 if !cur.is_empty() {
710 out.push(cur);
711 }
712 Ok(out)
713}
714
715#[allow(clippy::too_many_arguments)]
716fn lex_text(
717 text: &str,
718 offset: usize,
719 d: Rules,
720 out: &mut Vec<Vec<Token>>,
721 cur: &mut Vec<Token>,
722 in_comment: &mut bool,
723 braces: &mut usize,
724) -> Result<()> {
725 let mut i = 0usize;
726 while i < text.len() {
727 let ch = text[i..].chars().next().unwrap();
728 let clen = ch.len_utf8();
729 if *in_comment {
730 if ch == '\n' {
731 *in_comment = false;
732 end_sentence(out, cur);
733 }
734 i += clen;
735 continue;
736 }
737 match ch {
738 '\n' | '⋄' => {
741 if *braces > 0 {
742 cur.push(Token {
743 kind: Tok::Separator,
744 span: Span::new(offset + i, offset + i + clen),
745 });
746 } else {
747 end_sentence(out, cur);
748 }
749 i += clen;
750 }
751 ' ' | '\t' | '\r' => i += clen,
752 '⍝' => {
753 *in_comment = true;
754 i += clen;
755 }
756 '\'' => {
757 let (arr, next) = lex_string(text, i, offset)?;
758 cur.push(Token {
759 kind: Tok::Value(arr),
760 span: Span::new(offset + i, offset + next),
761 });
762 i = next;
763 }
764 '{' => {
765 *braces += 1;
766 cur.push(Token { kind: Tok::LBrace, span: Span::new(offset + i, offset + i + 1) });
767 i += 1;
768 }
769 '}' => {
770 *braces = braces.saturating_sub(1);
771 cur.push(Token { kind: Tok::RBrace, span: Span::new(offset + i, offset + i + 1) });
772 i += 1;
773 }
774 '∇' => {
775 cur.push(Token { kind: Tok::Del, span: Span::new(offset + i, offset + i + clen) });
776 i += clen;
777 }
778 '⍺' | '⍵' => {
780 let mut end = i + clen;
781 if text[end..].starts_with(ch) {
782 end += clen;
783 }
784 cur.push(Token {
785 kind: Tok::Name(text[i..end].to_string()),
786 span: Span::new(offset + i, offset + end),
787 });
788 i = end;
789 }
790 ':' => {
792 let mut j = i + 1;
793 while let Some(c) = text[j..].chars().next() {
794 if c.is_ascii_alphabetic() {
795 j += c.len_utf8();
796 } else {
797 break;
798 }
799 }
800 let span = Span::new(offset + i, offset + j);
801 match control_word(&text[i + 1..j]) {
802 Some(word) => cur.push(Token { kind: Tok::Control(word), span }),
803 None if j > i + 1 => {
804 return Err(Error::parse(
805 format!("unknown control word: {}", &text[i..j]),
806 span,
807 ));
808 }
809 None => cur.push(Token {
810 kind: Tok::Colon,
811 span: Span::new(offset + i, offset + i + 1),
812 }),
813 }
814 i = j;
815 }
816 '→' => {
817 cur.push(Token {
818 kind: Tok::Arrow,
819 span: Span::new(offset + i, offset + i + clen),
820 });
821 i += clen;
822 }
823 '⍬' => {
825 cur.push(Token {
826 kind: Tok::Value(Array::empty(crate::dtype::DType::I64)),
827 span: Span::new(offset + i, offset + i + clen),
828 });
829 i += clen;
830 }
831 '(' => {
832 cur.push(Token { kind: Tok::LParen, span: Span::new(offset + i, offset + i + 1) });
833 i += 1;
834 }
835 ')' => {
836 cur.push(Token { kind: Tok::RParen, span: Span::new(offset + i, offset + i + 1) });
837 i += 1;
838 }
839 '[' => {
840 cur.push(Token {
841 kind: Tok::LBracket,
842 span: Span::new(offset + i, offset + i + 1),
843 });
844 i += 1;
845 }
846 ']' => {
847 cur.push(Token {
848 kind: Tok::RBracket,
849 span: Span::new(offset + i, offset + i + 1),
850 });
851 i += 1;
852 }
853 ';' => {
854 cur.push(Token { kind: Tok::Semi, span: Span::new(offset + i, offset + i + 1) });
855 i += 1;
856 }
857 '←' => {
858 cur.push(Token {
859 kind: Tok::Assign,
860 span: Span::new(offset + i, offset + i + clen),
861 });
862 i += clen;
863 }
864 '⎕' => {
865 let after = i + clen;
866 let mut j = after;
867 while let Some(c) = text[j..].chars().next() {
868 if c.is_alphabetic() {
869 j += c.len_utf8();
870 } else {
871 break;
872 }
873 }
874 if j > after {
875 let span = Span::new(offset + i, offset + j);
876 let name = text[after..j].to_uppercase();
877 if text[j..].trim_start().starts_with('←') {
883 quad_name(&name, d, span)?;
884 return Err(Error::language(
885 format!(
886 "⎕{name} is read-only: libjay's system names are \
887 fixed before the program runs"
888 ),
889 span,
890 ));
891 }
892 cur.push(Token { kind: quad_name(&name, d, span)?, span });
893 i = j;
894 continue;
895 }
896 cur.push(Token { kind: Tok::Quad, span: Span::new(offset + i, offset + after) });
897 i = after;
898 }
899 _ if num_start(text, i) => {
900 let (tok, next) = lex_number_vector(text, i, offset)?;
901 cur.push(tok);
902 i = next;
903 }
904 _ if is_name_start(ch) => {
905 let start = i;
906 i += clen;
907 while let Some(c) = text[i..].chars().next() {
908 if is_name_body(c) {
909 i += c.len_utf8();
910 } else {
911 break;
912 }
913 }
914 cur.push(Token {
915 kind: Tok::Name(text[start..i].to_string()),
916 span: Span::new(offset + start, offset + i),
917 });
918 }
919 _ => {
920 let mut end = i + clen;
921 if let Some(v) = verb_for(ch, d) {
922 cur.push(Token {
923 kind: Tok::Func(v),
924 span: Span::new(offset + i, offset + end),
925 });
926 } else if let Some(mut op) = op_for(ch) {
927 if op == OpGlyph::Jot && text[end..].starts_with('.') {
930 op = OpGlyph::JotDot;
931 end += 1;
932 }
933 cur.push(Token {
934 kind: Tok::Op(op),
935 span: Span::new(offset + i, offset + end),
936 });
937 } else {
938 return Err(Error::parse(
939 format!("unknown symbol: {ch}"),
940 Span::new(offset + i, offset + end),
941 ));
942 }
943 i = end;
944 }
945 }
946 }
947 Ok(())
948}
949
950fn end_sentence(out: &mut Vec<Vec<Token>>, cur: &mut Vec<Token>) {
951 if !cur.is_empty() {
952 out.push(std::mem::take(cur));
953 }
954}
955
956fn is_name_start(c: char) -> bool {
957 c.is_alphabetic() || c == '∆' || c == '⍙'
958}
959
960fn is_name_body(c: char) -> bool {
961 c.is_alphanumeric() || c == '_' || c == '∆' || c == '⍙'
962}
963
964fn lex_string(text: &str, start: usize, offset: usize) -> Result<(Array, usize)> {
967 let mut chars: Vec<char> = Vec::new();
968 let mut i = start + 1;
969 loop {
970 let c = match text[i..].chars().next() {
971 Some(c) => c,
972 None => {
973 return Err(Error::parse(
974 "unterminated string",
975 Span::new(offset + start, offset + text.len()),
976 ));
977 }
978 };
979 if c == '\'' {
980 if text[i + 1..].starts_with('\'') {
981 chars.push('\'');
982 i += 2;
983 continue;
984 }
985 i += 1;
986 break;
987 }
988 chars.push(c);
989 i += c.len_utf8();
990 }
991 let shape = if chars.len() == 1 { vec![] } else { vec![chars.len()] };
992 Ok((Array::new(shape, Data::Char(chars.into())), i))
993}
994
995fn num_start(text: &str, i: usize) -> bool {
997 let s = match text.get(i..) {
998 Some(s) => s,
999 None => return false,
1000 };
1001 let mut cs = s.chars();
1002 let c0 = match cs.next() {
1003 Some(c) => c,
1004 None => return false,
1005 };
1006 if c0.is_ascii_digit() {
1007 return true;
1008 }
1009 if c0 == '.' {
1010 return cs.next().is_some_and(|d| d.is_ascii_digit());
1011 }
1012 if c0 == '¯' {
1013 return match cs.next() {
1014 Some(d) if d.is_ascii_digit() => true,
1015 Some('.') => cs.next().is_some_and(|d| d.is_ascii_digit()),
1016 _ => false,
1017 };
1018 }
1019 false
1020}
1021
1022fn lex_number(text: &str, start: usize, offset: usize) -> Result<(f64, bool, usize)> {
1025 let mut i = start;
1026 let mut buf = String::new();
1027 let mut saw_dot = false;
1028 if text[i..].starts_with('¯') {
1029 buf.push('-');
1030 i += '¯'.len_utf8();
1031 }
1032 i = take_digits(text, i, &mut buf);
1033 if text[i..].starts_with('.') && text[i + 1..].chars().next().is_some_and(|d| d.is_ascii_digit())
1034 {
1035 saw_dot = true;
1036 buf.push('.');
1037 i += 1;
1038 i = take_digits(text, i, &mut buf);
1039 }
1040 if let Some(c) = text[i..].chars().next() {
1041 if c == 'e' || c == 'E' {
1042 let after = i + 1;
1043 let neg = text[after..].starts_with('¯');
1044 let digits_at = if neg { after + '¯'.len_utf8() } else { after };
1045 if text[digits_at..].chars().next().is_some_and(|d| d.is_ascii_digit()) {
1046 buf.push('e');
1047 if neg {
1048 buf.push('-');
1049 }
1050 i = take_digits(text, digits_at, &mut buf);
1051 }
1052 }
1053 }
1054 let v: f64 = buf.parse().map_err(|_| {
1055 Error::parse(
1056 format!("cannot read the number {}", &text[start..i]),
1057 Span::new(offset + start, offset + i),
1058 )
1059 })?;
1060 let float = saw_dot || v.fract() != 0.0 || v.abs() >= 9.0e18;
1062 Ok((v, float, i))
1063}
1064
1065fn take_digits(text: &str, mut i: usize, buf: &mut String) -> usize {
1066 while let Some(c) = text[i..].chars().next() {
1067 if c.is_ascii_digit() {
1068 buf.push(c);
1069 i += 1;
1070 } else {
1071 break;
1072 }
1073 }
1074 i
1075}
1076
1077fn lex_number_vector(text: &str, start: usize, offset: usize) -> Result<(Token, usize)> {
1080 let mut vals: Vec<crate::complex::Cx> = Vec::new();
1081 let mut any_float = false;
1082 let mut any_complex = false;
1083 let mut i = start;
1084 let mut end;
1085 loop {
1086 let (v, float, mut next) = lex_number(text, i, offset)?;
1087 let mut imag = 0.0;
1088 if let Some(c) = text[next..].chars().next() {
1089 if (c == 'j' || c == 'J') && num_start(text, next + 1) {
1092 let (b, _, imag_end) = lex_number(text, next + 1, offset)?;
1093 imag = b;
1094 next = imag_end;
1095 any_complex = true;
1096 }
1097 }
1098 vals.push([v, imag]);
1099 any_float |= float;
1100 end = next;
1101 i = next;
1102 let mut k = i;
1103 while text[k..].starts_with(' ') || text[k..].starts_with('\t') {
1104 k += 1;
1105 }
1106 if k > i && num_start(text, k) {
1107 i = k;
1108 continue;
1109 }
1110 break;
1111 }
1112 let data = if any_complex {
1113 Data::Complex(vals.into())
1114 } else if any_float {
1115 Data::F64(vals.iter().map(|&v| v[0]).collect())
1116 } else {
1117 Data::I64(vals.iter().map(|&v| v[0] as i64).collect())
1118 };
1119 let shape = if data.len() == 1 { vec![] } else { vec![data.len()] };
1120 let tok = Token {
1121 kind: Tok::Nums(Array::new(shape, data)),
1122 span: Span::new(offset + start, offset + end),
1123 };
1124 Ok((tok, end))
1125}
1126
1127fn fold_operators(toks: Vec<Token>, d: Rules) -> Result<Vec<Token>> {
1135 let mut out: Vec<Token> = Vec::new();
1136 let mut it = toks.into_iter().peekable();
1137 while let Some(t) = it.next() {
1138 if let Tok::UserOp { def, omega } = &t.kind {
1141 let (def, omega) = (def.clone(), *omega);
1142 let right = if omega {
1143 match it.peek() {
1144 Some(tok) if matches!(tok.kind, Tok::Func(_)) => {
1145 let g = it.next().expect("peeked");
1146 let Tok::Func(g) = g.kind else { unreachable!("checked above") };
1147 Some(Box::new(g))
1148 }
1149 _ => {
1150 return Err(Error::parse("⍵⍵ needs a function on the operator's right", t.span));
1151 }
1152 }
1153 } else {
1154 None
1155 };
1156 let Some(Token { kind: Tok::Func(f), span: fspan }) = out.pop() else {
1157 return Err(Error::parse("⍺⍺ needs a function on the operator's left", t.span));
1158 };
1159 let derived = Verb::UserDerived {
1160 def: Box::new(def),
1161 alpha: Box::new(f),
1162 omega: right,
1163 };
1164 out.push(Token { kind: Tok::Func(derived), span: Span::merge(fspan, t.span) });
1165 continue;
1166 }
1167 let op = match t.kind {
1168 Tok::Op(op) => op,
1169 _ => {
1170 out.push(t);
1171 continue;
1172 }
1173 };
1174 if op == OpGlyph::JotDot {
1177 let ftok = match it.peek() {
1178 Some(tok) if matches!(tok.kind, Tok::Func(_)) => it.next().unwrap(),
1179 _ => {
1180 return Err(Error::parse("∘. needs a function on its right", t.span));
1181 }
1182 };
1183 let span = Span::merge(t.span, ftok.span);
1184 let Tok::Func(f) = ftok.kind else { unreachable!("checked above") };
1185 out.push(Token { kind: Tok::Func(Verb::Reduce(Box::new(f))), span });
1186 continue;
1187 }
1188 if matches!(op, OpGlyph::Jot | OpGlyph::Over | OpGlyph::Before) {
1191 let Some(gtok) = it.peek().filter(|x| matches!(x.kind, Tok::Func(_))) else {
1192 return Err(Error::not_yet(
1193 format!("{} with a value operand", op.glyph()),
1194 t.span,
1195 ));
1196 };
1197 let gspan = gtok.span;
1198 let Some(Token { kind: Tok::Func(g), .. }) = it.next() else {
1199 unreachable!("peeked a function")
1200 };
1201 let Some(Token { kind: Tok::Func(f), span: fspan }) = out.pop() else {
1202 return Err(Error::not_yet(
1203 format!("{} with a value operand", op.glyph()),
1204 t.span,
1205 ));
1206 };
1207 let span = Span::merge(fspan, gspan);
1208 let derived = match op {
1211 OpGlyph::Jot => Verb::Beside(Box::new(f), Box::new(g)),
1212 OpGlyph::Before => Verb::Before(Box::new(f), Box::new(g)),
1213 _ => Verb::Compose(Box::new(f), Box::new(g)),
1214 };
1215 out.push(Token { kind: Tok::Func(derived), span });
1216 continue;
1217 }
1218 let left_is_func = matches!(out.last().map(|x| &x.kind), Some(Tok::Func(_)));
1220 if !left_is_func {
1221 if out.last().is_some_and(|x| is_operand_end(&x.kind)) {
1225 let f = match op {
1226 OpGlyph::Slash => copy_verb(false),
1227 OpGlyph::SlashBar => copy_verb(true),
1228 OpGlyph::Backslash => expand_verb(false),
1229 OpGlyph::BackslashBar => expand_verb(true),
1230 OpGlyph::Rank
1231 | OpGlyph::Commute
1232 | OpGlyph::Power
1233 | OpGlyph::JotDot
1234 | OpGlyph::Jot
1235 | OpGlyph::Over
1236 | OpGlyph::Before
1237 | OpGlyph::Key
1238 | OpGlyph::Each => {
1239 return Err(Error::parse(
1240 format!("{} needs a function to its left", op.glyph()),
1241 t.span,
1242 ));
1243 }
1244 };
1245 out.push(Token { kind: Tok::Func(f), span: t.span });
1246 continue;
1247 }
1248 return Err(Error::parse(
1249 format!("{} needs a function to its left", op.glyph()),
1250 t.span,
1251 ));
1252 }
1253 let ftok = out.pop().unwrap();
1254 let f = match ftok.kind {
1255 Tok::Func(f) => f,
1256 _ => unreachable!("checked above"),
1257 };
1258 let span = Span::merge(ftok.span, t.span);
1259 if let Some((k, aspan)) = take_axis(&mut it, d)? {
1263 let inner = match op {
1264 OpGlyph::Slash | OpGlyph::SlashBar => Verb::Reduce(Box::new(f)),
1265 OpGlyph::Backslash | OpGlyph::BackslashBar => {
1266 Verb::Windowed(Box::new(Verb::Reduce(Box::new(f))), WindowKind::Scan)
1267 }
1268 _ => {
1269 return Err(Error::not_yet(
1270 format!("axis specification for {}", op.glyph()),
1271 aspan,
1272 ));
1273 }
1274 };
1275 out.push(Token {
1276 kind: Tok::Func(Verb::AlongAxis(Box::new(inner), k)),
1277 span: Span::merge(span, aspan),
1278 });
1279 continue;
1280 }
1281 let derived = match op {
1282 OpGlyph::Slash => Verb::Rank(Box::new(Verb::Reduce(Box::new(f))), [1, 1, 1]),
1285 OpGlyph::SlashBar => Verb::Reduce(Box::new(f)),
1286 OpGlyph::Backslash => Verb::Rank(
1290 Box::new(Verb::Windowed(Box::new(Verb::Reduce(Box::new(f))), WindowKind::Scan)),
1291 [1, 1, 1],
1292 ),
1293 OpGlyph::BackslashBar => {
1294 Verb::Windowed(Box::new(Verb::Reduce(Box::new(f))), WindowKind::Scan)
1295 }
1296 OpGlyph::Commute => Verb::Commute(Box::new(f)),
1297 OpGlyph::Key => Verb::KeyPairs(Box::new(f)),
1298 OpGlyph::Each => Verb::Each(Box::new(f), Enclose::ExceptSimpleScalar),
1302 OpGlyph::Power => {
1303 let spec = match it.peek() {
1304 Some(tok) if matches!(tok.kind, Tok::Func(_)) => {
1307 let gtok = it.next().unwrap();
1308 let Tok::Func(g) = gtok.kind else { unreachable!("checked above") };
1309 let v = Verb::PowerUntil(Box::new(f), Box::new(g));
1310 out.push(Token {
1311 kind: Tok::Func(v),
1312 span: Span::merge(span, gtok.span),
1313 });
1314 continue;
1315 }
1316 Some(tok) if literal(&tok.kind).is_some() => it.next().unwrap(),
1317 _ => {
1318 return Err(Error::not_yet("computed power (f⍣n)", t.span));
1319 }
1320 };
1321 let arr = literal(&spec.kind).expect("checked above");
1322 let p = power_spec(arr, spec.span)?;
1323 let f = Verb::PowerN(Box::new(f), p);
1324 out.push(Token { kind: Tok::Func(f), span: Span::merge(span, spec.span) });
1325 continue;
1326 }
1327 OpGlyph::Rank => {
1328 let spec = match it.peek() {
1329 Some(tok) if matches!(tok.kind, Tok::Func(_)) => {
1332 let gtok = it.next().unwrap();
1333 let Tok::Func(g) = gtok.kind else { unreachable!("checked above") };
1334 let v = Verb::Atop(Box::new(f), Box::new(g));
1335 out.push(Token {
1336 kind: Tok::Func(v),
1337 span: Span::merge(span, gtok.span),
1338 });
1339 continue;
1340 }
1341 Some(tok) if literal(&tok.kind).is_some() => it.next().unwrap(),
1342 _ => {
1343 return Err(Error::parse(
1344 "⍤ needs a rank specification on its right",
1345 t.span,
1346 ));
1347 }
1348 };
1349 let arr = literal(&spec.kind).expect("checked above");
1350 let ranks = rank_spec(arr, spec.span)?;
1351 let f = Verb::Rank(Box::new(f), ranks);
1352 out.push(Token { kind: Tok::Func(f), span: Span::merge(span, spec.span) });
1353 continue;
1354 }
1355 OpGlyph::JotDot | OpGlyph::Jot | OpGlyph::Over | OpGlyph::Before => {
1357 unreachable!("handled above")
1358 }
1359 };
1360 out.push(Token { kind: Tok::Func(derived), span });
1361 }
1362 Ok(out)
1363}
1364
1365fn take_axis(
1368 it: &mut std::iter::Peekable<std::vec::IntoIter<Token>>,
1369 d: Rules,
1370) -> Result<Option<(usize, Span)>> {
1371 if !matches!(it.peek().map(|t| &t.kind), Some(Tok::LBracket)) {
1372 return Ok(None);
1373 }
1374 let open = it.next().expect("peeked");
1375 let spec = match it.next() {
1376 Some(tok) if literal(&tok.kind).is_some() => tok,
1377 Some(tok) => return Err(Error::not_yet("a computed axis (f[k])", tok.span)),
1378 None => return Err(Error::parse("unterminated axis specification", open.span)),
1379 };
1380 let close = match it.next() {
1381 Some(tok) if matches!(tok.kind, Tok::RBracket) => tok,
1382 _ => return Err(Error::parse("unterminated axis specification", open.span)),
1383 };
1384 let span = Span::merge(open.span, close.span);
1385 let arr = literal(&spec.kind).expect("checked above");
1386 let ints = arr
1387 .to_i64_vec()
1388 .ok_or_else(|| Error::parse("an axis must be a whole number", spec.span))?;
1389 let [k] = ints[..] else {
1390 return Err(Error::not_yet("several axes in one specification", spec.span));
1391 };
1392 let origin = d.origin;
1393 let k = k - origin;
1394 if k < 0 {
1395 return Err(Error::domain(format!("axis {} does not exist", k + origin), spec.span));
1396 }
1397 Ok(Some((k as usize, span)))
1398}
1399
1400fn fold_paren_funcs(toks: Vec<Token>) -> Vec<Token> {
1403 let mut out: Vec<Token> = Vec::with_capacity(toks.len());
1404 for t in toks {
1405 if matches!(t.kind, Tok::RBracket) {
1406 out.push(t);
1407 continue;
1408 }
1409 let is_close = matches!(t.kind, Tok::RParen);
1410 let n = out.len();
1411 if is_close
1412 && n >= 2
1413 && matches!(out[n - 1].kind, Tok::Func(_))
1414 && matches!(out[n - 2].kind, Tok::LParen)
1415 {
1416 let f = out.pop().expect("checked above");
1417 let open = out.pop().expect("checked above");
1418 out.push(Token { kind: f.kind, span: Span::merge(open.span, t.span) });
1419 continue;
1420 }
1421 out.push(t);
1422 }
1423 out
1424}
1425
1426fn fold_axes(toks: Vec<Token>, d: Rules) -> Result<Vec<Token>> {
1428 let mut out: Vec<Token> = Vec::new();
1429 let mut it = toks.into_iter().peekable();
1430 while let Some(t) = it.next() {
1431 let Tok::Func(f) = &t.kind else {
1432 out.push(t);
1433 continue;
1434 };
1435 let Some((k, aspan)) = take_axis(&mut it, d)? else {
1436 out.push(t);
1437 continue;
1438 };
1439 let Some(inner) = leading_axis_form(f) else {
1440 return Err(Error::not_yet(format!("axis specification for {}", f.name()), aspan));
1441 };
1442 out.push(Token {
1443 kind: Tok::Func(Verb::AlongAxis(Box::new(inner), k)),
1444 span: Span::merge(t.span, aspan),
1445 });
1446 }
1447 Ok(out)
1448}
1449
1450fn leading_axis_form(v: &Verb) -> Option<Verb> {
1454 match v {
1455 Verb::Rank(inner, [1, 0, 1]) => leading_axis_form(inner),
1457 Verb::Prim(p) if matches!(p.monad, MonadOp::Reverse) => Some(v.clone()),
1458 _ => None,
1459 }
1460}
1461
1462fn select_axis_verb(axis: usize, rank: usize, d: Rules) -> Verb {
1464 Verb::Prim(Prim {
1465 name: "[…]",
1466 monad: MonadOp::None,
1467 dyad: DyadOp::SelectAxis { axis, rank, origin: d.origin },
1468 ranks: [RANK_INF; 3],
1469 })
1470}
1471
1472fn power_spec(a: &Array, span: Span) -> Result<Power> {
1475 let ints = a
1476 .to_i64_vec()
1477 .ok_or_else(|| Error::parse("⍣ needs a whole number on its right", span))?;
1478 let [n] = ints[..] else {
1479 return Err(Error::not_yet("power over a list of counts (f⍣n)", span));
1480 };
1481 if n < 0 {
1482 return Err(Error::not_yet("inverse power (f⍣¯1 and other negative powers)", span));
1483 }
1484 Ok(Power::Times(n as u64))
1485}
1486
1487fn rank_spec(a: &Array, span: Span) -> Result<[i64; 3]> {
1489 let ints = a
1490 .to_i64_vec()
1491 .ok_or_else(|| Error::parse("⍤ rank specification must be integers", span))?;
1492 match ints.len() {
1493 1 => Ok([ints[0], ints[0], ints[0]]),
1494 2 => Ok([ints[1], ints[0], ints[1]]),
1495 3 => Ok([ints[0], ints[1], ints[2]]),
1496 _ => Err(Error::parse("⍤ rank specification takes 1 to 3 integers", span)),
1497 }
1498}
1499
1500fn parse_range(toks: &[Token], lo: usize, hi: usize, hint: Span, d: Rules) -> Result<Expr> {
1507 let (mut acc, mut start) = parse_operand(toks, lo, hi, hint, d)?;
1508 let end = toks[hi - 1].span.end;
1509 loop {
1510 if start == lo {
1511 return Ok(acc);
1512 }
1513 let left = &toks[start - 1];
1514 match &left.kind {
1515 Tok::Func(f) => {
1516 let dyadic = start >= lo + 2 && is_operand_end(&toks[start - 2].kind);
1518 if dyadic {
1519 let (x, xstart) = parse_operand(toks, lo, start - 1, left.span, d)?;
1520 acc = Expr::Dyad {
1521 verb: f.clone(),
1522 x: Box::new(x),
1523 y: Box::new(acc),
1524 span: Span::new(toks[xstart].span.start, end),
1525 };
1526 start = xstart;
1527 } else {
1528 acc = Expr::Monad {
1529 verb: f.clone(),
1530 y: Box::new(acc),
1531 span: Span::new(left.span.start, end),
1532 };
1533 start -= 1;
1534 }
1535 }
1536 Tok::Assign => {
1537 if start < lo + 2 {
1538 return Err(Error::parse("assignment target must be a name", left.span));
1539 }
1540 let target = &toks[start - 2];
1541 let span = Span::new(target.span.start, end);
1542 match &target.kind {
1543 Tok::Name(n) => {
1544 acc = Expr::Assign {
1545 name: n.clone(),
1546 value: Box::new(acc),
1547 scope: Scope::Local,
1548 span,
1549 };
1550 }
1551 Tok::Quad => {
1552 acc = Expr::PrintPass { value: Box::new(acc), span };
1553 }
1554 _ => {
1555 return Err(Error::parse(
1556 "assignment target must be a name",
1557 target.span,
1558 ));
1559 }
1560 }
1561 start -= 2;
1562 }
1563 _ => break,
1566 }
1567 }
1568 let span = Span::new(toks[lo].span.start, toks[start - 1].span.end);
1569 if d.trains && toks[lo..start].iter().all(|t| matches!(t.kind, Tok::Func(_))) {
1572 return Err(Error::not_yet("a train (a function derived from a run of functions)", span));
1573 }
1574 Err(Error::parse("syntax error", span))
1575}
1576
1577fn parse_operand(
1584 toks: &[Token],
1585 lo: usize,
1586 hi: usize,
1587 hint: Span,
1588 d: Rules,
1589) -> Result<(Expr, usize)> {
1590 let (first, mut start) = parse_primary(toks, lo, hi, hint, d)?;
1591 if start == lo || !is_operand_end(&toks[start - 1].kind) {
1592 return Ok((first, start));
1593 }
1594 let mut items: Vec<Expr> = Vec::new();
1595 let mut cur = first;
1596 loop {
1597 push_items(&mut items, cur, &toks[start]);
1598 if start == lo || !is_operand_end(&toks[start - 1].kind) {
1599 break;
1600 }
1601 let (e, s) = parse_primary(toks, lo, start, toks[start - 1].span, d)?;
1602 cur = e;
1603 start = s;
1604 }
1605 let span = Span::new(toks[start].span.start, toks[hi - 1].span.end);
1606 let mut it = items.into_iter();
1607 let last = it.next().expect("a strand has at least one item");
1608 let mut acc = Expr::Monad { verb: strand_seed(d), y: Box::new(last), span };
1609 for item in it {
1610 acc = Expr::Dyad { verb: strand_verb(), x: Box::new(item), y: Box::new(acc), span };
1611 }
1612 Ok((acc, start))
1613}
1614
1615fn push_items(items: &mut Vec<Expr>, e: Expr, tok: &Token) {
1617 if let Tok::Nums(a) = &tok.kind {
1618 if a.rank() > 0 {
1619 for i in (0..a.count()).rev() {
1620 let atom = Array { shape: Vec::new(), data: a.data.slice(i, i + 1) };
1621 items.push(Expr::Const(atom, tok.span));
1622 }
1623 return;
1624 }
1625 }
1626 items.push(e);
1627}
1628
1629fn strand_seed(d: Rules) -> Verb {
1632 Verb::Atop(
1633 Box::new(Verb::Prim(prim_for(',', d).expect("`,` is a primitive"))),
1634 Box::new(Verb::Prim(prim_for('⊂', d).expect("`⊂` is a primitive"))),
1635 )
1636}
1637
1638fn strand_verb() -> Verb {
1640 Verb::Prim(Prim {
1641 name: "(vector notation)",
1642 monad: MonadOp::None,
1643 dyad: DyadOp::Strand,
1644 ranks: [RANK_INF; 3],
1645 })
1646}
1647
1648fn parse_primary(
1651 toks: &[Token],
1652 lo: usize,
1653 hi: usize,
1654 hint: Span,
1655 d: Rules,
1656) -> Result<(Expr, usize)> {
1657 if hi == lo {
1658 return Err(Error::parse("empty parentheses", hint));
1659 }
1660 let t = &toks[hi - 1];
1661 match &t.kind {
1662 Tok::Value(a) | Tok::Nums(a) => Ok((Expr::Const(a.clone(), t.span), hi - 1)),
1663 Tok::Param(i) => Ok((Expr::Param(*i, t.span), hi - 1)),
1664 Tok::Name(n) => Ok((Expr::Name(n.clone(), t.span), hi - 1)),
1665 Tok::Niladic(v) => Ok((
1668 Expr::Monad {
1669 verb: v.clone(),
1670 y: Box::new(Expr::Const(Array::empty(crate::dtype::DType::I64), t.span)),
1671 span: t.span,
1672 },
1673 hi - 1,
1674 )),
1675 Tok::RParen => {
1676 let l = match_lparen(toks, lo, hi - 1)?;
1677 let hint = Span::merge(toks[l].span, t.span);
1678 let inner = parse_range(toks, l + 1, hi - 1, hint, d)?;
1679 Ok((inner, l))
1680 }
1681 Tok::RBracket => index_brackets(toks, lo, hi, d),
1682 Tok::Func(_) if hi >= lo + 2 && matches!(toks[hi - 2].kind, Tok::Assign) => {
1687 let from = if hi >= lo + 3 { toks[hi - 3].span } else { toks[hi - 2].span };
1688 Err(Error::not_yet("function assignment (F←+/)", Span::merge(from, t.span)))
1689 }
1690 Tok::Func(_) => Err(Error::parse("missing right argument", t.span)),
1691 Tok::Assign => Err(Error::parse("← needs a value on its right", t.span)),
1692 Tok::Quad => Err(Error::parse("⎕ is only supported as ⎕← (print)", t.span)),
1693 Tok::LParen => Err(Error::parse("unmatched (", t.span)),
1694 Tok::LBracket => Err(Error::parse("unmatched [", t.span)),
1695 Tok::Semi => Err(Error::parse("; is only meaningful inside index brackets", t.span)),
1696 Tok::Colon => Err(Error::parse(": is only meaningful in a dfn guard", t.span)),
1697 Tok::UserOp { .. } => Err(Error::parse(
1698 "this dfn mentions ⍺⍺ or ⍵⍵, so it is an operator and needs a function operand",
1699 t.span,
1700 )),
1701 Tok::Arrow => Err(Error::parse(
1702 "→ branches, and only a line of a ∇ definition may begin with it",
1703 t.span,
1704 )),
1705 Tok::Del => Err(Error::parse("∇ opens a definition; it is not a value", t.span)),
1706 Tok::Control(w) => Err(Error::parse(
1707 format!(":{w} is only meaningful inside a ∇ definition"),
1708 t.span,
1709 )),
1710 Tok::LBrace | Tok::RBrace => Err(Error::parse("unmatched {", t.span)),
1711 Tok::Separator => Err(Error::internal("a statement break survived folding")),
1712 Tok::Op(_) => Err(Error::internal("operator survived folding")),
1713 }
1714}
1715
1716fn index_brackets(
1723 toks: &[Token],
1724 lo: usize,
1725 hi: usize,
1726 d: Rules,
1727) -> Result<(Expr, usize)> {
1728 let close = &toks[hi - 1];
1729 let open = match_lbracket(toks, lo, hi - 1)?;
1730 if open == lo || !is_operand_end(&toks[open - 1].kind) {
1731 return Err(Error::parse("[ needs a value on its left", toks[open].span));
1732 }
1733 let (base, start) = parse_primary(toks, lo, open, toks[open].span, d)?;
1734 let slots = index_slots(toks, open + 1, hi - 1, toks[open].span)?;
1735 let span = Span::new(toks[start].span.start, close.span.end);
1736 let rank = slots.len();
1737 let mut acc = base;
1738 let mut first = true;
1739 for (axis, slot) in slots.iter().enumerate().rev() {
1740 let Some((slo, shi)) = *slot else { continue };
1741 let idx = parse_range(toks, slo, shi, toks[open].span, d)?;
1742 let check = if first { rank } else { 0 };
1743 first = false;
1744 acc = Expr::Dyad {
1745 verb: select_axis_verb(axis, check, d),
1746 x: Box::new(idx),
1747 y: Box::new(acc),
1748 span,
1749 };
1750 }
1751 Ok((acc, start))
1752}
1753
1754fn index_slots(
1757 toks: &[Token],
1758 lo: usize,
1759 hi: usize,
1760 hint: Span,
1761) -> Result<Vec<Option<(usize, usize)>>> {
1762 let mut out = Vec::new();
1763 let mut depth = 0usize;
1764 let mut start = lo;
1765 for (i, t) in toks.iter().enumerate().take(hi).skip(lo) {
1766 match t.kind {
1767 Tok::LParen | Tok::LBracket => depth += 1,
1768 Tok::RParen | Tok::RBracket => depth -= 1,
1769 Tok::Semi if depth == 0 => {
1770 out.push((start < i).then_some((start, i)));
1771 start = i + 1;
1772 }
1773 _ => {}
1774 }
1775 }
1776 out.push((start < hi).then_some((start, hi)));
1777 if out.len() == 1 && out[0].is_none() {
1778 return Err(Error::parse("empty index brackets", hint));
1779 }
1780 Ok(out)
1781}
1782
1783fn match_lbracket(toks: &[Token], lo: usize, rbracket: usize) -> Result<usize> {
1784 let mut depth = 0usize;
1785 let mut i = rbracket;
1786 while i > lo {
1787 i -= 1;
1788 match toks[i].kind {
1789 Tok::RBracket => depth += 1,
1790 Tok::LBracket => {
1791 if depth == 0 {
1792 return Ok(i);
1793 }
1794 depth -= 1;
1795 }
1796 _ => {}
1797 }
1798 }
1799 Err(Error::parse("unmatched ]", toks[rbracket].span))
1800}
1801
1802fn match_lparen(toks: &[Token], lo: usize, rparen: usize) -> Result<usize> {
1803 let mut depth = 0usize;
1804 let mut i = rparen;
1805 while i > lo {
1806 i -= 1;
1807 match toks[i].kind {
1808 Tok::RParen => depth += 1,
1809 Tok::LParen => {
1810 if depth == 0 {
1811 return Ok(i);
1812 }
1813 depth -= 1;
1814 }
1815 _ => {}
1816 }
1817 }
1818 Err(Error::parse("unmatched )", toks[rparen].span))
1819}
1820
1821const CONTROL_WORDS: [&str; 18] = [
1833 "If", "ElseIf", "Else", "EndIf", "While", "EndWhile", "Repeat", "Until", "For", "In",
1834 "EndFor", "Select", "Case", "EndSelect", "Return", "Leave", "Continue", "End",
1835];
1836
1837fn control_word(word: &str) -> Option<&'static str> {
1840 CONTROL_WORDS.iter().copied().find(|w| w.eq_ignore_ascii_case(word))
1841}
1842
1843fn match_close(toks: &[Token], open: usize, opener: &Tok, closer: &Tok) -> Option<usize> {
1845 let same = |a: &Tok, b: &Tok| std::mem::discriminant(a) == std::mem::discriminant(b);
1846 let mut depth = 0usize;
1847 for (i, t) in toks.iter().enumerate().skip(open) {
1848 if same(&t.kind, opener) {
1849 depth += 1;
1850 } else if same(&t.kind, closer) {
1851 depth -= 1;
1852 if depth == 0 {
1853 return Some(i);
1854 }
1855 }
1856 }
1857 None
1858}
1859
1860fn fold_dfns(
1862 toks: Vec<Token>,
1863 d: Rules,
1864 verbs: &HashMap<String, Verb>,
1865) -> Result<Vec<Token>> {
1866 let Some(open) = toks.iter().position(|t| matches!(t.kind, Tok::LBrace)) else {
1867 return Ok(toks);
1868 };
1869 let close = match_close(&toks, open, &Tok::LBrace, &Tok::RBrace)
1870 .ok_or_else(|| Error::parse("unmatched {", toks[open].span))?;
1871 let span = Span::merge(toks[open].span, toks[close].span);
1872 let (verb, omega) = build_dfn(&toks[open + 1..close], d, verbs)?;
1873 let mut out: Vec<Token> = toks[..open].to_vec();
1874 let kind = match omega {
1875 Some(omega) => Tok::UserOp { def: verb, omega },
1876 None => Tok::Func(verb),
1877 };
1878 out.push(Token { kind, span });
1879 out.extend_from_slice(&toks[close + 1..]);
1880 fold_dfns(out, d, verbs)
1882}
1883
1884fn split_statements(toks: &[Token]) -> Vec<&[Token]> {
1887 let mut out = Vec::new();
1888 let mut depth = 0usize;
1889 let mut start = 0usize;
1890 for (i, t) in toks.iter().enumerate() {
1891 match t.kind {
1892 Tok::LBrace => depth += 1,
1893 Tok::RBrace => depth = depth.saturating_sub(1),
1894 Tok::Separator if depth == 0 => {
1895 out.push(&toks[start..i]);
1896 start = i + 1;
1897 }
1898 _ => {}
1899 }
1900 }
1901 out.push(&toks[start..]);
1902 out.into_iter().filter(|s| !s.is_empty()).collect()
1903}
1904
1905fn build_dfn(
1910 body: &[Token],
1911 d: Rules,
1912 verbs: &HashMap<String, Verb>,
1913) -> Result<(Verb, Option<bool>)> {
1914 let mut depth = 0usize;
1915 let mut dyadic = false;
1916 let mut alpha_op = false;
1917 let mut omega_op = false;
1918 for t in body {
1919 match &t.kind {
1920 Tok::LBrace => depth += 1,
1921 Tok::RBrace => depth = depth.saturating_sub(1),
1922 Tok::Name(n) if depth == 0 && n == "⍺" => dyadic = true,
1923 Tok::Name(n) if depth == 0 && n == "⍺⍺" => alpha_op = true,
1924 Tok::Name(n) if depth == 0 && n == "⍵⍵" => omega_op = true,
1925 _ => {}
1926 }
1927 }
1928 let mut inner = verbs.clone();
1929 if alpha_op || omega_op {
1932 inner.insert("⍺⍺".to_string(), Verb::Named("⍺⍺".to_string()));
1933 inner.insert("⍵⍵".to_string(), Verb::Named("⍵⍵".to_string()));
1934 }
1935 let stmts = parse_dfn_body(body, d, &mut inner)?;
1936 let span = body.first().map_or(Span::new(0, 0), |t| t.span);
1940 match d.dfn_result {
1941 DfnResult::LastSentence => {}
1942 DfnResult::FirstNonAssignment => {
1943 return Err(Error::not_yet("a dfn that answers with its first value", span))
1944 }
1945 }
1946 let pure = stmts.iter().all(is_pure_stmt);
1947 let operator = (alpha_op || omega_op).then_some(omega_op);
1948 let verb = Verb::Explicit(Arc::new(ExplicitDef {
1949 name: "{…}".to_string(),
1950 left: dyadic.then(|| "⍺".to_string()),
1951 right: "⍵".to_string(),
1952 dyad_only: false,
1955 result: None,
1956 locals: Vec::new(),
1957 body: stmts,
1958 empty: None,
1960 labels: Vec::new(),
1961 pure,
1962 }));
1963 Ok((verb, operator))
1964}
1965
1966fn parse_dfn_body(
1967 body: &[Token],
1968 d: Rules,
1969 verbs: &mut HashMap<String, Verb>,
1970) -> Result<Vec<Expr>> {
1971 let mut stmts = Vec::new();
1972 for stmt in split_statements(body) {
1973 let stmt: Vec<Token> = stmt
1975 .iter()
1976 .map(|t| match t.kind {
1977 Tok::Del => Token { kind: Tok::Func(Verb::SelfRef), span: t.span },
1978 _ => t.clone(),
1979 })
1980 .collect();
1981 stmts.push(parse_guarded(stmt, d, verbs)?);
1982 }
1983 Ok(stmts)
1984}
1985
1986fn parse_guarded(
1988 stmt: Vec<Token>,
1989 d: Rules,
1990 verbs: &mut HashMap<String, Verb>,
1991) -> Result<Expr> {
1992 let mut depth = 0usize;
1993 let mut colon = None;
1994 for (i, t) in stmt.iter().enumerate() {
1995 match t.kind {
1996 Tok::LBrace | Tok::LParen | Tok::LBracket => depth += 1,
1997 Tok::RBrace | Tok::RParen | Tok::RBracket => depth = depth.saturating_sub(1),
1998 Tok::Colon if depth == 0 => {
1999 colon = Some(i);
2000 break;
2001 }
2002 _ => {}
2003 }
2004 }
2005 if let Some(k) = colon {
2006 let span = Span::merge(stmt[0].span, stmt[stmt.len() - 1].span);
2007 let test = one_statement(stmt[..k].to_vec(), d, verbs, stmt[k].span)?;
2008 let body = one_statement(stmt[k + 1..].to_vec(), d, verbs, stmt[k].span)?;
2009 let arm = Branch {
2011 test: Some(vec![test]),
2012 body: vec![body, Expr::Control(Box::new(Control::Return), span)],
2013 fall_through: false,
2014 };
2015 return Ok(Expr::Control(
2016 Box::new(Control::If { arms: vec![arm], otherwise: None }),
2017 span,
2018 ));
2019 }
2020 let default = matches!(
2024 (stmt.first().map(|t| &t.kind), stmt.get(1).map(|t| &t.kind)),
2025 (Some(Tok::Name(n)), Some(Tok::Assign)) if n == "⍺"
2026 );
2027 let span = stmt.first().map_or(Span::new(0, 0), |t| t.span);
2028 let e = one_statement(stmt, d, verbs, span)?;
2029 if default {
2030 let scope = match d.default_arg {
2031 DefaultArg::Eager => Scope::LocalDefault,
2032 DefaultArg::Lazy => return Err(Error::not_yet("a lazy ⍺← default", span)),
2033 };
2034 if let Expr::Assign { name, value, span, .. } = e {
2035 return Ok(Expr::Assign { name, value, scope, span });
2036 }
2037 }
2038 Ok(e)
2039}
2040
2041fn one_statement(
2042 stmt: Vec<Token>,
2043 d: Rules,
2044 verbs: &mut HashMap<String, Verb>,
2045 hint: Span,
2046) -> Result<Expr> {
2047 parse_statement(stmt, d, verbs, true)?
2048 .ok_or_else(|| Error::parse("this needs an expression", hint))
2049}
2050
2051fn is_pure_stmt(e: &Expr) -> bool {
2053 match e {
2054 Expr::Const(..) | Expr::Param(..) | Expr::Name(..) => true,
2055 Expr::Monad { verb, y, .. } => verb.is_pure() && is_pure_stmt(y),
2056 Expr::Dyad { verb, x, y, .. } => verb.is_pure() && is_pure_stmt(x) && is_pure_stmt(y),
2057 Expr::Assign { value, .. } => is_pure_stmt(value),
2058 Expr::Control(c, _) => is_pure_control(c),
2059 _ => false,
2060 }
2061}
2062
2063fn is_pure_control(c: &Control) -> bool {
2064 let all = |b: &Vec<Expr>| b.iter().all(is_pure_stmt);
2065 match c {
2066 Control::Return | Control::Break | Control::Continue => true,
2067 Control::Branch(target) => is_pure_stmt(target),
2068 Control::If { arms, otherwise } => {
2069 arms.iter().all(|a| a.test.as_ref().is_none_or(all) && all(&a.body))
2070 && otherwise.as_ref().is_none_or(all)
2071 }
2072 Control::While { test, body, .. } => all(test) && all(body),
2073 Control::For { source, body, .. } => is_pure_stmt(source) && all(body),
2074 Control::Select { subject, cases } => {
2075 is_pure_stmt(subject)
2076 && cases.iter().all(|c| c.test.as_ref().is_none_or(all) && all(&c.body))
2077 }
2078 Control::Try { body, catch } => all(body) && all(catch),
2079 }
2080}
2081
2082fn parse_tradfn(
2089 sentences: &[Vec<Token>],
2090 i: &mut usize,
2091 d: Rules,
2092 verbs: &mut HashMap<String, Verb>,
2093) -> Result<Expr> {
2094 let header = &sentences[*i];
2095 let open = header[0].span;
2096 *i += 1;
2097 let (name, def_left, def_right, result, locals) = parse_header(&header[1..], open)?;
2098 let mut body_lines: Vec<Vec<Token>> = Vec::new();
2099 loop {
2100 let Some(line) = sentences.get(*i) else {
2101 return Err(Error::parse("this definition has no closing ∇", open));
2102 };
2103 *i += 1;
2104 if line.len() == 1 && matches!(line[0].kind, Tok::Del) {
2105 break;
2106 }
2107 body_lines.push(line.clone());
2108 }
2109 let close = sentences
2110 .get(i.saturating_sub(1))
2111 .and_then(|l| l.first())
2112 .map_or(open, |t| t.span);
2113 let span = Span::merge(open, close);
2114 let mut inner = verbs.clone();
2116 inner.insert(name.clone(), Verb::Named(name.clone()));
2117 let mut items = Vec::new();
2118 let mut labels: Vec<(String, usize)> = Vec::new();
2119 for line in &body_lines {
2120 let mut label = None;
2121 let item = to_item(line.clone(), d, &mut inner, &mut label)?;
2122 if let Some(name) = label {
2123 labels.push((name, items.len()));
2124 }
2125 items.push(item);
2126 }
2127 let item_count = items.len();
2128 let mut cursor = AplCursor { items: &items, at: 0, d };
2129 let mut body = parse_apl_block(&mut cursor, &[])?;
2130 if !labels.is_empty() && body.len() != item_count {
2134 return Err(Error::not_yet("a label and a control structure in one definition", span));
2135 }
2136 if let Some(item) = cursor.peek() {
2137 return Err(Error::parse(
2138 format!(":{} has no matching opening word", item.word().unwrap_or("?")),
2139 item.span(),
2140 ));
2141 }
2142 let mut own: Vec<String> = locals.clone();
2145 own.extend(result.clone());
2146 own.extend(def_left.clone());
2147 own.push(def_right.clone());
2148 for stmt in &mut body {
2149 set_scopes(stmt, &own);
2150 }
2151 let pure = body.iter().all(is_pure_stmt);
2152 let verb = Verb::Explicit(Arc::new(ExplicitDef {
2153 name: format!("∇{name}"),
2154 left: def_left,
2155 right: def_right,
2156 dyad_only: false,
2157 result,
2158 locals,
2159 body,
2160 empty: None,
2161 labels,
2162 pure,
2163 }));
2164 verbs.insert(name.clone(), verb.clone());
2165 Ok(Expr::VerbDef { name, verb, span })
2166}
2167
2168type Header = (String, Option<String>, String, Option<String>, Vec<String>);
2169
2170fn parse_header(toks: &[Token], span: Span) -> Result<Header> {
2172 let mut names: Vec<String> = Vec::new();
2173 let mut locals: Vec<String> = Vec::new();
2174 let mut result = None;
2175 let mut in_locals = false;
2176 let mut k = 0usize;
2177 if let (Some(Tok::Name(z)), Some(Tok::Assign)) =
2179 (toks.first().map(|t| &t.kind), toks.get(1).map(|t| &t.kind))
2180 {
2181 result = Some(z.clone());
2182 k = 2;
2183 }
2184 while k < toks.len() {
2185 match &toks[k].kind {
2186 Tok::Semi => in_locals = true,
2187 Tok::Name(n) if in_locals => locals.push(n.clone()),
2188 Tok::Name(n) => names.push(n.clone()),
2189 _ => {
2190 return Err(Error::parse("this is not a ∇ definition header", toks[k].span));
2191 }
2192 }
2193 k += 1;
2194 }
2195 match names.len() {
2196 3 => Ok((names[1].clone(), Some(names[0].clone()), names[2].clone(), result, locals)),
2197 2 => Ok((names[0].clone(), None, names[1].clone(), result, locals)),
2198 1 => Ok((names[0].clone(), None, crate::ir::NILADIC.to_string(), result, locals)),
2199 _ => Err(Error::parse("a ∇ definition header names a function and its arguments", span)),
2200 }
2201}
2202
2203enum AplItem {
2206 Sentence(Expr),
2207 Word { word: &'static str, rest: Vec<Token>, span: Span },
2208}
2209
2210impl AplItem {
2211 fn word(&self) -> Option<&'static str> {
2212 match self {
2213 AplItem::Word { word, .. } => Some(word),
2214 AplItem::Sentence(_) => None,
2215 }
2216 }
2217
2218 fn span(&self) -> Span {
2219 match self {
2220 AplItem::Word { span, .. } => *span,
2221 AplItem::Sentence(e) => e.span(),
2222 }
2223 }
2224}
2225
2226fn to_item(
2227 line: Vec<Token>,
2228 d: Rules,
2229 verbs: &mut HashMap<String, Verb>,
2230 label: &mut Option<String>,
2231) -> Result<AplItem> {
2232 let mut line = line;
2233 if let (Some(Tok::Name(n)), Some(Tok::Colon)) =
2236 (line.first().map(|t| &t.kind), line.get(1).map(|t| &t.kind))
2237 {
2238 *label = Some(n.clone());
2239 line.drain(..2);
2240 }
2241 if let Some(Tok::Control(word)) = line.first().map(|t| &t.kind) {
2242 let word = *word;
2243 let span = line[0].span;
2244 return Ok(AplItem::Word { word, rest: line[1..].to_vec(), span });
2245 }
2246 if matches!(line.first().map(|t| &t.kind), Some(Tok::Arrow)) {
2247 let span = line[0].span;
2248 let target = parse_statement(line[1..].to_vec(), d, verbs, true)?
2249 .ok_or_else(|| Error::parse("→ needs a line to branch to", span))?;
2250 let span = Span::merge(span, target.span());
2251 return Ok(AplItem::Sentence(Expr::Control(
2252 Box::new(Control::Branch(Box::new(target))),
2253 span,
2254 )));
2255 }
2256 if line.is_empty() {
2260 let span = label.as_ref().map_or(Span::new(0, 0), |_| Span::new(0, 0));
2261 let nowhere = Expr::Const(Array::empty(crate::dtype::DType::I64), span);
2262 return Ok(AplItem::Sentence(Expr::Control(
2263 Box::new(Control::Branch(Box::new(nowhere))),
2264 span,
2265 )));
2266 }
2267 let span = line.first().map_or(Span::new(0, 0), |t| t.span);
2268 let e = parse_statement(line, d, verbs, true)?
2269 .ok_or_else(|| Error::parse("this line has no sentence", span))?;
2270 Ok(AplItem::Sentence(e))
2271}
2272
2273struct AplCursor<'a> {
2274 items: &'a [AplItem],
2275 at: usize,
2276 d: Rules,
2278}
2279
2280impl<'a> AplCursor<'a> {
2281 fn peek(&self) -> Option<&'a AplItem> {
2282 self.items.get(self.at)
2283 }
2284
2285 fn peek_word(&self) -> Option<&'static str> {
2286 self.peek().and_then(AplItem::word)
2287 }
2288
2289 fn last_span(&self) -> Span {
2290 self.items
2291 .get(self.at.saturating_sub(1))
2292 .map_or_else(|| Span::new(0, 0), AplItem::span)
2293 }
2294
2295 fn close(&mut self, want: &str) -> Result<()> {
2297 match self.peek_word() {
2298 Some(w) if w == want || w == "End" => {
2299 self.at += 1;
2300 Ok(())
2301 }
2302 Some(w) => Err(Error::parse(
2303 format!("expected :{want} here, not :{w}"),
2304 self.peek().expect("a word").span(),
2305 )),
2306 None => Err(Error::parse(format!("this block needs a :{want}"), self.last_span())),
2307 }
2308 }
2309}
2310
2311fn parse_apl_block(cur: &mut AplCursor<'_>, stop: &[&str]) -> Result<Vec<Expr>> {
2312 let mut out = Vec::new();
2313 loop {
2314 match cur.peek() {
2315 None => return Ok(out),
2316 Some(AplItem::Word { word, .. }) if stop.contains(word) || *word == "End" => {
2317 return Ok(out);
2318 }
2319 Some(AplItem::Sentence(e)) => {
2320 cur.at += 1;
2321 out.push(e.clone());
2322 }
2323 Some(AplItem::Word { .. }) => out.push(parse_apl_control(cur)?),
2324 }
2325 }
2326}
2327
2328fn parse_apl_control(cur: &mut AplCursor<'_>) -> Result<Expr> {
2329 let Some(AplItem::Word { word, rest, span }) = cur.peek() else {
2330 return Err(Error::internal("expected a control word"));
2331 };
2332 let (word, rest, start) = (*word, rest.clone(), *span);
2333 cur.at += 1;
2334 let control = match word {
2335 "If" => {
2336 let mut arms = Vec::new();
2337 let mut otherwise = None;
2338 let mut test = rest;
2339 loop {
2340 let test_expr = condition(test, start, cur.d)?;
2341 let body = parse_apl_block(cur, &["ElseIf", "Else", "EndIf"])?;
2342 arms.push(Branch { test: Some(vec![test_expr]), body, fall_through: false });
2343 match cur.peek_word() {
2344 Some("ElseIf") => {
2345 let Some(AplItem::Word { rest, .. }) = cur.peek() else { unreachable!() };
2346 test = rest.clone();
2347 cur.at += 1;
2348 }
2349 Some("Else") => {
2350 cur.at += 1;
2351 otherwise = Some(parse_apl_block(cur, &["EndIf"])?);
2352 cur.close("EndIf")?;
2353 break;
2354 }
2355 _ => {
2356 cur.close("EndIf")?;
2357 break;
2358 }
2359 }
2360 }
2361 Control::If { arms, otherwise }
2362 }
2363 "While" => {
2364 let test = condition(rest, start, cur.d)?;
2365 let body = parse_apl_block(cur, &["EndWhile"])?;
2366 cur.close("EndWhile")?;
2367 Control::While { test: vec![test], body, body_first: false, until: false }
2368 }
2369 "Repeat" => {
2370 if !rest.is_empty() {
2371 return Err(Error::parse(":Repeat takes no condition", start));
2372 }
2373 let body = parse_apl_block(cur, &["Until"])?;
2374 let Some(AplItem::Word { rest, span, .. }) = cur.peek() else {
2375 return Err(Error::parse("this :Repeat needs an :Until", cur.last_span()));
2376 };
2377 let test = condition(rest.clone(), *span, cur.d)?;
2378 cur.at += 1;
2379 Control::While { test: vec![test], body, body_first: true, until: true }
2380 }
2381 "For" => {
2382 let (name, source) = for_header(&rest, start, cur.d)?;
2384 let body = parse_apl_block(cur, &["EndFor"])?;
2385 cur.close("EndFor")?;
2386 Control::For { name: Some(name), source: Box::new(source), body }
2387 }
2388 "Select" => {
2389 let subject = condition(rest, start, cur.d)?;
2390 let mut cases = Vec::new();
2391 loop {
2392 match cur.peek() {
2393 Some(AplItem::Word { word: "Case", rest, span }) => {
2394 let test = condition(rest.clone(), *span, cur.d)?;
2395 cur.at += 1;
2396 let body = parse_apl_block(cur, &["Case", "Else", "EndSelect"])?;
2397 cases.push(Branch {
2398 test: Some(vec![test]),
2399 body,
2400 fall_through: false,
2401 });
2402 }
2403 Some(AplItem::Word { word: "Else", .. }) => {
2404 cur.at += 1;
2405 let body = parse_apl_block(cur, &["EndSelect"])?;
2406 cases.push(Branch { test: None, body, fall_through: false });
2407 cur.close("EndSelect")?;
2408 break;
2409 }
2410 _ => {
2411 cur.close("EndSelect")?;
2412 break;
2413 }
2414 }
2415 }
2416 Control::Select { subject: Box::new(subject), cases }
2417 }
2418 "Return" => Control::Return,
2419 "Leave" => Control::Break,
2420 "Continue" => Control::Continue,
2421 other => {
2422 return Err(Error::parse(format!(":{other} has no matching opening word"), start));
2423 }
2424 };
2425 Ok(Expr::Control(Box::new(control), Span::merge(start, cur.last_span())))
2426}
2427
2428fn condition(rest: Vec<Token>, span: Span, d: Rules) -> Result<Expr> {
2430 match rest.first() {
2431 None => Err(Error::parse("this control word needs a condition", span)),
2432 Some(first) => {
2433 let hint = Span::merge(first.span, rest[rest.len() - 1].span);
2434 match &rest[0].kind {
2435 Tok::Control(w) => Err(Error::parse(format!("unexpected :{w}"), rest[0].span)),
2436 _ => Ok(AplItem::Sentence(parse_prepared(&rest, hint, d)?)).map(|it| match it {
2437 AplItem::Sentence(e) => e,
2438 AplItem::Word { .. } => unreachable!(),
2439 }),
2440 }
2441 }
2442 }
2443}
2444
2445fn for_header(rest: &[Token], span: Span, d: Rules) -> Result<(String, Expr)> {
2447 let Some(Tok::Name(name)) = rest.first().map(|t| &t.kind) else {
2448 return Err(Error::parse(":For needs a name to bind", span));
2449 };
2450 let Some(k) = rest.iter().position(|t| matches!(t.kind, Tok::Control("In"))) else {
2451 return Err(Error::parse(":For needs an :In", span));
2452 };
2453 if k != 1 {
2454 return Err(Error::not_yet("several :For names", span));
2455 }
2456 let source = &rest[k + 1..];
2457 let Some(first) = source.first() else {
2458 return Err(Error::parse(":In needs a value", span));
2459 };
2460 let hint = Span::merge(first.span, source[source.len() - 1].span);
2461 Ok((name.clone(), parse_prepared(source, hint, d)?))
2462}
2463
2464fn parse_prepared(toks: &[Token], hint: Span, d: Rules) -> Result<Expr> {
2466 let toks = fold_paren_funcs(fold_axes(fold_operators(toks.to_vec(), d)?, d)?);
2467 if toks.is_empty() {
2468 return Err(Error::parse("this needs an expression", hint));
2469 }
2470 parse_range(&toks, 0, toks.len(), hint, d)
2471}
2472
2473fn indexed_assignment(toks: &[Token], d: Rules, hint: Span) -> Result<Option<Expr>> {
2476 let Some(assign) = toks.iter().position(|t| matches!(t.kind, Tok::Assign)) else {
2477 return Ok(None);
2478 };
2479 if assign < 3 || !matches!(toks[assign - 1].kind, Tok::RBracket) {
2480 return Ok(None);
2481 }
2482 let close = assign - 1;
2483 let open = match_lbracket(toks, 0, close)?;
2484 if open == 0 {
2485 return Err(Error::parse("[ needs a value on its left", toks[open].span));
2486 }
2487 let Tok::Name(name) = &toks[open - 1].kind else {
2488 return Err(Error::not_yet("indexed assignment through an expression", hint));
2489 };
2490 if open != 1 {
2491 return Err(Error::not_yet("indexed assignment inside a larger sentence", hint));
2492 }
2493 let ranges = index_slots(toks, open + 1, close, toks[open].span)?;
2494 let mut slots = Vec::with_capacity(ranges.len());
2495 for slot in &ranges {
2496 slots.push(match *slot {
2497 None => None,
2498 Some((lo, hi)) => Some(parse_range(toks, lo, hi, toks[open].span, d)?),
2499 });
2500 }
2501 let value = parse_range(toks, assign + 1, toks.len(), toks[assign].span, d)?;
2502 let span = Span::merge(toks[0].span, toks[toks.len() - 1].span);
2503 Ok(Some(Expr::AmendIndex {
2504 name: name.clone(),
2505 slots,
2506 value: Box::new(value),
2507 origin: d.origin,
2508 scope: Scope::Local,
2509 span,
2510 }))
2511}
2512
2513fn set_scopes(e: &mut Expr, own: &[String]) {
2517 let pick = |name: &str| {
2518 if own.iter().any(|n| n == name) {
2519 Scope::Local
2520 } else {
2521 Scope::Global
2522 }
2523 };
2524 match e {
2525 Expr::Assign { name, value, scope, .. } => {
2526 *scope = pick(name);
2527 set_scopes(value, own);
2528 }
2529 Expr::AmendIndex { name, slots, value, scope, .. } => {
2530 *scope = pick(name);
2531 for slot in slots.iter_mut().flatten() {
2532 set_scopes(slot, own);
2533 }
2534 set_scopes(value, own);
2535 }
2536 Expr::Monad { y, .. } => set_scopes(y, own),
2537 Expr::Dyad { x, y, .. } => {
2538 set_scopes(x, own);
2539 set_scopes(y, own);
2540 }
2541 Expr::PrintPass { value, .. } => set_scopes(value, own),
2542 Expr::Control(c, _) => {
2543 let walk = |b: &mut Vec<Expr>| b.iter_mut().for_each(|s| set_scopes(s, own));
2544 match &mut **c {
2545 Control::Branch(target) => set_scopes(target, own),
2546 Control::If { arms, otherwise } => {
2547 for arm in arms {
2548 if let Some(t) = &mut arm.test {
2549 walk(t);
2550 }
2551 walk(&mut arm.body);
2552 }
2553 if let Some(b) = otherwise {
2554 walk(b);
2555 }
2556 }
2557 Control::While { test, body, .. } => {
2558 walk(test);
2559 walk(body);
2560 }
2561 Control::For { source, body, .. } => {
2562 set_scopes(source, own);
2563 walk(body);
2564 }
2565 Control::Select { subject, cases } => {
2566 set_scopes(subject, own);
2567 for case in cases {
2568 if let Some(t) = &mut case.test {
2569 walk(t);
2570 }
2571 walk(&mut case.body);
2572 }
2573 }
2574 Control::Try { body, catch } => {
2575 walk(body);
2576 walk(catch);
2577 }
2578 Control::Return | Control::Break | Control::Continue => {}
2579 }
2580 }
2581 Expr::Const(..)
2582 | Expr::Param(..)
2583 | Expr::Name(..)
2584 | Expr::Fused { .. }
2585 | Expr::Elided { .. }
2586 | Expr::VerbDef { .. } => {}
2587 }
2588}
2589
2590#[cfg(test)]
2591mod tests {
2592 use super::*;
2593 use crate::error::ErrorKind;
2594 use rstest::rstest;
2595
2596 fn rules(origin: i64) -> Rules {
2598 crate::Dialect { index_origin: Some(origin), ..crate::Dialect::default() }
2599 .rules(crate::Lang::Apl)
2600 .expect("the shipped dialect is implemented")
2601 }
2602
2603 fn p(src: &str) -> Result<Vec<Expr>> {
2605 parse(&SourceParts::from_source(src).unwrap(), rules(1))
2606 }
2607
2608 fn one(src: &str) -> Expr {
2609 let mut stmts = p(src).unwrap_or_else(|e| panic!("{src}: {e}"));
2610 assert_eq!(stmts.len(), 1, "{src}: expected one sentence");
2611 stmts.pop().unwrap()
2612 }
2613
2614 fn err(src: &str) -> Error {
2615 match p(src) {
2616 Ok(_) => panic!("{src}: expected an error"),
2617 Err(e) => e,
2618 }
2619 }
2620
2621 fn as_const(e: &Expr) -> &Array {
2622 match e {
2623 Expr::Const(a, _) => a,
2624 other => panic!("expected a constant, got {other:?}"),
2625 }
2626 }
2627
2628 fn as_prim(v: &Verb) -> Prim {
2630 match v {
2631 Verb::Prim(p) => *p,
2632 other => panic!("expected a primitive, got {other:?}"),
2633 }
2634 }
2635
2636 fn monad_of<'a>(e: &'a Expr, name: &str) -> &'a Expr {
2637 match e {
2638 Expr::Monad { verb, y, .. } => {
2639 assert_eq!(as_prim(verb).name, name, "monad name");
2640 y.as_ref()
2641 }
2642 other => panic!("expected a monad, got {other:?}"),
2643 }
2644 }
2645
2646 fn dyad_of<'a>(e: &'a Expr, name: &str) -> (&'a Expr, &'a Expr) {
2647 match e {
2648 Expr::Dyad { verb, x, y, .. } => {
2649 assert_eq!(as_prim(verb).name, name, "dyad name");
2650 (x.as_ref(), y.as_ref())
2651 }
2652 other => panic!("expected a dyad, got {other:?}"),
2653 }
2654 }
2655
2656 fn verb_of(e: &Expr) -> &Verb {
2657 match e {
2658 Expr::Monad { verb, .. } | Expr::Dyad { verb, .. } => verb,
2659 other => panic!("expected an application, got {other:?}"),
2660 }
2661 }
2662
2663 #[test]
2666 fn single_number_is_a_scalar() {
2667 let e = one("5");
2668 let a = as_const(&e);
2669 assert_eq!(a.shape, Vec::<usize>::new());
2670 assert_eq!(a.data, Data::I64(vec![5].into()));
2671 }
2672
2673 #[test]
2674 fn adjacent_numbers_merge_into_one_vector() {
2675 let a = as_const(&one("2 3 4")).clone();
2676 assert_eq!(a.shape, vec![3]);
2677 assert_eq!(a.data, Data::I64(vec![2, 3, 4].into()));
2678 }
2679
2680 #[test]
2681 fn one_float_makes_the_whole_vector_float() {
2682 let a = as_const(&one("1 2.5 3")).clone();
2683 assert_eq!(a.shape, vec![3]);
2684 assert_eq!(a.data, Data::F64(vec![1.0, 2.5, 3.0].into()));
2685 }
2686
2687 #[rstest]
2688 #[case("¯3", Data::I64(vec![-3].into()))]
2689 #[case("¯3.5", Data::F64(vec![-3.5].into()))]
2690 #[case("1e3", Data::I64(vec![1000].into()))]
2691 #[case("1e¯3", Data::F64(vec![0.001].into()))]
2692 #[case("2.5e2", Data::F64(vec![250.0].into()))]
2693 #[case("¯1 ¯2", Data::I64(vec![-1, -2].into()))]
2694 fn numeric_literals(#[case] src: &str, #[case] want: Data) {
2695 assert_eq!(as_const(&one(src)).data, want);
2696 }
2697
2698 #[test]
2699 fn single_char_string_is_rank_zero() {
2700 let a = as_const(&one("'a'")).clone();
2701 assert_eq!(a.shape, Vec::<usize>::new());
2702 assert_eq!(a.data, Data::Char(vec!['a'].into()));
2703 }
2704
2705 #[test]
2706 fn string_escape_doubles_the_quote() {
2707 let a = as_const(&one("'don''t'")).clone();
2708 assert_eq!(a.shape, vec![5]);
2709 assert_eq!(a.data, Data::Char("don't".chars().collect()));
2710 }
2711
2712 #[test]
2713 fn empty_string_is_an_empty_char_vector() {
2714 let a = as_const(&one("''")).clone();
2715 assert_eq!(a.shape, vec![0]);
2716 assert_eq!(a.data, Data::Char(vec![].into()));
2717 }
2718
2719 #[test]
2720 fn unterminated_string_is_a_parse_error() {
2721 let e = err("'abc");
2722 assert_eq!(e.kind, ErrorKind::Parse);
2723 assert!(e.msg.contains("unterminated"), "{}", e.msg);
2724 }
2725
2726 #[rstest]
2727 #[case("2j3", vec![[2.0, 3.0]])]
2728 #[case("1J¯1", vec![[1.0, -1.0]])]
2729 #[case("2 1j2", vec![[2.0, 0.0], [1.0, 2.0]])]
2730 fn complex_literals(#[case] src: &str, #[case] want: Vec<[f64; 2]>) {
2731 assert_eq!(as_const(&one(src)).data, Data::Complex(want.into()));
2732 }
2733
2734 #[test]
2737 fn a_comment_runs_to_the_end_of_the_line() {
2738 let stmts = p("2+2 ⍝ a note ⋄ still a note\n3").unwrap();
2739 assert_eq!(stmts.len(), 2);
2740 dyad_of(&stmts[0], "+");
2741 assert_eq!(as_const(&stmts[1]).data, Data::I64(vec![3].into()));
2742 }
2743
2744 #[test]
2745 fn blank_sentences_are_skipped() {
2746 let stmts = p("\n\n2 ⋄ ⋄ 3 ⋄\n").unwrap();
2747 assert_eq!(stmts.len(), 2);
2748 }
2749
2750 #[test]
2751 fn diamond_and_newline_both_separate_sentences() {
2752 let stmts = p("x←3 ⋄ x+1").unwrap();
2753 assert_eq!(stmts.len(), 2);
2754 match &stmts[0] {
2755 Expr::Assign { name, value, .. } => {
2756 assert_eq!(name, "x");
2757 assert_eq!(as_const(value).data, Data::I64(vec![3].into()));
2758 }
2759 other => panic!("expected an assignment, got {other:?}"),
2760 }
2761 let (x, y) = dyad_of(&stmts[1], "+");
2762 assert!(matches!(x, Expr::Name(n, _) if n == "x"));
2763 assert_eq!(as_const(y).data, Data::I64(vec![1].into()));
2764 }
2765
2766 #[rstest]
2767 #[case("x")]
2768 #[case("abc123")]
2769 #[case("∆x")]
2770 #[case("⍙y_2")]
2771 #[case("Σ")]
2772 fn names(#[case] src: &str) {
2773 match one(src) {
2774 Expr::Name(n, _) => assert_eq!(n, src),
2775 other => panic!("expected a name, got {other:?}"),
2776 }
2777 }
2778
2779 #[test]
2780 fn unknown_symbol_is_reported_with_its_position() {
2781 let e = err("2 @ 3");
2782 assert_eq!(e.kind, ErrorKind::Parse);
2783 assert_eq!(e.msg, "unknown symbol: @");
2784 assert_eq!(e.span, Some(Span::new(2, 3)));
2785 }
2786
2787 #[test]
2788 fn system_variables_are_read_only() {
2789 let e = err("⎕IO←0");
2792 assert_eq!(e.kind, ErrorKind::Language);
2793 assert!(e.msg.contains("read-only"), "{}", e.msg);
2794 let e = err("⎕TS");
2797 assert_eq!(e.kind, ErrorKind::Language);
2798 assert!(e.msg.contains("closed by the sandbox"), "{}", e.msg);
2799 }
2800
2801 #[rstest]
2804 #[case('+', MonadOp::Scalar(ScalarMonad::Conj), DyadOp::Scalar(ScalarDyad::Add))]
2805 #[case('-', MonadOp::Scalar(ScalarMonad::Neg), DyadOp::Scalar(ScalarDyad::Sub))]
2806 #[case('×', MonadOp::Scalar(ScalarMonad::Signum), DyadOp::Scalar(ScalarDyad::Mul))]
2807 #[case('÷', MonadOp::Scalar(ScalarMonad::Recip), DyadOp::Scalar(ScalarDyad::DivApl))]
2808 #[case('⌈', MonadOp::Scalar(ScalarMonad::Ceil), DyadOp::Scalar(ScalarDyad::Max))]
2809 #[case('⌊', MonadOp::Scalar(ScalarMonad::Floor), DyadOp::Scalar(ScalarDyad::Min))]
2810 #[case('*', MonadOp::Scalar(ScalarMonad::Exp), DyadOp::Scalar(ScalarDyad::Pow))]
2811 #[case('|', MonadOp::Scalar(ScalarMonad::Abs), DyadOp::Scalar(ScalarDyad::Residue))]
2812 #[case('=', MonadOp::None, DyadOp::Scalar(ScalarDyad::Eq))]
2813 #[case('<', MonadOp::None, DyadOp::Scalar(ScalarDyad::Lt))]
2814 #[case('≤', MonadOp::None, DyadOp::Scalar(ScalarDyad::Le))]
2815 #[case('>', MonadOp::None, DyadOp::Scalar(ScalarDyad::Gt))]
2816 #[case('≥', MonadOp::None, DyadOp::Scalar(ScalarDyad::Ge))]
2817 #[case('⍴', MonadOp::ShapeOf, DyadOp::Reshape)]
2818 #[case('⍉', MonadOp::TransposeAxes, DyadOp::NotYet("dyadic transpose"))]
2819 #[case(',', MonadOp::Ravel, DyadOp::AppendLast)]
2820 #[case('⍪', MonadOp::TableOf, DyadOp::AppendLeading)]
2821 #[case('!', MonadOp::Scalar(ScalarMonad::Factorial), DyadOp::Scalar(ScalarDyad::Binomial))]
2822 #[case('⍕', MonadOp::Format, DyadOp::FormatSpec)]
2823 #[case('⊥', MonadOp::None, DyadOp::Decode)]
2824 #[case('⊤', MonadOp::None, DyadOp::Encode)]
2825 #[case('≢', MonadOp::Tally, DyadOp::NotMatch)]
2826 #[case('≡', MonadOp::Depth, DyadOp::Match)]
2827 #[case('∊', MonadOp::Enlist, DyadOp::MemberApl)]
2828 #[case('∪', MonadOp::Nub, DyadOp::Union)]
2829 #[case('∧', MonadOp::None, DyadOp::Scalar(ScalarDyad::Lcm))]
2830 #[case('∨', MonadOp::None, DyadOp::Scalar(ScalarDyad::Gcd))]
2831 #[case('⍟', MonadOp::Scalar(ScalarMonad::Ln), DyadOp::Scalar(ScalarDyad::Log))]
2832 #[case('~', MonadOp::Scalar(ScalarMonad::Not), DyadOp::Less)]
2833 #[case('⊖', MonadOp::Reverse, DyadOp::Rotate)]
2834 #[case('⍋', MonadOp::GradeUp { origin: 1 }, DyadOp::NotYet("dyadic grade (collation)"))]
2835 #[case('⍒', MonadOp::GradeDown { origin: 1 }, DyadOp::NotYet("dyadic grade (collation)"))]
2836 #[case('⊢', MonadOp::Same, DyadOp::Right)]
2837 #[case('⊣', MonadOp::Same, DyadOp::Left)]
2838 #[case('↑', MonadOp::First, DyadOp::Take)]
2839 #[case('⊂', MonadOp::Enclose(Enclose::ExceptSimpleScalar), DyadOp::PartitionEnclose)]
2840 #[case('⊃', MonadOp::Open, DyadOp::Pick { origin: 1 })]
2841 #[case('↓', MonadOp::Split, DyadOp::Drop)]
2842 fn primitive_meanings(#[case] glyph: char, #[case] monad: MonadOp, #[case] dyad: DyadOp) {
2843 let src = format!("{glyph}1");
2844 let e = one(&src);
2845 match e {
2846 Expr::Monad { verb, .. } => {
2847 let prim = as_prim(&verb);
2848 assert_eq!(prim.monad, monad);
2849 assert_eq!(prim.dyad, dyad);
2850 assert_eq!(prim.name.chars().next(), Some(glyph));
2851 }
2852 other => panic!("expected a monad, got {other:?}"),
2853 }
2854 }
2855
2856 #[test]
2857 fn monadic_not_equal_is_the_nub_sieve() {
2858 let e = one("≠1");
2859 match e {
2860 Expr::Monad { verb, .. } => {
2861 assert_eq!(as_prim(&verb).monad, MonadOp::NubSieve);
2862 }
2863 other => panic!("expected a monad, got {other:?}"),
2864 }
2865 }
2866
2867 #[test]
2868 fn monadic_equals_parses_and_is_left_to_evaluation() {
2869 let e = one("=1");
2871 assert_eq!(as_prim(verb_of(&e)).monad, MonadOp::None);
2872 }
2873
2874 #[rstest]
2875 #[case(0)]
2876 #[case(1)]
2877 fn iota_carries_the_index_origin(#[case] origin: i64) {
2878 let sp = SourceParts::from_source("⍳3").unwrap();
2879 let stmts = parse(&sp, rules(origin)).unwrap();
2880 match &stmts[0] {
2881 Expr::Monad { verb, .. } => {
2882 assert_eq!(as_prim(verb).monad, MonadOp::IotaApl { origin });
2883 assert_eq!(as_prim(verb).dyad, DyadOp::IndexOf { origin });
2884 assert_eq!(as_prim(verb).ranks, [RANK_INF, RANK_INF, RANK_INF]);
2885 }
2886 other => panic!("expected a monad, got {other:?}"),
2887 }
2888 }
2889
2890 #[test]
2891 fn reverse_and_rotate_pick_their_axis() {
2892 let e = one("⌽2 3⍴⍳6");
2894 match verb_of(&e) {
2895 Verb::Rank(f, ranks) => {
2896 assert_eq!(*ranks, [1, 0, 1]);
2897 assert_eq!(as_prim(f).monad, MonadOp::Reverse);
2898 assert_eq!(as_prim(f).dyad, DyadOp::Rotate);
2899 }
2900 other => panic!("expected a ranked verb, got {other:?}"),
2901 }
2902 assert!(matches!(verb_of(&one("⊖2 3⍴⍳6")), Verb::Prim(_)));
2904 }
2905
2906 #[test]
2907 fn reshape_ranks_are_infinite_one_infinite() {
2908 let e = one("2 3⍴⍳6");
2909 assert_eq!(verb_of(&e).ranks(), [RANK_INF, 1, RANK_INF]);
2910 }
2911
2912 #[test]
2915 fn reshape_of_iota() {
2916 let e = one("2 3⍴⍳6");
2917 let (x, y) = dyad_of(&e, "⍴");
2918 assert_eq!(as_const(x).data, Data::I64(vec![2, 3].into()));
2919 let iy = monad_of(y, "⍳");
2920 assert_eq!(as_const(iy).data, Data::I64(vec![6].into()));
2921 }
2922
2923 #[test]
2924 fn leading_minus_is_monadic_and_the_rest_is_evaluated_first() {
2925 let e = one("-3+4");
2927 let inner = monad_of(&e, "-");
2928 let (x, y) = dyad_of(inner, "+");
2929 assert_eq!(as_const(x).data, Data::I64(vec![3].into()));
2930 assert_eq!(as_const(y).data, Data::I64(vec![4].into()));
2931 }
2932
2933 #[test]
2934 fn a_chain_of_dyads_associates_to_the_right() {
2935 let e = one("2×3+4");
2936 let (x, y) = dyad_of(&e, "×");
2937 assert_eq!(as_const(x).data, Data::I64(vec![2].into()));
2938 dyad_of(y, "+");
2939 }
2940
2941 #[test]
2942 fn parentheses_override_the_order() {
2943 let e = one("(2+3)×4");
2944 let (x, y) = dyad_of(&e, "×");
2945 dyad_of(x, "+");
2946 assert_eq!(as_const(y).data, Data::I64(vec![4].into()));
2947 }
2948
2949 #[test]
2950 fn nested_parentheses() {
2951 let e = one("((2+3))×4");
2952 let (x, _) = dyad_of(&e, "×");
2953 dyad_of(x, "+");
2954 }
2955
2956 #[test]
2957 fn a_function_left_of_a_function_is_monadic() {
2958 let e = one("⍴⍳5");
2960 monad_of(monad_of(&e, "⍴"), "⍳");
2961 }
2962
2963 #[test]
2966 fn slash_reduces_the_last_axis() {
2967 let e = one("+/2 3⍴⍳6");
2969 match &e {
2970 Expr::Monad { verb: Verb::Rank(inner, ranks), .. } => {
2971 assert_eq!(*ranks, [1, 1, 1]);
2972 match inner.as_ref() {
2973 Verb::Reduce(f) => assert_eq!(as_prim(f).name, "+"),
2974 other => panic!("expected a reduce, got {other:?}"),
2975 }
2976 }
2977 other => panic!("expected monadic Rank(Reduce(+)), got {other:?}"),
2978 }
2979 }
2980
2981 #[test]
2982 fn slashbar_reduces_the_leading_axis() {
2983 let e = one("+⌿2 3⍴⍳6");
2984 match &e {
2985 Expr::Monad { verb: Verb::Reduce(f), .. } => assert_eq!(as_prim(f).name, "+"),
2986 other => panic!("expected monadic Reduce(+), got {other:?}"),
2987 }
2988 }
2989
2990 #[test]
2991 fn backslash_scans_the_last_axis_and_backslashbar_the_leading_one() {
2992 let inner = |v: &Verb| match v {
2995 Verb::Windowed(g, WindowKind::Scan) => match &**g {
2996 Verb::Reduce(h) => as_prim(h).name,
2997 other => panic!("expected a reduction under the scan, got {other:?}"),
2998 },
2999 other => panic!("expected a scan, got {other:?}"),
3000 };
3001 match &one("+\\1 2 3") {
3002 Expr::Monad { verb: Verb::Rank(f, ranks), .. } => {
3003 assert_eq!(*ranks, [1, 1, 1]);
3004 assert_eq!(inner(f), "+");
3005 }
3006 other => panic!("expected a ranked scan, got {other:?}"),
3007 }
3008 match &one("+⍀1 2 3") {
3009 Expr::Monad { verb, .. } => assert_eq!(inner(verb), "+"),
3010 other => panic!("expected a leading-axis scan, got {other:?}"),
3011 }
3012 }
3013
3014 #[rstest]
3017 #[case("1 0 1/1 2 3", "/")]
3018 #[case("1 0 1⌿1 2 3", "⌿")]
3019 #[case("x/1 2 3", "/")]
3020 #[case("(1 0)/1 2 3", "/")]
3021 fn slash_after_an_operand_is_replicate(#[case] src: &str, #[case] name: &str) {
3022 let e = one(src);
3023 let (_, _) = dyad_of(&e, name);
3024 assert_eq!(as_prim(verb_of(&e)).dyad, DyadOp::Copy);
3025 }
3026
3027 #[rstest]
3028 #[case("1 0 1\\1 2 3")]
3029 #[case("1 0 1⍀1 2 3")]
3030 fn expand_after_a_value_is_a_function(#[case] src: &str) {
3031 let e = one(src);
3032 assert_eq!(as_prim(verb_of(&e)).dyad, DyadOp::Expand);
3033 }
3034
3035 #[test]
3036 fn commute_and_power_are_operators() {
3037 match one("2-⍨5") {
3038 Expr::Dyad { verb: Verb::Commute(f), .. } => assert_eq!(as_prim(&f).name, "-"),
3039 other => panic!("expected a commute, got {other:?}"),
3040 }
3041 match one("+⍣3⊢5") {
3042 Expr::Monad { verb: Verb::PowerN(_, p), .. } => assert_eq!(p, Power::Times(3)),
3043 other => panic!("expected a power, got {other:?}"),
3044 }
3045 match one("+⍣≡⊢5") {
3046 Expr::Monad { verb: Verb::PowerUntil(..), .. } => {}
3047 other => panic!("expected a power until, got {other:?}"),
3048 }
3049 let e = err("+⍣¯1⊢5");
3050 assert_eq!(e.kind, ErrorKind::NotYet);
3051 assert!(e.msg.contains("inverse power"), "{}", e.msg);
3052 }
3053
3054 #[rstest]
3055 #[case("+⍤2⊢5", [2, 2, 2])]
3056 #[case("+⍤1 2⊢5", [2, 1, 2])]
3057 #[case("+⍤0 1 2⊢5", [0, 1, 2])]
3058 #[case("+⍤¯1⊢5", [-1, -1, -1])]
3059 fn rank_operator_spec(#[case] src: &str, #[case] want: [i64; 3]) {
3060 let e = one(src);
3061 match &e {
3062 Expr::Monad { verb: Verb::Rank(f, ranks), .. } => {
3063 assert_eq!(*ranks, want);
3064 assert_eq!(as_prim(f).name, "+");
3065 }
3066 other => panic!("expected monadic Rank(+), got {other:?}"),
3067 }
3068 }
3069
3070 #[test]
3071 fn rank_operator_stacks_on_a_derived_function() {
3072 let e = one("+/⍤1⊢5");
3073 match &e {
3074 Expr::Monad { verb: Verb::Rank(inner, ranks), .. } => {
3075 assert_eq!(*ranks, [1, 1, 1]);
3076 assert!(matches!(inner.as_ref(), Verb::Rank(_, [1, 1, 1])));
3077 }
3078 other => panic!("expected Rank(Rank(Reduce(+))), got {other:?}"),
3079 }
3080 }
3081
3082 #[test]
3083 fn a_function_operand_makes_the_rank_operator_an_atop() {
3084 let e = one("+⍤×5");
3086 let Expr::Monad { verb, .. } = e else { panic!("expected a monad") };
3087 assert!(matches!(verb, Verb::Atop(..)), "{verb:?}");
3088 }
3089
3090 #[rstest]
3091 #[case("+⍤0 1 2 3⊢5", "1 to 3")]
3092 #[case("+⍤", "rank specification")]
3093 #[case("+⍤2.5⊢5", "must be integers")]
3094 #[case("+⍤'a'⊢5", "must be integers")]
3095 fn bad_rank_specifications(#[case] src: &str, #[case] fragment: &str) {
3096 let e = err(src);
3097 assert_eq!(e.kind, ErrorKind::Parse);
3098 assert!(e.msg.contains(fragment), "{}", e.msg);
3099 }
3100
3101 #[test]
3104 fn quad_arrow_is_print_pass() {
3105 let e = one("⎕←2+2");
3106 match &e {
3107 Expr::PrintPass { value, .. } => {
3108 dyad_of(value, "+");
3109 }
3110 other => panic!("expected PrintPass, got {other:?}"),
3111 }
3112 }
3113
3114 #[test]
3115 fn assignment_chains() {
3116 let e = one("a←b←5");
3117 match &e {
3118 Expr::Assign { name, value, .. } => {
3119 assert_eq!(name, "a");
3120 match value.as_ref() {
3121 Expr::Assign { name, value, .. } => {
3122 assert_eq!(name, "b");
3123 assert_eq!(as_const(value).data, Data::I64(vec![5].into()));
3124 }
3125 other => panic!("expected a nested assignment, got {other:?}"),
3126 }
3127 }
3128 other => panic!("expected an assignment, got {other:?}"),
3129 }
3130 }
3131
3132 #[test]
3133 fn assignment_inside_an_expression() {
3134 let e = one("2+a←3");
3135 let (x, y) = dyad_of(&e, "+");
3136 assert_eq!(as_const(x).data, Data::I64(vec![2].into()));
3137 match y {
3138 Expr::Assign { name, value, .. } => {
3139 assert_eq!(name, "a");
3140 assert_eq!(as_const(value).data, Data::I64(vec![3].into()));
3141 }
3142 other => panic!("expected an assignment, got {other:?}"),
3143 }
3144 }
3145
3146 #[rstest]
3147 #[case("2←3")]
3148 #[case("(2+2)←3")]
3149 fn assignment_target_must_be_a_name(#[case] src: &str) {
3150 let e = err(src);
3151 assert_eq!(e.kind, ErrorKind::Parse);
3152 assert_eq!(e.msg, "assignment target must be a name");
3153 }
3154
3155 #[test]
3158 fn a_parameter_hole_is_an_operand() {
3159 let sp = SourceParts::from_parts(&["", "+1"], &["x"]);
3160 let stmts = parse(&sp, rules(1)).unwrap();
3161 let (x, y) = dyad_of(&stmts[0], "+");
3162 assert!(matches!(x, Expr::Param(0, _)));
3163 assert_eq!(as_const(y).data, Data::I64(vec![1].into()));
3164 assert_eq!(x.span(), Span::new(0, 3));
3166 assert_eq!(sp.display, "{x}+1");
3167 }
3168
3169 #[test]
3170 fn a_parameter_can_be_reduced_over() {
3171 let sp = SourceParts::from_parts(&["+/", ""], &["m"]);
3172 let stmts = parse(&sp, rules(1)).unwrap();
3173 match &stmts[0] {
3174 Expr::Monad { verb: Verb::Rank(_, [1, 1, 1]), y, .. } => {
3175 assert!(matches!(y.as_ref(), Expr::Param(0, _)));
3176 }
3177 other => panic!("expected a reduction over a parameter, got {other:?}"),
3178 }
3179 }
3180
3181 #[test]
3182 fn a_parameter_inside_a_comment_is_dropped() {
3183 let sp = SourceParts::from_parts(&["1 ⍝ ", "\n2"], &["x"]);
3184 let stmts = parse(&sp, rules(1)).unwrap();
3185 assert_eq!(stmts.len(), 2);
3186 assert_eq!(as_const(&stmts[0]).data, Data::I64(vec![1].into()));
3187 assert_eq!(as_const(&stmts[1]).data, Data::I64(vec![2].into()));
3188 }
3189
3190 #[test]
3193 fn nodes_cover_their_source_extent() {
3194 let src = "2 3⍴⍳6";
3195 let e = one(src);
3196 assert_eq!(e.span(), Span::new(0, src.len()));
3197 let (x, y) = dyad_of(&e, "⍴");
3198 assert_eq!(x.span(), Span::new(0, 3));
3199 assert_eq!(y.span(), Span::new(6, src.len()));
3201 }
3202
3203 #[test]
3204 fn spans_of_a_later_sentence_are_absolute() {
3205 let src = "x←3 ⋄ x+1";
3206 let stmts = p(src).unwrap();
3207 assert_eq!(&src[10..], "x+1");
3209 assert_eq!(stmts[1].span(), Span::new(10, src.len()));
3210 }
3211
3212 #[test]
3213 fn a_dyad_span_includes_the_parenthesised_left_argument() {
3214 let src = "(2+3)×4";
3215 let e = one(src);
3216 assert_eq!(e.span(), Span::new(0, src.len()));
3217 }
3218
3219 #[rstest]
3224 #[case("(2 3)(4 5)", 2)]
3225 #[case("2 x", 2)]
3226 #[case("x y", 2)]
3227 #[case("2(3)", 2)]
3228 #[case("1 2 (3 4)", 3)]
3229 #[case("'ab' 'cd' 'ef'", 3)]
3230 fn juxtaposition_is_vector_notation(#[case] src: &str, #[case] items: usize) {
3231 let mut e = &one(src);
3234 for _ in 0..items - 1 {
3235 match e {
3236 Expr::Dyad { verb, y, .. } => {
3237 assert_eq!(verb.name(), "(vector notation)", "{src}");
3238 e = y.as_ref();
3239 }
3240 other => panic!("{src}: expected a strand, got {other:?}"),
3241 }
3242 }
3243 assert!(matches!(e, Expr::Monad { .. }), "{src}: {e:?}");
3244 }
3245
3246 #[rstest]
3247 #[case("2+", "missing right argument")]
3248 #[case("x←", "← needs a value")]
3249 #[case("(2+3", "syntax error")]
3250 #[case("2+3)", "unmatched )")]
3251 #[case("()", "empty parentheses")]
3252 #[case("⎕", "⎕ is only supported")]
3253 #[case("/2 3", "needs a function to its left")]
3254 fn syntax_errors(#[case] src: &str, #[case] fragment: &str) {
3255 let e = err(src);
3256 assert_eq!(e.kind, ErrorKind::Parse);
3257 assert!(e.msg.contains(fragment), "{src}: {}", e.msg);
3258 }
3259
3260 #[test]
3261 fn empty_source_has_no_statements() {
3262 assert!(p("").unwrap().is_empty());
3263 assert!(p(" ⍝ nothing here\n").unwrap().is_empty());
3264 }
3265
3266 #[rstest]
3268 #[case("2+2")]
3269 #[case("¯2×3")]
3270 #[case("-3+4")]
3271 #[case("0÷0")]
3272 #[case("⍳4")]
3273 #[case("⍳0")]
3274 #[case("2 3⍴⍳6")]
3275 #[case("⍴2 3⍴⍳6")]
3276 #[case("⍉2 3⍴⍳6")]
3277 #[case("≢7 8 9")]
3278 #[case("2↑9 8 7")]
3279 #[case("¯2↑9 8 7")]
3280 #[case("1↓3 3⍴⍳9")]
3281 #[case(",2 2⍴⍳4")]
3282 #[case("x←3 ⋄ x+1")]
3283 #[case("2+a←3")]
3284 #[case("⎕←2+2")]
3285 #[case("(2 3⍴⍳6)+10 20")]
3286 #[case("2+3 ⍝ sum")]
3287 #[case("+/2 3⍴⍳6")]
3288 #[case("+⌿2 3⍴⍳6")]
3289 #[case("⎕←'Hello, world!'")]
3290 fn the_evaluation_corpus_parses(#[case] src: &str) {
3291 p(src).unwrap_or_else(|e| panic!("{src}: {e}"));
3292 }
3293
3294 #[test]
3295 fn errors_render_against_the_display_source() {
3296 let src = "2 3⍴⍳6\n2 @ 3";
3297 let e = err(src);
3298 let rendered = e.render(src);
3299 assert!(rendered.contains("unknown symbol: @"), "{rendered}");
3300 assert!(rendered.contains("2 @ 3"), "{rendered}");
3301 }
3302}