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
55impl<N: Network> ConstructorCore<N> {
56 #[inline]
61 pub fn add_command(&mut self, command: Command<N>) -> Result<()> {
62 ensure!(self.commands.len() < N::MAX_COMMANDS, "Cannot add more than {} commands", N::MAX_COMMANDS);
64 if command.is_write() {
66 ensure!(
67 self.num_writes < N::MAX_WRITES,
68 "Cannot add more than {} 'set' & 'remove' commands",
69 N::MAX_WRITES
70 );
71 }
72
73 ensure!(!command.is_async(), "Forbidden operation: Constructor cannot invoke an 'async' instruction");
75 ensure!(!command.is_call(), "Forbidden operation: Constructor cannot invoke a 'call'");
77 ensure!(!command.is_cast_to_record(), "Forbidden operation: Constructor cannot cast to a record");
79 ensure!(!command.is_await(), "Forbidden operation: Constructor cannot 'await'");
81
82 for register in command.destinations() {
84 ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
86 }
87
88 if let Some(position) = command.branch_to() {
90 ensure!(!self.positions.contains_key(position), "Cannot branch to an earlier position '{position}'");
92 }
93
94 if let Some(position) = command.position() {
96 ensure!(!self.positions.contains_key(position), "Cannot redefine position '{position}'");
98 ensure!(self.positions.len() < N::MAX_POSITIONS, "Cannot add more than {} positions", N::MAX_POSITIONS);
100 self.positions.insert(*position, self.commands.len());
102 }
103
104 if command.is_write() {
106 self.num_writes += 1;
108 }
109
110 self.commands.push(command);
112 Ok(())
113 }
114}
115
116impl<N: Network> TypeName for ConstructorCore<N> {
117 #[inline]
119 fn type_name() -> &'static str {
120 "constructor"
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 use crate::{Command, Constructor};
129
130 type CurrentNetwork = console::network::MainnetV0;
131
132 #[test]
133 fn test_add_command() {
134 let mut constructor = Constructor::<CurrentNetwork> {
136 commands: Default::default(),
137 num_writes: 0,
138 positions: Default::default(),
139 };
140
141 let command = Command::<CurrentNetwork>::from_str("add r0 r1 into r2;").unwrap();
143 assert!(constructor.add_command(command).is_ok());
144
145 for i in 3..CurrentNetwork::MAX_COMMANDS * 2 {
147 let command = Command::<CurrentNetwork>::from_str(&format!("add r0 r1 into r{i};")).unwrap();
148
149 match constructor.commands.len() < CurrentNetwork::MAX_COMMANDS {
150 true => assert!(constructor.add_command(command).is_ok()),
151 false => assert!(constructor.add_command(command).is_err()),
152 }
153 }
154
155 let mut constructor = Constructor::<CurrentNetwork> {
159 commands: Default::default(),
160 num_writes: 0,
161 positions: Default::default(),
162 };
163
164 for _ in 0..CurrentNetwork::MAX_WRITES * 2 {
165 let command = Command::<CurrentNetwork>::from_str("remove object[r0];").unwrap();
166
167 match constructor.commands.len() < CurrentNetwork::MAX_WRITES as usize {
168 true => assert!(constructor.add_command(command).is_ok()),
169 false => assert!(constructor.add_command(command).is_err()),
170 }
171 }
172 }
173
174 #[test]
175 fn test_add_command_duplicate_positions() {
176 let mut constructor =
178 Constructor { commands: Default::default(), num_writes: 0, positions: Default::default() };
179
180 let command = Command::<CurrentNetwork>::from_str("position start;").unwrap();
182 assert!(constructor.add_command(command.clone()).is_ok());
183
184 assert!(constructor.add_command(command).is_err());
186
187 #[allow(clippy::cast_possible_truncation)]
189 fn to_unique_string(mut n: usize) -> String {
190 let mut s = String::new();
191 while n > 0 {
192 s.push((b'A' + (n % 26) as u8) as char);
193 n /= 26;
194 }
195 s.chars().rev().collect::<String>()
196 }
197
198 for i in 1..u8::MAX as usize * 2 {
200 let position = to_unique_string(i);
201 println!("position: {position}");
202 let command = Command::<CurrentNetwork>::from_str(&format!("position {position};")).unwrap();
203
204 match constructor.commands.len() < u8::MAX as usize {
205 true => assert!(constructor.add_command(command).is_ok()),
206 false => assert!(constructor.add_command(command).is_err()),
207 }
208 }
209 }
210}