Skip to main content

snarkvm_synthesizer_program/logic/command/
mod.rs

1// Copyright (c) 2019-2026 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16mod await_;
17pub use await_::*;
18
19mod branch;
20pub use branch::*;
21
22mod contains;
23pub use contains::*;
24
25mod get;
26pub use get::*;
27
28mod get_or_use;
29pub use get_or_use::*;
30
31mod rand_chacha;
32pub use crate::command::rand_chacha::*;
33
34mod remove;
35pub use remove::*;
36
37mod position;
38pub use position::*;
39
40mod set;
41pub use set::*;
42
43use crate::{
44    CastType,
45    FinalizeOperation,
46    FinalizeRegistersState,
47    FinalizeStoreTrait,
48    Instruction,
49    Operand,
50    StackTrait,
51};
52use console::{
53    network::{error, prelude::*},
54    program::{Identifier, Register},
55};
56
57#[derive(Clone, PartialEq, Eq, Hash)]
58pub enum Command<N: Network> {
59    /// Evaluates the instruction.
60    Instruction(Instruction<N>),
61    /// Awaits the result of a future.
62    Await(Await<N>),
63    /// Returns true if the `key` operand is present in `mapping`, and stores the result into `destination`.
64    Contains(Contains<N>),
65    /// Resolves the `program` and `mapping` operands, returns true if the `key` operand is present in the `mapping`, and stores the result into `destination`.
66    ContainsDynamic(ContainsDynamic<N>),
67    /// Gets the value stored at the `key` operand in `mapping` and stores the result into `destination`.
68    Get(Get<N>),
69    /// Resolves the `program` and `mapping` operands, gets the value stored at the `key` operand in `mapping`, and stores the result into `destination`.
70    GetDynamic(GetDynamic<N>),
71    /// Gets the value stored at the `key` operand in `mapping` and stores the result into `destination`.
72    /// If the key is not present, `default` is stored into `destination`.
73    GetOrUse(GetOrUse<N>),
74    /// Resolves the `program` and `mapping` operands, gets the value stored at the `key` operand in `mapping`, and stores the result into `destination`.
75    /// If the key is not present, `default` is stored into `destination`.
76    GetOrUseDynamic(GetOrUseDynamic<N>),
77    /// Generates a random value using the `rand.chacha` command and stores the result into `destination`.
78    RandChaCha(RandChaCha<N>),
79    /// Removes the (`key`, `value`) entry from the `mapping`.
80    Remove(Remove<N>),
81    /// Sets the value stored at the `key` operand in the `mapping` to `value`.
82    Set(Set<N>),
83    /// Jumps to the `position`, if `first` equals `second`.
84    BranchEq(BranchEq<N>),
85    /// Jumps to the `position`, if `first` does **not** equal `second`.
86    BranchNeq(BranchNeq<N>),
87    /// Indicates a position to which the program can branch to.
88    Position(Position<N>),
89}
90
91impl<N: Network> Command<N> {
92    /// Returns `true` if the command is an async instruction.
93    pub fn is_async(&self) -> bool {
94        matches!(self, Command::Instruction(Instruction::Async(_)))
95    }
96
97    /// Returns `true` if the command is an await command.
98    #[inline]
99    pub fn is_await(&self) -> bool {
100        matches!(self, Command::Await(_))
101    }
102
103    /// Returns `true` if the command is a call instruction.
104    pub fn is_call(&self) -> bool {
105        matches!(self, Command::Instruction(Instruction::Call(_) | Instruction::CallDynamic(_)))
106    }
107
108    /// Returns `true` if the command is a cast to record instruction.
109    pub fn is_cast_to_record(&self) -> bool {
110        matches!(self, Command::Instruction(Instruction::Cast(cast)) if matches!(cast.cast_type(), CastType::Record(_) | CastType::ExternalRecord(_)))
111    }
112
113    /// Returns `true` if the command is a write operation.
114    pub fn is_write(&self) -> bool {
115        matches!(self, Command::Set(_) | Command::Remove(_))
116    }
117
118    /// Returns the branch target, if the command is a branch command.
119    /// Otherwise, returns `None`.
120    pub fn branch_to(&self) -> Option<&Identifier<N>> {
121        match self {
122            Command::BranchEq(branch_eq) => Some(branch_eq.position()),
123            Command::BranchNeq(branch_neq) => Some(branch_neq.position()),
124            _ => None,
125        }
126    }
127
128    /// Returns the position name, if the command is a position command.
129    /// Otherwise, returns `None`.
130    pub fn position(&self) -> Option<&Identifier<N>> {
131        match self {
132            Command::Position(position) => Some(position.name()),
133            _ => None,
134        }
135    }
136
137    /// Returns the destination registers of the command.
138    pub fn destinations(&self) -> Vec<Register<N>> {
139        match self {
140            Command::Instruction(instruction) => instruction.destinations(),
141            Command::Contains(contains) => vec![contains.destination().clone()],
142            Command::ContainsDynamic(contains) => vec![contains.destination().clone()],
143            Command::Get(get) => vec![get.destination().clone()],
144            Command::GetDynamic(get) => vec![get.destination().clone()],
145            Command::GetOrUse(get_or_use) => vec![get_or_use.destination().clone()],
146            Command::GetOrUseDynamic(get_or_use) => vec![get_or_use.destination().clone()],
147            Command::RandChaCha(rand_chacha) => vec![rand_chacha.destination().clone()],
148            Command::Await(_)
149            | Command::BranchEq(_)
150            | Command::BranchNeq(_)
151            | Command::Position(_)
152            | Command::Remove(_)
153            | Command::Set(_) => vec![],
154        }
155    }
156
157    /// Returns the operands of the command.
158    #[inline]
159    pub fn operands(&self) -> &[Operand<N>] {
160        match self {
161            Command::Instruction(c) => c.operands(),
162            Command::Await(c) => c.operands(),
163            Command::Contains(c) => c.operands(),
164            Command::ContainsDynamic(c) => c.operands(),
165            Command::Get(c) => c.operands(),
166            Command::GetDynamic(c) => c.operands(),
167            Command::GetOrUse(c) => c.operands(),
168            Command::GetOrUseDynamic(c) => c.operands(),
169            Command::RandChaCha(c) => c.operands(),
170            Command::Remove(c) => c.operands(),
171            Command::Set(c) => c.operands(),
172            Command::BranchEq(c) => c.operands(),
173            Command::BranchNeq(c) => c.operands(),
174            Command::Position(_) => Default::default(),
175        }
176    }
177
178    /// Finalizes the command.
179    pub fn finalize(
180        &self,
181        stack: &impl StackTrait<N>,
182        store: &impl FinalizeStoreTrait<N>,
183        registers: &mut impl FinalizeRegistersState<N>,
184    ) -> Result<Option<FinalizeOperation<N>>> {
185        match self {
186            // Finalize the instruction, and return no finalize operation.
187            Command::Instruction(instruction) => {
188                instruction.finalize(stack, registers).map_err(Into::into).map(|_| None)
189            }
190            // `await` commands are processed by the caller of this method.
191            Command::Await(_) => bail!("`await` commands cannot be finalized directly."),
192            // Finalize the 'contains' command, and return no finalize operation.
193            Command::Contains(contains) => contains.finalize(stack, store, registers).map(|_| None),
194            // Finalize the `contains.dynamic` command, and return no finalize operation.
195            Command::ContainsDynamic(contains_dynamic) => {
196                contains_dynamic.finalize(stack, store, registers).map(|_| None)
197            }
198            // Finalize the 'get' command, and return no finalize operation.
199            Command::Get(get) => get.finalize(stack, store, registers).map(|_| None),
200            // Finalize the `get.dynamic` and return no finalize operation.
201            Command::GetDynamic(get_dynamic) => get_dynamic.finalize(stack, store, registers).map(|_| None),
202            // Finalize the 'get.or_use' command, and return no finalize operation.
203            Command::GetOrUse(get_or_use) => get_or_use.finalize(stack, store, registers).map(|_| None),
204            // Finalize the `get.or_use.dynamic` command, and return no finalize operation.
205            Command::GetOrUseDynamic(get_or_use_dynamic) => {
206                get_or_use_dynamic.finalize(stack, store, registers).map(|_| None)
207            }
208            // Finalize the `rand.chacha` command, and return no finalize operation.
209            Command::RandChaCha(rand_chacha) => rand_chacha.finalize(stack, registers).map(|_| None),
210            // Finalize the 'remove' command, and return the finalize operation.
211            Command::Remove(remove) => remove.finalize(stack, store, registers),
212            // Finalize the 'set' command, and return the finalize operation.
213            Command::Set(set) => set.finalize(stack, store, registers).map(Some),
214            // 'branch.eq' and 'branch.neq' commands are processed by the caller of this method.
215            Command::BranchEq(_) | Command::BranchNeq(_) => {
216                bail!("`branch` commands cannot be finalized directly.")
217            }
218            // Finalize the `position` command, and return no finalize operation.
219            Command::Position(position) => position.finalize().map(|_| None),
220        }
221    }
222
223    /// Returns whether this commands refers to an external struct.
224    pub fn contains_external_struct(&self) -> bool {
225        match self {
226            Command::Instruction(c) => c.contains_external_struct(),
227            Command::Await(c) => c.contains_external_struct(),
228            Command::Contains(c) => c.contains_external_struct(),
229            // `contains.dynamic` always produces a boolean result and has no type fields that could reference external structs.
230            Command::ContainsDynamic(_) => false,
231            Command::Get(c) => c.contains_external_struct(),
232            Command::GetDynamic(c) => c.destination_type().contains_external_struct(),
233            Command::GetOrUse(c) => c.contains_external_struct(),
234            Command::GetOrUseDynamic(c) => c.destination_type().contains_external_struct(),
235            Command::RandChaCha(c) => c.contains_external_struct(),
236            Command::Remove(c) => c.contains_external_struct(),
237            Command::Set(c) => c.contains_external_struct(),
238            Command::BranchEq(c) => c.contains_external_struct(),
239            Command::BranchNeq(c) => c.contains_external_struct(),
240            Command::Position(c) => c.contains_external_struct(),
241        }
242    }
243
244    /// Returns `true` if the command contains a string type.
245    pub fn contains_string_type(&self) -> bool {
246        self.operands().iter().any(|operand| operand.contains_string_type())
247    }
248
249    /// Returns `true` if the command contains an identifier type in its cast type.
250    pub fn contains_identifier_type(&self) -> Result<bool> {
251        match self {
252            Command::Instruction(instruction) => instruction.contains_identifier_type(),
253            _ => Ok(false),
254        }
255    }
256
257    /// Returns `true` if the command contains an array type with a size that exceeds the given maximum.
258    pub fn exceeds_max_array_size(&self, max_array_size: u32) -> bool {
259        match self {
260            Command::Instruction(instruction) => instruction.exceeds_max_array_size(max_array_size),
261            _ => false,
262        }
263    }
264}
265
266impl<N: Network> FromBytes for Command<N> {
267    /// Reads the command from a buffer.
268    fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
269        // Read the variant.
270        let variant = u8::read_le(&mut reader)?;
271        match variant {
272            // Read the instruction.
273            0 => Ok(Self::Instruction(Instruction::read_le(&mut reader)?)),
274            // Read the `await` operation.
275            1 => Ok(Self::Await(Await::read_le(&mut reader)?)),
276            // Read the `contains` operation.
277            2 => Ok(Self::Contains(Contains::read_le(&mut reader)?)),
278            // Read the `get` operation.
279            3 => Ok(Self::Get(Get::read_le(&mut reader)?)),
280            // Read the `get.or_use` operation.
281            4 => Ok(Self::GetOrUse(GetOrUse::read_le(&mut reader)?)),
282            // Read the `rand.chacha` operation.
283            5 => Ok(Self::RandChaCha(RandChaCha::read_le(&mut reader)?)),
284            // Read the `remove` operation.
285            6 => Ok(Self::Remove(Remove::read_le(&mut reader)?)),
286            // Read the `set` operation.
287            7 => Ok(Self::Set(Set::read_le(&mut reader)?)),
288            // Read the `branch.eq` command.
289            8 => Ok(Self::BranchEq(BranchEq::read_le(&mut reader)?)),
290            // Read the `branch.neq` command.
291            9 => Ok(Self::BranchNeq(BranchNeq::read_le(&mut reader)?)),
292            // Read the `position` command.
293            10 => Ok(Self::Position(Position::read_le(&mut reader)?)),
294            // Read the `contains.dynamic` command.
295            11 => Ok(Self::ContainsDynamic(ContainsDynamic::read_le(&mut reader)?)),
296            // Read the `get.dynamic` command.
297            12 => Ok(Self::GetDynamic(GetDynamic::read_le(&mut reader)?)),
298            // Read the `get.or_use.dynamic` command.
299            13 => Ok(Self::GetOrUseDynamic(GetOrUseDynamic::read_le(&mut reader)?)),
300            // Invalid variant.
301            14.. => Err(error(format!("Invalid command variant: {variant}"))),
302        }
303    }
304}
305
306impl<N: Network> ToBytes for Command<N> {
307    /// Writes the command to a buffer.
308    fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
309        match self {
310            Self::Instruction(instruction) => {
311                // Write the variant.
312                0u8.write_le(&mut writer)?;
313                // Write the instruction.
314                instruction.write_le(&mut writer)
315            }
316            Self::Await(await_) => {
317                // Write the variant.
318                1u8.write_le(&mut writer)?;
319                // Write the `await` operation.
320                await_.write_le(&mut writer)
321            }
322            Self::Contains(contains) => {
323                // Write the variant.
324                2u8.write_le(&mut writer)?;
325                // Write the `contains` operation.
326                contains.write_le(&mut writer)
327            }
328            Self::Get(get) => {
329                // Write the variant.
330                3u8.write_le(&mut writer)?;
331                // Write the `get` operation.
332                get.write_le(&mut writer)
333            }
334            Self::GetOrUse(get_or_use) => {
335                // Write the variant.
336                4u8.write_le(&mut writer)?;
337                // Write the defaulting `get` operation.
338                get_or_use.write_le(&mut writer)
339            }
340            Self::RandChaCha(rand_chacha) => {
341                // Write the variant.
342                5u8.write_le(&mut writer)?;
343                // Write the `rand.chacha` operation.
344                rand_chacha.write_le(&mut writer)
345            }
346            Self::Remove(remove) => {
347                // Write the variant.
348                6u8.write_le(&mut writer)?;
349                // Write the remove.
350                remove.write_le(&mut writer)
351            }
352            Self::Set(set) => {
353                // Write the variant.
354                7u8.write_le(&mut writer)?;
355                // Write the set.
356                set.write_le(&mut writer)
357            }
358            Self::BranchEq(branch_eq) => {
359                // Write the variant.
360                8u8.write_le(&mut writer)?;
361                // Write the `branch.eq` command.
362                branch_eq.write_le(&mut writer)
363            }
364            Self::BranchNeq(branch_neq) => {
365                // Write the variant.
366                9u8.write_le(&mut writer)?;
367                // Write the `branch.neq` command.
368                branch_neq.write_le(&mut writer)
369            }
370            Self::Position(position) => {
371                // Write the variant.
372                10u8.write_le(&mut writer)?;
373                // Write the position command.
374                position.write_le(&mut writer)
375            }
376            Self::ContainsDynamic(contains_dynamic) => {
377                // Write the variant.
378                11u8.write_le(&mut writer)?;
379                // Write the `contains.dynamic` command.
380                contains_dynamic.write_le(&mut writer)
381            }
382            Self::GetDynamic(get_dynamic) => {
383                // Write the variant.
384                12u8.write_le(&mut writer)?;
385                // Write the `get.dynamic` command.
386                get_dynamic.write_le(&mut writer)
387            }
388            Self::GetOrUseDynamic(get_or_use_dynamic) => {
389                // Write the variant.
390                13u8.write_le(&mut writer)?;
391                // Write the `get.or_use.dynamic` command.
392                get_or_use_dynamic.write_le(&mut writer)
393            }
394        }
395    }
396}
397
398impl<N: Network> Parser for Command<N> {
399    /// Parses the string into the command.
400    #[inline]
401    fn parse(string: &str) -> ParserResult<Self> {
402        // Parse the command.
403        // Note that the order of the parsers is important.
404        alt((
405            map(Await::parse, |await_| Self::Await(await_)),
406            map(ContainsDynamic::parse, |contains_dynamic| Self::ContainsDynamic(contains_dynamic)),
407            map(Contains::parse, |contains| Self::Contains(contains)),
408            map(GetOrUseDynamic::parse, |get_or_use_dynamic| Self::GetOrUseDynamic(get_or_use_dynamic)),
409            map(GetOrUse::parse, |get_or_use| Self::GetOrUse(get_or_use)),
410            map(GetDynamic::parse, |get_dynamic| Self::GetDynamic(get_dynamic)),
411            map(Get::parse, |get| Self::Get(get)),
412            map(RandChaCha::parse, |rand_chacha| Self::RandChaCha(rand_chacha)),
413            map(Remove::parse, |remove| Self::Remove(remove)),
414            map(Set::parse, |set| Self::Set(set)),
415            map(BranchEq::parse, |branch_eq| Self::BranchEq(branch_eq)),
416            map(BranchNeq::parse, |branch_neq| Self::BranchNeq(branch_neq)),
417            map(Position::parse, |position| Self::Position(position)),
418            map(Instruction::parse, |instruction| Self::Instruction(instruction)),
419        ))(string)
420    }
421}
422
423impl<N: Network> FromStr for Command<N> {
424    type Err = Error;
425
426    /// Parses the string into the command.
427    #[inline]
428    fn from_str(string: &str) -> Result<Self> {
429        match Self::parse(string) {
430            Ok((remainder, object)) => {
431                // Ensure the remainder is empty.
432                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
433                // Return the object.
434                Ok(object)
435            }
436            Err(error) => bail!("Failed to parse string. {error}"),
437        }
438    }
439}
440
441impl<N: Network> Debug for Command<N> {
442    /// Prints the command as a string.
443    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
444        Display::fmt(self, f)
445    }
446}
447
448impl<N: Network> Display for Command<N> {
449    /// Prints the command as a string.
450    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
451        match self {
452            Self::Instruction(instruction) => Display::fmt(instruction, f),
453            Self::Await(await_) => Display::fmt(await_, f),
454            Self::Contains(contains) => Display::fmt(contains, f),
455            Self::ContainsDynamic(contains_dynamic) => Display::fmt(contains_dynamic, f),
456            Self::Get(get) => Display::fmt(get, f),
457            Self::GetDynamic(get_dynamic) => Display::fmt(get_dynamic, f),
458            Self::GetOrUse(get_or_use) => Display::fmt(get_or_use, f),
459            Self::GetOrUseDynamic(get_or_use_dynamic) => Display::fmt(get_or_use_dynamic, f),
460            Self::RandChaCha(rand_chacha) => Display::fmt(rand_chacha, f),
461            Self::Remove(remove) => Display::fmt(remove, f),
462            Self::Set(set) => Display::fmt(set, f),
463            Self::BranchEq(branch_eq) => Display::fmt(branch_eq, f),
464            Self::BranchNeq(branch_neq) => Display::fmt(branch_neq, f),
465            Self::Position(position) => Display::fmt(position, f),
466        }
467    }
468}
469
470#[cfg(test)]
471mod tests {
472    use super::*;
473    use console::network::MainnetV0;
474
475    type CurrentNetwork = MainnetV0;
476
477    #[test]
478    fn test_command_bytes() {
479        // Decrement
480        let expected = "decrement object[r0] by r1;";
481        Command::<CurrentNetwork>::parse(expected).unwrap_err();
482
483        // Instruction
484        let expected = "add r0 r1 into r2;";
485        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
486        let bytes = command.to_bytes_le().unwrap();
487        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
488
489        // Increment
490        let expected = "increment object[r0] by r1;";
491        Command::<CurrentNetwork>::parse(expected).unwrap_err();
492
493        // Await
494        let expected = "await r1;";
495        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
496        let bytes = command.to_bytes_le().unwrap();
497        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
498
499        // Contains
500        let expected = "contains object[r0] into r1;";
501        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
502        let bytes = command.to_bytes_le().unwrap();
503        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
504
505        // ContainsDynamic
506        let expected = "contains.dynamic r0 r1 r2[r3] into r4;";
507        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
508        let bytes = command.to_bytes_le().unwrap();
509        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
510
511        // Get
512        let expected = "get object[r0] into r1;";
513        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
514        let bytes = command.to_bytes_le().unwrap();
515        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
516
517        // GetDynamic
518        let expected = "get.dynamic r0 r1 r2[r3] into r4 as field;";
519        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
520        let bytes = command.to_bytes_le().unwrap();
521        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
522
523        // GetOr
524        let expected = "get.or_use object[r0] r1 into r2;";
525        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
526        let bytes = command.to_bytes_le().unwrap();
527        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
528
529        // GetOrDynamic
530        let expected = "get.or_use.dynamic r0 r1 r2[r3] r4 into r5 as credits;";
531        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
532        let bytes = command.to_bytes_le().unwrap();
533        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
534
535        // RandChaCha
536        let expected = "rand.chacha into r1 as field;";
537        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
538        let bytes = command.to_bytes_le().unwrap();
539        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
540
541        // RandChaCha
542        let expected = "rand.chacha r0 r1 into r2 as group;";
543        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
544        let bytes = command.to_bytes_le().unwrap();
545        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
546
547        // Remove
548        let expected = "remove object[r0];";
549        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
550        let bytes = command.to_bytes_le().unwrap();
551        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
552
553        // Set
554        let expected = "set r0 into object[r1];";
555        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
556        let bytes = command.to_bytes_le().unwrap();
557        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
558
559        // BranchEq
560        let expected = "branch.eq r0 r1 to exit;";
561        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
562        let bytes = command.to_bytes_le().unwrap();
563        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
564
565        // BranchNeq
566        let expected = "branch.neq r2 r3 to start;";
567        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
568        let bytes = command.to_bytes_le().unwrap();
569        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
570
571        // Position
572        let expected = "position exit;";
573        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
574        let bytes = command.to_bytes_le().unwrap();
575        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
576    }
577
578    #[test]
579    fn test_command_parse() {
580        // Decrement
581        let expected = "decrement object[r0] by r1;";
582        Command::<CurrentNetwork>::parse(expected).unwrap_err();
583
584        // Instruction
585        let expected = "add r0 r1 into r2;";
586        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
587        assert_eq!(Command::Instruction(Instruction::from_str(expected).unwrap()), command);
588        assert_eq!(expected, command.to_string());
589
590        // Increment
591        let expected = "increment object[r0] by r1;";
592        Command::<CurrentNetwork>::parse(expected).unwrap_err();
593
594        // Contains
595        let expected = "contains object[r0] into r1;";
596        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
597        assert_eq!(Command::Contains(Contains::from_str(expected).unwrap()), command);
598        assert_eq!(expected, command.to_string());
599
600        // ContainsDynamic
601        let expected = "contains.dynamic r0 r1 r2[r3] into r4;";
602        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
603        assert_eq!(Command::ContainsDynamic(ContainsDynamic::from_str(expected).unwrap()), command);
604        assert_eq!(expected, command.to_string());
605
606        // Get
607        let expected = "get object[r0] into r1;";
608        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
609        assert_eq!(Command::Get(Get::from_str(expected).unwrap()), command);
610        assert_eq!(expected, command.to_string());
611
612        // GetDynamic
613        let expected = "get.dynamic r0 r1 r2[r3] into r4 as u8;";
614        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
615        assert_eq!(Command::GetDynamic(GetDynamic::from_str(expected).unwrap()), command);
616        assert_eq!(expected, command.to_string());
617
618        // GetOr
619        let expected = "get.or_use object[r0] r1 into r2;";
620        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
621        assert_eq!(Command::GetOrUse(GetOrUse::from_str(expected).unwrap()), command);
622        assert_eq!(expected, command.to_string());
623
624        // GetOrDynamic
625        let expected = "get.or_use.dynamic r0 r1 r2[r3] r4 into r5 as Foo;";
626        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
627        assert_eq!(Command::GetOrUseDynamic(GetOrUseDynamic::from_str(expected).unwrap()), command);
628        assert_eq!(expected, command.to_string());
629
630        // RandChaCha
631        let expected = "rand.chacha into r1 as field;";
632        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
633        assert_eq!(Command::RandChaCha(RandChaCha::from_str(expected).unwrap()), command);
634        assert_eq!(expected, command.to_string());
635
636        // RandChaCha
637        let expected = "rand.chacha r0 r1 into r2 as group;";
638        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
639        assert_eq!(Command::RandChaCha(RandChaCha::from_str(expected).unwrap()), command);
640        assert_eq!(expected, command.to_string());
641
642        // Remove
643        let expected = "remove object[r0];";
644        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
645        assert_eq!(Command::Remove(Remove::from_str(expected).unwrap()), command);
646        assert_eq!(expected, command.to_string());
647
648        // Set
649        let expected = "set r0 into object[r1];";
650        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
651        assert_eq!(Command::Set(Set::from_str(expected).unwrap()), command);
652        assert_eq!(expected, command.to_string());
653
654        // BranchEq
655        let expected = "branch.eq r0 r1 to exit;";
656        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
657        assert_eq!(Command::BranchEq(BranchEq::from_str(expected).unwrap()), command);
658        assert_eq!(expected, command.to_string());
659
660        // BranchNeq
661        let expected = "branch.neq r2 r3 to start;";
662        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
663        assert_eq!(Command::BranchNeq(BranchNeq::from_str(expected).unwrap()), command);
664        assert_eq!(expected, command.to_string());
665
666        // Position
667        let expected = "position exit;";
668        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
669        assert_eq!(Command::Position(Position::from_str(expected).unwrap()), command);
670        assert_eq!(expected, command.to_string());
671    }
672}