1use alloc::boxed::Box;
23use alloc::format;
24use alloc::string::{String, ToString};
25use alloc::vec::Vec;
26use core::ops::Range;
27
28use num_bigint::BigInt;
29
30use crate::builtins::{TyKind, MODULES};
31use crate::module::{ImportSpec, ModuleName};
32use crate::source::{ChunkLocs, ChunkSpan, DocSource, DocsBuilder, Loc, Pos};
33use crate::{Builtin, Ctx, Sym};
34
35pub type Text = hipstr::LocalHipStr<'static>;
40
41#[derive(Debug)]
43pub struct StaticChunk {
44 name: Text,
47 nodes: Box<[Node]>,
48 pats: Box<[Pat]>,
49 lambdas: Box<[Lambda]>,
50 captures: Box<[Capture]>,
51 modules: Box<[Module]>,
52 members: Box<[Member]>,
53 frame: Box<[Sym]>,
54 imports: Box<[ImportSpec]>,
55 ints: Box<[BigInt]>,
56 strs: Box<[Text]>,
57 pub(crate) locs: Box<ChunkLocs>,
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub enum Node {
65 Int(i64),
67 BigInt(IntId),
69 Unit,
70 Symbol(Sym),
72 Str(StrId),
73 Local {
75 name: Sym,
76 index: u32,
77 },
78 Builtin(Builtin),
79 ModItem {
82 index: u32,
83 depth: u32,
84 name: Sym,
85 },
86 Home {
87 leaf: HomeLeaf,
88 depth: u32,
89 },
90 App {
92 len: u32,
93 args: NodeId,
94 },
95 List {
96 len: u32,
97 elems: NodeId,
98 },
99 Map {
101 len: u32,
102 entries: NodeId,
103 },
104 Block {
107 len: u32,
108 binds: PatId,
109 exprs: NodeId,
110 },
111 Case {
114 len: u32,
115 conds: PatId,
116 exprs: NodeId,
117 },
118 Abs(LambdaId),
119 Recur(ModId),
121 #[cfg(debug_assertions)]
123 Barrier {
124 run: Run,
125 len: u32,
126 },
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq)]
131pub enum Pat {
132 None,
134 Discard,
135 Unit,
136 Bind(Sym),
137 At(Sym, PatId),
138 Equal(NodeId),
139 Less(NodeId),
140 Greater(NodeId),
141 TypeKind(TyKind, PatId),
143 TypeRef(NodeId, PatId),
145 Unwrap {
146 depth: u32,
147 inner: PatId,
148 },
149 Or(PatId, PatId),
150 List {
152 len: u32,
153 elems: PatId,
154 rest: PatId,
155 },
156 Map {
159 len: u32,
160 entries: PatId,
161 rest: PatId,
162 },
163 Lookup(NodeId),
165 When {
167 inner: PatId,
168 gpat: PatId,
169 cond: NodeId,
170 },
171 #[cfg(debug_assertions)]
173 Barrier {
174 run: Run,
175 len: u32,
176 },
177}
178
179#[derive(Debug, Clone, Copy, PartialEq, Eq)]
181pub struct Lambda {
182 pub head: IdRange<PatId>,
183 pub body: NodeId,
184 pub captures: Plan,
185}
186
187#[derive(Debug, Clone, PartialEq, Eq)]
189pub struct Module {
190 pub name: Option<ModuleName>,
192 pub parent: ModId,
194 pub members: IdRange<MemberId>,
197 pub items: u32,
199 pub locals: u32,
201 pub frame: IdRange<FrameId>,
203 pub imports: IdRange<ImportId>,
205 pub body: NodeId,
207}
208
209#[derive(Debug, Clone, Copy, PartialEq, Eq)]
211pub struct Member {
212 pub name: Sym,
213 pub node: NodeId,
214}
215
216#[derive(Debug, Clone, Copy, PartialEq, Eq)]
220pub enum HomeLeaf {
221 Module,
223 New,
226 Value,
229}
230
231impl HomeLeaf {
232 pub fn name(self) -> &'static str {
234 "Self"
235 }
236}
237
238#[derive(Debug, Clone, Copy, PartialEq, Eq)]
243pub struct Capture {
244 pub outer: u32,
245 pub slot: u32,
246}
247
248pub trait Id: Copy {
250 fn index(self) -> usize;
252 fn at(index: usize) -> Self;
254}
255
256macro_rules! ids {
257 ($($(#[$doc:meta])* $name:ident;)*) => {$(
258 $(#[$doc])*
259 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
260 pub struct $name(u32);
261
262 impl $name {
263 pub const NONE: Self = Self(u32::MAX);
265
266 pub fn get(self) -> Option<Self> {
268 (self != Self::NONE).then_some(self)
269 }
270
271 #[inline]
273 pub fn nth(self, i: u32) -> Self {
274 Self(self.0 + i)
275 }
276 }
277
278 impl Id for $name {
279 fn index(self) -> usize {
280 self.0 as usize
281 }
282
283 fn at(index: usize) -> Self {
284 assert!(index < u32::MAX as usize, "a chunk table outgrew u32");
285 Self(index as u32)
286 }
287 }
288 )*};
289}
290
291ids! {
292 ChunkId;
294 NodeId;
296 PatId;
298 LambdaId;
300 ModId;
302 MemberId;
304 CaptureId;
306 IntId;
308 StrId;
310 FrameId;
312 ImportId;
314}
315
316impl ChunkId {
317 pub const ELLY: ChunkId = ChunkId(0);
320}
321
322impl ModId {
323 pub(crate) fn builtin(slot: usize) -> ModId {
326 ModId::at(slot + 1)
327 }
328}
329
330#[derive(Debug, Clone, Copy, PartialEq, Eq)]
333pub struct IdRange<I> {
334 pub first: I,
335 pub len: u32,
336}
337
338impl<I: Id> IdRange<I> {
339 fn indices(self) -> Range<usize> {
340 let first = self.first.index();
341 first..first + self.len as usize
342 }
343}
344
345#[derive(Debug, Clone, Copy, PartialEq, Eq)]
348pub enum Run {
349 App,
351 List,
353 Map,
355 Block,
357 Case,
359 Head,
361 PatList,
363 PatMap,
365}
366
367#[derive(Debug, Clone, Copy, PartialEq, Eq)]
369pub enum Plan {
370 Chain,
374 Frame(IdRange<CaptureId>),
377}
378
379impl StaticChunk {
380 pub fn name(&self) -> &Text {
382 &self.name
383 }
384
385 pub fn top(&self) -> ModId {
387 ModId(0)
388 }
389
390 pub fn node(&self, id: NodeId) -> Node {
391 self.nodes[id.index()]
392 }
393
394 pub fn pat(&self, id: PatId) -> Pat {
395 self.pats[id.index()]
396 }
397
398 pub fn lambda(&self, id: LambdaId) -> &Lambda {
399 &self.lambdas[id.index()]
400 }
401
402 pub fn module(&self, id: ModId) -> &Module {
403 &self.modules[id.index()]
404 }
405
406 pub fn members(&self, module: &Module) -> &[Member] {
408 &self.members[module.members.indices()]
409 }
410
411 pub fn frame_names(&self, module: &Module) -> &[Sym] {
413 &self.frame[module.frame.indices()]
414 }
415
416 pub fn imports(&self, module: &Module) -> &[ImportSpec] {
418 &self.imports[module.imports.indices()]
419 }
420
421 pub fn captures(&self, plan: IdRange<CaptureId>) -> &[Capture] {
423 &self.captures[plan.indices()]
424 }
425
426 pub fn int(&self, id: IntId) -> &BigInt {
427 &self.ints[id.index()]
428 }
429
430 pub fn str(&self, id: StrId) -> &Text {
431 &self.strs[id.index()]
432 }
433
434 pub fn nodes(&self, run: Run, first: NodeId, len: u32) -> &[Node] {
436 let first = first.index();
437 #[cfg(debug_assertions)]
438 assert_eq!(
439 self.nodes[first - 1],
440 Node::Barrier { run, len },
441 "a run of nodes does not match its barrier"
442 );
443 let _ = run;
444 &self.nodes[first..first + len as usize]
445 }
446
447 pub fn pats(&self, run: Run, first: PatId, len: u32) -> &[Pat] {
449 let first = first.index();
450 #[cfg(debug_assertions)]
451 assert_eq!(
452 self.pats[first - 1],
453 Pat::Barrier { run, len },
454 "a run of patterns does not match its barrier"
455 );
456 let _ = run;
457 &self.pats[first..first + len as usize]
458 }
459
460 pub fn module_ids(&self) -> impl Iterator<Item = ModId> + Clone {
462 (0..self.modules.len()).map(ModId::at)
463 }
464
465 #[inline]
468 pub fn member(&self, m: ModId, index: u32) -> Option<Member> {
469 let module = self.module(m);
470 if index >= module.items + module.locals {
471 return None;
472 }
473 Some(self.members[module.members.first.index() + index as usize])
474 }
475
476 pub fn member_len(&self, m: ModId) -> u32 {
479 let module = self.module(m);
480 module.items + module.locals
481 }
482
483 pub fn iota(&self, m: ModId, index: u32) -> Option<Sym> {
485 let module = self.module(m);
486 let iotas = &self.members(module)[(module.items + module.locals) as usize..];
487 iotas.get(index as usize).map(|member| member.name)
488 }
489
490 pub fn index_of(&self, m: ModId, name: Sym) -> Option<u32> {
493 let module = self.module(m);
494 self.members(module)[..module.items as usize]
495 .binary_search_by(|member| member.name.cmp(&name))
496 .ok()
497 .map(|i| i as u32)
498 }
499
500 pub fn item_names(&self, m: ModId) -> impl Iterator<Item = Sym> + '_ {
502 let module = self.module(m);
503 self.members(module)[..module.items as usize]
504 .iter()
505 .map(|member| member.name)
506 }
507
508 pub fn local_names(&self, m: ModId) -> impl Iterator<Item = Sym> + '_ {
511 let module = self.module(m);
512 let start = module.items as usize;
513 self.members(module)[start..start + module.locals as usize]
514 .iter()
515 .map(|member| member.name)
516 }
517
518 pub fn iota_names(&self, m: ModId) -> impl Iterator<Item = Sym> + '_ {
520 let module = self.module(m);
521 self.members(module)[(module.items + module.locals) as usize..]
522 .iter()
523 .map(|member| member.name)
524 }
525}
526
527pub(crate) struct Builder {
534 nodes: Vec<Node>,
535 pats: Vec<Pat>,
536 lambdas: Vec<Lambda>,
537 captures: Vec<Capture>,
538 modules: Vec<Module>,
539 members: Vec<Member>,
540 frame: Vec<Sym>,
541 imports: Vec<ImportSpec>,
542 ints: Vec<BigInt>,
543 strs: Vec<Text>,
544 node_spans: Vec<ChunkSpan>,
545 pat_spans: Vec<ChunkSpan>,
546 lambda_spans: Vec<ChunkSpan>,
547 member_spans: Vec<ChunkSpan>,
548 pub(crate) docs: DocsBuilder,
549 pub(crate) scratch: Vec<(Node, ChunkSpan)>,
552 pub(crate) pat_scratch: Vec<(Pat, ChunkSpan)>,
554 pub(crate) member_scratch: Vec<(Member, ChunkSpan)>,
556 current: ModId,
558}
559
560pub(crate) fn len32(len: usize) -> u32 {
561 u32::try_from(len).expect("a run outgrew u32")
562}
563
564impl Builder {
565 pub(crate) fn new() -> Self {
566 Builder {
567 nodes: Vec::new(),
568 pats: Vec::new(),
569 lambdas: Vec::new(),
570 captures: Vec::new(),
571 modules: Vec::new(),
572 members: Vec::new(),
573 frame: Vec::new(),
574 imports: Vec::new(),
575 ints: Vec::new(),
576 strs: Vec::new(),
577 node_spans: Vec::new(),
578 pat_spans: Vec::new(),
579 lambda_spans: Vec::new(),
580 member_spans: Vec::new(),
581 docs: DocsBuilder::default(),
582 scratch: Vec::new(),
583 pat_scratch: Vec::new(),
584 member_scratch: Vec::new(),
585 current: ModId::NONE,
586 }
587 }
588
589 pub(crate) fn finish(self, name: Text, made: Option<u32>) -> StaticChunk {
591 debug_assert!(self.scratch.is_empty() && self.pat_scratch.is_empty());
592 debug_assert!(self.member_scratch.is_empty());
593 debug_assert!(!self.modules.is_empty(), "a chunk has a top module");
594 let (docs, line_starts) = self.docs.finish(made);
595 StaticChunk {
596 name,
597 nodes: self.nodes.into_boxed_slice(),
598 pats: self.pats.into_boxed_slice(),
599 lambdas: self.lambdas.into_boxed_slice(),
600 captures: self.captures.into_boxed_slice(),
601 modules: self.modules.into_boxed_slice(),
602 members: self.members.into_boxed_slice(),
603 frame: self.frame.into_boxed_slice(),
604 imports: self.imports.into_boxed_slice(),
605 ints: self.ints.into_boxed_slice(),
606 strs: self.strs.into_boxed_slice(),
607 locs: Box::new(ChunkLocs {
608 node_spans: self.node_spans.into_boxed_slice(),
609 pat_spans: self.pat_spans.into_boxed_slice(),
610 lambda_spans: self.lambda_spans.into_boxed_slice(),
611 member_spans: self.member_spans.into_boxed_slice(),
612 docs,
613 line_starts,
614 base: Loc::NONE,
615 }),
616 }
617 }
618
619 pub(crate) fn add_doc(&mut self, source: Option<DocSource>, text: &str) -> Pos {
622 self.docs.add(source, text)
623 }
624
625 pub(crate) fn push(&mut self, node: Node, span: ChunkSpan) -> NodeId {
626 let id = NodeId::at(self.nodes.len());
627 self.nodes.push(node);
628 self.node_spans.push(span);
629 id
630 }
631
632 pub(crate) fn push_pat(&mut self, pat: Pat, span: ChunkSpan) -> PatId {
633 let id = PatId::at(self.pats.len());
634 self.pats.push(pat);
635 self.pat_spans.push(span);
636 id
637 }
638
639 pub(crate) fn node(&self, id: NodeId) -> (Node, ChunkSpan) {
641 (self.nodes[id.index()], self.node_spans[id.index()])
642 }
643
644 pub(crate) fn push_run(&mut self, run: Run, mark: usize) -> NodeId {
646 #[cfg(debug_assertions)]
647 {
648 let len = len32(self.scratch.len() - mark);
649 self.push(Node::Barrier { run, len }, ChunkSpan::NONE);
650 }
651 let _ = run;
652 let first = NodeId::at(self.nodes.len());
653 for (node, span) in self.scratch.drain(mark..) {
654 self.nodes.push(node);
655 self.node_spans.push(span);
656 }
657 first
658 }
659
660 pub(crate) fn push_pat_run(&mut self, run: Run, mark: usize) -> PatId {
662 #[cfg(debug_assertions)]
663 {
664 let len = len32(self.pat_scratch.len() - mark);
665 self.push_pat(Pat::Barrier { run, len }, ChunkSpan::NONE);
666 }
667 let _ = run;
668 let first = PatId::at(self.pats.len());
669 for (pat, span) in self.pat_scratch.drain(mark..) {
670 self.pats.push(pat);
671 self.pat_spans.push(span);
672 }
673 first
674 }
675
676 pub(crate) fn int(&mut self, n: BigInt) -> Node {
678 match i64::try_from(&n) {
679 Ok(n) => Node::Int(n),
680 Err(_) => {
681 let id = IntId::at(self.ints.len());
682 self.ints.push(n);
683 Node::BigInt(id)
684 }
685 }
686 }
687
688 pub(crate) fn str(&mut self, s: Text) -> Node {
690 let id = StrId::at(self.strs.len());
691 self.strs.push(s);
692 Node::Str(id)
693 }
694
695 pub(crate) fn lambda(
699 &mut self,
700 mark: usize,
701 body: (Node, ChunkSpan),
702 plan: Option<&[Capture]>,
703 span: ChunkSpan,
704 ) -> Node {
705 let len = self.pat_scratch.len() - mark;
706 assert!(
708 len <= u8::MAX as usize,
709 "a lambda has at most 255 parameters"
710 );
711 let head = IdRange {
712 first: self.push_pat_run(Run::Head, mark),
713 len: len32(len),
714 };
715 let body = self.push(body.0, body.1);
716 let captures = match plan {
717 None => Plan::Chain,
718 Some(plan) => {
719 let first = CaptureId::at(self.captures.len());
720 self.captures.extend_from_slice(plan);
721 Plan::Frame(IdRange {
722 first,
723 len: len32(plan.len()),
724 })
725 }
726 };
727 let id = LambdaId::at(self.lambdas.len());
728 self.lambdas.push(Lambda {
729 head,
730 body,
731 captures,
732 });
733 self.lambda_spans.push(span);
734 Node::Abs(id)
735 }
736
737 pub(crate) fn enter_module(&mut self) -> ModId {
740 let id = ModId::at(self.modules.len());
741 self.modules.push(Module {
742 name: None,
743 parent: self.current,
744 members: IdRange {
745 first: MemberId::NONE,
746 len: 0,
747 },
748 items: 0,
749 locals: 0,
750 frame: IdRange {
751 first: FrameId::NONE,
752 len: 0,
753 },
754 imports: IdRange {
755 first: ImportId::NONE,
756 len: 0,
757 },
758 body: NodeId::NONE,
759 });
760 self.current = id;
761 id
762 }
763
764 #[allow(clippy::too_many_arguments)]
769 pub(crate) fn leave_module(
770 &mut self,
771 id: ModId,
772 name: Option<ModuleName>,
773 mark: usize,
774 items: u32,
775 locals: u32,
776 frame: &[Sym],
777 imports: Vec<ImportSpec>,
778 body: NodeId,
779 ) {
780 let members = IdRange {
781 first: MemberId::at(self.members.len()),
782 len: len32(self.member_scratch.len() - mark),
783 };
784 for (member, span) in self.member_scratch.drain(mark..) {
785 self.members.push(member);
786 self.member_spans.push(span);
787 }
788 let frame_range = IdRange {
789 first: FrameId::at(self.frame.len()),
790 len: len32(frame.len()),
791 };
792 self.frame.extend_from_slice(frame);
793 let imports_range = IdRange {
794 first: ImportId::at(self.imports.len()),
795 len: len32(imports.len()),
796 };
797 self.imports.extend(imports);
798 let parent = self.modules[id.index()].parent;
799 self.modules[id.index()] = Module {
800 name,
801 parent,
802 members,
803 items,
804 locals,
805 frame: frame_range,
806 imports: imports_range,
807 body,
808 };
809 self.current = parent;
810 }
811
812 pub(crate) fn builtin_modules(&mut self) {
815 let top = self.enter_module();
816 for (slot, (op, _, items, iotas)) in MODULES.iter().enumerate() {
817 let id = self.enter_module();
818 debug_assert_eq!(id, ModId::builtin(slot));
819 let mark = self.member_scratch.len();
820 for (name, op) in items.iter() {
821 let node = self.push(Node::Builtin(*op), ChunkSpan::NONE);
822 let member = Member { name: *name, node };
823 self.member_scratch.push((member, ChunkSpan::NONE));
824 }
825 for name in iotas.iter() {
826 let member = Member {
827 name: *name,
828 node: NodeId::NONE,
829 };
830 self.member_scratch.push((member, ChunkSpan::NONE));
831 }
832 let name = Some(Text::from_static(op.name()));
835 let items = len32(items.len());
836 self.leave_module(id, name, mark, items, 0, &[], Vec::new(), NodeId::NONE);
837 }
838 let mark = self.member_scratch.len();
839 self.leave_module(top, None, mark, 0, 0, &[], Vec::new(), NodeId::NONE);
840 }
841}
842
843impl StaticChunk {
848 pub fn to_sexp(&self, id: NodeId, ctx: &Ctx) -> String {
850 let mut out = String::new();
851 self.write_sexp(self.node(id), &mut out, ctx);
852 out
853 }
854
855 fn write_sexp(&self, node: Node, out: &mut String, ctx: &Ctx) {
856 match node {
857 Node::Int(n) => {
858 out.push_str("(int ");
859 out.push_str(&n.to_string());
860 out.push(')');
861 }
862 Node::BigInt(id) => {
863 out.push_str("(int ");
864 out.push_str(&self.int(id).to_string());
865 out.push(')');
866 }
867 Node::Local { name, .. } => {
868 out.push_str("(name ");
869 push_sym(name, out, ctx);
870 out.push(')');
871 }
872 Node::Builtin(op) => {
873 out.push_str("(name ");
874 out.push_str(op.name());
875 out.push(')');
876 }
877 Node::Home { leaf, .. } => match leaf {
878 HomeLeaf::Module => out.push_str("(self)"),
879 HomeLeaf::New => out.push_str("(new)"),
880 HomeLeaf::Value => out.push_str("(value)"),
881 },
882 Node::ModItem { name, .. } => {
883 out.push_str("(moditem ");
884 push_sym(name, out, ctx);
885 out.push(')');
886 }
887 Node::Recur(id) => {
889 let module = self.module(id);
890 out.push_str("(recur");
891 for member in self.items_by_text(module, ctx) {
892 out.push_str(" (bind ");
893 push_sym(member.name, out, ctx);
894 out.push(' ');
895 self.write_sexp(self.node(member.node), out, ctx);
896 out.push(')');
897 }
898 out.push(' ');
899 self.write_sexp(self.node(module.body), out, ctx);
900 out.push(')');
901 }
902 Node::Symbol(s) => {
903 out.push_str("(sym .");
904 push_sym(s, out, ctx);
905 out.push(')');
906 }
907 Node::Str(id) => json_str(self.str(id), out),
908 Node::App { len, args } => {
909 let items = self.nodes(Run::App, args, len);
910 let (open, body) = match items[0] {
911 Node::Home {
912 leaf: HomeLeaf::New,
913 ..
914 } => ("(new", &items[1..]),
915 Node::Home {
916 leaf: HomeLeaf::Value,
917 ..
918 } => ("(value", &items[1..]),
919 _ => ("(app", items),
920 };
921 out.push_str(open);
922 for &item in body {
923 out.push(' ');
924 self.write_sexp(item, out, ctx);
925 }
926 out.push(')');
927 }
928 Node::Abs(id) => {
929 let code = self.lambda(id);
930 let head = self.pats(Run::Head, code.head.first, code.head.len);
931 self.write_abs_sexp(head, code.body, out, ctx);
932 }
933 Node::Unit => out.push_str("(unit)"),
934 Node::List { len, elems } => {
935 out.push_str("(list");
936 for &elem in self.nodes(Run::List, elems, len) {
937 out.push(' ');
938 self.write_sexp(elem, out, ctx);
939 }
940 out.push(')');
941 }
942 Node::Map { len, entries } => {
943 let all = self.nodes(Run::Map, entries, 2 * len);
944 let (keys, values) = all.split_at(len as usize);
945 out.push_str("(map");
946 for (&key, &value) in keys.iter().zip(values) {
947 out.push_str(" (entry ");
948 self.write_sexp(key, out, ctx);
949 out.push(' ');
950 self.write_sexp(value, out, ctx);
951 out.push(')');
952 }
953 out.push(')');
954 }
955 Node::Block { len, binds, exprs } => {
956 let binds = self.pats(Run::Block, binds, len);
957 let exprs = self.nodes(Run::Block, exprs, len);
958 out.push_str("(block");
959 for (&pat, &expr) in binds.iter().zip(exprs) {
960 out.push(' ');
961 if pat == Pat::None {
962 self.write_sexp(expr, out, ctx);
963 } else {
964 out.push_str("(bind ");
965 self.write_pat_sexp(pat, out, ctx);
966 out.push(' ');
967 self.write_sexp(expr, out, ctx);
968 out.push(')');
969 }
970 }
971 out.push(')');
972 }
973 Node::Case { len, conds, exprs } => {
974 let conds = self.pats(Run::Case, conds, len);
975 let exprs = self.nodes(Run::Case, exprs, len + 1);
976 let (bodies, subject) = exprs.split_at(len as usize);
977 out.push_str("(case ");
978 self.write_sexp(subject[0], out, ctx);
979 for (&pat, &body) in conds.iter().zip(bodies) {
980 out.push_str(" (arm ");
981 self.write_pat_sexp(pat, out, ctx);
982 out.push(' ');
983 self.write_sexp(body, out, ctx);
984 out.push(')');
985 }
986 out.push(')');
987 }
988 #[cfg(debug_assertions)]
989 Node::Barrier { .. } => unreachable!("a barrier is not an operand"),
990 }
991 }
992
993 fn write_abs_sexp(&self, head: &[Pat], body: NodeId, out: &mut String, ctx: &Ctx) {
995 out.push_str("(abs ");
996 self.write_pat_sexp(head[0], out, ctx);
997 out.push(' ');
998 if head.len() == 1 {
999 self.write_sexp(self.node(body), out, ctx);
1000 } else {
1001 self.write_abs_sexp(&head[1..], body, out, ctx);
1002 }
1003 out.push(')');
1004 }
1005
1006 fn write_pat_sexp(&self, pat: Pat, out: &mut String, ctx: &Ctx) {
1007 match pat {
1008 Pat::None => unreachable!("only a block clause's slot binds nothing"),
1009 Pat::Discard => out.push('_'),
1010 Pat::Unit => out.push_str("(unit)"),
1011 Pat::Bind(name) => push_sym(name, out, ctx),
1012 Pat::At(name, inner) => {
1013 out.push_str("(at ");
1014 push_sym(name, out, ctx);
1015 out.push(' ');
1016 self.write_pat_sexp(self.pat(inner), out, ctx);
1017 out.push(')');
1018 }
1019 Pat::Equal(expr) => self.write_tagged_node("(eq ", expr, out, ctx),
1020 Pat::Less(expr) => self.write_tagged_node("(lt ", expr, out, ctx),
1021 Pat::Greater(expr) => self.write_tagged_node("(gt ", expr, out, ctx),
1022 Pat::TypeKind(kind, inner) => {
1023 out.push_str("(as ");
1024 out.push_str(kind.name());
1025 out.push(' ');
1026 self.write_pat_sexp(self.pat(inner), out, ctx);
1027 out.push(')');
1028 }
1029 Pat::TypeRef(proto, inner) => {
1030 out.push_str("(as ");
1031 self.write_sexp(self.node(proto), out, ctx);
1032 out.push(' ');
1033 self.write_pat_sexp(self.pat(inner), out, ctx);
1034 out.push(')');
1035 }
1036 Pat::Unwrap { inner, .. } => {
1037 out.push_str("(unwrap ");
1038 self.write_pat_sexp(self.pat(inner), out, ctx);
1039 out.push(')');
1040 }
1041 Pat::Or(left, right) => {
1042 out.push_str("(or ");
1043 self.write_pat_sexp(self.pat(left), out, ctx);
1044 out.push(' ');
1045 self.write_pat_sexp(self.pat(right), out, ctx);
1046 out.push(')');
1047 }
1048 Pat::List { len, elems, rest } => {
1049 out.push_str("(list");
1050 for &elem in self.pats(Run::PatList, elems, len) {
1051 out.push(' ');
1052 self.write_pat_sexp(elem, out, ctx);
1053 }
1054 self.write_rest_sexp(rest, out, ctx);
1055 out.push(')');
1056 }
1057 Pat::Map { len, entries, rest } => {
1058 let all = self.pats(Run::PatMap, entries, 2 * len);
1059 let (keys, values) = all.split_at(len as usize);
1060 out.push_str("(map");
1061 for (&key, &value) in keys.iter().zip(values) {
1062 out.push_str(" (entry ");
1063 match key {
1064 Pat::Lookup(expr) => self.write_sexp(self.node(expr), out, ctx),
1065 key => {
1066 out.push_str("(capture ");
1067 self.write_pat_sexp(key, out, ctx);
1068 out.push(')');
1069 }
1070 }
1071 out.push(' ');
1072 self.write_pat_sexp(value, out, ctx);
1073 out.push(')');
1074 }
1075 self.write_rest_sexp(rest, out, ctx);
1076 out.push(')');
1077 }
1078 Pat::Lookup(_) => unreachable!("a lookup is only a map pattern's key"),
1079 Pat::When { inner, gpat, cond } => {
1081 out.push_str("(when ");
1082 self.write_pat_sexp(self.pat(inner), out, ctx);
1083 out.push(' ');
1084 match gpat.get() {
1085 None => self.write_sexp(self.node(cond), out, ctx),
1086 Some(gpat) => {
1087 out.push_str("(bind ");
1088 self.write_pat_sexp(self.pat(gpat), out, ctx);
1089 out.push(' ');
1090 self.write_sexp(self.node(cond), out, ctx);
1091 out.push(')');
1092 }
1093 }
1094 out.push(')');
1095 }
1096 #[cfg(debug_assertions)]
1097 Pat::Barrier { .. } => unreachable!("a barrier is not an operand"),
1098 }
1099 }
1100
1101 fn write_tagged_node(&self, open: &str, expr: NodeId, out: &mut String, ctx: &Ctx) {
1102 out.push_str(open);
1103 self.write_sexp(self.node(expr), out, ctx);
1104 out.push(')');
1105 }
1106
1107 fn write_rest_sexp(&self, rest: PatId, out: &mut String, ctx: &Ctx) {
1109 if let Some(rest) = rest.get() {
1110 out.push_str(" ...");
1111 let rest = self.pat(rest);
1112 if rest != Pat::Discard {
1113 self.write_pat_sexp(rest, out, ctx);
1114 }
1115 }
1116 }
1117}
1118
1119impl StaticChunk {
1120 pub fn to_json(&self, id: NodeId, ctx: &Ctx) -> String {
1122 let mut out = String::new();
1123 self.write_json(self.node(id), &mut out, ctx);
1124 out
1125 }
1126
1127 fn write_json(&self, node: Node, out: &mut String, ctx: &Ctx) {
1128 match node {
1129 Node::Int(n) if (-(1 << 53)..=(1 << 53)).contains(&n) => {
1132 out.push_str(&n.to_string());
1133 }
1134 Node::Int(n) => write_tagged_int(&n.to_string(), out),
1135 Node::BigInt(id) => write_tagged_int(&self.int(id).to_string(), out),
1136 Node::Local { name, .. } => {
1137 out.push_str("{\"name\":");
1138 json_sym(name, out, ctx);
1139 out.push('}');
1140 }
1141 Node::Builtin(op) => {
1142 out.push_str("{\"name\":");
1143 json_str(op.name(), out);
1144 out.push('}');
1145 }
1146 Node::Home { leaf, .. } => match leaf {
1148 HomeLeaf::Module => out.push_str("{\"self\":true}"),
1149 HomeLeaf::New => out.push_str("{\"new\":true}"),
1150 HomeLeaf::Value => out.push_str("{\"value\":true}"),
1151 },
1152 Node::ModItem { name, .. } => {
1153 out.push_str("{\"moditem\":");
1154 json_sym(name, out, ctx);
1155 out.push('}');
1156 }
1157 Node::Recur(id) => {
1158 let module = self.module(id);
1159 out.push_str("{\"recur\":[");
1160 for (i, member) in self.items_by_text(module, ctx).into_iter().enumerate() {
1161 if i > 0 {
1162 out.push(',');
1163 }
1164 out.push('[');
1165 json_sym(member.name, out, ctx);
1166 out.push(',');
1167 self.write_json(self.node(member.node), out, ctx);
1168 out.push(']');
1169 }
1170 out.push_str("],\"body\":");
1171 self.write_json(self.node(module.body), out, ctx);
1172 out.push('}');
1173 }
1174 Node::Symbol(s) => {
1175 let mut dotted = String::from(".");
1176 ctx.with_str(s, |s| dotted.push_str(s));
1177 out.push_str("{\"sym\":");
1178 json_str(&dotted, out);
1179 out.push('}');
1180 }
1181 Node::Str(id) => {
1182 out.push_str("{\"str\":");
1183 json_str(self.str(id), out);
1184 out.push('}');
1185 }
1186 Node::App { len, args } => {
1188 let items = self.nodes(Run::App, args, len);
1189 let (tag, body) = match items[0] {
1190 Node::Home {
1191 leaf: HomeLeaf::New,
1192 ..
1193 } => ("new", &items[1..]),
1194 Node::Home {
1195 leaf: HomeLeaf::Value,
1196 ..
1197 } => ("value", &items[1..]),
1198 _ => ("app", items),
1199 };
1200 out.push_str("{\"");
1201 out.push_str(tag);
1202 out.push_str("\":");
1203 self.write_json_list(body, out, ctx);
1204 out.push('}');
1205 }
1206 Node::Abs(id) => {
1207 let code = self.lambda(id);
1208 let head = self.pats(Run::Head, code.head.first, code.head.len);
1209 self.write_abs_json(head, code.body, out, ctx);
1210 }
1211 Node::Unit => out.push_str("{\"unit\":true}"),
1212 Node::List { len, elems } => {
1213 out.push_str("{\"list\":");
1214 self.write_json_list(self.nodes(Run::List, elems, len), out, ctx);
1215 out.push('}');
1216 }
1217 Node::Map { len, entries } => {
1218 let all = self.nodes(Run::Map, entries, 2 * len);
1219 let (keys, values) = all.split_at(len as usize);
1220 out.push_str("{\"map\":[");
1221 for (i, (&key, &value)) in keys.iter().zip(values).enumerate() {
1222 if i > 0 {
1223 out.push(',');
1224 }
1225 out.push('[');
1226 self.write_json(key, out, ctx);
1227 out.push(',');
1228 self.write_json(value, out, ctx);
1229 out.push(']');
1230 }
1231 out.push_str("]}");
1232 }
1233 Node::Block { len, binds, exprs } => {
1236 let binds = self.pats(Run::Block, binds, len);
1237 let exprs = self.nodes(Run::Block, exprs, len);
1238 out.push_str("{\"block\":[");
1239 for (i, (&pat, &expr)) in binds.iter().zip(exprs).enumerate() {
1240 if i > 0 {
1241 out.push(',');
1242 }
1243 if pat == Pat::None {
1244 self.write_json(expr, out, ctx);
1245 } else {
1246 out.push_str("{\"bind\":{\"pat\":");
1247 self.write_pat_json(pat, out, ctx);
1248 out.push_str(",\"value\":");
1249 self.write_json(expr, out, ctx);
1250 out.push_str("}}");
1251 }
1252 }
1253 out.push_str("]}");
1254 }
1255 Node::Case { len, conds, exprs } => {
1257 let conds = self.pats(Run::Case, conds, len);
1258 let exprs = self.nodes(Run::Case, exprs, len + 1);
1259 let (bodies, subject) = exprs.split_at(len as usize);
1260 out.push_str("{\"case\":{\"subject\":");
1261 self.write_json(subject[0], out, ctx);
1262 out.push_str(",\"arms\":[");
1263 for (i, (&pat, &body)) in conds.iter().zip(bodies).enumerate() {
1264 if i > 0 {
1265 out.push(',');
1266 }
1267 out.push_str("{\"pat\":");
1268 self.write_pat_json(pat, out, ctx);
1269 out.push_str(",\"body\":");
1270 self.write_json(body, out, ctx);
1271 out.push('}');
1272 }
1273 out.push_str("]}}");
1274 }
1275 #[cfg(debug_assertions)]
1276 Node::Barrier { .. } => unreachable!("a barrier is not an operand"),
1277 }
1278 }
1279
1280 fn write_json_list(&self, nodes: &[Node], out: &mut String, ctx: &Ctx) {
1282 out.push('[');
1283 for (i, &node) in nodes.iter().enumerate() {
1284 if i > 0 {
1285 out.push(',');
1286 }
1287 self.write_json(node, out, ctx);
1288 }
1289 out.push(']');
1290 }
1291
1292 fn write_abs_json(&self, head: &[Pat], body: NodeId, out: &mut String, ctx: &Ctx) {
1294 out.push_str("{\"abs\":{\"param\":");
1295 self.write_pat_json(head[0], out, ctx);
1296 out.push_str(",\"body\":");
1297 if head.len() == 1 {
1298 self.write_json(self.node(body), out, ctx);
1299 } else {
1300 self.write_abs_json(&head[1..], body, out, ctx);
1301 }
1302 out.push_str("}}");
1303 }
1304
1305 fn write_pat_json(&self, pat: Pat, out: &mut String, ctx: &Ctx) {
1306 match pat {
1307 Pat::None => unreachable!("only a block clause's slot binds nothing"),
1308 Pat::Discard => out.push_str("{\"discard\":true}"),
1309 Pat::Unit => out.push_str("{\"unit\":true}"),
1310 Pat::Bind(name) => {
1311 out.push_str("{\"bind\":");
1312 json_sym(name, out, ctx);
1313 out.push('}');
1314 }
1315 Pat::At(name, inner) => {
1316 out.push_str("{\"at\":[");
1317 json_sym(name, out, ctx);
1318 out.push(',');
1319 self.write_pat_json(self.pat(inner), out, ctx);
1320 out.push_str("]}");
1321 }
1322 Pat::Equal(expr) => self.write_tagged_json("{\"eq\":", expr, out, ctx),
1323 Pat::Less(expr) => self.write_tagged_json("{\"lt\":", expr, out, ctx),
1324 Pat::Greater(expr) => self.write_tagged_json("{\"gt\":", expr, out, ctx),
1325 Pat::TypeKind(kind, inner) => {
1326 out.push_str("{\"as\":[");
1327 json_str(kind.name(), out);
1328 out.push(',');
1329 self.write_pat_json(self.pat(inner), out, ctx);
1330 out.push_str("]}");
1331 }
1332 Pat::TypeRef(proto, inner) => {
1333 out.push_str("{\"as\":[");
1334 self.write_json(self.node(proto), out, ctx);
1335 out.push(',');
1336 self.write_pat_json(self.pat(inner), out, ctx);
1337 out.push_str("]}");
1338 }
1339 Pat::Unwrap { inner, .. } => {
1340 out.push_str("{\"unwrap\":");
1341 self.write_pat_json(self.pat(inner), out, ctx);
1342 out.push('}');
1343 }
1344 Pat::Or(left, right) => {
1345 out.push_str("{\"or\":[");
1346 self.write_pat_json(self.pat(left), out, ctx);
1347 out.push(',');
1348 self.write_pat_json(self.pat(right), out, ctx);
1349 out.push_str("]}");
1350 }
1351 Pat::List { len, elems, rest } => {
1352 out.push_str("{\"listpat\":{\"elems\":[");
1353 for (i, &elem) in self.pats(Run::PatList, elems, len).iter().enumerate() {
1354 if i > 0 {
1355 out.push(',');
1356 }
1357 self.write_pat_json(elem, out, ctx);
1358 }
1359 out.push_str("],\"rest\":");
1360 self.write_rest_json(rest, out, ctx);
1361 out.push_str("}}");
1362 }
1363 Pat::Map { len, entries, rest } => {
1364 let all = self.pats(Run::PatMap, entries, 2 * len);
1365 let (keys, values) = all.split_at(len as usize);
1366 out.push_str("{\"mappat\":{\"entries\":[");
1367 for (i, (&key, &value)) in keys.iter().zip(values).enumerate() {
1368 if i > 0 {
1369 out.push(',');
1370 }
1371 out.push('[');
1372 match key {
1373 Pat::Lookup(expr) => self.write_tagged_json("{\"lookup\":", expr, out, ctx),
1374 key => {
1375 out.push_str("{\"capture\":");
1376 self.write_pat_json(key, out, ctx);
1377 out.push('}');
1378 }
1379 }
1380 out.push(',');
1381 self.write_pat_json(value, out, ctx);
1382 out.push(']');
1383 }
1384 out.push_str("],\"rest\":");
1385 self.write_rest_json(rest, out, ctx);
1386 out.push_str("}}");
1387 }
1388 Pat::Lookup(_) => unreachable!("a lookup is only a map pattern's key"),
1389 Pat::When { inner, gpat, cond } => {
1390 out.push_str("{\"when\":{\"inner\":");
1391 self.write_pat_json(self.pat(inner), out, ctx);
1392 out.push_str(",\"guard\":");
1393 match gpat.get() {
1394 None => out.push_str("null"),
1395 Some(gpat) => self.write_pat_json(self.pat(gpat), out, ctx),
1396 }
1397 out.push_str(",\"cond\":");
1398 self.write_json(self.node(cond), out, ctx);
1399 out.push_str("}}");
1400 }
1401 #[cfg(debug_assertions)]
1402 Pat::Barrier { .. } => unreachable!("a barrier is not an operand"),
1403 }
1404 }
1405
1406 fn write_tagged_json(&self, open: &str, expr: NodeId, out: &mut String, ctx: &Ctx) {
1407 out.push_str(open);
1408 self.write_json(self.node(expr), out, ctx);
1409 out.push('}');
1410 }
1411
1412 fn write_rest_json(&self, rest: PatId, out: &mut String, ctx: &Ctx) {
1414 match rest.get() {
1415 None => out.push_str("null"),
1416 Some(rest) => self.write_pat_json(self.pat(rest), out, ctx),
1417 }
1418 }
1419
1420 fn items_by_text(&self, module: &Module, ctx: &Ctx) -> Vec<&Member> {
1423 let mut items: Vec<&Member> = self.members(module)[..module.items as usize]
1424 .iter()
1425 .collect();
1426 items.sort_by_cached_key(|member| ctx.text(member.name));
1427 items
1428 }
1429}
1430
1431fn write_tagged_int(decimal: &str, out: &mut String) {
1433 out.push_str("{\"Int\":");
1434 json_str(decimal, out);
1435 out.push('}');
1436}
1437
1438pub(crate) fn push_sym(sym: Sym, out: &mut String, ctx: &Ctx) {
1440 ctx.with_str(sym, |s| out.push_str(s));
1441}
1442
1443fn json_sym(sym: Sym, out: &mut String, ctx: &Ctx) {
1445 ctx.with_str(sym, |s| json_str(s, out));
1446}
1447
1448pub(crate) fn json_str(s: &str, out: &mut String) {
1450 out.push('"');
1451 for ch in s.chars() {
1452 match ch {
1453 '"' => out.push_str("\\\""),
1454 '\\' => out.push_str("\\\\"),
1455 '\n' => out.push_str("\\n"),
1456 '\r' => out.push_str("\\r"),
1457 '\t' => out.push_str("\\t"),
1458 c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
1459 c => out.push(c),
1460 }
1461 }
1462 out.push('"');
1463}
1464
1465#[cfg(test)]
1466mod tests {
1467 use alloc::vec;
1468
1469 use super::*;
1470
1471 #[test]
1474 fn chunks_are_named_by_what_made_them() {
1475 let ctx = Ctx::new();
1476 let ids = [
1477 ChunkId::ELLY,
1478 ctx.parse("1").expect("parses"),
1479 ctx.compile_module("x = 1").expect("compiles"),
1480 ctx.parse("1").expect("parses"),
1481 ctx.compile_module_named("x = 1", &[], Some(Text::from("Foo")))
1482 .expect("compiles"),
1483 ];
1484 let names: Vec<String> = ids
1485 .iter()
1486 .map(|id| ctx.chunk(*id).name().to_string())
1487 .collect();
1488 assert_eq!(names, ["Elly", "__eval_0", "__module_1", "__eval_2", "Foo"]);
1489 }
1490
1491 #[test]
1493 fn a_failed_parse_adds_no_chunk() {
1494 let ctx = Ctx::new();
1495 assert!(ctx.parse("f x").is_err());
1496 assert!(ctx.compile_module("x = y").is_err());
1497 let id = ctx.parse("1").expect("parses");
1498 assert_eq!(id, ChunkId::at(1));
1499 assert_eq!(ctx.chunk(id).name().as_str(), "__eval_0");
1500 }
1501
1502 #[test]
1505 fn a_nested_module_comes_after_its_parent() {
1506 let ctx = Ctx::new();
1507 let src = "f = recur (g = recur (h = 1) h) g\nlocal l = 2\nconst c\n__main = f";
1508 let id = ctx.compile_module(src).expect("compiles");
1509 let chunk = ctx.chunk(id);
1510 let parents: Vec<ModId> = chunk.modules.iter().map(|m| m.parent).collect();
1511 assert_eq!(parents, vec![ModId::NONE, ModId(0), ModId(1)]);
1512 let top = chunk.module(chunk.top());
1513 let names: Vec<String> = chunk
1514 .members(top)
1515 .iter()
1516 .map(|m| ctx.text(m.name).to_string())
1517 .collect();
1518 assert_eq!(names, ["f", "l", "c"]);
1519 assert_eq!((top.items, top.locals), (1, 1));
1520 assert!(
1521 chunk.members(top)[2].node.get().is_none(),
1522 "an iota has no body"
1523 );
1524 assert_eq!(
1525 chunk.to_sexp(top.body, &ctx),
1526 "(moditem f)",
1527 "__main is the body"
1528 );
1529 }
1530}