1use alloc::collections::{BTreeMap, VecDeque};
6use alloc::string::ToString;
7use alloc::boxed::Box;
8use alloc::vec::Vec;
9use alloc::rc::Rc;
10
11use core::fmt::Write as _;
12use core::mem;
13
14#[cfg(feature = "serde")]
15use serde::{Serialize, Deserialize};
16
17use monostate::MustBeU128;
18use num_traits::FromPrimitive;
19use bin_pool::BinPool;
20
21use crate::*;
22use crate::meta::*;
23use crate::runtime::{Color, Number, NumberError, Event, KeyCode, Property, PrintStyle, Type, CustomTypes, System, ImageProperty, AudioProperty};
24use crate::util::LosslessJoin;
25use crate::vecmap::VecMap;
26use crate::compact_str::{CompactString, ToCompactString};
27
28#[cfg(feature = "std")]
30const BYTES_PER_LINE: usize = 10;
31
32const MAX_U64_ENCODED_BYTES: usize = 10;
34
35const SHRINK_CYCLES: usize = 3;
37
38#[derive(Debug)]
40pub enum CompileError<'a> {
41 UnsupportedStmt { kind: &'a ast::StmtKind },
42 UnsupportedExpr { kind: &'a ast::ExprKind },
43 UnsupportedEvent { kind: &'a ast::HatKind },
44 BadKeycode { key: &'a str },
45 InvalidLocation { loc: &'a str },
46 BadNumber { error: NumberError },
47 UndefinedRef { value: &'a ast::Value },
48 CurrentlyUnsupported { info: CompactString },
49 InvalidBlock { loc: Option<&'a str> },
50}
51impl From<NumberError> for CompileError<'_> { fn from(error: NumberError) -> Self { Self::BadNumber { error } } }
52
53#[derive(Clone, Copy, Debug, FromPrimitive)]
54#[repr(u8)]
55pub(crate) enum Relation {
56 Equal, NotEqual, Less, LessEq, Greater, GreaterEq,
57}
58#[derive(Clone, Copy, Debug, FromPrimitive)]
59#[repr(u8)]
60pub enum AbortMode {
61 All, Current, Others, MyOthers,
62}
63#[derive(Clone, Copy, Debug, FromPrimitive)]
64#[repr(u8)]
65pub(crate) enum TimeQuery {
66 Year, Month, Date, DayOfWeek, Hour, Minute, Second, UnixTimestampMs,
67}
68impl From<&'_ ast::TimeQuery> for TimeQuery {
69 fn from(value: &'_ ast::TimeQuery) -> Self {
70 match value {
71 ast::TimeQuery::Year => TimeQuery::Year,
72 ast::TimeQuery::Month => TimeQuery::Month,
73 ast::TimeQuery::Date => TimeQuery::Date,
74 ast::TimeQuery::DayOfWeek => TimeQuery::DayOfWeek,
75 ast::TimeQuery::Hour => TimeQuery::Hour,
76 ast::TimeQuery::Minute => TimeQuery::Minute,
77 ast::TimeQuery::Second => TimeQuery::Second,
78 ast::TimeQuery::UnixTimestampMs => TimeQuery::UnixTimestampMs,
79 }
80 }
81}
82
83#[derive(Clone, Copy, Debug, FromPrimitive)]
84#[repr(u8)]
85pub(crate) enum BinaryOp {
86 Add, Sub, Mul, Div, Mod, Pow, Log, Atan2,
87 SplitBy,
88 Range, Random,
89 StrGet,
90}
91#[derive(Clone, Copy, Debug, FromPrimitive)]
92#[repr(u8)]
93pub(crate) enum UnaryOp {
94 ToNumber,
95 Not,
96 Abs, Neg,
97 Sqrt,
98 Round, Floor, Ceil,
99 Sin, Cos, Tan,
100 Asin, Acos, Atan,
101 SplitLetter, SplitWord, SplitTab, SplitCR, SplitLF, SplitCsv, SplitJson,
102 StrLen,
103 StrGetLast, StrGetRandom,
104 UnicodeToChar, CharToUnicode,
105}
106
107impl From<Relation> for Instruction<'_> { fn from(relation: Relation) -> Self { Self::Cmp { relation } } }
108impl From<BinaryOp> for Instruction<'_> { fn from(op: BinaryOp) -> Self { Self::BinaryOp { op } } }
109impl From<UnaryOp> for Instruction<'_> { fn from(op: UnaryOp) -> Self { Self::UnaryOp { op } } }
110
111#[derive(Clone, Copy, Debug, FromPrimitive)]
112#[repr(u8)]
113pub(crate) enum VariadicOp {
114 Add, Mul, Min, Max,
115 StrCat,
116 MakeList, ListCat,
117}
118#[derive(Clone, Copy, Debug)]
119#[repr(u8)]
120pub(crate) enum VariadicLen {
121 Fixed(usize), Dynamic,
122}
123impl BinaryRead<'_> for VariadicLen {
124 fn read(code: &[u8], data: &[u8], start: usize) -> (Self, usize) {
125 match BinaryRead::read(code, data, start) {
126 (0, aft) => (VariadicLen::Dynamic, aft),
127 (x, aft) => (VariadicLen::Fixed(x - 1), aft),
128 }
129 }
130}
131impl BinaryWrite for VariadicLen {
132 fn append(val: &Self, code: &mut Vec<u8>, data: &mut BinPool, relocate_info: &mut Vec<RelocateInfo>) {
133 let raw = match val { VariadicLen::Fixed(x) => x + 1, VariadicLen::Dynamic => 0 };
134 BinaryWrite::append(&raw, code, data, relocate_info)
135 }
136}
137
138#[derive(Clone, Copy, Debug, FromPrimitive)]
139#[repr(u8)]
140pub(crate) enum BasicType {
141 Number, Text, Bool, List, Entity, Image, Audio,
142}
143impl BasicType {
144 pub fn to_type<C: CustomTypes<S>, S: System<C>>(self) -> Type<C, S> {
145 match self {
146 BasicType::Number => Type::Number,
147 BasicType::Text => Type::Text,
148 BasicType::Bool => Type::Bool,
149 BasicType::List => Type::List,
150 BasicType::Entity => Type::Entity,
151 BasicType::Image => Type::Image,
152 BasicType::Audio => Type::Audio,
153 }
154 }
155}
156
157enum InternalInstruction<'a> {
158 Illegal,
162 Valid(Instruction<'a>),
164}
165impl<'a> From<Instruction<'a>> for InternalInstruction<'a> { fn from(ins: Instruction<'a>) -> Self { Self::Valid(ins) } }
166
167#[derive(Debug)]
168#[repr(u8)]
169pub(crate) enum Instruction<'a> {
170 Yield,
172 WarpStart,
174 WarpStop,
176
177 PushBool { value: bool },
179 PushInt { value: i32 },
181 PushIntString { value: i32 },
183 PushNumber { value: f64 },
185 PushColor { value: Color },
187 PushString { value: &'a str },
189 PushVariable { var: &'a str },
191 PushEntity { name: &'a str },
193 PushSelf,
195 PopValue,
197
198 DupeValue { top_index: u8 },
201 SwapValues { top_index_1: u8, top_index_2: u8 },
203
204 TypeQuery { ty: BasicType },
206 ToBool,
208 ToNumber,
210
211 ListCons,
214 ListCdr,
217
218 ListFind,
221 ListContains,
224
225 ListIsEmpty,
227 ListLength,
229 ListDims,
231 ListRank,
233
234 ListRev,
236 ListFlatten,
238 ListReshape { len: VariadicLen },
241 ListCartesianProduct { len: VariadicLen },
244
245 ListJson,
247 ListCsv,
249 ListColumns,
251 ListLines,
253
254 ListInsert,
256 ListInsertLast,
258 ListInsertRandom,
260
261 ListGet,
263 ListGetLast,
265 ListGetRandom,
267
268 ListAssign,
270 ListAssignLast,
272 ListAssignRandom,
274
275 ListRemove,
277 ListRemoveLast,
279 ListRemoveAll,
281
282 ListPopFirstOrElse { goto: usize },
285
286 BinaryOp { op: BinaryOp },
288 VariadicOp { op: VariadicOp, len: VariadicLen },
290 Cmp { relation: Relation },
292 Identical,
294 UnaryOp { op: UnaryOp },
296
297 DeclareLocal { var: &'a str },
301 InitUpvar { var: &'a str },
305 Assign { var: &'a str },
307 BinaryOpAssign { var: &'a str, op: BinaryOp },
310
311 Watcher { create: bool, var: &'a str },
317 Pause,
319
320 Jump { to: usize },
322 ConditionalJump { to: usize, when: bool },
324
325 Call { pos: usize, tokens: &'a str },
329 MakeClosure { pos: usize, params: usize, tokens: &'a str },
333 CallClosure { new_entity: bool, args: usize },
337 ForkClosure { args: usize },
340 Return,
345 Abort { mode: AbortMode },
347
348 PushHandler { pos: usize, var: &'a str },
350 PopHandler,
352 Throw,
354
355 CallRpc { tokens: &'a str },
358 PushRpcError,
360
361 Syscall { len: VariadicLen },
365 PushSyscallError,
367
368 SendLocalMessage { wait: bool, target: bool },
372 PushLocalMessage,
374
375 Print { style: PrintStyle },
377 Ask,
380 PushAnswer,
383
384 ResetTimer,
386 PushTimer,
388 Sleep,
391 PushRealTime { query: TimeQuery },
393
394 SendNetworkMessage { tokens: &'a str, expect_reply: bool },
400 SendNetworkReply,
403
404 PushProperty { prop: Property },
406 SetProperty { prop: Property },
408 ChangeProperty { prop: Property },
410
411 PushCostume,
413 PushCostumeNumber,
415 PushCostumeList,
417 PushCostumeProperty { prop: ImageProperty },
419
420 SetCostume,
424 NextCostume,
427
428 PushSoundList,
430 PushSoundProperty { prop: AudioProperty },
432
433 PlaySound { blocking: bool },
437 PlayNotes { blocking: bool },
440 StopSounds,
442
443 Clone,
445 DeleteClone,
447
448 ClearEffects,
450 ClearDrawings,
452
453 GotoXY,
455 Goto,
458
459 PointTowardsXY,
461 PointTowards,
464
465 Forward,
467
468 UnknownBlock { name: &'a str, args: usize },
472}
473#[test]
474fn test_bin_sizes() {
475 if core::mem::size_of::<Instruction>() > 40 {
476 panic!("instructions are too big!");
477 }
478 if core::mem::size_of::<InternalInstruction>() > 40 {
479 panic!("internal instructions are too big!");
480 }
481}
482
483pub(crate) enum RelocateInfo {
484 Code { code_addr: usize },
485 Data { code_addr: usize },
486}
487
488pub(crate) trait BinaryRead<'a>: Sized {
489 fn read(code: &'a [u8], data: &'a [u8], start: usize) -> (Self, usize);
492}
493trait BinaryWrite: Sized {
494 fn append(val: &Self, code: &mut Vec<u8>, data: &mut BinPool, relocate_info: &mut Vec<RelocateInfo>);
498}
499
500impl BinaryRead<'_> for u8 { fn read(code: &[u8], _: &[u8], start: usize) -> (Self, usize) { (code[start], start + 1) } }
501impl BinaryWrite for u8 { fn append(val: &Self, code: &mut Vec<u8>, _: &mut BinPool, _: &mut Vec<RelocateInfo>) { code.push(*val) } }
502
503impl BinaryRead<'_> for bool { fn read(code: &[u8], _: &[u8], start: usize) -> (Self, usize) { (code[start] != 0, start + 1) } }
504impl BinaryWrite for bool { fn append(val: &Self, code: &mut Vec<u8>, _: &mut BinPool, _: &mut Vec<RelocateInfo>) { code.push(if *val { 1 } else { 0 }) } }
505
506macro_rules! read_write_u8_type {
507 ($($t:ty),+$(,)?) => {$(
508 impl BinaryRead<'_> for $t {
509 fn read(code: &[u8], _: &[u8], start: usize) -> (Self, usize) {
510 (Self::from_u8(code[start]).unwrap(), start + 1)
511 }
512 }
513 impl BinaryWrite for $t {
514 fn append(val: &Self, code: &mut Vec<u8>, _: &mut BinPool, _: &mut Vec<RelocateInfo>) {
515 debug_assert_eq!(mem::size_of::<Self>(), 1);
516 code.push((*val) as u8)
517 }
518 }
519 )*}
520}
521read_write_u8_type! { PrintStyle, ImageProperty, AudioProperty, Property, Relation, TimeQuery, BinaryOp, UnaryOp, VariadicOp, BasicType, AbortMode }
522
523fn encode_u64(mut val: u64, out: &mut Vec<u8>, bytes: Option<usize>) {
526 let mut blocks = ((64 - val.leading_zeros() as usize + 6) / 7).max(1);
527 if let Some(bytes) = bytes {
528 debug_assert!(bytes >= blocks);
529 blocks = bytes;
530 }
531
532 debug_assert!((1..=MAX_U64_ENCODED_BYTES).contains(&blocks));
533 for _ in 1..blocks {
534 out.push((val as u8 & 0x7f) | 0x80);
535 val >>= 7;
536 }
537 debug_assert!(val <= 0x7f);
538 out.push(val as u8);
539}
540fn decode_u64(data: &[u8], start: usize) -> (u64, usize) {
541 let (mut val, mut aft) = (0, start);
542 for &b in &data[start..] {
543 aft += 1;
544 if b & 0x80 == 0 { break }
545 }
546 for &b in data[start..aft].iter().rev() {
547 val = (val << 7) | (b & 0x7f) as u64;
548 }
549 (val, aft)
550}
551
552impl BinaryRead<'_> for u64 {
553 fn read(code: &[u8], _: &[u8], start: usize) -> (Self, usize) {
554 decode_u64(code, start)
555 }
556}
557impl BinaryWrite for u64 {
558 fn append(val: &Self, code: &mut Vec<u8>, _: &mut BinPool, _: &mut Vec<RelocateInfo>) {
559 encode_u64(*val, code, None)
560 }
561}
562
563#[test]
564fn test_binary_u64() {
565 let mut buf = vec![];
566 let tests = [
567 (0, [0x00].as_slice(), [0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00].as_slice()),
568 (1, [0x01].as_slice(), [0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00].as_slice()),
569 (2, [0x02].as_slice(), [0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00].as_slice()),
570 (0x53, [0x53].as_slice(), [0xd3, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00].as_slice()),
571 (0x7f, [0x7f].as_slice(), [0xff, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00].as_slice()),
572 (0x80, [0x80, 0x01].as_slice(), [0x80, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00].as_slice()),
573 (0x347462356236574, [0xf4, 0xca, 0x8d, 0xb1, 0xb5, 0xc4, 0xd1, 0xa3, 0x03].as_slice(), [0xf4, 0xca, 0x8d, 0xb1, 0xb5, 0xc4, 0xd1, 0xa3, 0x83, 0x00].as_slice()),
574 (u64::MAX >> 1, [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f].as_slice(), [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00].as_slice()),
575 (u64::MAX, [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01].as_slice(), [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01].as_slice()),
576 ];
577 for (v, expect_small, expect_large) in tests {
578 for (expect, expanded) in [(expect_small, false), (expect_large, true)] {
579 for prefix_bytes in 0..8 {
580 buf.clear();
581 buf.extend(core::iter::once(0x53).cycle().take(prefix_bytes));
582 encode_u64(v, &mut buf, if expanded { Some(MAX_U64_ENCODED_BYTES) } else { None });
583 assert!(buf[..prefix_bytes].iter().all(|&x| x == 0x53));
584 assert_eq!(&buf[prefix_bytes..], expect);
585 buf.extend(core::iter::once(0xff).cycle().take(8));
586 let (back, aft) = <u64 as BinaryRead>::read(&buf, &[], prefix_bytes);
587 assert_eq!(back, v);
588 assert_eq!(aft, prefix_bytes + expect.len());
589 }
590 }
591 }
592}
593
594impl BinaryRead<'_> for i32 {
597 fn read(code: &[u8], data: &[u8], start: usize) -> (Self, usize) {
598 let (raw, aft) = <u64 as BinaryRead>::read(code, data, start);
599 let v = (raw >> 1) as u32;
600 (if raw & 1 == 0 { v } else { !v } as i32, aft)
601 }
602}
603impl BinaryWrite for i32 {
604 fn append(val: &Self, code: &mut Vec<u8>, data: &mut BinPool, relocate_info: &mut Vec<RelocateInfo>) {
605 let v: u64 = ((*val) as u64) << 1;
606 BinaryWrite::append(&if v & 0x8000000000000000 == 0 { v } else { !v }, code, data, relocate_info)
607 }
608}
609
610impl BinaryRead<'_> for Color {
613 fn read(code: &[u8], _: &[u8], start: usize) -> (Self, usize) {
614 let mut res = [0u8; 4];
615 res[..].copy_from_slice(&code[start..start + 4]);
616 let [a, r, g, b] = res;
617 (Color { r, g, b, a }, start + 4)
618 }
619}
620impl BinaryWrite for Color {
621 fn append(val: &Self, code: &mut Vec<u8>, _: &mut BinPool, _: &mut Vec<RelocateInfo>) {
622 let Color { r, g, b, a } = *val;
623 code.extend_from_slice(&[a, r, g ,b]);
624 }
625}
626
627#[test]
628fn test_binary_i32() {
629 let mut buf = vec![];
630 let mut discard = (BinPool::new(), vec![]);
631 let tests = [
632 (0, [0x00].as_slice()),
633 (-1, [0x01].as_slice()),
634 (1, [0x02].as_slice()),
635 (-2, [0x03].as_slice()),
636 (2, [0x04].as_slice()),
637 (0x543245, [0x8a, 0xc9, 0xa1, 0x05].as_slice()),
638 (-0x376224, [0xc7, 0x88, 0xbb, 0x03].as_slice()),
639 (-i32::MAX, [0xfd, 0xff, 0xff, 0xff, 0x0f].as_slice()),
640 (i32::MAX, [0xfe, 0xff, 0xff, 0xff, 0x0f].as_slice()),
641 (i32::MIN, [0xff, 0xff, 0xff, 0xff, 0x0f].as_slice()),
642 ];
643 for (v, expect) in tests {
644 for prefix_bytes in 0..8 {
645 buf.clear();
646 buf.extend(core::iter::once(0x53).cycle().take(prefix_bytes));
647 BinaryWrite::append(&v, &mut buf, &mut discard.0, &mut discard.1);
648 assert_eq!(discard.0.len(), 0);
649 assert_eq!(discard.1.len(), 0);
650 assert!(buf[..prefix_bytes].iter().all(|&x| x == 0x53));
651 assert_eq!(&buf[prefix_bytes..], expect);
652 buf.extend(core::iter::once(0xff).cycle().take(8));
653 let (back, aft) = <i32 as BinaryRead>::read(&buf, &[], prefix_bytes);
654 assert_eq!(back, v);
655 assert_eq!(aft, prefix_bytes + expect.len());
656 }
657 }
658}
659
660impl BinaryRead<'_> for f64 {
661 fn read(code: &[u8], data: &[u8], start: usize) -> (Self, usize) {
662 let (v, aft) = <u64 as BinaryRead>::read(code, data, start);
663 (f64::from_bits(v.swap_bytes()), aft)
664 }
665}
666impl BinaryWrite for f64 {
667 fn append(val: &Self, code: &mut Vec<u8>, data: &mut BinPool, relocate_info: &mut Vec<RelocateInfo>) {
668 BinaryWrite::append(&val.to_bits().swap_bytes(), code, data, relocate_info)
669 }
670}
671
672impl BinaryRead<'_> for usize {
673 fn read(code: &[u8], data: &[u8], start: usize) -> (Self, usize) {
674 let (v, aft) = <u64 as BinaryRead>::read(code, data, start);
675 debug_assert!(v <= usize::MAX as u64);
676 (v as usize, aft)
677 }
678}
679impl BinaryWrite for usize {
680 fn append(val: &Self, code: &mut Vec<u8>, data: &mut BinPool, relocate_info: &mut Vec<RelocateInfo>) {
681 BinaryWrite::append(&(*val as u64), code, data, relocate_info)
682 }
683}
684
685impl<'a> BinaryRead<'a> for &'a str {
686 fn read(code: &'a [u8], data: &'a [u8], start: usize) -> (Self, usize) {
687 let (data_pos, aft) = <usize as BinaryRead>::read(code, data, start);
688 let (data_len, aft) = <usize as BinaryRead>::read(code, data, aft);
689 (core::str::from_utf8(&data[data_pos..data_pos + data_len]).unwrap(), aft)
690 }
691}
692
693impl<'a> BinaryRead<'a> for Instruction<'a> {
694 fn read(code: &'a [u8], data: &'a [u8], start: usize) -> (Self, usize) {
695 macro_rules! read_prefixed {
696 (Instruction::$root:ident) => {
697 (Instruction::$root, start + 1)
698 };
699 (Instruction::$root:ident { $($tt:tt)* } $(: $($vals:ident),+$(,)? )?) => {{
700 #[allow(unused_mut)]
701 let mut parsing_stop = start + 1;
702 $($(let $vals = {
703 let x = BinaryRead::read(code, data, parsing_stop);
704 parsing_stop = x.1;
705 x.0
706 };)*)?
707 (Instruction::$root { $($tt)* $($($vals),+ )? }, parsing_stop)
708 }};
709 }
710 match code[start] {
711 0 => read_prefixed!(Instruction::Yield),
712 1 => read_prefixed!(Instruction::WarpStart),
713 2 => read_prefixed!(Instruction::WarpStop),
714
715 3 => read_prefixed!(Instruction::PushBool { value: false }),
716 4 => read_prefixed!(Instruction::PushBool { value: true }),
717
718 5 => read_prefixed!(Instruction::PushInt {} : value),
719 6 => read_prefixed!(Instruction::PushIntString {} : value),
720 7 => read_prefixed!(Instruction::PushNumber {} : value),
721 8 => read_prefixed!(Instruction::PushColor {} : value),
722
723 9 => read_prefixed!(Instruction::PushString { value: "" }),
724 10 => read_prefixed!(Instruction::PushString {} : value),
725
726 11 => read_prefixed!(Instruction::PushVariable {} : var),
727 12 => read_prefixed!(Instruction::PushEntity {} : name),
728 13 => read_prefixed!(Instruction::PushSelf),
729
730 14 => read_prefixed!(Instruction::PopValue),
731
732 15 => read_prefixed!(Instruction::DupeValue { top_index: 0 }),
733 16 => read_prefixed!(Instruction::DupeValue { top_index: 1 }),
734 17 => read_prefixed!(Instruction::DupeValue { top_index: 2 }),
735 18 => read_prefixed!(Instruction::DupeValue { top_index: 3 }),
736
737 19 => read_prefixed!(Instruction::SwapValues { top_index_1: 0, top_index_2: 1 }),
738 20 => read_prefixed!(Instruction::SwapValues { top_index_1: 0, top_index_2: 2 }),
739 21 => read_prefixed!(Instruction::SwapValues { top_index_1: 1, top_index_2: 2 }),
740 22 => read_prefixed!(Instruction::SwapValues { top_index_1: 1, top_index_2: 3 }),
741
742 23 => read_prefixed!(Instruction::TypeQuery {} : ty),
743 24 => read_prefixed!(Instruction::ToBool),
744 25 => read_prefixed!(Instruction::ToNumber),
745
746 26 => read_prefixed!(Instruction::ListCons),
747 27 => read_prefixed!(Instruction::ListCdr),
748
749 28 => read_prefixed!(Instruction::ListFind),
750 29 => read_prefixed!(Instruction::ListContains),
751
752 30 => read_prefixed!(Instruction::ListIsEmpty),
753 31 => read_prefixed!(Instruction::ListLength),
754 32 => read_prefixed!(Instruction::ListDims),
755 33 => read_prefixed!(Instruction::ListRank),
756
757 34 => read_prefixed!(Instruction::ListRev),
758 35 => read_prefixed!(Instruction::ListFlatten),
759 36 => read_prefixed!(Instruction::ListReshape {} : len),
760 37 => read_prefixed!(Instruction::ListCartesianProduct {} : len),
761
762 38 => read_prefixed!(Instruction::ListJson),
763 39 => read_prefixed!(Instruction::ListCsv),
764 40 => read_prefixed!(Instruction::ListColumns),
765 41 => read_prefixed!(Instruction::ListLines),
766
767 42 => read_prefixed!(Instruction::ListInsert),
768 43 => read_prefixed!(Instruction::ListInsertLast),
769 44 => read_prefixed!(Instruction::ListInsertRandom),
770
771 45 => read_prefixed!(Instruction::ListGet),
772 46 => read_prefixed!(Instruction::ListGetLast),
773 47 => read_prefixed!(Instruction::ListGetRandom),
774
775 48 => read_prefixed!(Instruction::ListAssign),
776 49 => read_prefixed!(Instruction::ListAssignLast),
777 50 => read_prefixed!(Instruction::ListAssignRandom),
778
779 51 => read_prefixed!(Instruction::ListRemove),
780 52 => read_prefixed!(Instruction::ListRemoveLast),
781 53 => read_prefixed!(Instruction::ListRemoveAll),
782
783 54 => read_prefixed!(Instruction::ListPopFirstOrElse {} : goto),
784
785 55 => read_prefixed!(Instruction::Cmp { relation: Relation::Equal }),
786 56 => read_prefixed!(Instruction::Cmp { relation: Relation::NotEqual }),
787 57 => read_prefixed!(Instruction::Cmp { relation: Relation::Less }),
788 58 => read_prefixed!(Instruction::Cmp { relation: Relation::LessEq }),
789 59 => read_prefixed!(Instruction::Cmp { relation: Relation::Greater }),
790 60 => read_prefixed!(Instruction::Cmp { relation: Relation::GreaterEq }),
791 61 => read_prefixed!(Instruction::Identical),
792
793 62 => read_prefixed!(Instruction::BinaryOp { op: BinaryOp::Add }),
794 63 => read_prefixed!(Instruction::BinaryOp { op: BinaryOp::Sub }),
795 64 => read_prefixed!(Instruction::BinaryOp { op: BinaryOp::Mul }),
796 65 => read_prefixed!(Instruction::BinaryOp { op: BinaryOp::Div }),
797 66 => read_prefixed!(Instruction::BinaryOp {} : op),
798
799 67 => read_prefixed!(Instruction::VariadicOp { op: VariadicOp::Add, } : len),
800 68 => read_prefixed!(Instruction::VariadicOp { op: VariadicOp::Mul, } : len),
801 69 => read_prefixed!(Instruction::VariadicOp { op: VariadicOp::StrCat, } : len),
802 70 => read_prefixed!(Instruction::VariadicOp { op: VariadicOp::MakeList, } : len),
803 71 => read_prefixed!(Instruction::VariadicOp {} : op, len),
804
805 72 => read_prefixed!(Instruction::UnaryOp { op: UnaryOp::Not }),
806 73 => read_prefixed!(Instruction::UnaryOp { op: UnaryOp::Round }),
807 74 => read_prefixed!(Instruction::UnaryOp {} : op),
808
809 75 => read_prefixed!(Instruction::DeclareLocal {} : var),
810 76 => read_prefixed!(Instruction::InitUpvar {} : var),
811 77 => read_prefixed!(Instruction::Assign {} : var),
812
813 78 => read_prefixed!(Instruction::BinaryOpAssign { op: BinaryOp::Add, } : var),
814 79 => read_prefixed!(Instruction::BinaryOpAssign {} : var, op),
815
816 80 => read_prefixed!(Instruction::Watcher {} : create, var),
817 81 => read_prefixed!(Instruction::Pause),
818
819 82 => read_prefixed!(Instruction::Jump {} : to),
820 83 => read_prefixed!(Instruction::ConditionalJump { when: false, } : to),
821 84 => read_prefixed!(Instruction::ConditionalJump { when: true, } : to),
822
823 85 => read_prefixed!(Instruction::Call { tokens: "", } : pos),
824 86 => read_prefixed!(Instruction::Call {} : pos, tokens),
825 87 => read_prefixed!(Instruction::MakeClosure {} : pos, params, tokens),
826 88 => read_prefixed!(Instruction::CallClosure { new_entity: false, } : args),
827 89 => read_prefixed!(Instruction::CallClosure { new_entity: true, } : args),
828 90 => read_prefixed!(Instruction::ForkClosure {} : args),
829 91 => read_prefixed!(Instruction::Return),
830 92 => read_prefixed!(Instruction::Abort {} : mode),
831
832 93 => read_prefixed!(Instruction::PushHandler {} : pos, var),
833 94 => read_prefixed!(Instruction::PopHandler),
834 95 => read_prefixed!(Instruction::Throw),
835
836 96 => read_prefixed!(Instruction::CallRpc {} : tokens),
837 97 => read_prefixed!(Instruction::PushRpcError),
838
839 98 => read_prefixed!(Instruction::Syscall {} : len),
840 99 => read_prefixed!(Instruction::PushSyscallError),
841
842 100 => read_prefixed!(Instruction::SendLocalMessage { wait: false, target: false }),
843 101 => read_prefixed!(Instruction::SendLocalMessage { wait: false, target: true }),
844 102 => read_prefixed!(Instruction::SendLocalMessage { wait: true, target: false }),
845 103 => read_prefixed!(Instruction::SendLocalMessage { wait: true, target: true }),
846
847 104 => read_prefixed!(Instruction::PushLocalMessage),
848
849 105 => read_prefixed!(Instruction::Print { style: PrintStyle::Say }),
850 106 => read_prefixed!(Instruction::Print { style: PrintStyle::Think }),
851 107 => read_prefixed!(Instruction::Ask),
852 108 => read_prefixed!(Instruction::PushAnswer),
853
854 109 => read_prefixed!(Instruction::ResetTimer),
855 110 => read_prefixed!(Instruction::PushTimer),
856 111 => read_prefixed!(Instruction::Sleep),
857 112 => read_prefixed!(Instruction::PushRealTime {} : query),
858
859 113 => read_prefixed!(Instruction::SendNetworkMessage { expect_reply: false, } : tokens),
860 114 => read_prefixed!(Instruction::SendNetworkMessage { expect_reply: true, } : tokens),
861 115 => read_prefixed!(Instruction::SendNetworkReply),
862
863 116 => read_prefixed!(Instruction::PushProperty {} : prop),
864 117 => read_prefixed!(Instruction::SetProperty {} : prop),
865 118 => read_prefixed!(Instruction::ChangeProperty {} : prop),
866
867 119 => read_prefixed!(Instruction::PushCostume),
868 120 => read_prefixed!(Instruction::PushCostumeNumber),
869 121 => read_prefixed!(Instruction::PushCostumeList),
870 122 => read_prefixed!(Instruction::PushCostumeProperty {} : prop),
871
872 123 => read_prefixed!(Instruction::SetCostume),
873 124 => read_prefixed!(Instruction::NextCostume),
874
875 125 => read_prefixed!(Instruction::PushSoundList),
876 126 => read_prefixed!(Instruction::PushSoundProperty {} : prop),
877
878 127 => read_prefixed!(Instruction::PlaySound { blocking: true }),
879 128 => read_prefixed!(Instruction::PlaySound { blocking: false }),
880 129 => read_prefixed!(Instruction::PlayNotes { blocking: true }),
881 130 => read_prefixed!(Instruction::PlayNotes { blocking: false }),
882 131 => read_prefixed!(Instruction::StopSounds),
883
884 132 => read_prefixed!(Instruction::Clone),
885 133 => read_prefixed!(Instruction::DeleteClone),
886
887 134 => read_prefixed!(Instruction::ClearEffects),
888 135 => read_prefixed!(Instruction::ClearDrawings),
889
890 136 => read_prefixed!(Instruction::GotoXY),
891 137 => read_prefixed!(Instruction::Goto),
892
893 138 => read_prefixed!(Instruction::PointTowardsXY),
894 139 => read_prefixed!(Instruction::PointTowards),
895
896 140 => read_prefixed!(Instruction::Forward),
897
898 141 => read_prefixed!(Instruction::UnknownBlock {} : name, args),
899
900 _ => unreachable!(),
901 }
902 }
903}
904impl BinaryWrite for Instruction<'_> {
905 fn append(val: &Self, code: &mut Vec<u8>, data: &mut BinPool, relocate_info: &mut Vec<RelocateInfo>) {
906 macro_rules! append_prefixed {
907 ($op:literal $(: $($($vals:ident)+),+)?) => {{
908 code.push($op);
909 $($( append_prefixed!(@single $($vals)+); )*)?
910 }};
911 (@single move $val:ident) => {{
912 relocate_info.push(RelocateInfo::Code { code_addr: code.len() });
913 encode_u64(*$val as u64, code, Some(MAX_U64_ENCODED_BYTES));
914 }};
915 (@single move str $val:ident) => {{
916 let pool_index = data.add($val.as_bytes());
917 relocate_info.push(RelocateInfo::Data { code_addr: code.len() });
918 encode_u64(pool_index as u64, code, Some(MAX_U64_ENCODED_BYTES));
919 BinaryWrite::append(&$val.len(), code, data, relocate_info);
920 }};
921 (@single $val:ident) => { BinaryWrite::append($val, code, data, relocate_info) };
922 }
923 match val {
924 Instruction::Yield => append_prefixed!(0),
925 Instruction::WarpStart => append_prefixed!(1),
926 Instruction::WarpStop => append_prefixed!(2),
927
928 Instruction::PushBool { value: false } => append_prefixed!(3),
929 Instruction::PushBool { value: true } => append_prefixed!(4),
930
931 Instruction::PushInt { value } => append_prefixed!(5: value),
932 Instruction::PushIntString { value } => append_prefixed!(6: value),
933 Instruction::PushNumber { value } => append_prefixed!(7: value),
934 Instruction::PushColor { value } => append_prefixed!(8: value),
935
936 Instruction::PushString { value: "" } => append_prefixed!(9),
937 Instruction::PushString { value } => append_prefixed!(10: move str value),
938
939 Instruction::PushVariable { var } => append_prefixed!(11: move str var),
940 Instruction::PushEntity { name } => append_prefixed!(12: move str name),
941 Instruction::PushSelf => append_prefixed!(13),
942
943 Instruction::PopValue => append_prefixed!(14),
944
945 Instruction::DupeValue { top_index: 0 } => append_prefixed!(15),
946 Instruction::DupeValue { top_index: 1 } => append_prefixed!(16),
947 Instruction::DupeValue { top_index: 2 } => append_prefixed!(17),
948 Instruction::DupeValue { top_index: 3 } => append_prefixed!(18),
949 Instruction::DupeValue { top_index: _ } => unreachable!(),
950
951 Instruction::SwapValues { top_index_1: 0, top_index_2: 1 } => append_prefixed!(19),
952 Instruction::SwapValues { top_index_1: 0, top_index_2: 2 } => append_prefixed!(20),
953 Instruction::SwapValues { top_index_1: 1, top_index_2: 2 } => append_prefixed!(21),
954 Instruction::SwapValues { top_index_1: 1, top_index_2: 3 } => append_prefixed!(22),
955 Instruction::SwapValues { top_index_1: _, top_index_2: _ } => unreachable!(),
956
957 Instruction::TypeQuery { ty } => append_prefixed!(23: ty),
958 Instruction::ToBool => append_prefixed!(24),
959 Instruction::ToNumber => append_prefixed!(25),
960
961 Instruction::ListCons => append_prefixed!(26),
962 Instruction::ListCdr => append_prefixed!(27),
963
964 Instruction::ListFind => append_prefixed!(28),
965 Instruction::ListContains => append_prefixed!(29),
966
967 Instruction::ListIsEmpty => append_prefixed!(30),
968 Instruction::ListLength => append_prefixed!(31),
969 Instruction::ListDims => append_prefixed!(32),
970 Instruction::ListRank => append_prefixed!(33),
971
972 Instruction::ListRev => append_prefixed!(34),
973 Instruction::ListFlatten => append_prefixed!(35),
974 Instruction::ListReshape { len } => append_prefixed!(36: len),
975 Instruction::ListCartesianProduct { len } => append_prefixed!(37: len),
976
977 Instruction::ListJson => append_prefixed!(38),
978 Instruction::ListCsv => append_prefixed!(39),
979 Instruction::ListColumns => append_prefixed!(40),
980 Instruction::ListLines => append_prefixed!(41),
981
982 Instruction::ListInsert => append_prefixed!(42),
983 Instruction::ListInsertLast => append_prefixed!(43),
984 Instruction::ListInsertRandom => append_prefixed!(44),
985
986 Instruction::ListGet => append_prefixed!(45),
987 Instruction::ListGetLast => append_prefixed!(46),
988 Instruction::ListGetRandom => append_prefixed!(47),
989
990 Instruction::ListAssign => append_prefixed!(48),
991 Instruction::ListAssignLast => append_prefixed!(49),
992 Instruction::ListAssignRandom => append_prefixed!(50),
993
994 Instruction::ListRemove => append_prefixed!(51),
995 Instruction::ListRemoveLast => append_prefixed!(52),
996 Instruction::ListRemoveAll => append_prefixed!(53),
997
998 Instruction::ListPopFirstOrElse { goto } => append_prefixed!(54: move goto),
999
1000 Instruction::Cmp { relation: Relation::Equal } => append_prefixed!(55),
1001 Instruction::Cmp { relation: Relation::NotEqual } => append_prefixed!(56),
1002 Instruction::Cmp { relation: Relation::Less } => append_prefixed!(57),
1003 Instruction::Cmp { relation: Relation::LessEq } => append_prefixed!(58),
1004 Instruction::Cmp { relation: Relation::Greater } => append_prefixed!(59),
1005 Instruction::Cmp { relation: Relation::GreaterEq } => append_prefixed!(60),
1006 Instruction::Identical => append_prefixed!(61),
1007
1008 Instruction::BinaryOp { op: BinaryOp::Add } => append_prefixed!(62),
1009 Instruction::BinaryOp { op: BinaryOp::Sub } => append_prefixed!(63),
1010 Instruction::BinaryOp { op: BinaryOp::Mul } => append_prefixed!(64),
1011 Instruction::BinaryOp { op: BinaryOp::Div } => append_prefixed!(65),
1012 Instruction::BinaryOp { op } => append_prefixed!(66: op),
1013
1014 Instruction::VariadicOp { op: VariadicOp::Add, len } => append_prefixed!(67: len),
1015 Instruction::VariadicOp { op: VariadicOp::Mul, len } => append_prefixed!(68: len),
1016 Instruction::VariadicOp { op: VariadicOp::StrCat, len } => append_prefixed!(69: len),
1017 Instruction::VariadicOp { op: VariadicOp::MakeList, len } => append_prefixed!(70: len),
1018 Instruction::VariadicOp { op, len } => append_prefixed!(71: op, len),
1019
1020 Instruction::UnaryOp { op: UnaryOp::Not } => append_prefixed!(72),
1021 Instruction::UnaryOp { op: UnaryOp::Round } => append_prefixed!(73),
1022 Instruction::UnaryOp { op } => append_prefixed!(74: op),
1023
1024 Instruction::DeclareLocal { var } => append_prefixed!(75: move str var),
1025 Instruction::InitUpvar { var } => append_prefixed!(76: move str var),
1026 Instruction::Assign { var } => append_prefixed!(77: move str var),
1027
1028 Instruction::BinaryOpAssign { var, op: BinaryOp::Add } => append_prefixed!(78: move str var),
1029 Instruction::BinaryOpAssign { var, op } => append_prefixed!(79: move str var, op),
1030
1031 Instruction::Watcher { create, var } => append_prefixed!(80: create, move str var),
1032 Instruction::Pause => append_prefixed!(81),
1033
1034 Instruction::Jump { to } => append_prefixed!(82: move to),
1035 Instruction::ConditionalJump { to, when: false } => append_prefixed!(83: move to),
1036 Instruction::ConditionalJump { to, when: true } => append_prefixed!(84: move to),
1037
1038 Instruction::Call { pos, tokens: "" } => append_prefixed!(85: move pos),
1039 Instruction::Call { pos, tokens } => append_prefixed!(86: move pos, move str tokens),
1040 Instruction::MakeClosure { pos, params, tokens } => append_prefixed!(87: move pos, params, move str tokens),
1041 Instruction::CallClosure { new_entity: false, args } => append_prefixed!(88: args),
1042 Instruction::CallClosure { new_entity: true, args } => append_prefixed!(89: args),
1043 Instruction::ForkClosure { args } => append_prefixed!(90: args),
1044 Instruction::Return => append_prefixed!(91),
1045 Instruction::Abort { mode } => append_prefixed!(92: mode),
1046
1047 Instruction::PushHandler { pos, var } => append_prefixed!(93: move pos, move str var),
1048 Instruction::PopHandler => append_prefixed!(94),
1049 Instruction::Throw => append_prefixed!(95),
1050
1051 Instruction::CallRpc { tokens } => append_prefixed!(96: move str tokens),
1052 Instruction::PushRpcError => append_prefixed!(97),
1053
1054 Instruction::Syscall { len } => append_prefixed!(98: len),
1055 Instruction::PushSyscallError => append_prefixed!(99),
1056
1057 Instruction::SendLocalMessage { wait: false, target: false } => append_prefixed!(100),
1058 Instruction::SendLocalMessage { wait: false, target: true } => append_prefixed!(101),
1059 Instruction::SendLocalMessage { wait: true, target: false } => append_prefixed!(102),
1060 Instruction::SendLocalMessage { wait: true, target: true } => append_prefixed!(103),
1061
1062 Instruction::PushLocalMessage => append_prefixed!(104),
1063
1064 Instruction::Print { style: PrintStyle::Say } => append_prefixed!(105),
1065 Instruction::Print { style: PrintStyle::Think } => append_prefixed!(106),
1066 Instruction::Ask => append_prefixed!(107),
1067 Instruction::PushAnswer => append_prefixed!(108),
1068
1069 Instruction::ResetTimer => append_prefixed!(109),
1070 Instruction::PushTimer => append_prefixed!(110),
1071 Instruction::Sleep => append_prefixed!(111),
1072 Instruction::PushRealTime { query } => append_prefixed!(112: query),
1073
1074 Instruction::SendNetworkMessage { tokens, expect_reply: false } => append_prefixed!(113: move str tokens),
1075 Instruction::SendNetworkMessage { tokens, expect_reply: true } => append_prefixed!(114: move str tokens),
1076 Instruction::SendNetworkReply => append_prefixed!(115),
1077
1078 Instruction::PushProperty { prop } => append_prefixed!(116: prop),
1079 Instruction::SetProperty { prop } => append_prefixed!(117: prop),
1080 Instruction::ChangeProperty { prop } => append_prefixed!(118: prop),
1081
1082 Instruction::PushCostume => append_prefixed!(119),
1083 Instruction::PushCostumeNumber => append_prefixed!(120),
1084 Instruction::PushCostumeList => append_prefixed!(121),
1085 Instruction::PushCostumeProperty { prop } => append_prefixed!(122: prop),
1086
1087 Instruction::SetCostume => append_prefixed!(123),
1088 Instruction::NextCostume => append_prefixed!(124),
1089
1090 Instruction::PushSoundList => append_prefixed!(125),
1091 Instruction::PushSoundProperty { prop } => append_prefixed!(126: prop),
1092
1093 Instruction::PlaySound { blocking: true } => append_prefixed!(127),
1094 Instruction::PlaySound { blocking: false } => append_prefixed!(128),
1095 Instruction::PlayNotes { blocking: true } => append_prefixed!(129),
1096 Instruction::PlayNotes { blocking: false } => append_prefixed!(130),
1097 Instruction::StopSounds => append_prefixed!(131),
1098
1099 Instruction::Clone => append_prefixed!(132),
1100 Instruction::DeleteClone => append_prefixed!(133),
1101
1102 Instruction::ClearEffects => append_prefixed!(134),
1103 Instruction::ClearDrawings => append_prefixed!(135),
1104
1105 Instruction::GotoXY => append_prefixed!(136),
1106 Instruction::Goto => append_prefixed!(137),
1107
1108 Instruction::PointTowardsXY => append_prefixed!(138),
1109 Instruction::PointTowards => append_prefixed!(139),
1110
1111 Instruction::Forward => append_prefixed!(140),
1112
1113 Instruction::UnknownBlock { name, args } => append_prefixed!(141: move str name, args),
1114 }
1115 }
1116}
1117
1118#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1129pub struct ByteCode {
1130 #[allow(dead_code)] tag: MustBeU128<FINGERPRINT>,
1131
1132 pub(crate) code: Box<[u8]>,
1133 pub(crate) data: Box<[u8]>,
1134}
1135
1136#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1137pub(crate) enum InitValue {
1138 Bool(bool),
1139 Number(Number),
1140 Ref(usize),
1141}
1142#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1143pub(crate) enum RefValue {
1144 List(Vec<InitValue>),
1145 Image(Vec<u8>, Option<(Number, Number)>, CompactString),
1146 Audio(Vec<u8>, CompactString),
1147 Text(CompactString),
1148}
1149#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1150pub(crate) struct EntityInitInfo {
1151 pub(crate) name: CompactString,
1152 pub(crate) fields: VecMap<CompactString, InitValue, false>,
1153 pub(crate) costumes: VecMap<CompactString, InitValue, false>,
1154 pub(crate) sounds: VecMap<CompactString, InitValue, false>,
1155 pub(crate) scripts: Vec<(Event, usize)>,
1156
1157 pub(crate) visible: bool,
1158 pub(crate) active_costume: Option<usize>,
1159 pub(crate) size: Number,
1160 pub(crate) color: (u8, u8, u8, u8),
1161 pub(crate) pos: (Number, Number),
1162 pub(crate) heading: Number,
1163}
1164#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1173pub struct InitInfo {
1174 #[allow(dead_code)] tag: MustBeU128<FINGERPRINT>,
1175
1176 pub(crate) proj_name: CompactString,
1177 pub(crate) ref_values: Vec<RefValue>,
1178 pub(crate) globals: VecMap<CompactString, InitValue, false>,
1179 pub(crate) entities: Vec<EntityInitInfo>,
1180}
1181
1182pub struct EntityScriptInfo<'a> {
1184 pub funcs: Vec<(&'a ast::Function, usize)>,
1185 pub scripts: Vec<(&'a ast::Script, usize)>,
1186}
1187pub struct ScriptInfo<'a> {
1196 pub funcs: Vec<(&'a ast::Function, usize)>,
1197 pub entities: Vec<(&'a ast::Entity, EntityScriptInfo<'a>)>,
1198}
1199
1200struct LocationTokenizer<'a> {
1201 src: &'a str,
1202 state: bool,
1203}
1204impl<'a> LocationTokenizer<'a> {
1205 fn new(src: &'a str) -> Self {
1206 Self { src, state: false }
1207 }
1208}
1209impl<'a> Iterator for LocationTokenizer<'a> {
1210 type Item = &'a str;
1211 fn next(&mut self) -> Option<Self::Item> {
1212 if self.src.is_empty() {
1213 return None;
1214 }
1215
1216 let p = self.src.char_indices().find(|x| self.state ^ (x.1 == '-' || ('0'..='9').contains(&x.1))).map(|x| x.0).unwrap_or(self.src.len());
1217 let res;
1218 (res, self.src) = self.src.split_at(p);
1219 self.state ^= true;
1220 Some(res)
1221 }
1222}
1223#[test]
1224fn test_locations_tokenizer() {
1225 assert_eq!(LocationTokenizer::new("").collect::<Vec<_>>(), &[] as &[&str]);
1226 assert_eq!(LocationTokenizer::new("x").collect::<Vec<_>>(), &["x"]);
1227 assert_eq!(LocationTokenizer::new("3").collect::<Vec<_>>(), &["", "3"]);
1228 assert_eq!(LocationTokenizer::new("collab_").collect::<Vec<_>>(), &["collab_"]);
1229 assert_eq!(LocationTokenizer::new("collab_-1").collect::<Vec<_>>(), &["collab_", "-1"]);
1230 assert_eq!(LocationTokenizer::new("collab_23_43").collect::<Vec<_>>(), &["collab_", "23", "_", "43"]);
1231 assert_eq!(LocationTokenizer::new("cab_=_2334fhd__43").collect::<Vec<_>>(), &["cab_=_", "2334", "fhd__", "43"]);
1232 assert_eq!(LocationTokenizer::new("cab_=_2334fhd__43__").collect::<Vec<_>>(), &["cab_=_", "2334", "fhd__", "43", "__"]);
1233 assert_eq!(LocationTokenizer::new("-4cab_=_2334fhd__43__").collect::<Vec<_>>(), &["", "-4", "cab_=_", "2334", "fhd__", "43", "__"]);
1234 assert_eq!(LocationTokenizer::new("714cab_=_2334fhd__43__").collect::<Vec<_>>(), &["", "714", "cab_=_", "2334", "fhd__", "43", "__"]);
1235}
1236
1237#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1245#[derive(Debug)]
1246pub struct Locations {
1247 #[allow(dead_code)] tag: MustBeU128<FINGERPRINT>,
1248
1249 prefix: CompactString,
1250 separator: CompactString,
1251 suffix: CompactString,
1252
1253 base_token: isize,
1254 value_data: Vec<u8>,
1255 locs: Vec<(usize, usize)>,
1256}
1257impl Locations {
1258 fn condense<'a>(orig_locs: &BTreeMap<usize, &'a str>) -> Result<Self, CompileError<'a>> {
1259 if orig_locs.is_empty() {
1260 return Ok(Self {
1261 tag: Default::default(),
1262
1263 prefix: CompactString::default(),
1264 separator: CompactString::default(),
1265 suffix: CompactString::default(),
1266
1267 base_token: 0,
1268 value_data: Default::default(),
1269 locs: Default::default(),
1270 });
1271 }
1272
1273 let (prefix, suffix) = {
1274 let mut tokens = LocationTokenizer::new(*orig_locs.values().next().unwrap()).enumerate();
1275 let prefix = tokens.next().map(|x| CompactString::new(x.1)).unwrap_or_default();
1276 let suffix = tokens.last().filter(|x| x.0 & 1 == 0).map(|x| CompactString::new(x.1)).unwrap_or_default();
1277 (prefix, suffix)
1278 };
1279 let mut separator = None;
1280
1281 let mut value_map = Vec::with_capacity(orig_locs.len());
1282 for (&pos, &loc) in orig_locs.iter() {
1283 let mut tokens = LocationTokenizer::new(loc).peekable();
1284 let mut values = vec![];
1285 for i in 0.. {
1286 match tokens.next() {
1287 Some(token) => {
1288 if i & 1 != 0 {
1289 let v: isize = match token.parse() {
1290 Ok(x) => x,
1291 Err(_) => return Err(CompileError::InvalidLocation { loc }),
1292 };
1293 if v.to_string() != token {
1294 return Err(CompileError::InvalidLocation { loc });
1295 }
1296 values.push(v);
1297 }
1298 else if i == 0 {
1299 if token != prefix {
1300 return Err(CompileError::InvalidLocation { loc });
1301 }
1302 }
1303 else if tokens.peek().is_none() {
1304 if token != suffix {
1305 return Err(CompileError::InvalidLocation { loc });
1306 }
1307 } else {
1308 match &separator {
1309 Some(separator) => if token != *separator {
1310 return Err(CompileError::InvalidLocation { loc });
1311 }
1312 None => separator = Some(CompactString::new(token)),
1313 }
1314 }
1315 }
1316 None => {
1317 if i < 2 || (i & 1 == 0 && !suffix.is_empty()) {
1318 return Err(CompileError::InvalidLocation { loc });
1319 }
1320 break;
1321 }
1322 }
1323 }
1324 debug_assert!(values.len() >= 1);
1325 value_map.push((pos, values));
1326 }
1327
1328 let base_token = value_map.iter().flat_map(|x| &x.1).copied().min().unwrap_or(0);
1329
1330 let mut value_data = Vec::with_capacity(value_map.len());
1331 let mut locs = Vec::with_capacity(value_map.len());
1332 for (pos, toks) in value_map {
1333 locs.push((pos, value_data.len()));
1334 for tok in toks {
1335 encode_u64((tok - base_token + 1) as u64, &mut value_data, None);
1336 }
1337 encode_u64(0, &mut value_data, None); }
1339
1340 let res = Self { tag: Default::default(), separator: separator.unwrap_or_default(), prefix, suffix, base_token, value_data, locs };
1341
1342 #[cfg(test)]
1343 {
1344 for pos in 0..orig_locs.iter().last().unwrap().0 + 16 {
1345 let expected = orig_locs.range(pos + 1..).next().map(|x| *x.1);
1346 let actual = res.lookup(pos);
1347 assert_eq!(expected, actual.as_deref());
1348 }
1349 }
1350
1351 Ok(res)
1352 }
1353
1354 pub fn lookup(&self, bytecode_pos: usize) -> Option<CompactString> {
1361 let mut start = {
1362 let p = self.locs.partition_point(|x| x.0 <= bytecode_pos);
1363 debug_assert!(p <= self.locs.len());
1364 self.locs.get(p)?.1
1365 };
1366
1367 let mut res = self.prefix.clone();
1368 let mut first = true;
1369 loop {
1370 let (v, aft) = decode_u64(&self.value_data, start);
1371 if v == 0 {
1372 break;
1373 }
1374
1375 start = aft;
1376 if !first {
1377 res.push_str(&self.separator);
1378 }
1379 first = false;
1380 write!(res, "{}", v as isize - 1 + self.base_token).unwrap();
1381 }
1382 res.push_str(&self.suffix);
1383 Some(res)
1384 }
1385}
1386#[test]
1387fn test_locations_formats() {
1388 fn test_vals<'a>(orig_locs: &BTreeMap<usize, &'a str>) -> Result<(), CompileError<'a>> {
1389 let condensed = Locations::condense(orig_locs)?;
1390
1391 let mut back = BTreeMap::new();
1392 for &k in orig_locs.keys() {
1393 back.insert(k, condensed.lookup(k - 1).unwrap());
1394 }
1395
1396 if orig_locs.len() != back.len() || orig_locs.iter().zip(back.iter()).any(|(a, b)| a.0 != b.0 || a.1 != b.1) {
1397 panic!("expected {orig_locs:?}\ngot {back:?}");
1398 }
1399
1400 Ok(())
1401 }
1402 macro_rules! map {
1403 ($($k:expr => $v:expr),*$(,)?) => {{
1404 #[allow(unused_mut)]
1405 let mut res = BTreeMap::new();
1406 $(res.insert($k, $v);)*
1407 res
1408 }}
1409 }
1410
1411 test_vals(&map! { }).unwrap();
1412 test_vals(&map! { 43 => "collab_3_4" }).unwrap();
1413 test_vals(&map! { 43 => "collab_3_4", 2 => "collab_-2_-34" }).unwrap();
1414 test_vals(&map! { 43 => "collab_3_4", 2 => "collab_-02_-34" }).unwrap_err();
1415 test_vals(&map! { 43 => "collab_3_4", 2 => "coLlab_-2_-34" }).unwrap_err();
1416 test_vals(&map! { 43 => "_31_-24", 2 => "_-2_-342", 6 => "_23" }).unwrap();
1417 test_vals(&map! { 43 => "31_-24", 2 => "-2_-342", 6 => "23" }).unwrap();
1418 test_vals(&map! { 43 => "31_-24", 2 => "-2_-342", 6 => "g23" }).unwrap_err();
1419 test_vals(&map! { 43 => "31_-24", 2 => "g-2_-342", 6 => "23" }).unwrap_err();
1420 test_vals(&map! { 43 => "g31_-24", 2 => "g-2_-342", 6 => "g23" }).unwrap();
1421 test_vals(&map! { 43 => "31_-24", 2 => "-2_-342<", 6 => "23" }).unwrap_err();
1422 test_vals(&map! { 43 => "31_-24", 2 => "-2_-342", 6 => "23&" }).unwrap_err();
1423 test_vals(&map! { 43 => "31_-24&", 2 => "-2_-342&", 6 => "23&" }).unwrap();
1424 test_vals(&map! { 43 => "31::-24&", 2 => "-2::-342&", 6 => "23&" }).unwrap();
1425 test_vals(&map! { 43 => "31::-24&", 2 => "-2::-342&", 6 => "23&", 7 => "&" }).unwrap_err();
1426 test_vals(&map! { 43 => "31::-24&", 2 => "-2::-342&", 6 => "23&", 0 => "&" }).unwrap_err();
1427 test_vals(&map! { 43 => "31::-24&", 2 => "-2::-342&", 6 => "23&" }).unwrap();
1428 test_vals(&map! { 43 => "31::-24&", 2 => "-2::-342&", 6 => "23&&" }).unwrap_err();
1429 test_vals(&map! { 43 => "<>31::-24&", 2 => "<>-2::-342:.:5::-22&", 6 => "<>23&" }).unwrap_err();
1430 test_vals(&map! { 43 => "<>31::-24&", 2 => "<>-2::-342::5::-22&", 6 => "<>23&" }).unwrap();
1431 test_vals(&map! { 43 => "<>31::-24&", 2 => "<>-2::-342::5::-22&", 6 => "<>&" }).unwrap_err();
1432 test_vals(&map! { 43 => "<>31::-24&", 2 => "<>-2::-342::5::-22&", 0 => "<>&" }).unwrap_err();
1433 test_vals(&map! { 43 => "<>31::-24&", 2 => "<>-2::-342::5::-22&", 6 => "<>23" }).unwrap_err();
1434 test_vals(&map! { 43 => "31,-24", 2 => "-2,-342,5,-22", 6 => "23", 7 => "" }).unwrap_err();
1435 test_vals(&map! { 43 => "31,-24", 2 => "-2,-342,5,-22", 6 => "23", 0 => "" }).unwrap_err();
1436}
1437
1438struct ByteCodeBuilder<'a: 'b, 'b> {
1439 ins: Vec<InternalInstruction<'b>>,
1440 call_holes: Vec<(usize, &'a ast::FnRef, Option<&'a ast::Entity>)>, closure_holes: VecDeque<(usize, &'a [ast::VariableDef], &'a [ast::VariableRef], &'a [ast::Stmt], Option<&'a ast::Entity>)>, ins_locations: BTreeMap<usize, &'a str>,
1443 string_arena: &'b typed_arena::Arena<CompactString>,
1444}
1445impl<'a: 'b, 'b> ByteCodeBuilder<'a, 'b> {
1446 fn append_simple_ins(&mut self, entity: Option<&'a ast::Entity>, values: &[&'a ast::Expr], op: Instruction<'a>) -> Result<(), CompileError<'a>> {
1447 for value in values {
1448 self.append_expr(value, entity)?;
1449 }
1450 self.ins.push(op.into());
1451 Ok(())
1452 }
1453 fn append_variadic_op(&mut self, entity: Option<&'a ast::Entity>, src: &'a ast::Expr, op: VariadicOp) -> Result<(), CompileError<'a>> {
1454 let len = self.append_variadic(src, entity)?;
1455 self.ins.push(Instruction::VariadicOp { op, len}.into());
1456 Ok(())
1457 }
1458 fn append_variadic(&mut self, src: &'a ast::Expr, entity: Option<&'a ast::Entity>) -> Result<VariadicLen, CompileError<'a>> {
1459 Ok(match &src.kind {
1460 ast::ExprKind::Value(ast::Value::List(values, _)) => {
1461 for value in values.iter() {
1462 self.append_value(value, entity)?;
1463 }
1464 VariadicLen::Fixed(values.len())
1465 }
1466 ast::ExprKind::MakeList { values } => {
1467 for value in values.iter() {
1468 self.append_expr(value, entity)?;
1469 }
1470 VariadicLen::Fixed(values.len())
1471 }
1472 _ => {
1473 self.append_expr(src, entity)?;
1474 VariadicLen::Dynamic
1475 }
1476 })
1477 }
1478 fn append_value(&mut self, value: &'a ast::Value, entity: Option<&'a ast::Entity>) -> Result<(), CompileError<'a>> {
1479 match value {
1480 ast::Value::Number(v) => self.ins.push(Instruction::PushNumber { value: *v }.into()),
1481 ast::Value::String(v) => self.ins.push(Instruction::PushString { value: v }.into()),
1482 ast::Value::Constant(v) => self.ins.push(Instruction::PushNumber { value: match v {
1483 ast::Constant::Pi => core::f64::consts::PI,
1484 ast::Constant::E => core::f64::consts::E,
1485 }}.into()),
1486 ast::Value::Bool(v) => self.ins.push(Instruction::PushBool { value: *v }.into()),
1487 ast::Value::List(values, _) => {
1488 for v in values {
1489 self.append_value(v, entity)?;
1490 }
1491 self.ins.push(Instruction::VariadicOp { op: VariadicOp::MakeList, len: VariadicLen::Fixed(values.len()) }.into());
1492 }
1493 ast::Value::Image(_) => unreachable!(), ast::Value::Audio(_) => unreachable!(), ast::Value::Ref(_) => unreachable!(), }
1497 Ok(())
1498 }
1499 fn append_expr(&mut self, expr: &'a ast::Expr, entity: Option<&'a ast::Entity>) -> Result<(), CompileError<'a>> {
1500 match &expr.kind {
1501 ast::ExprKind::Value(v) => self.append_value(v, entity)?,
1502 ast::ExprKind::Variable { var } => self.ins.push(Instruction::PushVariable { var: &var.trans_name }.into()),
1503 ast::ExprKind::Atan2 { y, x } => self.append_simple_ins(entity, &[y, x], BinaryOp::Atan2.into())?,
1504 ast::ExprKind::Sub { left, right } => self.append_simple_ins(entity, &[left, right], BinaryOp::Sub.into())?,
1505 ast::ExprKind::Div { left, right } => self.append_simple_ins(entity, &[left, right], BinaryOp::Div.into())?,
1506 ast::ExprKind::Pow { base, power } => self.append_simple_ins(entity, &[base, power], BinaryOp::Pow.into())?,
1507 ast::ExprKind::Mod { left, right } => self.append_simple_ins(entity, &[left, right], BinaryOp::Mod.into())?,
1508 ast::ExprKind::Log { base, value } => self.append_simple_ins(entity, &[base, value], BinaryOp::Log.into())?,
1509 ast::ExprKind::Neg { value } => self.append_simple_ins(entity, &[value], UnaryOp::Neg.into())?,
1510 ast::ExprKind::Abs { value } => self.append_simple_ins(entity, &[value], UnaryOp::Abs.into())?,
1511 ast::ExprKind::Sqrt { value } => self.append_simple_ins(entity, &[value], UnaryOp::Sqrt.into())?,
1512 ast::ExprKind::Sin { value } => self.append_simple_ins(entity, &[value], UnaryOp::Sin.into())?,
1513 ast::ExprKind::Cos { value } => self.append_simple_ins(entity, &[value], UnaryOp::Cos.into())?,
1514 ast::ExprKind::Tan { value } => self.append_simple_ins(entity, &[value], UnaryOp::Tan.into())?,
1515 ast::ExprKind::Asin { value } => self.append_simple_ins(entity, &[value], UnaryOp::Asin.into())?,
1516 ast::ExprKind::Acos { value } => self.append_simple_ins(entity, &[value], UnaryOp::Acos.into())?,
1517 ast::ExprKind::Atan { value } => self.append_simple_ins(entity, &[value], UnaryOp::Atan.into())?,
1518 ast::ExprKind::Round { value } => self.append_simple_ins(entity, &[value], UnaryOp::Round.into())?,
1519 ast::ExprKind::Floor { value } => self.append_simple_ins(entity, &[value], UnaryOp::Floor.into())?,
1520 ast::ExprKind::Ceil { value } => self.append_simple_ins(entity, &[value], UnaryOp::Ceil.into())?,
1521 ast::ExprKind::Not { value } => self.append_simple_ins(entity, &[value], UnaryOp::Not.into())?,
1522 ast::ExprKind::StrLen { value } => self.append_simple_ins(entity, &[value], UnaryOp::StrLen.into())?,
1523 ast::ExprKind::UnicodeToChar { value } => self.append_simple_ins(entity, &[value], UnaryOp::UnicodeToChar.into())?,
1524 ast::ExprKind::CharToUnicode { value } => self.append_simple_ins(entity, &[value], UnaryOp::CharToUnicode.into())?,
1525 ast::ExprKind::Greater { left, right } => self.append_simple_ins(entity, &[left, right], Relation::Greater.into())?,
1526 ast::ExprKind::GreaterEq { left, right } => self.append_simple_ins(entity, &[left, right], Relation::GreaterEq.into())?,
1527 ast::ExprKind::Less { left, right } => self.append_simple_ins(entity, &[left, right], Relation::Less.into())?,
1528 ast::ExprKind::LessEq { left, right } => self.append_simple_ins(entity, &[left, right], Relation::LessEq.into())?,
1529 ast::ExprKind::Eq { left, right } => self.append_simple_ins(entity, &[left, right], Relation::Equal.into())?,
1530 ast::ExprKind::Neq { left, right } => self.append_simple_ins(entity, &[left, right], Relation::NotEqual.into())?,
1531 ast::ExprKind::Identical { left, right } => self.append_simple_ins(entity, &[left, right], Instruction::Identical)?,
1532 ast::ExprKind::ListGet { list, index } => self.append_simple_ins(entity, &[index, list], Instruction::ListGet)?,
1533 ast::ExprKind::ListGetLast { list } => self.append_simple_ins(entity, &[list], Instruction::ListGetLast)?,
1534 ast::ExprKind::ListGetRandom { list } => self.append_simple_ins(entity, &[list], Instruction::ListGetRandom)?,
1535 ast::ExprKind::ListLen { value } => self.append_simple_ins(entity, &[value], Instruction::ListLength)?,
1536 ast::ExprKind::ListDims { value } => self.append_simple_ins(entity, &[value], Instruction::ListDims)?,
1537 ast::ExprKind::ListRank { value } => self.append_simple_ins(entity, &[value], Instruction::ListRank)?,
1538 ast::ExprKind::ListRev { value } => self.append_simple_ins(entity, &[value], Instruction::ListRev)?,
1539 ast::ExprKind::ListFlatten { value } => self.append_simple_ins(entity, &[value], Instruction::ListFlatten)?,
1540 ast::ExprKind::ListIsEmpty { value } => self.append_simple_ins(entity, &[value], Instruction::ListIsEmpty)?,
1541 ast::ExprKind::ListCons { item, list } => self.append_simple_ins(entity, &[item, list], Instruction::ListCons)?,
1542 ast::ExprKind::ListCdr { value } => self.append_simple_ins(entity, &[value], Instruction::ListCdr)?,
1543 ast::ExprKind::ListFind { list, value } => self.append_simple_ins(entity, &[value, list], Instruction::ListFind)?,
1544 ast::ExprKind::ListContains { list, value } => self.append_simple_ins(entity, &[list, value], Instruction::ListContains)?,
1545 ast::ExprKind::Range { start, stop } => self.append_simple_ins(entity, &[start, stop], BinaryOp::Range.into())?,
1546 ast::ExprKind::Random { a, b } => self.append_simple_ins(entity, &[a, b], BinaryOp::Random.into())?,
1547 ast::ExprKind::ListColumns { value } => self.append_simple_ins(entity, &[value], Instruction::ListColumns)?,
1548 ast::ExprKind::ListLines { value } => self.append_simple_ins(entity, &[value], Instruction::ListLines)?,
1549 ast::ExprKind::ListJson { value } => self.append_simple_ins(entity, &[value], Instruction::ListJson)?,
1550 ast::ExprKind::ListCsv { value } => self.append_simple_ins(entity, &[value], Instruction::ListCsv)?,
1551 ast::ExprKind::StrGet { string, index } => self.append_simple_ins(entity, &[index, string], BinaryOp::StrGet.into())?,
1552 ast::ExprKind::StrGetLast { string } => self.append_simple_ins(entity, &[string], UnaryOp::StrGetLast.into())?,
1553 ast::ExprKind::StrGetRandom { string } => self.append_simple_ins(entity, &[string], UnaryOp::StrGetRandom.into())?,
1554 ast::ExprKind::Effect { kind } => self.append_simple_ins(entity, &[], Instruction::PushProperty { prop: Property::from_effect(kind) })?,
1555 ast::ExprKind::Clone { target } => self.append_simple_ins(entity, &[target], Instruction::Clone)?,
1556 ast::ExprKind::This => self.ins.push(Instruction::PushSelf.into()),
1557 ast::ExprKind::Heading => self.ins.push(Instruction::PushProperty { prop: Property::Heading }.into()),
1558 ast::ExprKind::RpcError => self.ins.push(Instruction::PushRpcError.into()),
1559 ast::ExprKind::Answer => self.ins.push(Instruction::PushAnswer.into()),
1560 ast::ExprKind::Message => self.ins.push(Instruction::PushLocalMessage.into()),
1561 ast::ExprKind::Timer => self.ins.push(Instruction::PushTimer.into()),
1562 ast::ExprKind::RealTime { query } => self.ins.push(Instruction::PushRealTime { query: query.into() }.into()),
1563 ast::ExprKind::XPos => self.ins.push(Instruction::PushProperty { prop: Property::XPos }.into()),
1564 ast::ExprKind::YPos => self.ins.push(Instruction::PushProperty { prop: Property::YPos }.into()),
1565 ast::ExprKind::PenDown => self.ins.push(Instruction::PushProperty { prop: Property::PenDown }.into()),
1566 ast::ExprKind::PenAttr { attr } => self.ins.push(Instruction::PushProperty { prop: Property::from_pen_attr(attr) }.into()),
1567 ast::ExprKind::Costume => self.ins.push(Instruction::PushCostume.into()),
1568 ast::ExprKind::CostumeNumber => self.ins.push(Instruction::PushCostumeNumber.into()),
1569 ast::ExprKind::CostumeList => self.ins.push(Instruction::PushCostumeList.into()),
1570 ast::ExprKind::CostumeName { costume } => self.append_simple_ins(entity, &[costume], Instruction::PushCostumeProperty { prop: ImageProperty::Name })?,
1571 ast::ExprKind::SoundList => self.ins.push(Instruction::PushSoundList.into()),
1572 ast::ExprKind::SoundName { sound } => self.append_simple_ins(entity, &[sound], Instruction::PushSoundProperty { prop: AudioProperty::Name })?,
1573 ast::ExprKind::Size => self.ins.push(Instruction::PushProperty { prop: Property::Size }.into()),
1574 ast::ExprKind::IsVisible => self.ins.push(Instruction::PushProperty { prop: Property::Visible }.into()),
1575 ast::ExprKind::Entity { trans_name, .. } => self.ins.push(Instruction::PushEntity { name: trans_name }.into()),
1576 ast::ExprKind::Add { values } => self.append_variadic_op(entity, values, VariadicOp::Add)?,
1577 ast::ExprKind::Mul { values } => self.append_variadic_op(entity, values, VariadicOp::Mul)?,
1578 ast::ExprKind::Min { values } => self.append_variadic_op(entity, values, VariadicOp::Min)?,
1579 ast::ExprKind::Max { values } => self.append_variadic_op(entity, values, VariadicOp::Max)?,
1580 ast::ExprKind::StrCat { values } => self.append_variadic_op(entity, values, VariadicOp::StrCat)?,
1581 ast::ExprKind::ListCat { lists } => self.append_variadic_op(entity, lists, VariadicOp::ListCat)?,
1582 ast::ExprKind::CopyList { list } => self.append_variadic_op(entity, list, VariadicOp::MakeList)?,
1583 ast::ExprKind::MakeList { values } => {
1584 for value in values {
1585 self.append_expr(value, entity)?;
1586 }
1587 self.ins.push(Instruction::VariadicOp { op: VariadicOp::MakeList, len: VariadicLen::Fixed(values.len()) }.into());
1588 }
1589 ast::ExprKind::UnknownBlock { name, args } => match name.as_str() {
1590 "nativeCallSyscall" => {
1591 let (name, args) = match args.as_slice() {
1592 [name, args] => (name, args),
1593 _ => return Err(CompileError::InvalidBlock { loc: expr.info.location.as_deref() }),
1594 };
1595 self.append_expr(name, entity)?;
1596 let len = self.append_variadic(args, entity)?;
1597 self.ins.push(Instruction::Syscall { len }.into());
1598 }
1599 "nativeSyscallError" => {
1600 if !args.is_empty() { return Err(CompileError::InvalidBlock { loc: expr.info.location.as_deref() }) }
1601 self.ins.push(Instruction::PushSyscallError.into());
1602 }
1603 _ => {
1604 for arg in args {
1605 self.append_expr(arg, entity)?;
1606 }
1607 self.ins.push(Instruction::UnknownBlock { name, args: args.len() }.into());
1608 }
1609 }
1610 ast::ExprKind::TypeQuery { value, ty } => {
1611 self.append_expr(value, entity)?;
1612 let ty = match ty {
1613 ast::ValueType::Number => BasicType::Number,
1614 ast::ValueType::Text => BasicType::Text,
1615 ast::ValueType::Bool => BasicType::Bool,
1616 ast::ValueType::List => BasicType::List,
1617 ast::ValueType::Sprite => BasicType::Entity,
1618 ast::ValueType::Costume => BasicType::Image,
1619 ast::ValueType::Sound => BasicType::Audio,
1620 ast::ValueType::Command | ast::ValueType::Reporter | ast::ValueType::Predicate => return Err(CompileError::CurrentlyUnsupported { info: "closure types are indistinguishable".into() }),
1621 };
1622 self.ins.push(Instruction::TypeQuery { ty }.into());
1623 }
1624 ast::ExprKind::ListReshape { value, dims } => {
1625 self.append_expr(value, entity)?;
1626 let len = self.append_variadic(dims, entity)?;
1627 self.ins.push(Instruction::ListReshape { len }.into());
1628 }
1629 ast::ExprKind::ListCombinations { sources } => {
1630 let len = self.append_variadic(sources, entity)?;
1631 self.ins.push(Instruction::ListCartesianProduct { len }.into());
1632 }
1633 ast::ExprKind::Conditional { condition, then, otherwise } => {
1634 self.append_expr(condition, entity)?;
1635 let test_pos = self.ins.len();
1636 self.ins.push(InternalInstruction::Illegal);
1637
1638 self.append_expr(then, entity)?;
1639 let jump_aft_pos = self.ins.len();
1640 self.ins.push(InternalInstruction::Illegal);
1641
1642 let test_false_pos = self.ins.len();
1643 self.append_expr(otherwise, entity)?;
1644 let aft_pos = self.ins.len();
1645
1646 self.ins[test_pos] = Instruction::ConditionalJump { to: test_false_pos, when: false }.into();
1647 self.ins[jump_aft_pos] = Instruction::Jump { to: aft_pos }.into();
1648 }
1649 ast::ExprKind::Or { left, right } => {
1650 self.append_expr(left, entity)?;
1651 self.ins.push(Instruction::DupeValue { top_index: 0 }.into());
1652 let check_pos = self.ins.len();
1653 self.ins.push(InternalInstruction::Illegal);
1654 self.ins.push(Instruction::PopValue.into());
1655 self.append_expr(right, entity)?;
1656 let aft = self.ins.len();
1657
1658 self.ins[check_pos] = Instruction::ConditionalJump { to: aft, when: true }.into();
1659
1660 self.ins.push(Instruction::ToBool.into());
1661 }
1662 ast::ExprKind::And { left, right } => {
1663 self.append_expr(left, entity)?;
1664 self.ins.push(Instruction::DupeValue { top_index: 0 }.into());
1665 let check_pos = self.ins.len();
1666 self.ins.push(InternalInstruction::Illegal);
1667 self.ins.push(Instruction::PopValue.into());
1668 self.append_expr(right, entity)?;
1669 let aft = self.ins.len();
1670
1671 self.ins[check_pos] = Instruction::ConditionalJump { to: aft, when: false }.into();
1672
1673 self.ins.push(Instruction::ToBool.into());
1674 }
1675 ast::ExprKind::CallFn { function, args, upvars } => {
1676 for upvar in upvars {
1677 self.ins.push(Instruction::DeclareLocal { var: &upvar.name }.into());
1678 }
1679 for arg in args {
1680 self.append_expr(arg, entity)?;
1681 }
1682 let call_hole_pos = self.ins.len();
1683 self.ins.push(InternalInstruction::Illegal);
1684
1685 self.call_holes.push((call_hole_pos, function, entity));
1686 }
1687 ast::ExprKind::CallClosure { new_entity, closure, args } => {
1688 if let Some(new_entity) = new_entity {
1689 self.append_expr(new_entity, entity)?;
1690 }
1691 self.append_expr(closure, entity)?;
1692 for arg in args {
1693 self.append_expr(arg, entity)?;
1694 }
1695 self.ins.push(Instruction::CallClosure { new_entity: new_entity.is_some(), args: args.len() }.into());
1696 }
1697 ast::ExprKind::CallRpc { host, service, rpc, args } => {
1698 let mut tokens = LosslessJoin::new();
1699 tokens.push(host.as_deref().unwrap_or(""));
1700 tokens.push(service);
1701 tokens.push(rpc);
1702 for (arg_name, arg) in args {
1703 tokens.push(arg_name);
1704 self.append_expr(arg, entity)?;
1705 }
1706 self.ins.push(Instruction::CallRpc { tokens: self.string_arena.alloc(tokens.finish()) }.into());
1707 }
1708 ast::ExprKind::NetworkMessageReply { target, msg_type, values } => {
1709 let mut tokens = LosslessJoin::new();
1710 tokens.push(msg_type);
1711 for (field, value) in values {
1712 self.append_expr(value, entity)?;
1713 tokens.push(field);
1714 }
1715 self.append_expr(target, entity)?;
1716 self.ins.push(Instruction::SendNetworkMessage { tokens: self.string_arena.alloc(tokens.finish()), expect_reply: true }.into());
1717 }
1718 ast::ExprKind::Closure { kind: _, params, captures, stmts } => {
1719 let closure_hole_pos = self.ins.len();
1720 self.ins.push(InternalInstruction::Illegal);
1721 self.closure_holes.push_back((closure_hole_pos, params, captures, stmts, entity));
1722 }
1723 ast::ExprKind::TextSplit { text, mode } => {
1724 self.append_expr(text, entity)?;
1725 let ins: Instruction = match mode {
1726 ast::TextSplitMode::Letter => UnaryOp::SplitLetter.into(),
1727 ast::TextSplitMode::Word => UnaryOp::SplitWord.into(),
1728 ast::TextSplitMode::Tab => UnaryOp::SplitTab.into(),
1729 ast::TextSplitMode::CR => UnaryOp::SplitCR.into(),
1730 ast::TextSplitMode::LF => UnaryOp::SplitLF.into(),
1731 ast::TextSplitMode::Csv => UnaryOp::SplitCsv.into(),
1732 ast::TextSplitMode::Json => UnaryOp::SplitJson.into(),
1733 ast::TextSplitMode::Custom(pattern) => {
1734 self.append_expr(pattern, entity)?;
1735 BinaryOp::SplitBy.into()
1736 }
1737 };
1738 self.ins.push(ins.into());
1739 }
1740 ast::ExprKind::Map { f, list } => {
1741 self.append_expr(f, entity)?;
1742 self.append_expr(list, entity)?;
1743 self.ins.push(Instruction::VariadicOp { op: VariadicOp::MakeList, len: VariadicLen::Dynamic }.into()); self.ins.push(Instruction::VariadicOp { op: VariadicOp::MakeList, len: VariadicLen::Fixed(0) }.into()); let top = self.ins.len();
1747 self.ins.push(Instruction::DupeValue { top_index: 2 }.into());
1748 self.ins.push(Instruction::DupeValue { top_index: 2 }.into());
1749 let exit_jump_pos = self.ins.len();
1750 self.ins.push(InternalInstruction::Illegal);
1751 self.ins.push(Instruction::CallClosure { new_entity: false, args: 1 }.into());
1752 self.ins.push(Instruction::DupeValue { top_index: 1 }.into());
1753 self.ins.push(Instruction::ListInsertLast.into());
1754 self.ins.push(Instruction::Yield.into());
1755 self.ins.push(Instruction::Jump { to: top }.into());
1756 let aft = self.ins.len();
1757
1758 self.ins[exit_jump_pos] = Instruction::ListPopFirstOrElse { goto: aft }.into();
1759
1760 self.ins.push(Instruction::SwapValues { top_index_1: 1, top_index_2: 3 }.into());
1761 self.ins.push(Instruction::PopValue.into());
1762 self.ins.push(Instruction::PopValue.into());
1763 self.ins.push(Instruction::PopValue.into());
1764 }
1765 ast::ExprKind::Keep { f, list } => {
1766 self.append_expr(f, entity)?;
1767 self.append_expr(list, entity)?;
1768 self.ins.push(Instruction::VariadicOp { op: VariadicOp::MakeList, len: VariadicLen::Dynamic }.into()); self.ins.push(Instruction::VariadicOp { op: VariadicOp::MakeList, len: VariadicLen::Fixed(0) }.into()); let top = self.ins.len();
1772 self.ins.push(Instruction::DupeValue { top_index: 1 }.into());
1773 let exit_jump_pos = self.ins.len();
1774 self.ins.push(InternalInstruction::Illegal);
1775 self.ins.push(Instruction::DupeValue { top_index: 3 }.into());
1776 self.ins.push(Instruction::DupeValue { top_index: 1 }.into());
1777 self.ins.push(Instruction::CallClosure { new_entity: false, args: 1 }.into());
1778 let skip_append_pos = self.ins.len();
1779 self.ins.push(InternalInstruction::Illegal);
1780 self.ins.push(Instruction::DupeValue { top_index: 1 }.into());
1781 self.ins.push(Instruction::ListInsertLast.into());
1782 let kept_jump_pos = self.ins.len();
1783 self.ins.push(InternalInstruction::Illegal);
1784 let pop_cont = self.ins.len();
1785 self.ins.push(Instruction::PopValue.into());
1786 let cont = self.ins.len();
1787 self.ins.push(Instruction::Yield.into());
1788 self.ins.push(Instruction::Jump { to: top }.into());
1789 let aft = self.ins.len();
1790
1791 self.ins[exit_jump_pos] = Instruction::ListPopFirstOrElse { goto: aft }.into();
1792 self.ins[skip_append_pos] = Instruction::ConditionalJump { to: pop_cont, when: false }.into();
1793 self.ins[kept_jump_pos] = Instruction::Jump { to: cont }.into();
1794
1795 self.ins.push(Instruction::SwapValues { top_index_1: 0, top_index_2: 2 }.into());
1796 self.ins.push(Instruction::PopValue.into());
1797 self.ins.push(Instruction::PopValue.into());
1798 }
1799 ast::ExprKind::FindFirst { f, list } => {
1800 self.append_expr(f, entity)?;
1801 self.append_expr(list, entity)?;
1802 self.ins.push(Instruction::VariadicOp { op: VariadicOp::MakeList, len: VariadicLen::Dynamic }.into()); let top = self.ins.len();
1805 self.ins.push(Instruction::DupeValue { top_index: 0 }.into());
1806 let exit_jump_pos = self.ins.len();
1807 self.ins.push(InternalInstruction::Illegal);
1808 self.ins.push(Instruction::DupeValue { top_index: 2 }.into());
1809 self.ins.push(Instruction::DupeValue { top_index: 1 }.into());
1810 self.ins.push(Instruction::CallClosure { new_entity: false, args: 1 }.into());
1811 let skip_jump_pos = self.ins.len();
1812 self.ins.push(InternalInstruction::Illegal);
1813 self.ins.push(Instruction::PopValue.into());
1814 self.ins.push(Instruction::Yield.into());
1815 self.ins.push(Instruction::Jump { to: top }.into());
1816
1817 let aft_loop = self.ins.len();
1818 self.ins.push(Instruction::PushString { value: "" }.into());
1819 let ret = self.ins.len();
1820 self.ins.push(Instruction::SwapValues { top_index_1: 0, top_index_2: 2 }.into());
1821 self.ins.push(Instruction::PopValue.into());
1822 self.ins.push(Instruction::PopValue.into());
1823
1824 self.ins[exit_jump_pos] = Instruction::ListPopFirstOrElse { goto: aft_loop }.into();
1825 self.ins[skip_jump_pos] = Instruction::ConditionalJump { to: ret, when: true }.into();
1826 }
1827 ast::ExprKind::Combine { f, list } => {
1828 self.append_expr(list, entity)?;
1829 self.ins.push(Instruction::VariadicOp { op: VariadicOp::MakeList, len: VariadicLen::Dynamic }.into()); self.append_expr(f, entity)?;
1831
1832 self.ins.push(Instruction::DupeValue { top_index: 1 }.into());
1833 let first_check_pos = self.ins.len();
1834 self.ins.push(InternalInstruction::Illegal);
1835
1836 let top = self.ins.len();
1837 self.ins.push(Instruction::DupeValue { top_index: 1 }.into());
1838 self.ins.push(Instruction::DupeValue { top_index: 3 }.into());
1839 let loop_done_pos = self.ins.len();
1840 self.ins.push(InternalInstruction::Illegal);
1841 self.ins.push(Instruction::SwapValues { top_index_1: 1, top_index_2: 2 }.into());
1842 self.ins.push(Instruction::CallClosure { new_entity: false, args: 2 }.into());
1843 self.ins.push(Instruction::Yield.into());
1844 self.ins.push(Instruction::Jump { to: top }.into());
1845
1846 let clean_inner = self.ins.len();
1847 self.ins.push(Instruction::PopValue.into());
1848 let clean_inner_ret = self.ins.len();
1849 self.ins.push(InternalInstruction::Illegal);
1850
1851 let empty_list = self.ins.len();
1852 self.ins.push(Instruction::PushInt { value: 0 }.into());
1853 let ret = self.ins.len();
1854 self.ins.push(Instruction::SwapValues { top_index_1: 0, top_index_2: 2 }.into());
1855 self.ins.push(Instruction::PopValue.into());
1856 self.ins.push(Instruction::PopValue.into());
1857
1858 self.ins[first_check_pos] = Instruction::ListPopFirstOrElse { goto: empty_list }.into();
1859 self.ins[loop_done_pos] = Instruction::ListPopFirstOrElse { goto: clean_inner }.into();
1860 self.ins[clean_inner_ret] = Instruction::Jump { to: ret }.into();
1861 }
1862 kind => return Err(CompileError::UnsupportedExpr { kind }),
1863 }
1864
1865 if let Some(location) = expr.info.location.as_deref() {
1866 self.ins_locations.insert(self.ins.len(), location);
1867 }
1868
1869 Ok(())
1870 }
1871 fn append_stmt(&mut self, stmt: &'a ast::Stmt, entity: Option<&'a ast::Entity>) -> Result<(), CompileError<'a>> {
1872 match &stmt.kind {
1873 ast::StmtKind::Assign { var, value } => self.append_simple_ins(entity, &[value], Instruction::Assign { var: &var.trans_name })?,
1874 ast::StmtKind::AddAssign { var, value } => self.append_simple_ins(entity, &[value], Instruction::BinaryOpAssign { var: &var.trans_name, op: BinaryOp::Add })?,
1875 ast::StmtKind::ShowVar { var } => self.ins.push(Instruction::Watcher { create: true, var: &var.trans_name }.into()),
1876 ast::StmtKind::HideVar { var } => self.ins.push(Instruction::Watcher { create: false, var: &var.trans_name }.into()),
1877 ast::StmtKind::Pause => self.ins.push(Instruction::Pause.into()),
1878 ast::StmtKind::ListInsert { list, value, index } => self.append_simple_ins(entity, &[value, index, list], Instruction::ListInsert)?,
1879 ast::StmtKind::ListInsertLast { list, value } => self.append_simple_ins(entity, &[value, list], Instruction::ListInsertLast)?,
1880 ast::StmtKind::ListInsertRandom { list, value } => self.append_simple_ins(entity, &[value, list], Instruction::ListInsertRandom)?,
1881 ast::StmtKind::ListRemove { list, index } => self.append_simple_ins(entity, &[index, list], Instruction::ListRemove)?,
1882 ast::StmtKind::ListRemoveLast { list } => self.append_simple_ins(entity, &[list], Instruction::ListRemoveLast)?,
1883 ast::StmtKind::ListRemoveAll { list } => self.append_simple_ins(entity, &[list], Instruction::ListRemoveAll)?,
1884 ast::StmtKind::ListAssign { list, index, value } => self.append_simple_ins(entity, &[index, list, value], Instruction::ListAssign)?,
1885 ast::StmtKind::ListAssignLast { list, value } => self.append_simple_ins(entity, &[list, value], Instruction::ListAssignLast)?,
1886 ast::StmtKind::ListAssignRandom { list, value } => self.append_simple_ins(entity, &[list, value], Instruction::ListAssignRandom)?,
1887 ast::StmtKind::Return { value } => self.append_simple_ins(entity, &[value], Instruction::Return)?,
1888 ast::StmtKind::Throw { error } => self.append_simple_ins(entity, &[error], Instruction::Throw)?,
1889 ast::StmtKind::Ask { prompt } => self.append_simple_ins(entity, &[prompt], Instruction::Ask)?,
1890 ast::StmtKind::Sleep { seconds } => self.append_simple_ins(entity, &[seconds], Instruction::Sleep)?,
1891 ast::StmtKind::SendNetworkReply { value } => self.append_simple_ins(entity, &[value], Instruction::SendNetworkReply)?,
1892 ast::StmtKind::SetSize { value } => self.append_simple_ins(entity, &[value], Instruction::SetProperty { prop: Property::Size })?,
1893 ast::StmtKind::ChangeSize { delta } => self.append_simple_ins(entity, &[delta], Instruction::ChangeProperty { prop: Property::Size })?,
1894 ast::StmtKind::SetEffect { kind, value } => self.append_simple_ins(entity, &[value], Instruction::SetProperty { prop: Property::from_effect(kind) })?,
1895 ast::StmtKind::ChangeEffect { kind, delta } => self.append_simple_ins(entity, &[delta], Instruction::ChangeProperty { prop: Property::from_effect(kind) })?,
1896 ast::StmtKind::ClearEffects => self.ins.push(Instruction::ClearEffects.into()),
1897 ast::StmtKind::Forward { distance } => self.append_simple_ins(entity, &[distance], Instruction::Forward)?,
1898 ast::StmtKind::ResetTimer => self.ins.push(Instruction::ResetTimer.into()),
1899 ast::StmtKind::ChangePenAttr { attr, delta } => self.append_simple_ins(entity, &[delta], Instruction::ChangeProperty { prop: Property::from_pen_attr(attr) })?,
1900 ast::StmtKind::SetPenAttr { attr, value } => self.append_simple_ins(entity, &[value], Instruction::SetProperty { prop: Property::from_pen_attr(attr) })?,
1901 ast::StmtKind::GotoXY { x, y } => self.append_simple_ins(entity, &[x, y], Instruction::GotoXY)?,
1902 ast::StmtKind::ChangeX { delta } => self.append_simple_ins(entity, &[delta], Instruction::ChangeProperty { prop: Property::XPos })?,
1903 ast::StmtKind::ChangeY { delta } => self.append_simple_ins(entity, &[delta], Instruction::ChangeProperty { prop: Property::YPos })?,
1904 ast::StmtKind::SetX { value } => self.append_simple_ins(entity, &[value], Instruction::SetProperty { prop: Property::XPos })?,
1905 ast::StmtKind::SetY { value } => self.append_simple_ins(entity, &[value], Instruction::SetProperty { prop: Property::YPos })?,
1906 ast::StmtKind::TurnRight { angle } => self.append_simple_ins(entity, &[angle], Instruction::ChangeProperty { prop: Property::Heading })?,
1907 ast::StmtKind::SetHeading { value } => self.append_simple_ins(entity, &[value], Instruction::SetProperty { prop: Property::Heading })?,
1908 ast::StmtKind::PointTowards { target } => self.append_simple_ins(entity, &[target], Instruction::PointTowards)?,
1909 ast::StmtKind::PointTowardsXY { x, y } => self.append_simple_ins(entity, &[x, y], Instruction::PointTowardsXY)?,
1910 ast::StmtKind::Goto { target } => self.append_simple_ins(entity, &[target], Instruction::Goto)?,
1911 ast::StmtKind::SetPenSize { value } => self.append_simple_ins(entity, &[value], Instruction::SetProperty { prop: Property::PenSize })?,
1912 ast::StmtKind::ChangePenSize { delta } => self.append_simple_ins(entity, &[delta], Instruction::ChangeProperty { prop: Property::PenSize })?,
1913 ast::StmtKind::NextCostume => self.ins.push(Instruction::NextCostume.into()),
1914 ast::StmtKind::PenClear => self.ins.push(Instruction::ClearDrawings.into()),
1915 ast::StmtKind::DeleteClone => self.ins.push(Instruction::DeleteClone.into()),
1916 ast::StmtKind::Stop { mode: ast::StopMode::ThisScript } => self.ins.push(Instruction::Abort { mode: AbortMode::Current }.into()),
1917 ast::StmtKind::Stop { mode: ast::StopMode::All } => self.ins.push(Instruction::Abort { mode: AbortMode::All }.into()),
1918 ast::StmtKind::Stop { mode: ast::StopMode::AllButThisScript } => self.ins.push(Instruction::Abort { mode: AbortMode::Others }.into()),
1919 ast::StmtKind::Stop { mode: ast::StopMode::OtherScriptsInSprite } => self.ins.push(Instruction::Abort { mode: AbortMode::MyOthers }.into()),
1920 ast::StmtKind::SetCostume { costume } => self.append_simple_ins(entity, &[costume], Instruction::SetCostume)?,
1921 ast::StmtKind::PlaySound { sound, blocking } => self.append_simple_ins(entity, &[sound], Instruction::PlaySound { blocking: *blocking })?,
1922 ast::StmtKind::PlayNotes { notes, beats, blocking } => self.append_simple_ins(entity, &[notes, beats], Instruction::PlayNotes { blocking: *blocking })?,
1923 ast::StmtKind::Rest { beats } => {
1924 self.ins.push(Instruction::VariadicOp { op: VariadicOp::MakeList, len: VariadicLen::Fixed(0) }.into());
1925 self.append_expr(beats, entity)?;
1926 self.ins.push(Instruction::PlayNotes { blocking: true }.into());
1927 }
1928 ast::StmtKind::StopSounds => self.ins.push(Instruction::StopSounds.into()),
1929 ast::StmtKind::Stop { mode: ast::StopMode::ThisBlock } => {
1930 self.ins.push(Instruction::PushString { value: "" }.into());
1931 self.ins.push(Instruction::Return.into());
1932 }
1933 ast::StmtKind::SetVisible { value } => {
1934 self.ins.push(Instruction::PushBool { value: *value }.into());
1935 self.ins.push(Instruction::SetProperty { prop: Property::Visible }.into());
1936 }
1937 ast::StmtKind::SetPenDown { value } => {
1938 self.ins.push(Instruction::PushBool { value: *value }.into());
1939 self.ins.push(Instruction::SetProperty { prop: Property::PenDown }.into());
1940 }
1941 ast::StmtKind::TurnLeft { angle } => {
1942 self.append_expr(angle, entity)?;
1943 self.ins.push(Instruction::from(UnaryOp::Neg).into());
1944 self.ins.push(Instruction::ChangeProperty { prop: Property::Heading }.into());
1945 }
1946 ast::StmtKind::SetPenColor { color } => {
1947 let (r, g, b, a) = *color;
1948 self.ins.push(Instruction::PushColor { value: Color { r, g, b, a } }.into());
1949 self.ins.push(Instruction::SetProperty { prop: Property::PenColor }.into());
1950 }
1951 x @ (ast::StmtKind::Say { content, duration } | ast::StmtKind::Think { content, duration }) => {
1952 let style = match x {
1953 ast::StmtKind::Say { .. } => PrintStyle::Say,
1954 ast::StmtKind::Think { .. } => PrintStyle::Think,
1955 _ => unreachable!(),
1956 };
1957
1958 self.append_simple_ins(entity, &[content], Instruction::Print { style })?;
1959 if let Some(t) = duration {
1960 self.append_simple_ins(entity, &[t], Instruction::Sleep)?;
1961 self.ins.push(Instruction::PushString { value: "" }.into());
1962 self.ins.push(Instruction::Print { style }.into());
1963 }
1964 }
1965 ast::StmtKind::DeclareLocals { vars } => {
1966 for var in vars {
1967 self.ins.push(Instruction::DeclareLocal { var: &var.trans_name }.into());
1968 }
1969 }
1970 ast::StmtKind::CallFn { function, args, upvars } => {
1971 for upvar in upvars {
1972 self.ins.push(Instruction::DeclareLocal { var: &upvar.name }.into());
1973 }
1974 for arg in args {
1975 self.append_expr(arg, entity)?;
1976 }
1977 let call_hole_pos = self.ins.len();
1978 self.ins.push(InternalInstruction::Illegal);
1979 self.ins.push(Instruction::PopValue.into());
1980
1981 self.call_holes.push((call_hole_pos, function, entity));
1982 }
1983 ast::StmtKind::CallClosure { new_entity, closure, args } => {
1984 if let Some(new_entity) = new_entity {
1985 self.append_expr(new_entity, entity)?;
1986 }
1987 self.append_expr(closure, entity)?;
1988 for arg in args {
1989 self.append_expr(arg, entity)?;
1990 }
1991 self.ins.push(Instruction::CallClosure { new_entity: new_entity.is_some(), args: args.len() }.into());
1992 self.ins.push(Instruction::PopValue.into());
1993 }
1994 ast::StmtKind::ForkClosure { closure, args } => {
1995 self.append_expr(closure, entity)?;
1996 for arg in args {
1997 self.append_expr(arg, entity)?;
1998 }
1999 self.ins.push(Instruction::ForkClosure { args: args.len() }.into());
2000 }
2001 ast::StmtKind::Warp { stmts } => {
2002 self.ins.push(Instruction::WarpStart.into());
2003 for stmt in stmts {
2004 self.append_stmt(stmt, entity)?;
2005 }
2006 self.ins.push(Instruction::WarpStop.into());
2007 }
2008 ast::StmtKind::InfLoop { stmts } => {
2009 let top = self.ins.len();
2010 for stmt in stmts {
2011 self.append_stmt(stmt, entity)?;
2012 }
2013 self.ins.push(Instruction::Yield.into());
2014 self.ins.push(Instruction::Jump { to: top }.into());
2015 }
2016 ast::StmtKind::WaitUntil { condition } => {
2017 let top = self.ins.len();
2018 self.append_expr(condition, entity)?;
2019 let jump_pos = self.ins.len();
2020 self.ins.push(InternalInstruction::Illegal);
2021 self.ins.push(Instruction::Yield.into());
2022 self.ins.push(Instruction::Jump { to: top }.into());
2023 let aft = self.ins.len();
2024
2025 self.ins[jump_pos] = Instruction::ConditionalJump { to: aft, when: true }.into();
2026 }
2027 ast::StmtKind::UntilLoop { condition, stmts } => {
2028 let top = self.ins.len();
2029 self.append_expr(condition, entity)?;
2030 let exit_jump_pos = self.ins.len();
2031 self.ins.push(InternalInstruction::Illegal);
2032
2033 for stmt in stmts {
2034 self.append_stmt(stmt, entity)?;
2035 }
2036 self.ins.push(Instruction::Yield.into());
2037 self.ins.push(Instruction::Jump { to: top }.into());
2038 let aft = self.ins.len();
2039
2040 self.ins[exit_jump_pos] = Instruction::ConditionalJump { to: aft, when: true }.into();
2041 }
2042 ast::StmtKind::Repeat { times, stmts } => {
2043 self.append_expr(times, entity)?;
2044
2045 let top = self.ins.len();
2046 self.ins.push(Instruction::DupeValue { top_index: 0 }.into());
2047 self.ins.push(Instruction::PushInt { value: 0 }.into());
2048 self.ins.push(Instruction::from(Relation::Greater).into());
2049 let aft_jump_pos = self.ins.len();
2050 self.ins.push(InternalInstruction::Illegal);
2051
2052 for stmt in stmts {
2053 self.append_stmt(stmt, entity)?;
2054 }
2055
2056 self.ins.push(Instruction::PushInt { value: 1 }.into());
2057 self.ins.push(Instruction::from(BinaryOp::Sub).into());
2058 self.ins.push(Instruction::Yield.into());
2059 self.ins.push(Instruction::Jump { to: top }.into());
2060 let aft = self.ins.len();
2061
2062 self.ins[aft_jump_pos] = Instruction::ConditionalJump { to: aft, when: false }.into();
2063
2064 self.ins.push(Instruction::PopValue.into());
2065 }
2066 ast::StmtKind::ForLoop { var, start, stop, stmts } => {
2067 self.ins.push(Instruction::DeclareLocal { var: &var.name }.into());
2068
2069 self.append_expr(start, entity)?;
2070 self.ins.push(Instruction::ToNumber.into());
2071 self.append_expr(stop, entity)?;
2072 self.ins.push(Instruction::ToNumber.into());
2073
2074 self.ins.push(Instruction::DupeValue { top_index: 1 }.into());
2075 self.ins.push(Instruction::DupeValue { top_index: 1 }.into());
2076 self.ins.push(Instruction::from(Relation::Greater).into());
2077 let delta_jump_pos = self.ins.len();
2078 self.ins.push(InternalInstruction::Illegal);
2079
2080 self.ins.push(Instruction::PushInt { value: 1 }.into());
2081 let positive_delta_end = self.ins.len();
2082 self.ins.push(InternalInstruction::Illegal);
2083 let negative_delta_pos = self.ins.len();
2084 self.ins.push(Instruction::PushInt { value: -1 }.into());
2085 let aft_delta = self.ins.len();
2086
2087 self.ins[delta_jump_pos] = Instruction::ConditionalJump { to: negative_delta_pos, when: true }.into();
2088 self.ins[positive_delta_end] = Instruction::Jump { to: aft_delta }.into();
2089
2090 self.ins.push(Instruction::SwapValues { top_index_1: 0, top_index_2: 2 }.into());
2091 self.ins.push(Instruction::SwapValues { top_index_1: 0, top_index_2: 1 }.into());
2092 self.ins.push(Instruction::DupeValue { top_index: 1 }.into());
2093 self.ins.push(Instruction::from(BinaryOp::Sub).into());
2094 self.ins.push(Instruction::from(UnaryOp::Abs).into());
2095
2096 let top = self.ins.len();
2097 self.ins.push(Instruction::DupeValue { top_index: 0 }.into());
2098 self.ins.push(Instruction::PushInt { value: 0 }.into());
2099 self.ins.push(Instruction::from(Relation::Less).into());
2100 let exit_jump_pos = self.ins.len();
2101 self.ins.push(InternalInstruction::Illegal);
2102
2103 self.ins.push(Instruction::DupeValue { top_index: 1 }.into());
2104 self.ins.push(Instruction::Assign { var: &var.trans_name }.into());
2105 for stmt in stmts {
2106 self.append_stmt(stmt, entity)?;
2107 }
2108
2109 self.ins.push(Instruction::PushInt { value: 1 }.into());
2110 self.ins.push(Instruction::from(BinaryOp::Sub).into());
2111 self.ins.push(Instruction::DupeValue { top_index: 1 }.into());
2112 self.ins.push(Instruction::DupeValue { top_index: 3 }.into());
2113 self.ins.push(Instruction::from(BinaryOp::Add).into());
2114 self.ins.push(Instruction::SwapValues { top_index_1: 0, top_index_2: 2 }.into());
2115 self.ins.push(Instruction::PopValue.into());
2116 self.ins.push(Instruction::Yield.into());
2117 self.ins.push(Instruction::Jump { to: top }.into());
2118 let aft = self.ins.len();
2119
2120 self.ins[exit_jump_pos] = Instruction::ConditionalJump { to: aft, when: true }.into();
2121
2122 for _ in 0..3 {
2123 self.ins.push(Instruction::PopValue.into());
2124 }
2125 }
2126 ast::StmtKind::ForeachLoop { var, items, stmts } => {
2127 self.ins.push(Instruction::DeclareLocal { var: &var.name }.into());
2128
2129 self.append_expr(items, entity)?;
2130 self.ins.push(Instruction::VariadicOp { op: VariadicOp::MakeList, len: VariadicLen::Dynamic }.into()); let top = self.ins.len();
2133 self.ins.push(Instruction::DupeValue { top_index: 0 }.into());
2134 let exit_jump_pos = self.ins.len();
2135 self.ins.push(InternalInstruction::Illegal);
2136 self.ins.push(Instruction::Assign { var: &var.trans_name }.into());
2137 for stmt in stmts {
2138 self.append_stmt(stmt, entity)?;
2139 }
2140 self.ins.push(Instruction::Yield.into());
2141 self.ins.push(Instruction::Jump { to: top }.into());
2142 let aft = self.ins.len();
2143
2144 self.ins[exit_jump_pos] = Instruction::ListPopFirstOrElse { goto: aft }.into();
2145
2146 self.ins.push(Instruction::PopValue.into());
2147 }
2148 ast::StmtKind::If { condition, then } => {
2149 self.append_expr(condition, entity)?;
2150 let patch_pos = self.ins.len();
2151 self.ins.push(InternalInstruction::Illegal);
2152 for stmt in then {
2153 self.append_stmt(stmt, entity)?;
2154 }
2155 let else_pos = self.ins.len();
2156
2157 self.ins[patch_pos] = Instruction::ConditionalJump { to: else_pos, when: false }.into();
2158 }
2159 ast::StmtKind::IfElse { condition, then, otherwise } => {
2160 self.append_expr(condition, entity)?;
2161 let check_pos = self.ins.len();
2162 self.ins.push(InternalInstruction::Illegal);
2163 for stmt in then {
2164 self.append_stmt(stmt, entity)?;
2165 }
2166 let jump_pos = self.ins.len();
2167 self.ins.push(InternalInstruction::Illegal);
2168 let else_pos = self.ins.len();
2169 for stmt in otherwise {
2170 self.append_stmt(stmt, entity)?;
2171 }
2172 let aft = self.ins.len();
2173
2174 self.ins[check_pos] = Instruction::ConditionalJump { to: else_pos, when: false }.into();
2175 self.ins[jump_pos] = Instruction::Jump { to: aft }.into();
2176 }
2177 ast::StmtKind::TryCatch { code, var, handler } => {
2178 let push_pos = self.ins.len();
2179 self.ins.push(InternalInstruction::Illegal);
2180
2181 for stmt in code {
2182 self.append_stmt(stmt, entity)?;
2183 }
2184 self.ins.push(Instruction::PopHandler.into());
2185 let success_end_pos = self.ins.len();
2186 self.ins.push(InternalInstruction::Illegal);
2187
2188 let error_handler_pos = self.ins.len();
2189 self.ins.push(Instruction::PopHandler.into());
2190 for stmt in handler {
2191 self.append_stmt(stmt, entity)?;
2192 }
2193 let aft = self.ins.len();
2194
2195 self.ins[push_pos] = Instruction::PushHandler { pos: error_handler_pos, var: &var.trans_name }.into();
2196 self.ins[success_end_pos] = Instruction::Jump { to: aft }.into();
2197 }
2198 ast::StmtKind::SendLocalMessage { target, msg_type, wait } => match target {
2199 Some(target) => self.append_simple_ins(entity, &[msg_type, target], Instruction::SendLocalMessage { wait: *wait, target: true })?,
2200 None => self.append_simple_ins(entity, &[msg_type], Instruction::SendLocalMessage { wait: *wait, target: false })?,
2201 }
2202 ast::StmtKind::CallRpc { host, service, rpc, args } => {
2203 let mut tokens = LosslessJoin::new();
2204 tokens.push(host.as_deref().unwrap_or(""));
2205 tokens.push(service);
2206 tokens.push(rpc);
2207 for (arg_name, arg) in args {
2208 tokens.push(arg_name);
2209 self.append_expr(arg, entity)?;
2210 }
2211 self.ins.push(Instruction::CallRpc { tokens: self.string_arena.alloc(tokens.finish()) }.into());
2212 self.ins.push(Instruction::PopValue.into());
2213 }
2214 ast::StmtKind::SendNetworkMessage { target, msg_type, values } => {
2215 let mut tokens = LosslessJoin::new();
2216 tokens.push(msg_type);
2217 for (field, value) in values {
2218 self.append_expr(value, entity)?;
2219 tokens.push(field);
2220 }
2221 self.append_expr(target, entity)?;
2222 self.ins.push(Instruction::SendNetworkMessage { tokens: self.string_arena.alloc(tokens.finish()), expect_reply: false }.into());
2223 }
2224 ast::StmtKind::Clone { target } => {
2225 self.append_expr(target, entity)?;
2226 self.ins.push(Instruction::Clone.into());
2227 self.ins.push(Instruction::PopValue.into());
2228 }
2229 ast::StmtKind::UnknownBlock { name, args } => match name.as_str() {
2230 "nativeRunSyscall" => {
2231 let (name, args) = match args.as_slice() {
2232 [name, args] => (name, args),
2233 _ => return Err(CompileError::InvalidBlock { loc: stmt.info.location.as_deref() }),
2234 };
2235 self.append_expr(name, entity)?;
2236 let len = self.append_variadic(args, entity)?;
2237 self.ins.push(Instruction::Syscall { len }.into());
2238 self.ins.push(Instruction::PopValue.into());
2239 }
2240 _ => {
2241 for arg in args {
2242 self.append_expr(arg, entity)?;
2243 }
2244 self.ins.push(Instruction::UnknownBlock { name, args: args.len() }.into());
2245 self.ins.push(Instruction::PopValue.into());
2246 }
2247 }
2248 kind => return Err(CompileError::UnsupportedStmt { kind }),
2249 }
2250
2251 if let Some(location) = stmt.info.location.as_deref() {
2252 self.ins_locations.insert(self.ins.len(), location);
2253 }
2254
2255 Ok(())
2256 }
2257 fn append_stmts_ret(&mut self, upvars: &'a [ast::VariableRef], stmts: &'a [ast::Stmt], entity: Option<&'a ast::Entity>) -> Result<(), CompileError<'a>> {
2258 for upvar in upvars {
2259 self.ins.push(Instruction::InitUpvar { var: &upvar.name }.into());
2260 }
2261 for stmt in stmts {
2262 self.append_stmt(stmt, entity)?;
2263 }
2264 self.ins.push(Instruction::PushString { value: "" }.into());
2265 self.ins.push(Instruction::Return.into());
2266 Ok(())
2267 }
2268 fn link(mut self, funcs: Vec<(&'a ast::Function, usize)>, entities: Vec<(&'a ast::Entity, EntityScriptInfo<'a>)>) -> Result<(ByteCode, ScriptInfo<'a>, Locations), CompileError<'a>> {
2269 assert!(self.closure_holes.is_empty());
2270
2271 let global_fn_to_info = {
2272 let mut res = BTreeMap::new();
2273 for (func, pos) in funcs.iter() {
2274 res.insert(&*func.trans_name, (*pos, *func));
2275 }
2276 res
2277 };
2278 let entity_fn_to_info = {
2279 let mut res = BTreeMap::new();
2280 for (entity, entity_locs) in entities.iter() {
2281 let mut inner = BTreeMap::new();
2282 for (func, pos) in entity_locs.funcs.iter() {
2283 inner.insert(&*func.trans_name, (*pos, *func));
2284 }
2285 res.insert(*entity as *const ast::Entity, inner);
2286 }
2287 res
2288 };
2289
2290 let get_ptr = |x: Option<&ast::Entity>| x.map(|x| x as *const ast::Entity).unwrap_or(core::ptr::null());
2291 for (hole_pos, hole_fn, hole_ent) in self.call_holes.iter() {
2292 let sym = &*hole_fn.trans_name;
2293 let &(pos, fn_info) = entity_fn_to_info.get(&get_ptr(*hole_ent)).and_then(|tab| tab.get(sym)).or_else(|| global_fn_to_info.get(sym)).unwrap();
2294
2295 let mut tokens = LosslessJoin::new();
2296 for param in fn_info.params.iter() {
2297 tokens.push(¶m.trans_name);
2298 }
2299
2300 self.ins[*hole_pos] = Instruction::Call { pos, tokens: self.string_arena.alloc(tokens.finish()) }.into();
2301 }
2302
2303 self.optimize();
2304 self.finalize(funcs, entities)
2305 }
2306 fn optimize(&mut self) {
2307 let mut code = vec![];
2308 let mut data = BinPool::new(); let mut relocate_info = vec![]; for ins in &mut self.ins {
2312 match ins {
2313 InternalInstruction::Illegal => unreachable!(),
2314 InternalInstruction::Valid(ins) => match ins {
2315 Instruction::PushString { value: str_value } => match str_value.parse::<i32>() {
2316 Ok(value) => {
2317 if value.to_compact_string() != *str_value { continue }
2318
2319 code.clear();
2320 BinaryWrite::append(&value, &mut code, &mut data, &mut relocate_info);
2321 debug_assert!(code.len() > 0);
2322 if code.len() >= 3 { continue }
2323
2324 *ins = Instruction::PushIntString { value };
2325 }
2326 Err(_) => (),
2327 }
2328 _ => (),
2329 }
2330 }
2331 }
2332
2333 debug_assert!(data.is_empty());
2334 debug_assert!(relocate_info.is_empty());
2335 }
2336 fn finalize(self, funcs: Vec<(&'a ast::Function, usize)>, entities: Vec<(&'a ast::Entity, EntityScriptInfo<'a>)>) -> Result<(ByteCode, ScriptInfo<'a>, Locations), CompileError<'a>> {
2337 let mut code = Vec::with_capacity(self.ins.len() * 4);
2338 let mut data = BinPool::new();
2339 let mut relocate_info = Vec::with_capacity(64);
2340
2341 let mut final_ins_pos = Vec::with_capacity(self.ins.len());
2342 for ins in self.ins.iter() {
2343 final_ins_pos.push(code.len());
2344 match ins {
2345 InternalInstruction::Illegal => unreachable!(),
2346 InternalInstruction::Valid(val) => BinaryWrite::append(val, &mut code, &mut data, &mut relocate_info),
2347 }
2348 }
2349
2350 let data_backing = data.into_backing();
2351 let (data, data_backing_pos) = {
2352 let mut data = Vec::with_capacity(data_backing.0.iter().map(Vec::len).sum::<usize>());
2353 let mut data_backing_pos = Vec::with_capacity(data_backing.0.len());
2354 for backing in data_backing.0.iter() {
2355 data_backing_pos.push(data.len());
2356 data.extend_from_slice(backing);
2357 }
2358 (data, data_backing_pos)
2359 };
2360
2361 fn apply_shrinking_plan(plan: &[(usize, usize, usize)], final_relocates: &mut [usize], code: &mut Vec<u8>, final_ins_pos: &mut [usize]) -> usize {
2362 let old_pos_to_ins: BTreeMap<usize, usize> = final_ins_pos.iter().copied().enumerate().map(|(a, b)| (b, a)).collect();
2363 let orig_code_size = code.len();
2364
2365 let mut final_ins_pos_update_iter = final_ins_pos.iter_mut().fuse().peekable();
2366 let mut old_hole_pos_to_new_pos = BTreeMap::default();
2367 let (mut dest_pos, mut src_pos, mut total_shift) = (0, 0, 0);
2368 for (code_addr, prev_size, new_size) in plan.iter().copied() {
2369 debug_assert!(prev_size >= new_size);
2370 debug_assert!(code_addr >= src_pos);
2371
2372 while let Some(old) = final_ins_pos_update_iter.peek() {
2373 if **old > code_addr { break }
2374 *final_ins_pos_update_iter.next().unwrap() -= total_shift;
2375 }
2376
2377 code.copy_within(src_pos..code_addr + new_size, dest_pos);
2378 dest_pos += code_addr + new_size - src_pos;
2379 src_pos = code_addr + prev_size;
2380 old_hole_pos_to_new_pos.insert(code_addr, dest_pos - new_size);
2381 total_shift += prev_size - new_size;
2382 }
2383 for old in final_ins_pos_update_iter { *old -= total_shift; }
2384 code.copy_within(src_pos..src_pos + (orig_code_size - total_shift - dest_pos), dest_pos);
2385 code.truncate(orig_code_size - total_shift);
2386
2387 let mut buf = Vec::with_capacity(MAX_U64_ENCODED_BYTES);
2388 for code_addr in final_relocates.iter_mut() {
2389 *code_addr = old_hole_pos_to_new_pos[code_addr];
2390 let old_pos = <usize as BinaryRead>::read(code, &[], *code_addr);
2391 buf.clear();
2392 encode_u64(final_ins_pos[old_pos_to_ins[&old_pos.0]] as u64, &mut buf, Some(old_pos.1 - *code_addr));
2393 debug_assert_eq!(buf.len(), old_pos.1 - *code_addr);
2394 code[*code_addr..old_pos.1].copy_from_slice(&buf);
2395 }
2396
2397 total_shift
2398 }
2399
2400 let mut fmt_buf = Vec::with_capacity(MAX_U64_ENCODED_BYTES);
2401 let mut shrinking_plan = vec![];
2402 let mut final_relocates = vec![];
2403 for info in relocate_info {
2404 fmt_buf.clear();
2405 let (code_addr, prev_size) = match info {
2406 RelocateInfo::Code { code_addr } => {
2407 final_relocates.push(code_addr);
2408 let pos = <usize as BinaryRead>::read(&code, &data, code_addr);
2409 encode_u64(final_ins_pos[pos.0] as u64, &mut fmt_buf, None);
2410 (code_addr, pos.1 - code_addr)
2411 }
2412 RelocateInfo::Data { code_addr } => {
2413 let pool_index = <usize as BinaryRead>::read(&code, &data, code_addr);
2414 let slice = &data_backing.1[pool_index.0];
2415 encode_u64((data_backing_pos[slice.src] + slice.start) as u64, &mut fmt_buf, None);
2416 (code_addr, pool_index.1 - code_addr)
2417 }
2418 };
2419 debug_assert!(prev_size >= fmt_buf.len());
2420 shrinking_plan.push((code_addr, prev_size, fmt_buf.len()));
2421 code[code_addr..code_addr + fmt_buf.len()].copy_from_slice(&fmt_buf);
2422 }
2423
2424 apply_shrinking_plan(&shrinking_plan, &mut final_relocates, &mut code, &mut final_ins_pos);
2425
2426 for _ in 0..SHRINK_CYCLES {
2427 shrinking_plan.clear();
2428 for code_addr in final_relocates.iter().copied() {
2429 let val = <usize as BinaryRead>::read(&code, &data, code_addr);
2430 fmt_buf.clear();
2431 encode_u64(val.0 as u64, &mut fmt_buf, None);
2432 debug_assert!(fmt_buf.len() <= val.1 - code_addr);
2433 code[code_addr..code_addr + fmt_buf.len()].copy_from_slice(&fmt_buf);
2434 shrinking_plan.push((code_addr, val.1 - code_addr, fmt_buf.len()));
2435 }
2436 let delta = apply_shrinking_plan(&shrinking_plan, &mut final_relocates, &mut code, &mut final_ins_pos);
2437 if delta == 0 { break }
2438 }
2439
2440 let (mut funcs, mut entities) = (funcs, entities);
2441
2442 for func in funcs.iter_mut() { func.1 = final_ins_pos[func.1]; }
2443 for entity in entities.iter_mut() {
2444 for func in entity.1.funcs.iter_mut() { func.1 = final_ins_pos[func.1]; }
2445 for script in entity.1.scripts.iter_mut() { script.1 = final_ins_pos[script.1]; }
2446 }
2447 let locations = Locations::condense(&self.ins_locations.iter().map(|(p, v)| (final_ins_pos[*p], *v)).collect())?;
2448
2449 Ok((ByteCode { tag: Default::default(), code: code.into_boxed_slice(), data: data.into_boxed_slice() }, ScriptInfo { funcs, entities }, locations))
2450 }
2451}
2452
2453impl ByteCode {
2454 pub fn compile(role: &ast::Role) -> Result<(ByteCode, InitInfo, Locations, ScriptInfo), CompileError> {
2462 let string_arena = Default::default();
2463 let mut code = ByteCodeBuilder {
2464 ins: Default::default(),
2465 call_holes: Default::default(),
2466 closure_holes: Default::default(),
2467 ins_locations: Default::default(),
2468 string_arena: &string_arena,
2469 };
2470
2471 let mut funcs = Vec::with_capacity(role.funcs.len());
2472 for func in role.funcs.iter() {
2473 funcs.push((func, code.ins.len()));
2474 code.append_stmts_ret(&func.upvars, &func.stmts, None)?;
2475 }
2476
2477 let mut entities = Vec::with_capacity(role.entities.len());
2478 for entity in role.entities.iter() {
2479 let mut funcs = Vec::with_capacity(entity.funcs.len());
2480 for func in entity.funcs.iter() {
2481 funcs.push((func, code.ins.len()));
2482 code.append_stmts_ret(&func.upvars, &func.stmts, Some(entity))?;
2483 }
2484
2485 let mut scripts = Vec::with_capacity(entity.scripts.len());
2486 for script in entity.scripts.iter() {
2487 scripts.push((script, code.ins.len()));
2488 code.append_stmts_ret(&[], &script.stmts, Some(entity))?;
2489 }
2490
2491 entities.push((entity, EntityScriptInfo { funcs, scripts }));
2492 }
2493
2494 while let Some((hole_pos, params, captures, stmts, entity)) = code.closure_holes.pop_front() {
2495 let pos = code.ins.len();
2496 code.append_stmts_ret(&[], stmts, entity)?;
2497
2498 let mut tokens = LosslessJoin::new();
2499 for param in params {
2500 tokens.push(¶m.trans_name);
2501 }
2502 for param in captures {
2503 tokens.push(¶m.trans_name);
2504 }
2505
2506 code.ins[hole_pos] = Instruction::MakeClosure { pos, params: params.len(), tokens: string_arena.alloc(tokens.finish()) }.into();
2507 }
2508
2509 let (bytecode, script_info, locations) = code.link(funcs, entities)?;
2510 let init_info = Self::extract_init_info(role, &script_info)?;
2511
2512 Ok((bytecode, init_info, locations, script_info))
2513 }
2514 fn extract_init_info<'a>(role: &'a ast::Role, script_info: &ScriptInfo<'a>) -> Result<InitInfo, CompileError<'a>> {
2515 let mut ref_values = vec![];
2516
2517 let mut refs = BTreeMap::new();
2518 let mut string_refs = BTreeMap::new();
2519 let mut image_refs = BTreeMap::new();
2520 let mut audio_refs = BTreeMap::new();
2521
2522 fn register_ref_values<'a>(value: &'a ast::Value, ref_values: &mut Vec<(Option<RefValue>, &'a ast::Value)>, refs: &mut BTreeMap<usize, usize>) {
2523 match value {
2524 ast::Value::Bool(_) | ast::Value::Number(_) | ast::Value::Constant(_) => (), ast::Value::Ref(_) | ast::Value::String(_) | ast::Value::Image(_) | ast::Value::Audio(_) => (), ast::Value::List(values, ref_id) => {
2527 if let Some(ref_id) = ref_id {
2528 refs.entry(ref_id.0).or_insert_with(|| {
2529 ref_values.push((None, value)); ref_values.len() - 1
2531 });
2532 }
2533 for value in values {
2534 register_ref_values(value, ref_values, refs);
2535 }
2536 }
2537 }
2538 }
2539
2540 for global in role.globals.iter() {
2541 register_ref_values(&global.init, &mut ref_values, &mut refs);
2542 }
2543 for entity in role.entities.iter() {
2544 for field in entity.fields.iter() {
2545 register_ref_values(&field.init, &mut ref_values, &mut refs);
2546 }
2547 for costume in entity.costumes.iter() {
2548 register_ref_values(&costume.init, &mut ref_values, &mut refs);
2549 }
2550 }
2551
2552 fn get_value<'a>(value: &'a ast::Value, ref_values: &mut Vec<(Option<RefValue>, &'a ast::Value)>, refs: &BTreeMap<usize, usize>, string_refs: &mut BTreeMap<&'a str, usize>, image_refs: &mut BTreeMap<*const (Vec<u8>, Option<(f64, f64)>, CompactString), usize>, audio_refs: &mut BTreeMap<*const (Vec<u8>, CompactString), usize>) -> Result<InitValue, CompileError<'a>> {
2553 Ok(match value {
2554 ast::Value::Bool(x) => InitValue::Bool(*x),
2555 ast::Value::Number(x) => InitValue::Number(Number::new(*x)?),
2556 ast::Value::Constant(x) => match x {
2557 ast::Constant::E => InitValue::Number(Number::new(core::f64::consts::E)?),
2558 ast::Constant::Pi => InitValue::Number(Number::new(core::f64::consts::PI)?),
2559 }
2560 ast::Value::Ref(x) => {
2561 let idx = *refs.get(&x.0).ok_or(CompileError::UndefinedRef { value })?;
2562 InitValue::Ref(idx)
2563 }
2564 ast::Value::String(x) => {
2565 let idx = *string_refs.entry(x).or_insert_with(|| {
2566 ref_values.push((Some(RefValue::Text(x.clone())), value));
2567 ref_values.len() - 1
2568 });
2569 InitValue::Ref(idx)
2570 }
2571 ast::Value::Image(x) => {
2572 let center = x.1.map(|(x, y)| Ok::<_,NumberError>((Number::new(x)?, Number::new(y)?))).transpose()?;
2573 let idx = *image_refs.entry(Rc::as_ptr(x)).or_insert_with(|| {
2574 ref_values.push((Some(RefValue::Image(x.0.clone(), center, x.2.clone())), value));
2575 ref_values.len() - 1
2576 });
2577 InitValue::Ref(idx)
2578 }
2579 ast::Value::Audio(x) => {
2580 let idx = *audio_refs.entry(Rc::as_ptr(x)).or_insert_with(|| {
2581 ref_values.push((Some(RefValue::Audio(x.0.clone(), x.1.clone())), value));
2582 ref_values.len() - 1
2583 });
2584 InitValue::Ref(idx)
2585 }
2586 ast::Value::List(values, ref_id) => {
2587 let res = RefValue::List(values.iter().map(|x| get_value(x, ref_values, refs, string_refs, image_refs, audio_refs)).collect::<Result<_,_>>()?);
2588 match ref_id {
2589 Some(ref_id) => {
2590 let idx = *refs.get(&ref_id.0).ok_or(CompileError::UndefinedRef { value })?;
2591 let target = &mut ref_values[idx];
2592 debug_assert!(target.0.is_none());
2593 target.0 = Some(res);
2594 InitValue::Ref(idx)
2595 }
2596 None => {
2597 ref_values.push((Some(res), value));
2598 InitValue::Ref(ref_values.len() - 1)
2599 }
2600 }
2601 }
2602 })
2603 }
2604
2605 let proj_name = role.name.clone();
2608 let mut globals = VecMap::new();
2609 let mut entities = vec![];
2610
2611 for global in role.globals.iter() {
2612 globals.insert(global.def.name.clone(), get_value(&global.init, &mut ref_values, &refs, &mut string_refs, &mut image_refs, &mut audio_refs)?);
2613 }
2614
2615 for (entity, entity_info) in script_info.entities.iter() {
2616 let name = entity.name.clone();
2617 let mut fields = VecMap::new();
2618 let mut costumes = VecMap::new();
2619 let mut sounds = VecMap::new();
2620 let mut scripts = vec![];
2621
2622 let visible = entity.visible;
2623 let active_costume = entity.active_costume;
2624 let color = entity.color;
2625 let size = Number::new(entity.scale * 100.0)?;
2626 let pos = (Number::new(entity.pos.0)?, Number::new(entity.pos.1)?);
2627 let heading = Number::new(util::modulus(entity.heading, 360.0))?;
2628
2629 for field in entity.fields.iter() {
2630 fields.insert(field.def.name.clone(), get_value(&field.init, &mut ref_values, &refs, &mut string_refs, &mut image_refs, &mut audio_refs)?);
2631 }
2632 for costume in entity.costumes.iter() {
2633 costumes.insert(costume.def.name.clone(), get_value(&costume.init, &mut ref_values, &refs, &mut string_refs, &mut image_refs, &mut audio_refs)?);
2634 }
2635 for sound in entity.sounds.iter() {
2636 sounds.insert(sound.def.name.clone(), get_value(&sound.init, &mut ref_values, &refs, &mut string_refs, &mut image_refs, &mut audio_refs)?);
2637 }
2638
2639 for (script, pos) in entity_info.scripts.iter().copied() {
2640 let hat = match script.hat.as_ref() {
2641 Some(x) => x,
2642 None => continue,
2643 };
2644 let event = match &hat.kind {
2645 ast::HatKind::OnFlag => Event::OnFlag,
2646 ast::HatKind::OnClone => Event::OnClone,
2647 ast::HatKind::LocalMessage { msg_type } => Event::LocalMessage { msg_type: msg_type.clone() },
2648 ast::HatKind::NetworkMessage { msg_type, fields } => Event::NetworkMessage { msg_type: msg_type.clone(), fields: fields.iter().map(|x| x.trans_name.clone()).collect() },
2649 ast::HatKind::Unknown { name, fields } => Event::Custom { name: name.clone(), fields: fields.iter().map(|x| x.trans_name.clone()).collect() },
2650 ast::HatKind::OnKey { key } => Event::OnKey {
2651 key_filter: match key.as_str() {
2652 "any key" => None,
2653 "up arrow" => Some(KeyCode::Up),
2654 "down arrow" => Some(KeyCode::Down),
2655 "left arrow" => Some(KeyCode::Left),
2656 "right arrow" => Some(KeyCode::Right),
2657 "enter" => Some(KeyCode::Enter),
2658 "space" => Some(KeyCode::Char(' ')),
2659 _ => {
2660 let mut chars = key.chars();
2661 let res = chars.next().map(|x| KeyCode::Char(x.to_ascii_lowercase()));
2662 if res.is_none() || chars.next().is_some() { return Err(CompileError::BadKeycode { key }); }
2663 Some(res.unwrap())
2664 }
2665 }
2666 },
2667 kind => return Err(CompileError::UnsupportedEvent { kind }),
2668 };
2669 scripts.push((event, pos));
2670 }
2671
2672 entities.push(EntityInitInfo { name, fields, sounds, costumes, scripts, active_costume, pos, heading, size, visible, color });
2673 }
2674
2675 let ref_values = ref_values.into_iter().map(|x| x.0.ok_or(CompileError::UndefinedRef { value: x.1 })).collect::<Result<_,_>>()?;
2676
2677 Ok(InitInfo { tag: Default::default(), proj_name, ref_values, globals, entities })
2678 }
2679 #[cfg(feature = "std")]
2681 pub fn dump_code(&self, f: &mut dyn std::io::Write) -> std::io::Result<()> {
2682 let mut pos = 0;
2683 while pos < self.code.len() {
2684 let (ins, aft) = Instruction::read(&self.code, &self.data, pos);
2685 for (i, bytes) in self.code[pos..aft].chunks(BYTES_PER_LINE).enumerate() {
2686 if i == 0 {
2687 write!(f, "{pos:08} ")?;
2688 } else {
2689 write!(f, " ")?;
2690 }
2691
2692 for &b in bytes {
2693 write!(f, " {b:02x}")?;
2694 }
2695 for _ in bytes.len()..BYTES_PER_LINE {
2696 write!(f, " ")?;
2697 }
2698
2699 if i == 0 {
2700 write!(f, " {ins:?}")?;
2701 }
2702 writeln!(f)?;
2703 }
2704 pos = aft;
2705 }
2706 Ok(())
2707 }
2708 #[cfg(feature = "std")]
2710 pub fn dump_data(&self, f: &mut dyn std::io::Write) -> std::io::Result<()> {
2711 for (i, bytes) in self.data.chunks(BYTES_PER_LINE).enumerate() {
2712 write!(f, "{:08} ", i * BYTES_PER_LINE)?;
2713 for &b in bytes {
2714 write!(f, " {b:02x}")?;
2715 }
2716 for _ in bytes.len()..BYTES_PER_LINE {
2717 write!(f, " ")?;
2718 }
2719 write!(f, " ")?;
2720 for &b in bytes {
2721 write!(f, "{}", if (0x21..=0x7e).contains(&b) { b as char } else { '.' })?;
2722 }
2723 writeln!(f)?;
2724 }
2725 Ok(())
2726 }
2727 pub fn total_size(&self) -> usize {
2729 self.code.len() + self.data.len()
2730 }
2731}