snarkvm_synthesizer_program/constructor/
mod.rs1mod bytes;
17mod parse;
18
19use crate::Command;
20
21use console::{
22 network::prelude::*,
23 program::{Identifier, Register},
24};
25
26use std::collections::HashMap;
27
28#[derive(Clone, PartialEq, Eq)]
29pub struct ConstructorCore<N: Network> {
30 commands: Vec<Command<N>>,
32 num_writes: u16,
34 positions: HashMap<Identifier<N>, usize>,
36}
37
38impl<N: Network> ConstructorCore<N> {
39 pub fn commands(&self) -> &[Command<N>] {
41 &self.commands
42 }
43
44 pub const fn num_writes(&self) -> u16 {
46 self.num_writes
47 }
48
49 pub const fn positions(&self) -> &HashMap<Identifier<N>, usize> {
51 &self.positions
52 }
53
54 pub fn exceeds_max_array_size(&self, max_array_size: u32) -> bool {
56 self.commands.iter().any(|command| {
57 matches!(
58 command,
59 Command::Instruction(instruction) if instruction.exceeds_max_array_size(max_array_size)
60 )
61 })
62 }
63}
64
65impl<N: Network> ConstructorCore<N> {
66 #[inline]
71 pub fn add_command(&mut self, command: Command<N>) -> Result<()> {
72 ensure!(self.commands.len() < N::MAX_COMMANDS, "Cannot add more than {} commands", N::MAX_COMMANDS);
74 if command.is_write() {
76 ensure!(
77 self.num_writes < N::MAX_WRITES,
78 "Cannot add more than {} 'set' & 'remove' commands",
79 N::MAX_WRITES
80 );
81 }
82
83 ensure!(!command.is_async(), "Forbidden operation: Constructor cannot invoke an 'async' instruction");
85 ensure!(!command.is_call(), "Forbidden operation: Constructor cannot invoke a 'call'");
87 ensure!(!command.is_cast_to_record(), "Forbidden operation: Constructor cannot cast to a record");
89 ensure!(!command.is_await(), "Forbidden operation: Constructor cannot 'await'");
91
92 for register in command.destinations() {
94 ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
96 }
97
98 if let Some(position) = command.branch_to() {
100 ensure!(!self.positions.contains_key(position), "Cannot branch to an earlier position '{position}'");
102 }
103
104 if let Some(position) = command.position() {
106 ensure!(!self.positions.contains_key(position), "Cannot redefine position '{position}'");
108 ensure!(self.positions.len() < N::MAX_POSITIONS, "Cannot add more than {} positions", N::MAX_POSITIONS);
110 self.positions.insert(*position, self.commands.len());
112 }
113
114 if command.is_write() {
116 self.num_writes += 1;
118 }
119
120 self.commands.push(command);
122 Ok(())
123 }
124}
125
126impl<N: Network> TypeName for ConstructorCore<N> {
127 #[inline]
129 fn type_name() -> &'static str {
130 "constructor"
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 use crate::{Command, Constructor};
139
140 type CurrentNetwork = console::network::MainnetV0;
141
142 #[test]
143 fn test_add_command() {
144 let mut constructor = Constructor::<CurrentNetwork> {
146 commands: Default::default(),
147 num_writes: 0,
148 positions: Default::default(),
149 };
150
151 let command = Command::<CurrentNetwork>::from_str("add r0 r1 into r2;").unwrap();
153 assert!(constructor.add_command(command).is_ok());
154
155 for i in 3..CurrentNetwork::MAX_COMMANDS * 2 {
157 let command = Command::<CurrentNetwork>::from_str(&format!("add r0 r1 into r{i};")).unwrap();
158
159 match constructor.commands.len() < CurrentNetwork::MAX_COMMANDS {
160 true => assert!(constructor.add_command(command).is_ok()),
161 false => assert!(constructor.add_command(command).is_err()),
162 }
163 }
164
165 let mut constructor = Constructor::<CurrentNetwork> {
169 commands: Default::default(),
170 num_writes: 0,
171 positions: Default::default(),
172 };
173
174 for _ in 0..CurrentNetwork::MAX_WRITES * 2 {
175 let command = Command::<CurrentNetwork>::from_str("remove object[r0];").unwrap();
176
177 match constructor.commands.len() < CurrentNetwork::MAX_WRITES as usize {
178 true => assert!(constructor.add_command(command).is_ok()),
179 false => assert!(constructor.add_command(command).is_err()),
180 }
181 }
182 }
183
184 #[test]
185 fn test_add_command_duplicate_positions() {
186 let mut constructor =
188 Constructor { commands: Default::default(), num_writes: 0, positions: Default::default() };
189
190 let command = Command::<CurrentNetwork>::from_str("position start;").unwrap();
192 assert!(constructor.add_command(command.clone()).is_ok());
193
194 assert!(constructor.add_command(command).is_err());
196
197 #[allow(clippy::cast_possible_truncation)]
199 fn to_unique_string(mut n: usize) -> String {
200 let mut s = String::new();
201 while n > 0 {
202 s.push((b'A' + (n % 26) as u8) as char);
203 n /= 26;
204 }
205 s.chars().rev().collect::<String>()
206 }
207
208 for i in 1..u8::MAX as usize * 2 {
210 let position = to_unique_string(i);
211 println!("position: {position}");
212 let command = Command::<CurrentNetwork>::from_str(&format!("position {position};")).unwrap();
213
214 match constructor.commands.len() < u8::MAX as usize {
215 true => assert!(constructor.add_command(command).is_ok()),
216 false => assert!(constructor.add_command(command).is_err()),
217 }
218 }
219 }
220}