1use std::collections::HashMap;
4use std::sync::Arc;
5
6use crate::array::{Array, Data};
7use crate::error::{Error, ErrorKind, Result, Span};
8use crate::fmt::{format_array, FmtOpts};
9use crate::frontend::Rules;
10use crate::fuse::FusedKernel;
11use crate::verb::{arrays_match, Agreement, Ctx, Env, EvalCfg, Verb};
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub enum Scope {
17 Local,
20 Global,
22 LocalDefault,
25}
26
27#[derive(Clone, Debug)]
28pub enum Expr {
29 Const(Array, Span),
30 Param(usize, Span),
32 Name(String, Span),
34 Assign { name: String, value: Box<Expr>, scope: Scope, span: Span },
37 AmendIndex {
41 name: String,
42 slots: Vec<Option<Expr>>,
43 value: Box<Expr>,
44 origin: i64,
45 scope: Scope,
46 span: Span,
47 },
48 Control(Box<Control>, Span),
53 Monad { verb: Verb, y: Box<Expr>, span: Span },
54 Dyad { verb: Verb, x: Box<Expr>, y: Box<Expr>, span: Span },
55 PrintPass { value: Box<Expr>, bare: bool, span: Span },
59 Input { eval: bool, span: Span },
63 Fused { kernel: FusedKernel, inputs: Vec<Expr>, orig: Box<Expr>, span: Span },
67 Elided { orig: Vec<Expr>, span: Span },
72 VerbDef { name: String, verb: Verb, span: Span },
77 ModDef { name: String, spelling: String, conjunction: bool, span: Span },
82}
83
84#[derive(Clone, Debug)]
87pub enum Control {
88 If { arms: Vec<Branch>, otherwise: Option<Vec<Expr>> },
91 While { test: Vec<Expr>, body: Vec<Expr>, body_first: bool, until: bool },
94 For { name: Option<String>, source: Box<Expr>, body: Vec<Expr> },
97 Select { subject: Box<Expr>, cases: Vec<Branch> },
100 Try { body: Vec<Expr>, catch: Vec<Expr> },
103 Return,
105 Break,
107 Branch(Box<Expr>),
111 Continue,
113}
114
115#[derive(Clone, Debug)]
118pub struct Branch {
119 pub test: Option<Vec<Expr>>,
120 pub body: Vec<Expr>,
121 pub fall_through: bool,
123}
124
125pub const NILADIC: &str = "(no argument)";
129
130#[derive(Debug)]
133pub struct ExplicitDef {
134 pub name: String,
136 pub left: Option<String>,
139 pub right: String,
140 pub dyad_only: bool,
145 pub result: Option<String>,
148 pub locals: Vec<String>,
150 pub body: Vec<Expr>,
151 pub empty: Option<Array>,
153 pub labels: Vec<(String, usize)>,
157 pub pure: bool,
159}
160
161impl Expr {
162 pub(crate) fn depth(&self) -> usize {
166 let mut deepest = 0usize;
167 let mut stack: Vec<(&Expr, usize)> = vec![(self, 1)];
168 while let Some((e, d)) = stack.pop() {
169 deepest = deepest.max(d);
170 let kids: Vec<&Expr> = match e {
171 Expr::Const(..)
172 | Expr::Param(..)
173 | Expr::Name(..)
174 | Expr::Control(..)
175 | Expr::VerbDef { .. }
176 | Expr::Input { .. }
177 | Expr::ModDef { .. } => Vec::new(),
178 Expr::Assign { value, .. } | Expr::PrintPass { value, .. } => vec![value],
179 Expr::AmendIndex { slots, value, .. } => {
180 slots.iter().flatten().chain(std::iter::once(&**value)).collect()
181 }
182 Expr::Monad { y, .. } => vec![y],
183 Expr::Dyad { x, y, .. } => vec![x, y],
184 Expr::Fused { inputs, orig, .. } => {
185 inputs.iter().chain(std::iter::once(&**orig)).collect()
186 }
187 Expr::Elided { orig, .. } => orig.iter().collect(),
188 };
189 stack.extend(kids.into_iter().map(|c| (c, d + 1)));
190 }
191 deepest
192 }
193
194 pub fn span(&self) -> Span {
195 match self {
196 Expr::Const(_, s) | Expr::Param(_, s) | Expr::Name(_, s) => *s,
197 Expr::Control(_, s) => *s,
198 Expr::AmendIndex { span, .. } | Expr::Input { span, .. } => *span,
199 Expr::Assign { span, .. }
200 | Expr::Monad { span, .. }
201 | Expr::Dyad { span, .. }
202 | Expr::PrintPass { span, .. }
203 | Expr::Fused { span, .. }
204 | Expr::Elided { span, .. }
205 | Expr::VerbDef { span, .. }
206 | Expr::ModDef { span, .. } => *span,
207 }
208 }
209
210 pub fn set_span(&mut self, to: Span) {
214 match self {
215 Expr::Const(_, s) | Expr::Param(_, s) | Expr::Name(_, s) => *s = to,
216 Expr::Control(_, s) => *s = to,
217 Expr::AmendIndex { span, .. } | Expr::Input { span, .. } => *span = to,
218 Expr::Assign { span, .. }
219 | Expr::Monad { span, .. }
220 | Expr::Dyad { span, .. }
221 | Expr::PrintPass { span, .. }
222 | Expr::Fused { span, .. }
223 | Expr::Elided { span, .. }
224 | Expr::VerbDef { span, .. }
225 | Expr::ModDef { span, .. } => *span = to,
226 }
227 }
228
229 fn is_silent(&self) -> bool {
233 matches!(
234 self,
235 Expr::Assign { .. }
236 | Expr::AmendIndex { .. }
237 | Expr::PrintPass { .. }
238 | Expr::Elided { .. }
239 | Expr::VerbDef { .. }
240 | Expr::ModDef { .. }
241 )
242 }
243}
244
245#[derive(Clone, Debug)]
246pub struct ParamSpec {
247 pub name: String,
248}
249
250#[derive(Clone, Debug)]
252pub struct Program {
253 pub stmts: Vec<Expr>,
254 pub params: Vec<ParamSpec>,
255 pub display_src: String,
258 pub agreement: Agreement,
259 pub fmt: FmtOpts,
260 pub rules: Rules,
262}
263
264#[derive(Clone, Debug)]
266pub(crate) struct Note {
267 pub shape: Vec<usize>,
268 pub dtype: crate::dtype::DType,
269 pub layout: crate::array::Layout,
272 pub kernel_ran: Option<bool>,
275 pub decline: Option<crate::fuse::Decline>,
276 pub placement: crate::device::Placement,
279}
280
281pub(crate) type Trace = HashMap<usize, Note>;
285
286pub(crate) fn key(e: &Expr) -> usize {
287 std::ptr::from_ref(e) as usize
288}
289
290impl Program {
291 pub fn run(&self, args: &[Array], out: &mut dyn FnMut(&str)) -> Result<Option<Array>> {
298 self.exec(args, out, None, &mut None, None)
299 }
300
301 pub fn run_io(
305 &self,
306 args: &[Array],
307 out: &mut dyn FnMut(&str),
308 inp: &mut dyn FnMut() -> Option<String>,
309 ) -> Result<Option<Array>> {
310 self.exec(args, out, Some(inp), &mut None, None)
311 }
312
313 pub fn run_on_io(
315 &self,
316 device: &crate::device::Device,
317 args: &[Array],
318 out: &mut dyn FnMut(&str),
319 inp: &mut dyn FnMut() -> Option<String>,
320 ) -> Result<Option<Array>> {
321 self.exec(args, out, Some(inp), &mut None, Some(device))
322 }
323
324 pub fn run_on(
330 &self,
331 device: &crate::device::Device,
332 args: &[Array],
333 out: &mut dyn FnMut(&str),
334 ) -> Result<Option<Array>> {
335 self.exec(args, out, None, &mut None, Some(device))
336 }
337
338 pub(crate) fn trace(
342 &self,
343 args: &[Array],
344 out: &mut dyn FnMut(&str),
345 device: Option<&crate::device::Device>,
346 ) -> (Result<Option<Array>>, Trace) {
347 let mut rec = Some(Trace::new());
348 let r = self.exec(args, out, None, &mut rec, device);
349 (r, rec.expect("the recorder stays in place"))
350 }
351
352 fn exec(
353 &self,
354 args: &[Array],
355 out: &mut dyn FnMut(&str),
356 inp: crate::verb::InputFn<'_>,
357 rec: &mut Option<Trace>,
358 device: Option<&crate::device::Device>,
359 ) -> Result<Option<Array>> {
360 if args.len() != self.params.len() {
361 let names: Vec<&str> = self.params.iter().map(|p| p.name.as_str()).collect();
362 let wanted = if names.is_empty() {
363 "no arguments".to_string()
364 } else {
365 format!("one value for each of {}", names.join(", "))
366 };
367 return Err(Error::new(
368 ErrorKind::Value,
369 format!("this program takes {wanted}, and was given {}", args.len()),
370 None,
371 ));
372 }
373 let cfg = EvalCfg {
374 agreement: self.agreement,
375 fmt: self.fmt,
376 tol: self.rules.tol(),
377 rules: self.rules,
378 };
379 let mut env = Env::new(args.to_vec());
380 let mut inp = inp;
381 let inp = crate::verb::reborrow_input(&mut inp);
382 let mut ctx = Ctx { cfg, out, inp, env: &mut env, device };
383 let mut last = None;
384 for stmt in &self.stmts {
385 let (v, flow) = eval_stmt(stmt, &mut ctx, rec)?;
388 if flow != Flow::Normal {
389 return Err(Error::internal("a control signal escaped to the top level"));
390 }
391 last = if stmt.is_silent() { None } else { v };
392 }
393 Ok(last)
394 }
395
396 pub fn render_error(&self, e: &Error) -> String {
397 e.render(&self.display_src)
398 }
399
400 pub fn explain(&self, args: Option<&[Array]>) -> String {
410 crate::explain::explain(self, args, None)
411 }
412
413 pub fn explain_on(
417 &self,
418 device: &crate::device::Device,
419 args: Option<&[Array]>,
420 ) -> String {
421 crate::explain::explain(self, args, Some(device))
422 }
423}
424
425#[derive(Clone, Copy, Debug, PartialEq, Eq)]
427pub(crate) enum Flow {
428 Normal,
429 Return,
430 Break,
431 Continue,
432 Goto(usize),
434}
435
436pub(crate) fn run_block(
440 stmts: &[Expr],
441 last: Option<Array>,
442 ctx: &mut Ctx<'_>,
443 rec: &mut Option<Trace>,
444) -> Result<(Option<Array>, Flow)> {
445 let mut last = last;
446 for stmt in stmts {
447 let (v, flow) = eval_stmt(stmt, ctx, rec)?;
448 if let Some(v) = v {
451 last = Some(v);
452 }
453 if flow != Flow::Normal {
454 return Ok((last, flow));
455 }
456 }
457 Ok((last, Flow::Normal))
458}
459
460fn eval_stmt(
463 e: &Expr,
464 ctx: &mut Ctx<'_>,
465 rec: &mut Option<Trace>,
466) -> Result<(Option<Array>, Flow)> {
467 let Expr::Control(c, span) = e else {
468 return Ok((Some(eval(e, ctx, rec)?), Flow::Normal));
469 };
470 let (v, flow) = eval_control(c, *span, ctx, rec)?;
471 let v = match (v, flow) {
476 (Some(v), _) => Some(v),
477 (None, Flow::Normal) => ctx.env.current_def().and_then(|d| d.empty.clone()),
478 (None, _) => None,
479 };
480 if let (Some(t), Some(v)) = (rec.as_mut(), v.as_ref()) {
481 t.insert(
482 key(e),
483 Note {
484 shape: v.shape.clone(),
485 dtype: v.dtype(),
486 layout: v.layout(),
487 kernel_ran: None,
488 decline: None,
489 placement: crate::device::Placement::Default,
490 },
491 );
492 }
493 Ok((v, flow))
494}
495
496pub(crate) fn empty_result() -> Array {
498 Array::new(vec![0, 0], Data::I64(Vec::new().into()))
499}
500
501fn is_true(a: &Array, span: Span) -> Result<bool> {
504 if a.count() == 0 {
505 return Ok(true);
506 }
507 match &a.data {
508 Data::I64(v) => Ok(v.as_slice()[0] != 0),
509 Data::F64(v) => Ok(v.as_slice()[0] != 0.0),
510 Data::Bool(v) => Ok(v.as_slice()[0] != 0),
511 Data::Char(v) => Ok(v.as_slice()[0] as u32 != 0),
512 Data::Complex(v) => Ok(v.as_slice()[0] != crate::complex::ZERO),
513 Data::Ext(v) => Ok(v.as_slice()[0] != crate::exact::Ext::default()),
514 Data::Rat(v) => Ok(!v.as_slice()[0].is_zero()),
515 Data::Box(_) => Err(Error::domain("a condition must be numeric, not boxed", span)),
516 }
517}
518
519fn eval_control(
520 c: &Control,
521 span: Span,
522 ctx: &mut Ctx<'_>,
523 rec: &mut Option<Trace>,
524) -> Result<(Option<Array>, Flow)> {
525 match c {
526 Control::Return => Ok((None, Flow::Return)),
527 Control::Branch(target) => {
530 let to = eval(target, ctx, rec)?;
531 if to.count() == 0 {
532 return Ok((None, Flow::Normal));
533 }
534 let line = to
535 .to_i64_vec()
536 .and_then(|v| v.first().copied())
537 .ok_or_else(|| Error::domain("a branch target is a line number", span))?;
538 let lines = ctx.env.current_def().map_or(0, |d| d.body.len() as i64);
539 if line >= 1 && line <= lines {
540 return Ok((None, Flow::Goto(line as usize - 1)));
541 }
542 Ok((None, Flow::Return))
543 }
544 Control::Break => Ok((None, Flow::Break)),
545 Control::Continue => Ok((None, Flow::Continue)),
546 Control::If { arms, otherwise } => {
547 for arm in arms {
548 let test = arm.test.as_deref().unwrap_or(&[]);
549 let (t, flow) = run_block(test, None, ctx, rec)?;
550 if flow != Flow::Normal {
551 return Ok((t, flow));
552 }
553 let taken = match &t {
554 Some(v) => is_true(v, span)?,
555 None => true,
556 };
557 if taken {
558 return run_block(&arm.body, None, ctx, rec);
559 }
560 }
561 match otherwise {
562 Some(body) => run_block(body, None, ctx, rec),
563 None => Ok((None, Flow::Normal)),
564 }
565 }
566 Control::While { test, body, body_first, until } => {
567 let mut last = None;
568 let mut first = *body_first;
569 loop {
570 if !first {
571 let (t, flow) = run_block(test, None, ctx, rec)?;
572 if flow != Flow::Normal {
573 return Ok((t, flow));
574 }
575 let mut go = match &t {
576 Some(v) => is_true(v, span)?,
577 None => false,
578 };
579 if *until {
580 go = !go;
581 }
582 if !go {
583 return Ok((last, Flow::Normal));
584 }
585 }
586 first = false;
587 let (v, flow) = run_block(body, last, ctx, rec)?;
588 last = v;
589 match flow {
590 Flow::Normal | Flow::Continue => {}
591 Flow::Break => return Ok((last, Flow::Normal)),
592 other => return Ok((last, other)),
595 }
596 }
597 }
598 Control::For { name, source, body } => {
599 let src = eval(source, ctx, rec)?;
600 let n = if src.rank() == 0 { 1 } else { src.shape[0] };
601 let mut last = None;
602 for i in 0..n {
603 if let Some(name) = name {
604 let item = if src.rank() == 0 { src.clone() } else { src.item(i) };
605 ctx.env.assign(name.clone(), item, Scope::Local);
606 ctx.env.assign(
607 format!("{name}_index"),
608 Array::scalar_i64(i as i64),
609 Scope::Local,
610 );
611 }
612 let (v, flow) = run_block(body, last, ctx, rec)?;
613 last = v;
614 match flow {
615 Flow::Normal | Flow::Continue => {}
616 Flow::Break => return Ok((last, Flow::Normal)),
617 other => return Ok((last, other)),
620 }
621 }
622 Ok((last, Flow::Normal))
623 }
624 Control::Select { subject, cases } => {
625 let subject = eval(subject, ctx, rec)?;
626 let tol = ctx.cfg.tol;
627 let mut running = false;
628 let mut last = None;
629 for case in cases {
630 if !running {
631 match &case.test {
632 None => running = true,
633 Some(test) => {
634 let (t, flow) = run_block(test, None, ctx, rec)?;
635 if flow != Flow::Normal {
636 return Ok((t, flow));
637 }
638 running = t.is_some_and(|v| arrays_match(&subject, &v, tol));
641 }
642 }
643 }
644 if running {
645 let (v, flow) = run_block(&case.body, last, ctx, rec)?;
646 last = v;
647 if flow != Flow::Normal {
648 return Ok((last, flow));
649 }
650 if !case.fall_through {
651 return Ok((last, Flow::Normal));
652 }
653 running = true;
655 }
656 }
657 Ok((last, Flow::Normal))
658 }
659 Control::Try { body, catch } => {
660 match run_block(body, None, ctx, rec) {
664 Ok(r) => Ok(r),
665 Err(e) if matches!(e.kind, ErrorKind::NotYet | ErrorKind::Internal) => Err(e),
666 Err(_) => run_block(catch, None, ctx, rec),
667 }
668 }
669 }
670}
671
672pub(crate) fn call_explicit(
674 def: &Arc<ExplicitDef>,
675 x: Option<&Array>,
676 y: &Array,
677 ctx: &mut Ctx<'_>,
678 span: Span,
679) -> Result<Array> {
680 if x.is_some() && def.left.is_none() {
681 return Err(Error::new(
682 ErrorKind::Domain,
683 format!("{} has no dyadic definition", def.name),
684 Some(span),
685 ));
686 }
687 if x.is_none() && def.dyad_only {
688 return Err(Error::new(
689 ErrorKind::Domain,
690 format!(
691 "{} has no monadic definition: it names {}",
692 def.name,
693 def.left.as_deref().unwrap_or("a left argument")
694 ),
695 Some(span),
696 ));
697 }
698 let mut frame: HashMap<String, Array> = HashMap::new();
699 frame.insert(def.right.clone(), y.clone());
700 if let (Some(name), Some(v)) = (&def.left, x) {
701 frame.insert(name.clone(), v.clone());
702 }
703 for (label, at) in &def.labels {
705 frame.insert(label.clone(), Array::scalar_i64(*at as i64 + 1));
706 }
707 ctx.env.enter(frame, Arc::clone(def), span)?;
708 let mut rec = None;
709 let out = run_body(&def.body, ctx, &mut rec);
710 let frame = ctx.env.leave();
711 let value = out?;
712 if let Some(name) = &def.result {
715 return frame.get(name).cloned().ok_or_else(|| {
716 Error::new(
717 ErrorKind::Value,
718 format!("{} did not set its result {name}", def.name),
719 Some(span),
720 )
721 });
722 }
723 match value {
724 Some(v) => Ok(v),
725 None => def.empty.clone().ok_or_else(|| {
726 Error::new(
727 ErrorKind::Value,
728 format!("{} produced no result", def.name),
729 Some(span),
730 )
731 }),
732 }
733}
734
735const BRANCH_LIMIT: usize = 1 << 22;
738
739fn run_body(
742 stmts: &[Expr],
743 ctx: &mut Ctx<'_>,
744 rec: &mut Option<Trace>,
745) -> Result<Option<Array>> {
746 let mut last = None;
747 let mut at = 0usize;
748 let mut steps = 0usize;
749 while at < stmts.len() {
750 steps += 1;
751 if steps > BRANCH_LIMIT {
752 return Err(Error::new(
753 ErrorKind::Domain,
754 format!("a definition branched more than {BRANCH_LIMIT} times"),
755 Some(stmts[at].span()),
756 )
757 .note("a loop written with → needs a branch that leaves it"));
758 }
759 let (v, flow) = eval_stmt(&stmts[at], ctx, rec)?;
760 if let Some(v) = v {
761 last = Some(v);
762 }
763 match flow {
764 Flow::Normal => at += 1,
765 Flow::Goto(to) => at = to,
766 _ => break,
767 }
768 }
769 Ok(last)
770}
771
772pub(crate) fn fold_const(e: &Expr, cfg: EvalCfg) -> Option<Array> {
777 fn closed(e: &Expr) -> bool {
778 match e {
779 Expr::Const(..) => true,
780 Expr::Monad { verb, y, .. } => verb.is_pure() && closed(y),
781 Expr::Dyad { verb, x, y, .. } => verb.is_pure() && closed(x) && closed(y),
782 _ => false,
783 }
784 }
785 if !closed(e) {
786 return None;
787 }
788 cfg.pure(|ctx| eval(e, ctx, &mut None).ok())
789}
790
791fn eval(e: &Expr, ctx: &mut Ctx<'_>, rec: &mut Option<Trace>) -> Result<Array> {
792 let _depth = crate::verb::Nesting::enter(e.span())?;
795 let v = eval_node(e, ctx, rec)?;
796 if let Some(t) = rec.as_mut() {
797 let (kernel_ran, decline, placement) = t.get(&key(e)).map_or(
799 (None, None, crate::device::Placement::Default),
800 |n| (n.kernel_ran, n.decline, n.placement.clone()),
801 );
802 t.insert(
803 key(e),
804 Note {
805 shape: v.shape.clone(),
806 dtype: v.dtype(),
807 layout: v.layout(),
808 kernel_ran,
809 decline,
810 placement,
811 },
812 );
813 }
814 Ok(v)
815}
816
817fn eval_node(e: &Expr, ctx: &mut Ctx<'_>, rec: &mut Option<Trace>) -> Result<Array> {
818 match e {
819 Expr::Const(a, _) => Ok(a.clone()),
820 Expr::Param(i, _) => ctx.env.arg(*i),
821 Expr::Name(n, span) => ctx.env.get(n).ok_or_else(|| {
822 Error::new(ErrorKind::Value, format!("undefined name: {n}"), Some(*span))
823 }),
824 Expr::Assign { name, value, scope, .. } => {
825 let v = eval(value, ctx, rec)?;
826 ctx.env.assign(name.clone(), v.clone(), *scope);
827 Ok(v)
828 }
829 Expr::AmendIndex { name, slots, value, origin, scope, span } => {
830 let base = ctx.env.get(name).ok_or_else(|| {
831 Error::new(ErrorKind::Value, format!("undefined name: {name}"), Some(*span))
832 })?;
833 let v = eval(value, ctx, rec)?;
835 let mut idx = Vec::with_capacity(slots.len());
836 for slot in slots {
837 idx.push(match slot {
838 Some(e) => Some(eval(e, ctx, rec)?),
839 None => None,
840 });
841 }
842 let out = crate::verb::amend_at(&base, &idx, &v, *origin, *span)?;
843 ctx.env.assign(name.clone(), out.clone(), *scope);
844 Ok(out)
845 }
846 Expr::Control(..) => {
849 Err(Error::internal("a control sentence appeared in expression position"))
850 }
851 Expr::Monad { verb, y, span } => {
852 let vy = eval(y, ctx, rec)?;
853 verb.monad(&vy, ctx, *span)
854 }
855 Expr::Dyad { verb, x, y, span } => {
856 let vy = eval(y, ctx, rec)?;
859 let vx = eval(x, ctx, rec)?;
860 verb.dyad(&vx, &vy, ctx, *span)
861 }
862 Expr::PrintPass { value, bare, .. } => {
863 let v = eval(value, ctx, rec)?;
864 let text = format_array(&v, &ctx.cfg.fmt);
865 (ctx.out)(&text);
866 if !bare {
869 (ctx.out)("\n");
870 }
871 Ok(v)
872 }
873 Expr::Input { eval: run_it, span } => {
876 let line = ctx.read_line(*span)?;
877 if !run_it {
878 return Ok(Array::from_chars(line.chars().collect()));
879 }
880 crate::verb::execute_source(&line, true, ctx, *span)
881 }
882 Expr::Fused { kernel, inputs, orig, .. } => {
883 let mut vals = Vec::with_capacity(inputs.len());
884 for e in inputs {
885 vals.push(eval(e, ctx, rec)?);
886 }
887 let (ran, placement) = crate::fuse::eval_on(ctx.device, kernel, &vals);
888 if let Some(t) = rec.as_mut() {
889 let decline =
890 if ran.is_none() { crate::fuse::decline_reason(kernel, &vals) } else { None };
891 t.insert(
894 key(e),
895 Note {
896 shape: Vec::new(),
897 dtype: crate::dtype::DType::I64,
898 layout: crate::array::Layout::RowMajor,
899 kernel_ran: Some(ran.is_some()),
900 decline,
901 placement,
902 },
903 );
904 }
905 match ran {
906 Some(a) => Ok(a),
907 None => {
911 let tree = crate::fuse::fallback_tree(kernel, orig, &vals);
912 let v = eval(&tree, ctx, &mut None)?;
915 Ok(crate::fuse::fallback_finish(kernel, v))
916 }
917 }
918 }
919 Expr::VerbDef { name, verb, .. } => {
922 ctx.env.define(name.clone(), verb.clone());
923 Ok(Array::scalar_i64(0))
924 }
925 Expr::Elided { .. } | Expr::ModDef { .. } => Ok(Array::scalar_i64(0)),
929 }
930}