1use std::collections::HashMap;
58use std::sync::Arc;
59
60use beck_diag::depth::Nesting;
61use beck_diag::{Diagnostic, Diagnostics, Span};
62use beck_syntax::{print, sym, Head, Lit, Node, Symbol};
63
64use crate::typed::{TyRepr, TypeEnv};
65
66pub const MAX_STEPS: u64 = 1_000_000;
80
81pub const RESTRICTED: &[(&str, &str)] = &[
93 ("awareness", "cap.presence"),
94 ("digest_keyed", "cap.sign"),
95 ("durable", "durable"),
96 ("gestures", "dom"),
97 ("http_fetch", "net(host)"),
98 ("merge_clients", "ingress"),
99 ("now", "nondet"),
100 ("presence", "cap.presence"),
101 ("reveal", "cap.internal"),
102 ("secret_env", "env"),
103 ("uuid", "nondet"),
104];
105
106#[derive(Clone, Debug)]
113pub enum Val {
114 Unit,
115 Int(i64),
116 Float(f64),
117 Str(Arc<str>),
118 Bool(bool),
119 Keyword(Arc<str>),
120 List(Arc<Vec<Val>>),
121 Record(Arc<Vec<(Arc<str>, Val)>>),
122 Syntax(Node),
123 Fun(Arc<Lambda>),
124 Type(Arc<TyRepr>),
128}
129
130impl Val {
131 pub fn type_name(&self) -> &'static str {
132 match self {
133 Val::Unit => "unit",
134 Val::Int(_) => "Int",
135 Val::Float(_) => "Float",
136 Val::Str(_) => "Str",
137 Val::Bool(_) => "Bool",
138 Val::Keyword(_) => "keyword",
139 Val::List(_) => "list",
140 Val::Record(_) => "record",
141 Val::Syntax(_) => "syntax",
142 Val::Fun(_) => "function",
143 Val::Type(_) => "type",
144 }
145 }
146
147 fn str_(s: impl AsRef<str>) -> Val {
148 Val::Str(Arc::from(s.as_ref()))
149 }
150
151 fn list(xs: Vec<Val>) -> Val {
152 Val::List(Arc::new(xs))
153 }
154}
155
156#[derive(Debug)]
158pub struct Lambda {
159 params: Vec<Arc<str>>,
160 body: Node,
161 captured: HashMap<Arc<str>, Val>,
162}
163
164#[derive(Clone, Debug)]
170pub struct FnDef {
171 pub params: Vec<Arc<str>>,
172 pub body: Node,
173 pub span: Span,
174}
175
176#[derive(Debug)]
178pub struct Halt;
179
180type Eval<T> = Result<T, Halt>;
181
182enum Flow {
184 Fell,
185 Returned(Val),
186}
187
188pub(crate) fn ran_too_long(span: Span) -> Diagnostic {
191 Diagnostic::error("B0215", "a macro body ran too long", span)
192 .with_primary_label("the interpreter stopped here")
193 .with_note(format!(
194 "the budget is {MAX_STEPS} steps for the whole module — a bound on how long a compile \
195 takes, which is what answers a macro body that does not terminate"
196 ))
197}
198
199pub struct Interp<'a> {
200 defs: &'a HashMap<Arc<str>, FnDef>,
201 types: Option<&'a TypeEnv>,
204 call: Span,
207 diags: &'a mut Diagnostics,
208 pub steps: u64,
210 pub exhausted: bool,
213 nesting: Nesting,
218}
219
220impl<'a> Interp<'a> {
221 pub fn new(
222 defs: &'a HashMap<Arc<str>, FnDef>,
223 diags: &'a mut Diagnostics,
224 steps: u64,
225 exhausted: bool,
226 ) -> Interp<'a> {
227 Interp {
228 defs,
229 types: None,
230 call: Span::NONE,
231 diags,
232 steps,
233 exhausted,
234 nesting: Nesting::new(),
235 }
236 }
237
238 pub fn called_at(mut self, span: Span) -> Interp<'a> {
240 self.call = span;
241 self
242 }
243
244 pub fn knowing(mut self, types: Option<&'a TypeEnv>) -> Interp<'a> {
246 self.types = types;
247 self
248 }
249
250 pub fn run_body(
255 &mut self,
256 name: &str,
257 body: &Node,
258 env: HashMap<Arc<str>, Val>,
259 def_span: Span,
260 ) -> Option<Node> {
261 let mut frame = env;
262 match self.block(body, &mut frame) {
263 Ok(Flow::Returned(v)) => {
264 let span = body.span();
265 self.reflect(&v, span).ok()
266 }
267 Ok(Flow::Fell) => {
268 self.diags.push(Diagnostic::error(
269 "B0204",
270 format!("macro `{name}` returns nothing"),
271 def_span,
272 ));
273 None
274 }
275 Err(Halt) => None,
276 }
277 }
278
279 fn step(&mut self, span: Span) -> Eval<()> {
282 if self.steps == 0 {
283 if !self.exhausted {
284 self.exhausted = true;
285 self.diags.push(ran_too_long(span));
286 }
287 return Err(Halt);
288 }
289 self.steps -= 1;
290 Ok(())
291 }
292
293 fn enter(&mut self, span: Span) -> Eval<()> {
294 if self.nesting.enter() {
295 return Ok(());
296 }
297 if self.nesting.should_report() {
298 let note = self.nesting.note();
299 self.diags.push(
300 Diagnostic::error("B0216", "a macro body recursed too deep", span)
301 .with_primary_label("the interpreter gave up here")
302 .with_note(note),
303 );
304 }
305 Err(Halt)
306 }
307
308 fn refusal(&mut self, msg: Arc<str>, span: Span) -> Halt {
314 let at = match self.call.is_none() {
315 true => span,
316 false => self.call,
317 };
318 self.diags.push(
319 Diagnostic::error("B0224", msg.to_string(), at)
320 .with_primary_label("refused by the macro expanding here")
321 .with_label(span, "the macro said so here"),
322 );
323 Halt
324 }
325
326 fn refusal_at(&mut self, msg: Arc<str>, at: Span, span: Span) -> Halt {
331 if at.is_none() {
332 return self.refusal(msg, span);
333 }
334 self.diags.push(
335 Diagnostic::error("B0224", msg.to_string(), at)
336 .with_primary_label("the macro expanding here refused this")
337 .with_label(span, "the macro said so here"),
338 );
339 Halt
340 }
341
342 fn wrong(&mut self, msg: impl Into<String>, span: Span) -> Halt {
343 self.diags.push(
344 Diagnostic::error("B0209", msg, span)
345 .with_primary_label("computed while expanding a macro"),
346 );
347 Halt
348 }
349
350 fn block(&mut self, block: &Node, frame: &mut HashMap<Arc<str>, Val>) -> Eval<Flow> {
353 self.enter(block.span())?;
354 let out = self.block_inner(block, frame);
355 self.nesting.leave();
356 out
357 }
358
359 fn block_inner(&mut self, block: &Node, frame: &mut HashMap<Arc<str>, Val>) -> Eval<Flow> {
360 let stmts: &[Node] = if block.is_form(sym::DO) {
361 &block.args
362 } else {
363 std::slice::from_ref(block)
364 };
365 for stmt in stmts {
366 match self.stmt(stmt, frame)? {
367 Flow::Fell => {}
368 done => return Ok(done),
369 }
370 }
371 Ok(Flow::Fell)
372 }
373
374 fn stmt(&mut self, s: &Node, frame: &mut HashMap<Arc<str>, Val>) -> Eval<Flow> {
375 self.step(s.span())?;
376
377 if (s.is_form(sym::LET) || s.is_form(sym::VAR)) && s.args.len() == 2 {
378 let target = &s.args[0];
379 let name = if target.is_form(sym::ANNOT) {
380 target.args.first().and_then(Node::as_var)
381 } else {
382 target.as_var()
383 };
384 let Some(name) = name.map(|v| v.name.clone()) else {
385 return Err(self.wrong("a macro body binds a name, not a pattern", s.span()));
386 };
387 let v = self.eval(&s.args[1], frame)?;
388 frame.insert(name, v);
389 return Ok(Flow::Fell);
390 }
391
392 if s.is_form(sym::RETURN) {
393 let v = match s.args.first() {
394 Some(e) => self.eval(e, frame)?,
395 None => Val::Unit,
396 };
397 return Ok(Flow::Returned(v));
398 }
399
400 if s.is_form(sym::IF) && s.args.len() >= 2 {
401 let cond = self.eval(&s.args[0], frame)?;
402 return if self.truth(&cond, s.args[0].span())? {
403 self.block(&s.args[1], frame)
404 } else if let Some(alt) = s.args.get(2) {
405 self.block(alt, frame)
406 } else {
407 Ok(Flow::Fell)
408 };
409 }
410
411 if s.is_form(sym::FOR) && s.args.len() == 3 {
412 let Some(binder) = s.args[0].as_var().map(|v| v.name.clone()) else {
413 return Err(self.wrong("a `for` binds one name", s.args[0].span()));
414 };
415 let seq = self.eval(&s.args[1], frame)?;
416 let items = match &seq {
417 Val::List(xs) => xs.as_ref().clone(),
418 Val::Syntax(n) if n.is_form(sym::LIST) || n.is_form(sym::DO) => {
419 n.args.iter().cloned().map(Val::Syntax).collect()
420 }
421 other => {
422 let msg = format!("a `for` walks a list, not {}", other.type_name());
423 return Err(self.wrong(msg, s.args[1].span()));
424 }
425 };
426 for item in items {
427 self.step(s.span())?;
428 frame.insert(binder.clone(), item);
429 match self.block(&s.args[2], frame)? {
430 Flow::Fell => {}
431 done => return Ok(done),
432 }
433 }
434 return Ok(Flow::Fell);
435 }
436
437 if s.is_form(sym::WHILE) && s.args.len() == 2 {
438 loop {
439 self.step(s.span())?;
440 let cond = self.eval(&s.args[0], frame)?;
441 if !self.truth(&cond, s.args[0].span())? {
442 return Ok(Flow::Fell);
443 }
444 match self.block(&s.args[1], frame)? {
445 Flow::Fell => {}
446 done => return Ok(done),
447 }
448 }
449 }
450
451 if s.is_form(sym::DO) {
452 return self.block(s, frame);
453 }
454
455 self.eval(s, frame)?;
457 Ok(Flow::Fell)
458 }
459
460 fn refuse_program_form(&mut self, n: &Node) -> Eval<()> {
466 let Some(head) = PROGRAM_ONLY.iter().find(|f| n.is_form(f)) else {
467 return Ok(());
468 };
469 self.diags.push(
470 Diagnostic::error(
471 "B0205",
472 format!("`{head}` is not available in a macro body"),
473 n.span(),
474 )
475 .with_primary_label("this belongs to the program the macro expands to")
476 .with_note(
477 "a macro body is pure compile-time computation: bindings, `if`, `for`, `while`, \
478 lambdas, calls and `quote:`. Failure, declarations and pattern matching on \
479 variants are the program's, not the expander's",
480 )
481 .with_fix("put it inside the `quote:` the macro returns"),
482 );
483 Err(Halt)
484 }
485
486 fn truth(&mut self, v: &Val, span: Span) -> Eval<bool> {
487 match v {
488 Val::Bool(b) => Ok(*b),
489 other => {
490 let msg = format!("a condition is a Bool, not {}", other.type_name());
491 Err(self.wrong(msg, span))
492 }
493 }
494 }
495
496 fn eval(&mut self, e: &Node, frame: &mut HashMap<Arc<str>, Val>) -> Eval<Val> {
499 self.step(e.span())?;
500 self.enter(e.span())?;
501 let out = self.eval_inner(e, frame);
502 self.nesting.leave();
503 out
504 }
505
506 fn eval_inner(&mut self, e: &Node, frame: &mut HashMap<Arc<str>, Val>) -> Eval<Val> {
507 self.refuse_program_form(e)?;
508
509 if let Some(l) = e.as_lit() {
511 return Ok(match l {
512 Lit::Int(n) => Val::Int(*n),
513 Lit::Float(f) => Val::Float(*f),
514 Lit::Str(s) => Val::Str(s.clone()),
515 Lit::Bool(b) => Val::Bool(*b),
516 Lit::Keyword(k) => Val::Keyword(k.clone()),
517 });
518 }
519
520 if let Some(v) = e.as_var() {
522 if let Some(bound) = frame.get(&v.name) {
523 return Ok(bound.clone());
524 }
525 if v.name.as_ref() == "unit" {
526 return Ok(Val::Unit);
527 }
528 if let Some(def) = self.defs.get(&v.name) {
529 return Ok(Val::Fun(Arc::new(Lambda {
531 params: def.params.clone(),
532 body: def.body.clone(),
533 captured: HashMap::new(),
534 })));
535 }
536 return Err(self.unbound(&v.name, e.span()));
537 }
538
539 if e.is_form(sym::QUOTE) && e.args.len() == 1 {
541 let body = self.template(&e.args[0], frame)?;
542 return Ok(Val::Syntax(unwrap_block(body)));
543 }
544 if e.is_form(sym::UNQUOTE) || e.is_form(sym::SPLICE) {
545 let head = if e.is_form(sym::UNQUOTE) { "$" } else { "$*" };
546 let msg = format!("`{head}` is only meaningful inside a `quote:`");
547 return Err(self.wrong(msg, e.span()));
548 }
549
550 if e.is_form(sym::LIST) {
552 let mut out = Vec::with_capacity(e.args.len());
553 for a in &e.args {
554 out.push(self.eval(a, frame)?);
555 }
556 return Ok(Val::list(out));
557 }
558 if e.is_form(sym::RECORD) {
559 let mut fields: Vec<(Arc<str>, Val)> = Vec::new();
560 let mut i = 0;
561 while i + 1 < e.args.len() {
562 let Some(k) = e.args[i].as_keyword() else {
563 return Err(self.wrong("a record field is a name", e.args[i].span()));
564 };
565 let v = self.eval(&e.args[i + 1], frame)?;
566 fields.push((Arc::from(k), v));
567 i += 2;
568 }
569 return Ok(Val::Record(Arc::new(fields)));
570 }
571
572 if e.is_form(sym::IF) && e.args.len() == 3 {
574 let c = self.eval(&e.args[0], frame)?;
575 return if self.truth(&c, e.args[0].span())? {
576 self.eval(&e.args[1], frame)
577 } else {
578 self.eval(&e.args[2], frame)
579 };
580 }
581
582 if (e.is_form("and") || e.is_form("or")) && e.args.len() == 2 {
585 let left = self.eval(&e.args[0], frame)?;
586 let left = self.truth(&left, e.args[0].span())?;
587 if e.is_form("and") && !left {
588 return Ok(Val::Bool(false));
589 }
590 if e.is_form("or") && left {
591 return Ok(Val::Bool(true));
592 }
593 let right = self.eval(&e.args[1], frame)?;
594 return Ok(Val::Bool(self.truth(&right, e.args[1].span())?));
595 }
596
597 if e.is_form(sym::FN) && e.args.len() == 2 {
601 let params = e.args[0]
602 .args
603 .iter()
604 .filter_map(|p| {
605 let target = if p.is_form(sym::ANNOT) { &p.args[0] } else { p };
606 target.as_var().map(|s| s.name.clone())
607 })
608 .collect();
609 return Ok(Val::Fun(Arc::new(Lambda {
610 params,
611 body: e.args[1].clone(),
612 captured: frame.clone(),
613 })));
614 }
615
616 if e.is_form("index") && e.args.len() == 2 {
618 let subject = self.eval(&e.args[0], frame)?;
619 let idx = self.eval(&e.args[1], frame)?;
620 return self.index(&subject, &idx, e.span());
621 }
622
623 if e.is_form(sym::DOT) && e.args.len() == 2 {
625 let subject = self.eval(&e.args[0], frame)?;
626 let Some(field) = e.args[1].as_var().map(|s| s.name.clone()) else {
627 return Err(self.wrong("a field is a name", e.args[1].span()));
628 };
629 if let Val::Type(t) = &subject {
630 return self.type_field(t, &field, e.span());
631 }
632 let Val::Record(fields) = &subject else {
633 let msg = format!("{} has no fields", subject.type_name());
634 return Err(self.wrong(msg, e.span()));
635 };
636 return match fields.iter().find(|(k, _)| *k == field) {
637 Some((_, v)) => Ok(v.clone()),
638 None => {
639 let msg = format!("this record has no field `{field}`");
640 Err(self.wrong(msg, e.args[1].span()))
641 }
642 };
643 }
644 if e.is_form(sym::DOT) {
645 return Err(self.wrong(
646 "a macro body calls functions, not methods — the compile-time environment has no \
647 traits",
648 e.span(),
649 ));
650 }
651
652 if e.applied {
654 let (callee, args): (Option<Node>, &[Node]) = if e.is_form(sym::CALL) {
655 (e.args.first().cloned(), &e.args[1..])
656 } else {
657 (None, &e.args[..])
658 };
659
660 if let Some(name) = e.head_sym().filter(|_| callee.is_none()) {
663 if !frame.contains_key(&name.name)
664 && !self.defs.contains_key(&name.name)
665 && RESTRICTED.iter().any(|(n, _)| *n == name.name.as_ref())
666 {
667 return Err(self.unbound(&name.name.clone(), e.span()));
668 }
669 if matches!(frame.get(&name.name), Some(Val::Syntax(_))) {
675 let msg = format!(
676 "`{name}` is syntax and cannot be called at compile time — inside a \
677 `quote:`, write `{name}(…)` rather than `${name}(…)`"
678 );
679 return Err(self.wrong(msg, e.span()));
680 }
681 }
682
683 let mut values = Vec::with_capacity(args.len());
684 for a in args {
685 if a.is_form(sym::KW_ARG) {
688 return Err(self.wrong(
689 "a compile-time call passes its arguments by position",
690 a.span(),
691 ));
692 }
693 values.push(self.eval(a, frame)?);
694 }
695
696 if let Some(callee) = callee {
697 let f = self.eval(&callee, frame)?;
698 return self.apply(&f, values, e.span());
699 }
700
701 let Some(name) = e.head_sym().map(|s| s.name.clone()) else {
702 return Err(self.wrong("a call needs a callee", e.span()));
703 };
704
705 if let Some(bound) = frame.get(&name).cloned() {
709 return self.apply(&bound, values, e.span());
710 }
711 if let Some(def) = self.defs.get(&name).cloned() {
712 return self.call_def(&name, &def, values, e.span());
713 }
714 if let Some(out) = self.builtin(&name, &values, e.span()) {
715 return out;
716 }
717 return Err(self.unbound(&name, e.span()));
718 }
719
720 let head = e.head_name().unwrap_or("this form").to_string();
721 Err(self.wrong(format!("`{head}` has no value at compile time"), e.span()))
722 }
723
724 fn unbound(&mut self, name: &str, span: Span) -> Halt {
725 if let Some((_, atom)) = RESTRICTED.iter().find(|(n, _)| *n == name) {
726 self.diags.push(
727 Diagnostic::error(
728 "B0207",
729 format!("`{name}` may not be called while expanding a macro"),
730 span,
731 )
732 .with_primary_label(format!("performs `{atom}`"))
733 .with_note(
734 "macro expansion is capability-restricted (`docs/02` §2.4): it is pure \
735 computation over the module's own definitions, so that what a compile \
736 produces depends on the source and on nothing else",
737 )
738 .with_fix("compute this in the program the macro expands to, not in the macro"),
739 );
740 return Halt;
741 }
742 self.diags.push(
743 Diagnostic::error(
744 "B0208",
745 format!("cannot find `{name}` at compile time"),
746 span,
747 )
748 .with_primary_label(
749 "not a local, a `def` in this module or one it imports, or a compile-time builtin",
750 )
751 .with_note(
752 "the macro interpreter's environment is deliberately small: the pure part of the \
753 prelude, the definitions of this module and the ones it imports, and the \
754 `node_*` reflection over syntax",
755 ),
756 );
757 Halt
758 }
759
760 fn index(&mut self, subject: &Val, idx: &Val, span: Span) -> Eval<Val> {
761 let Val::Int(i) = idx else {
762 let msg = format!("an index is an Int, not {}", idx.type_name());
763 return Err(self.wrong(msg, span));
764 };
765 let items: Vec<Val> = match subject {
766 Val::List(xs) => xs.as_ref().clone(),
767 Val::Syntax(n) if n.is_form(sym::LIST) || n.is_form(sym::DO) => {
768 n.args.iter().cloned().map(Val::Syntax).collect()
769 }
770 other => {
771 let msg = format!("{} is not indexable", other.type_name());
772 return Err(self.wrong(msg, span));
773 }
774 };
775 match usize::try_from(*i).ok().and_then(|i| items.get(i)) {
776 Some(v) => Ok(v.clone()),
777 None => {
778 let msg = format!("index {i} is outside a list of {}", items.len());
779 Err(self.wrong(msg, span))
780 }
781 }
782 }
783
784 fn apply(&mut self, f: &Val, args: Vec<Val>, span: Span) -> Eval<Val> {
785 let Val::Fun(lambda) = f else {
786 let msg = format!("{} is not a function", f.type_name());
787 return Err(self.wrong(msg, span));
788 };
789 if lambda.params.len() != args.len() {
790 let msg = format!(
791 "this function takes {} argument(s) and got {}",
792 lambda.params.len(),
793 args.len()
794 );
795 return Err(self.wrong(msg, span));
796 }
797 let mut frame = lambda.captured.clone();
798 for (p, a) in lambda.params.iter().zip(args) {
799 frame.insert(p.clone(), a);
800 }
801 self.enter(span)?;
802 let out = self.block(&lambda.body, &mut frame);
803 self.nesting.leave();
804 match out? {
805 Flow::Returned(v) => Ok(v),
806 Flow::Fell => self.last_value(&lambda.body.clone(), &mut frame),
809 }
810 }
811
812 fn last_value(&mut self, body: &Node, frame: &mut HashMap<Arc<str>, Val>) -> Eval<Val> {
813 let last = if body.is_form(sym::DO) {
814 body.args.last()
815 } else {
816 Some(body)
817 };
818 match last {
819 Some(e) => self.eval(e, frame),
820 None => Ok(Val::Unit),
821 }
822 }
823
824 fn call_def(&mut self, name: &str, def: &FnDef, args: Vec<Val>, span: Span) -> Eval<Val> {
825 if def.params.len() != args.len() {
826 let msg = format!(
827 "`{name}` takes {} argument(s) and got {}",
828 def.params.len(),
829 args.len()
830 );
831 return Err(self.wrong(msg, span));
832 }
833 let mut frame: HashMap<Arc<str>, Val> = HashMap::new();
834 for (p, a) in def.params.iter().zip(args) {
835 frame.insert(p.clone(), a);
836 }
837 self.enter(span)?;
838 let out = self.block(&def.body, &mut frame);
839 self.nesting.leave();
840 match out? {
841 Flow::Returned(v) => Ok(v),
842 Flow::Fell => Ok(Val::Unit),
843 }
844 }
845
846 fn template(&mut self, t: &Node, frame: &mut HashMap<Arc<str>, Val>) -> Eval<Node> {
853 self.step(t.span())?;
854 self.enter(t.span())?;
855 let out = self.template_inner(t, frame);
856 self.nesting.leave();
857 out
858 }
859
860 fn template_inner(&mut self, t: &Node, frame: &mut HashMap<Arc<str>, Val>) -> Eval<Node> {
861 if t.is_form(sym::UNQUOTE) && t.args.len() == 1 {
862 if let Some(v) = t.args[0].as_var() {
865 if !frame.contains_key(&v.name) && !self.defs.contains_key(&v.name) {
866 self.diags.push(
867 Diagnostic::error(
868 "B0206",
869 format!("`${v}` is not bound in this macro"),
870 t.span(),
871 )
872 .with_primary_label("unquoting an unbound name")
873 .with_note("`$e` evaluates `e` in the macro body's own environment"),
874 );
875 return Err(Halt);
876 }
877 }
878 let v = self.eval(&t.args[0], frame)?;
879 return self.reflect(&v, t.span());
880 }
881
882 let mut args = Vec::with_capacity(t.args.len());
883 for a in &t.args {
884 if a.is_form(sym::SPLICE) && a.args.len() == 1 {
885 let v = self.eval(&a.args[0], frame)?;
886 for piece in self.spliced(&v, a.span())? {
887 args.push(piece);
888 }
889 continue;
890 }
891 args.push(self.template(a, frame)?);
892 }
893
894 let head = match &t.head {
897 Head::Sym(s) => match frame.get(&s.name) {
898 Some(Val::Syntax(bound)) if t.applied && !bound.applied => match &bound.head {
899 Head::Sym(bs) => Head::Sym(bs.clone()),
900 _ => Head::Sym(s.clone()),
901 },
902 _ => Head::Sym(s.clone()),
903 },
904 Head::Lit(l) => Head::Lit(l.clone()),
905 };
906
907 Ok(Node {
908 head,
909 args,
910 applied: t.applied,
911 meta: t.meta.clone(),
912 })
913 }
914
915 fn spliced(&mut self, v: &Val, span: Span) -> Eval<Vec<Node>> {
917 match v {
918 Val::List(xs) => {
919 let mut out = Vec::with_capacity(xs.len());
920 for x in xs.iter() {
921 out.push(self.reflect(x, span)?);
922 }
923 Ok(out)
924 }
925 Val::Syntax(n) if n.is_form(sym::LIST) || n.is_form(sym::DO) => Ok(n.args.clone()),
926 other => Ok(vec![self.reflect(other, span)?]),
927 }
928 }
929
930 pub fn reflect(&mut self, v: &Val, span: Span) -> Eval<Node> {
935 Ok(match v {
936 Val::Unit => Node::sym("unit", span),
937 Val::Int(n) => Node::lit(Lit::Int(*n), span),
938 Val::Float(f) => Node::lit(Lit::Float(*f), span),
939 Val::Str(s) => Node::lit(Lit::Str(s.clone()), span),
940 Val::Bool(b) => Node::lit(Lit::Bool(*b), span),
941 Val::Keyword(k) => Node::lit(Lit::Keyword(k.clone()), span),
942 Val::Syntax(n) => n.clone(),
943 Val::List(xs) => {
944 let mut args = Vec::with_capacity(xs.len());
945 for x in xs.iter() {
946 args.push(self.reflect(x, span)?);
947 }
948 Node::form(sym::LIST, args, span)
949 }
950 Val::Record(fields) => {
951 let mut args = Vec::with_capacity(fields.len() * 2);
952 for (k, val) in fields.iter() {
953 args.push(Node::lit(Lit::Keyword(k.clone()), span));
954 args.push(self.reflect(val, span)?);
955 }
956 Node::form(sym::RECORD, args, span)
957 }
958 Val::Fun(_) => {
959 return Err(self.wrong(
960 "a function has no syntax — a macro returns the code that builds one",
961 span,
962 ))
963 }
964 Val::Type(t) => {
965 let msg = format!(
966 "`{t}` is a type and has no syntax — a macro reads a type and writes the code \
967 that a value of it goes through"
968 );
969 return Err(self.wrong(msg, span));
970 }
971 })
972 }
973
974 fn type_field(&mut self, t: &TyRepr, field: &str, span: Span) -> Eval<Val> {
980 let types = self.types;
981 let of = |r: &TyRepr| Val::Type(Arc::new(r.clone()));
982 let pairs = |fs: crate::typed::Fields| {
983 Val::list(
984 fs.into_iter()
985 .map(|(n, ft)| {
986 Val::Record(Arc::new(vec![
987 (Arc::from("name"), Val::Str(n)),
988 (Arc::from("ty"), Val::Type(Arc::new(ft))),
989 ]))
990 })
991 .collect(),
992 )
993 };
994 match field {
995 "name" => Ok(Val::Str(t.head())),
996 "kind" => Ok(Val::str_(t.kind_name())),
997 "args" => Ok(Val::list(t.args().iter().map(of).collect())),
998 "result" => match t {
999 TyRepr::Fun { result, .. } => Ok(of(result)),
1000 _ => {
1001 let msg = format!("`{t}` is not a function, so it has no result type");
1002 Err(self.wrong(msg, span))
1003 }
1004 },
1005 "fields" => Ok(pairs(types.map(|e| e.fields(t)).unwrap_or_default())),
1006 "inner" => Ok(of(&types.map(|e| e.inner(t)).unwrap_or(TyRepr::Unknown))),
1007 "variants" => {
1008 let vs = types.map(|e| e.variants(t)).unwrap_or_default();
1009 Ok(Val::list(
1010 vs.into_iter()
1011 .map(|(n, fs)| {
1012 Val::Record(Arc::new(vec![
1013 (Arc::from("name"), Val::Str(n)),
1014 (Arc::from("fields"), pairs(fs)),
1015 ]))
1016 })
1017 .collect(),
1018 ))
1019 }
1020 other => {
1021 let msg = format!(
1022 "a type answers `name`, `kind`, `args`, `result`, `fields`, `variants` and \
1023 `inner` — not `{other}`"
1024 );
1025 Err(self.wrong(msg, span))
1026 }
1027 }
1028 }
1029
1030 fn builtin(&mut self, name: &str, args: &[Val], span: Span) -> Option<Eval<Val>> {
1034 if !is_builtin(name) {
1035 return None;
1036 }
1037 Some(self.builtin_inner(name, args, span))
1038 }
1039
1040 fn builtin_inner(&mut self, name: &str, args: &[Val], span: Span) -> Eval<Val> {
1041 let arity = |want: usize, this: &mut Self| -> Eval<()> {
1042 if args.len() == want {
1043 Ok(())
1044 } else {
1045 let msg = format!("`{name}` takes {want} argument(s) and got {}", args.len());
1046 Err(this.wrong(msg, span))
1047 }
1048 };
1049
1050 macro_rules! s {
1051 ($i:expr) => {{
1052 match &args[$i] {
1053 Val::Str(s) => s.clone(),
1054 other => {
1055 let msg = format!("`{name}` expects a Str, not {}", other.type_name());
1056 return Err(self.wrong(msg, span));
1057 }
1058 }
1059 }};
1060 }
1061 macro_rules! i {
1062 ($i:expr) => {{
1063 match &args[$i] {
1064 Val::Int(n) => *n,
1065 other => {
1066 let msg = format!("`{name}` expects an Int, not {}", other.type_name());
1067 return Err(self.wrong(msg, span));
1068 }
1069 }
1070 }};
1071 }
1072 macro_rules! l {
1073 ($i:expr) => {{
1074 match &args[$i] {
1075 Val::List(xs) => xs.as_ref().clone(),
1076 other => {
1077 let msg = format!("`{name}` expects a list, not {}", other.type_name());
1078 return Err(self.wrong(msg, span));
1079 }
1080 }
1081 }};
1082 }
1083 macro_rules! n {
1084 ($i:expr) => {{
1085 match &args[$i] {
1086 Val::Syntax(n) => n.clone(),
1087 other => {
1088 let msg = format!("`{name}` expects syntax, not {}", other.type_name());
1089 return Err(self.wrong(msg, span));
1090 }
1091 }
1092 }};
1093 }
1094
1095 match name {
1096 "str" => {
1099 arity(1, self)?;
1100 Ok(Val::str_(display(&args[0])))
1101 }
1102 "str_len" => {
1103 arity(1, self)?;
1104 Ok(Val::Int(s!(0).chars().count() as i64))
1105 }
1106 "str_is_empty" => {
1107 arity(1, self)?;
1108 Ok(Val::Bool(s!(0).is_empty()))
1109 }
1110 "str_to_int" => {
1114 arity(1, self)?;
1115 let text = s!(0);
1116 match text.trim().parse::<i64>() {
1117 Ok(n) => Ok(Val::Int(n)),
1118 Err(_) => {
1119 let msg = format!("`{text}` is not an integer");
1120 Err(self.wrong(msg, span))
1121 }
1122 }
1123 }
1124 "str_trim" => {
1125 arity(1, self)?;
1126 Ok(Val::str_(s!(0).trim()))
1127 }
1128 "str_upper" => {
1129 arity(1, self)?;
1130 Ok(Val::str_(beck_prim::text::upper(&s!(0))))
1131 }
1132 "str_lower" => {
1133 arity(1, self)?;
1134 Ok(Val::str_(beck_prim::text::lower(&s!(0))))
1135 }
1136 "str_contains" => {
1137 arity(2, self)?;
1138 Ok(Val::Bool(s!(0).contains(s!(1).as_ref())))
1139 }
1140 "str_starts_with" => {
1141 arity(2, self)?;
1142 Ok(Val::Bool(s!(0).starts_with(s!(1).as_ref())))
1143 }
1144 "str_ends_with" => {
1145 arity(2, self)?;
1146 Ok(Val::Bool(s!(0).ends_with(s!(1).as_ref())))
1147 }
1148 "str_slice" => {
1149 arity(3, self)?;
1150 let (start, len) = (i!(1).max(0) as usize, i!(2).max(0) as usize);
1151 let out: String = s!(0).chars().skip(start).take(len).collect();
1152 Ok(Val::str_(out))
1153 }
1154 "str_replace" => {
1155 arity(3, self)?;
1156 Ok(Val::str_(beck_prim::text::replace(&s!(0), &s!(1), &s!(2))))
1157 }
1158 "str_repeat" => {
1159 arity(2, self)?;
1160 let n = i!(1).clamp(0, 1_000_000) as usize;
1161 Ok(Val::str_(s!(0).repeat(n)))
1162 }
1163 "str_chars" => {
1164 arity(1, self)?;
1165 Ok(Val::list(
1166 s!(0).chars().map(|c| Val::str_(c.to_string())).collect(),
1167 ))
1168 }
1169 "str_split" => {
1170 arity(2, self)?;
1171 let (hay, sep) = (s!(0), s!(1));
1172 let parts: Vec<Val> = if sep.is_empty() {
1173 hay.chars().map(|c| Val::str_(c.to_string())).collect()
1174 } else {
1175 hay.split(sep.as_ref()).map(Val::str_).collect()
1176 };
1177 Ok(Val::list(parts))
1178 }
1179 "str_join" => {
1180 arity(2, self)?;
1181 let xs = l!(0);
1182 let sep = s!(1);
1183 let parts: Vec<String> = xs.iter().map(display).collect();
1184 Ok(Val::str_(parts.join(sep.as_ref())))
1185 }
1186
1187 "list_len" => {
1189 arity(1, self)?;
1190 Ok(Val::Int(l!(0).len() as i64))
1191 }
1192 "list_is_empty" => {
1193 arity(1, self)?;
1194 Ok(Val::Bool(l!(0).is_empty()))
1195 }
1196 "list_append" => {
1197 arity(2, self)?;
1198 let mut xs = l!(0);
1199 xs.push(args[1].clone());
1200 Ok(Val::list(xs))
1201 }
1202 "list_reverse" => {
1203 arity(1, self)?;
1204 let mut xs = l!(0);
1205 xs.reverse();
1206 Ok(Val::list(xs))
1207 }
1208 "list_contains" => {
1209 arity(2, self)?;
1210 Ok(Val::Bool(l!(0).iter().any(|x| val_eq(x, &args[1]))))
1211 }
1212 "list_take" => {
1213 arity(2, self)?;
1214 let n = i!(1).max(0) as usize;
1215 Ok(Val::list(l!(0).into_iter().take(n).collect()))
1216 }
1217 "list_drop" => {
1218 arity(2, self)?;
1219 let n = i!(1).max(0) as usize;
1220 Ok(Val::list(l!(0).into_iter().skip(n).collect()))
1221 }
1222 "list_slice" => {
1223 arity(3, self)?;
1224 let (start, len) = (i!(1).max(0) as usize, i!(2).max(0) as usize);
1225 Ok(Val::list(l!(0).into_iter().skip(start).take(len).collect()))
1226 }
1227 "concat_lists" => {
1229 arity(1, self)?;
1230 let mut out = Vec::new();
1231 for group in l!(0) {
1232 match group {
1233 Val::List(ys) => out.extend(ys.as_ref().clone()),
1234 other => {
1235 let msg = format!(
1236 "`concat_lists` takes a list of lists, and found {}",
1237 other.type_name()
1238 );
1239 return Err(self.wrong(msg, span));
1240 }
1241 }
1242 }
1243 Ok(Val::list(out))
1244 }
1245 "map_list" => {
1246 arity(2, self)?;
1247 let xs = l!(0);
1248 let f = args[1].clone();
1249 let mut out = Vec::with_capacity(xs.len());
1250 for x in xs {
1251 out.push(self.apply(&f, vec![x], span)?);
1252 }
1253 Ok(Val::list(out))
1254 }
1255 "filter_list" => {
1256 arity(2, self)?;
1257 let xs = l!(0);
1258 let f = args[1].clone();
1259 let mut out = Vec::new();
1260 for x in xs {
1261 let keep = self.apply(&f, vec![x.clone()], span)?;
1262 if self.truth(&keep, span)? {
1263 out.push(x);
1264 }
1265 }
1266 Ok(Val::list(out))
1267 }
1268 "list_fold" => {
1269 arity(3, self)?;
1270 let xs = l!(0);
1271 let mut acc = args[1].clone();
1272 let f = args[2].clone();
1273 for x in xs {
1274 acc = self.apply(&f, vec![acc, x], span)?;
1275 }
1276 Ok(acc)
1277 }
1278 "list_all" | "list_any" => {
1279 arity(2, self)?;
1280 let xs = l!(0);
1281 let f = args[1].clone();
1282 let all = name == "list_all";
1283 for x in xs {
1284 let got = self.apply(&f, vec![x], span)?;
1285 if self.truth(&got, span)? != all {
1286 return Ok(Val::Bool(!all));
1287 }
1288 }
1289 Ok(Val::Bool(all))
1290 }
1291 "list_flat_map" => {
1292 arity(2, self)?;
1293 let xs = l!(0);
1294 let f = args[1].clone();
1295 let mut out = Vec::new();
1296 for x in xs {
1297 match self.apply(&f, vec![x], span)? {
1298 Val::List(ys) => out.extend(ys.as_ref().clone()),
1299 other => {
1300 let msg =
1301 format!("`list_flat_map` expects lists, not {}", other.type_name());
1302 return Err(self.wrong(msg, span));
1303 }
1304 }
1305 }
1306 Ok(Val::list(out))
1307 }
1308
1309 "abs" => {
1312 arity(1, self)?;
1313 match &args[0] {
1314 Val::Int(n) => Ok(Val::Int(n.abs())),
1315 Val::Float(f) => Ok(Val::Float(f.abs())),
1316 other => {
1317 let msg = format!("`abs` expects a number, not {}", other.type_name());
1318 Err(self.wrong(msg, span))
1319 }
1320 }
1321 }
1322 "float" => {
1323 arity(1, self)?;
1324 Ok(Val::Float(i!(0) as f64))
1325 }
1326 "trunc" => {
1327 arity(1, self)?;
1328 match &args[0] {
1329 Val::Float(f) => Ok(Val::Int(f.trunc() as i64)),
1330 other => {
1331 let msg = format!("`trunc` expects a Float, not {}", other.type_name());
1332 Err(self.wrong(msg, span))
1333 }
1334 }
1335 }
1336
1337 "node_head" => {
1340 arity(1, self)?;
1341 match &n!(0).head {
1342 Head::Sym(s) => Ok(Val::str_(s.as_str())),
1343 Head::Lit(_) => Err(self.wrong(
1344 "this node is a literal and has no head symbol — ask `node_is_lit` first",
1345 span,
1346 )),
1347 }
1348 }
1349 "node_args" => {
1350 arity(1, self)?;
1351 Ok(Val::list(
1352 n!(0).args.iter().cloned().map(Val::Syntax).collect(),
1353 ))
1354 }
1355 "node_is_call" => {
1356 arity(1, self)?;
1357 Ok(Val::Bool(n!(0).applied))
1358 }
1359 "node_is_lit" => {
1360 arity(1, self)?;
1361 Ok(Val::Bool(n!(0).as_lit().is_some()))
1362 }
1363 "node_lit" => {
1368 arity(1, self)?;
1369 let node = n!(0);
1370 match node.as_lit() {
1371 Some(Lit::Int(n)) => Ok(Val::Int(*n)),
1372 Some(Lit::Float(f)) => Ok(Val::Float(*f)),
1373 Some(Lit::Str(s)) => Ok(Val::Str(s.clone())),
1374 Some(Lit::Bool(b)) => Ok(Val::Bool(*b)),
1375 Some(Lit::Keyword(k)) => Ok(Val::Keyword(k.clone())),
1376 None => Err(self.wrong(
1377 "this node is not a literal and has no value — ask `node_is_lit` first",
1378 span,
1379 )),
1380 }
1381 }
1382 "node_sym" => {
1383 arity(1, self)?;
1384 Ok(Val::Syntax(Node::sym(s!(0).as_ref(), span)))
1385 }
1386 "node_form" => {
1387 arity(2, self)?;
1388 let head = s!(0);
1389 let mut items = Vec::new();
1390 for a in l!(1) {
1391 items.push(self.reflect(&a, span)?);
1392 }
1393 Ok(Val::Syntax(Node::form(head.as_ref(), items, span)))
1394 }
1395 "node_str" => {
1396 arity(1, self)?;
1397 Ok(Val::str_(print::to_sexpr(&n!(0))))
1398 }
1399 "node_ty" => {
1402 arity(1, self)?;
1403 let node = n!(0);
1404 let Some(types) = self.types else {
1405 return Err(self.wrong(
1406 "`node_ty` needs the checker's answers, which only a `typed macro` has — \
1407 write `typed macro` rather than `macro`",
1408 span,
1409 ));
1410 };
1411 match types.of(node.span()) {
1412 Some(t) => Ok(Val::Type(Arc::new(t.clone()))),
1413 None => {
1417 let msg = format!(
1418 "`{}` has no inferred type: `node_ty` answers about the expressions \
1419 this macro was called with, and this is syntax the body built",
1420 print::to_sexpr(&node)
1421 );
1422 Err(self.wrong(msg, span))
1423 }
1424 }
1425 }
1426 "refuse" => {
1429 if args.len() != 1 && args.len() != 2 {
1430 let msg = format!(
1431 "`refuse` expects a message, and optionally somewhere to \
1432 point — {} arguments given",
1433 args.len()
1434 );
1435 return Err(self.wrong(msg, span));
1436 }
1437 let msg = s!(0);
1438 match args.len() {
1443 2 => {
1444 let at = n!(1).span();
1445 Err(self.refusal_at(msg, at, span))
1446 }
1447 _ => Err(self.refusal(msg, span)),
1448 }
1449 }
1450 "splice" => {
1453 arity(1, self)?;
1454 let mut items = Vec::new();
1455 for a in l!(0) {
1456 items.push(self.reflect(&a, span)?);
1457 }
1458 Ok(Val::Syntax(Node::form(sym::DO, items, span)))
1459 }
1460
1461 "+" | "-" | "*" | "/" | "%" => {
1463 arity(2, self)?;
1464 self.arith(name, &args[0], &args[1], span)
1465 }
1466 "negate" => {
1467 arity(1, self)?;
1468 match &args[0] {
1469 Val::Int(n) => Ok(Val::Int(-n)),
1470 Val::Float(f) => Ok(Val::Float(-f)),
1471 other => {
1472 let msg = format!("`-` expects a number, not {}", other.type_name());
1473 Err(self.wrong(msg, span))
1474 }
1475 }
1476 }
1477 "==" => {
1478 arity(2, self)?;
1479 Ok(Val::Bool(val_eq(&args[0], &args[1])))
1480 }
1481 "!=" => {
1482 arity(2, self)?;
1483 Ok(Val::Bool(!val_eq(&args[0], &args[1])))
1484 }
1485 "<" | "<=" | ">" | ">=" => {
1486 arity(2, self)?;
1487 self.compare(name, &args[0], &args[1], span)
1488 }
1489 "not" => {
1490 arity(1, self)?;
1491 let b = self.truth(&args[0], span)?;
1492 Ok(Val::Bool(!b))
1493 }
1494 other => unreachable!("`{other}` is listed as a builtin and not implemented"),
1495 }
1496 }
1497
1498 fn arith(&mut self, op: &str, a: &Val, b: &Val, span: Span) -> Eval<Val> {
1499 match (a, b) {
1500 (Val::Int(x), Val::Int(y)) => {
1501 if matches!(op, "/" | "%") && *y == 0 {
1502 return Err(self.wrong("division by zero while expanding a macro", span));
1503 }
1504 Ok(Val::Int(match op {
1505 "+" => x.wrapping_add(*y),
1506 "-" => x.wrapping_sub(*y),
1507 "*" => x.wrapping_mul(*y),
1508 "/" => x.wrapping_div(*y),
1509 _ => x.wrapping_rem(*y),
1510 }))
1511 }
1512 (Val::Float(x), Val::Float(y)) => Ok(Val::Float(match op {
1513 "+" => x + y,
1514 "-" => x - y,
1515 "*" => x * y,
1516 "/" => x / y,
1517 _ => x % y,
1518 })),
1519 (Val::Str(x), Val::Str(y)) if op == "+" => Ok(Val::str_(format!("{x}{y}"))),
1521 (Val::List(x), Val::List(y)) if op == "+" => {
1522 let mut out = x.as_ref().clone();
1523 out.extend(y.as_ref().clone());
1524 Ok(Val::list(out))
1525 }
1526 _ => {
1527 let msg = format!(
1528 "`{op}` does not apply to {} and {}",
1529 a.type_name(),
1530 b.type_name()
1531 );
1532 Err(self.wrong(msg, span))
1533 }
1534 }
1535 }
1536
1537 fn compare(&mut self, op: &str, a: &Val, b: &Val, span: Span) -> Eval<Val> {
1538 let ord = match (a, b) {
1539 (Val::Int(x), Val::Int(y)) => x.cmp(y),
1540 (Val::Float(x), Val::Float(y)) => match x.partial_cmp(y) {
1541 Some(o) => o,
1542 None => return Err(self.wrong("a NaN has no order", span)),
1543 },
1544 (Val::Str(x), Val::Str(y)) => x.as_ref().cmp(y.as_ref()),
1545 _ => {
1546 let msg = format!(
1547 "`{op}` does not compare {} with {}",
1548 a.type_name(),
1549 b.type_name()
1550 );
1551 return Err(self.wrong(msg, span));
1552 }
1553 };
1554 Ok(Val::Bool(match op {
1555 "<" => ord.is_lt(),
1556 "<=" => ord.is_le(),
1557 ">" => ord.is_gt(),
1558 _ => ord.is_ge(),
1559 }))
1560 }
1561}
1562
1563fn unwrap_block(n: Node) -> Node {
1565 if n.is_form(sym::DO) && n.args.len() == 1 {
1566 return n.args[0].clone();
1567 }
1568 n
1569}
1570
1571const PROGRAM_ONLY: &[&str] = &[
1574 sym::MATCH,
1575 sym::TRY,
1576 sym::RAISE,
1577 sym::PARALLEL,
1578 sym::DEF,
1579 sym::MODEL,
1580 sym::UNION,
1581 sym::TRAIT,
1582 sym::IMPL,
1583 sym::TYPE,
1584 sym::NEWTYPE,
1585 sym::IMPORT,
1586 sym::TEST,
1587 sym::PROPERTY,
1588 sym::SERVICE,
1589 sym::UI,
1590];
1591
1592pub const BUILTINS: &[&str] = &[
1598 "+",
1599 "-",
1600 "*",
1601 "/",
1602 "%",
1603 "==",
1604 "!=",
1605 "<",
1606 "<=",
1607 ">",
1608 ">=",
1609 "abs",
1610 "concat_lists",
1611 "filter_list",
1612 "float",
1613 "list_all",
1614 "list_any",
1615 "list_append",
1616 "list_contains",
1617 "list_drop",
1618 "list_flat_map",
1619 "list_fold",
1620 "list_is_empty",
1621 "list_len",
1622 "list_reverse",
1623 "list_slice",
1624 "list_take",
1625 "map_list",
1626 "negate",
1627 "node_args",
1628 "node_form",
1629 "node_head",
1630 "node_is_call",
1631 "node_is_lit",
1632 "node_lit",
1633 "node_str",
1634 "node_sym",
1635 "node_ty",
1636 "not",
1637 "refuse",
1638 "splice",
1639 "str",
1640 "str_chars",
1641 "str_contains",
1642 "str_ends_with",
1643 "str_is_empty",
1644 "str_join",
1645 "str_len",
1646 "str_lower",
1647 "str_repeat",
1648 "str_replace",
1649 "str_slice",
1650 "str_split",
1651 "str_starts_with",
1652 "str_to_int",
1653 "str_trim",
1654 "str_upper",
1655 "trunc",
1656];
1657
1658pub fn is_builtin(name: &str) -> bool {
1659 BUILTINS.contains(&name)
1660}
1661
1662fn display(v: &Val) -> String {
1664 match v {
1665 Val::Unit => "unit".to_string(),
1666 Val::Int(n) => n.to_string(),
1667 Val::Float(f) => {
1668 if f.fract() == 0.0 && f.is_finite() {
1669 format!("{f:.1}")
1670 } else {
1671 f.to_string()
1672 }
1673 }
1674 Val::Str(s) => s.to_string(),
1675 Val::Bool(b) => b.to_string(),
1676 Val::Keyword(k) => format!(":{k}"),
1677 Val::List(xs) => {
1678 let parts: Vec<String> = xs.iter().map(display).collect();
1679 format!("[{}]", parts.join(", "))
1680 }
1681 Val::Record(fields) => {
1682 let parts: Vec<String> = fields
1683 .iter()
1684 .map(|(k, v)| format!("{k}: {}", display(v)))
1685 .collect();
1686 format!("{{{}}}", parts.join(", "))
1687 }
1688 Val::Syntax(n) => print::to_sexpr(n),
1689 Val::Fun(_) => "<function>".to_string(),
1690 Val::Type(t) => t.to_string(),
1691 }
1692}
1693
1694fn val_eq(a: &Val, b: &Val) -> bool {
1695 match (a, b) {
1696 (Val::Unit, Val::Unit) => true,
1697 (Val::Int(x), Val::Int(y)) => x == y,
1698 (Val::Float(x), Val::Float(y)) => x == y,
1699 (Val::Str(x), Val::Str(y)) => x == y,
1700 (Val::Bool(x), Val::Bool(y)) => x == y,
1701 (Val::Keyword(x), Val::Keyword(y)) => x == y,
1702 (Val::List(x), Val::List(y)) => {
1703 x.len() == y.len() && x.iter().zip(y.iter()).all(|(a, b)| val_eq(a, b))
1704 }
1705 (Val::Record(x), Val::Record(y)) => {
1706 x.len() == y.len()
1707 && x.iter()
1708 .zip(y.iter())
1709 .all(|((ka, va), (kb, vb))| ka == kb && val_eq(va, vb))
1710 }
1711 (Val::Syntax(x), Val::Syntax(y)) => x.structurally_eq(y),
1712 (Val::Type(x), Val::Type(y)) => x == y,
1715 _ => false,
1716 }
1717}
1718
1719pub fn param_names(params: &Node) -> Vec<Arc<str>> {
1721 params
1722 .args
1723 .iter()
1724 .filter_map(|p| {
1725 let target = if p.is_form(sym::ANNOT) { &p.args[0] } else { p };
1726 target.as_var().map(|s: &Symbol| s.name.clone())
1727 })
1728 .collect()
1729}
1730
1731#[cfg(test)]
1732mod tests {
1733 use super::*;
1734
1735 #[test]
1736 fn every_builtin_is_implemented() {
1737 let defs = HashMap::new();
1741 for name in BUILTINS {
1742 let mut diags = Diagnostics::new();
1743 let mut interp = Interp::new(&defs, &mut diags, MAX_STEPS, false);
1744 let _ = interp.builtin(name, &[], Span::NONE);
1745 }
1746 }
1747
1748 #[test]
1760 fn the_interpreters_ceiling_fits_the_declared_stack() {
1761 let spent = std::thread::Builder::new()
1764 .stack_size(256 * 1024 * 1024)
1765 .spawn(|| {
1766 let src = "\
1767def down(n: Int) -> Int:
1768 return down(n + 1)
1769
1770macro deep(x):
1771 y = down(1)
1772 return quote:
1773 $x
1774
1775def f() -> Int:
1776 return deep(1)
1777";
1778 beck_diag::depth::probe::stack_spent(|| {
1779 let mut map = beck_diag::SourceMap::new();
1780 let file = map.add("probe.beck", src);
1781 let mut diags = Diagnostics::new();
1782 let parsed = beck_syntax::parser::parse_module(file, "probe", src, &mut diags);
1783 let out = crate::expand_module(&parsed, &mut diags);
1784 assert!(
1785 diags.iter().any(|d| d.code == "B0216"),
1786 "the probe must reach the ceiling for this to be measuring it"
1787 );
1788 out
1789 })
1790 })
1791 .expect("a thread")
1792 .join()
1793 .expect("the probe expands");
1794
1795 println!(
1796 "the macro interpreter spends {spent} bytes reaching its ceiling of {} levels",
1797 beck_diag::depth::MAX_NESTING
1798 );
1799 assert!(
1802 spent * 2 < beck_diag::depth::STACK_BYTES,
1803 "reaching the ceiling costs {spent} bytes, and {} with the margin, against a declared \
1804 STACK_BYTES of {} — raise the declaration or lower the ceiling",
1805 spent * 2,
1806 beck_diag::depth::STACK_BYTES
1807 );
1808 }
1809
1810 #[test]
1811 fn the_restricted_list_is_sorted_and_unique() {
1812 let names: Vec<&str> = RESTRICTED.iter().map(|(n, _)| *n).collect();
1813 let mut sorted = names.clone();
1814 sorted.sort_unstable();
1815 sorted.dedup();
1816 assert_eq!(names, sorted, "keep `RESTRICTED` sorted and duplicate-free");
1817 let mut builtins = BUILTINS.to_vec();
1818 builtins.sort_unstable();
1819 for (name, _) in RESTRICTED {
1820 assert!(
1821 !builtins.contains(name),
1822 "`{name}` is both restricted and a builtin"
1823 );
1824 }
1825 }
1826}