snarkvm_synthesizer_program/
parse.rsuse super::*;
impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> Parser
for ProgramCore<N, Instruction, Command>
{
#[inline]
fn parse(string: &str) -> ParserResult<Self> {
enum P<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> {
M(Mapping<N>),
I(StructType<N>),
R(RecordType<N>),
C(ClosureCore<N, Instruction>),
F(FunctionCore<N, Instruction, Command>),
}
let (string, imports) = many0(Import::parse)(string)?;
let (string, _) = Sanitizer::parse(string)?;
let (string, _) = tag(Self::type_name())(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, id) = ProgramID::parse(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, _) = tag(";")(string)?;
fn intermediate<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>>(
string: &str,
) -> ParserResult<P<N, Instruction, Command>> {
let (string, _) = Sanitizer::parse(string)?;
if string.starts_with(Mapping::<N>::type_name()) {
map(Mapping::parse, |mapping| P::<N, Instruction, Command>::M(mapping))(string)
} else if string.starts_with(StructType::<N>::type_name()) {
map(StructType::parse, |struct_| P::<N, Instruction, Command>::I(struct_))(string)
} else if string.starts_with(RecordType::<N>::type_name()) {
map(RecordType::parse, |record| P::<N, Instruction, Command>::R(record))(string)
} else if string.starts_with(ClosureCore::<N, Instruction>::type_name()) {
map(ClosureCore::parse, |closure| P::<N, Instruction, Command>::C(closure))(string)
} else if string.starts_with(FunctionCore::<N, Instruction, Command>::type_name()) {
map(FunctionCore::parse, |function| P::<N, Instruction, Command>::F(function))(string)
} else {
Err(Err::Error(make_error(string, ErrorKind::Alt)))
}
}
let (string, components) = many1(intermediate)(string)?;
let (string, _) = Sanitizer::parse(string)?;
let mut program = match ProgramCore::<N, Instruction, Command>::new(id) {
Ok(program) => program,
Err(error) => {
eprintln!("{error}");
return map_res(take(0usize), Err)(string);
}
};
for component in components {
let result = match component {
P::M(mapping) => program.add_mapping(mapping),
P::I(struct_) => program.add_struct(struct_),
P::R(record) => program.add_record(record),
P::C(closure) => program.add_closure(closure),
P::F(function) => program.add_function(function),
};
match result {
Ok(_) => (),
Err(error) => {
eprintln!("{error}");
return map_res(take(0usize), Err)(string);
}
}
}
for import in imports {
match program.add_import(import) {
Ok(_) => (),
Err(error) => {
eprintln!("{error}");
return map_res(take(0usize), Err)(string);
}
}
}
Ok((string, program))
}
}
impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> FromStr
for ProgramCore<N, Instruction, Command>
{
type Err = Error;
fn from_str(string: &str) -> Result<Self> {
ensure!(string.len() <= N::MAX_PROGRAM_SIZE, "Program length exceeds N::MAX_PROGRAM_SIZE.");
match Self::parse(string) {
Ok((remainder, object)) => {
ensure!(remainder.is_empty(), "Failed to parse string. Remaining invalid string is: \"{remainder}\"");
Ok(object)
}
Err(error) => bail!("Failed to parse string. {error}"),
}
}
}
impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> Debug
for ProgramCore<N, Instruction, Command>
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> Display
for ProgramCore<N, Instruction, Command>
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if !self.imports.is_empty() {
for import in self.imports.values() {
writeln!(f, "{import}")?;
}
writeln!(f)?;
}
write!(f, "{} {};\n\n", Self::type_name(), self.id)?;
let mut identifier_iter = self.identifiers.iter().peekable();
while let Some((identifier, definition)) = identifier_iter.next() {
match definition {
ProgramDefinition::Mapping => match self.mappings.get(identifier) {
Some(mapping) => writeln!(f, "{mapping}")?,
None => return Err(fmt::Error),
},
ProgramDefinition::Struct => match self.structs.get(identifier) {
Some(struct_) => writeln!(f, "{struct_}")?,
None => return Err(fmt::Error),
},
ProgramDefinition::Record => match self.records.get(identifier) {
Some(record) => writeln!(f, "{record}")?,
None => return Err(fmt::Error),
},
ProgramDefinition::Closure => match self.closures.get(identifier) {
Some(closure) => writeln!(f, "{closure}")?,
None => return Err(fmt::Error),
},
ProgramDefinition::Function => match self.functions.get(identifier) {
Some(function) => writeln!(f, "{function}")?,
None => return Err(fmt::Error),
},
}
if identifier_iter.peek().is_some() {
writeln!(f)?;
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Program;
use console::network::MainnetV0;
type CurrentNetwork = MainnetV0;
#[test]
fn test_program_parse() -> Result<()> {
let (string, program) = Program::<CurrentNetwork>::parse(
r"
program to_parse.aleo;
struct message:
first as field;
second as field;
function compute:
input r0 as message.private;
add r0.first r0.second into r1;
output r1 as field.private;",
)
.unwrap();
assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
assert!(program.contains_struct(&Identifier::from_str("message")?));
assert!(program.contains_function(&Identifier::from_str("compute")?));
Ok(())
}
#[test]
fn test_program_parse_function_zero_inputs() -> Result<()> {
let (string, program) = Program::<CurrentNetwork>::parse(
r"
program to_parse.aleo;
function compute:
add 1u32 2u32 into r0;
output r0 as u32.private;",
)
.unwrap();
assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
assert!(program.contains_function(&Identifier::from_str("compute")?));
Ok(())
}
#[test]
fn test_program_display() -> Result<()> {
let expected = r"program to_parse.aleo;
struct message:
first as field;
second as field;
function compute:
input r0 as message.private;
add r0.first r0.second into r1;
output r1 as field.private;
";
let program = Program::<CurrentNetwork>::from_str(expected)?;
assert_eq!(expected, format!("{program}"));
Ok(())
}
#[test]
fn test_program_size() {
let var_name = "a";
let gen_import_string = |n: usize| -> String {
let mut s = String::new();
for i in 0..n {
s.push_str(&format!("import foo{i}.aleo;\n"));
}
s
};
let gen_struct_string = |n: usize| -> String {
let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE);
for i in 0..n {
s.push_str(&format!("struct m{}:\n", i));
for j in 0..10 {
s.push_str(&format!(" {}{} as u128;\n", var_name, j));
}
}
s
};
let gen_record_string = |n: usize| -> String {
let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE);
for i in 0..n {
s.push_str(&format!("record r{}:\n owner as address.private;\n", i));
for j in 0..10 {
s.push_str(&format!(" {}{} as u128.private;\n", var_name, j));
}
}
s
};
let gen_mapping_string = |n: usize| -> String {
let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE);
for i in 0..n {
s.push_str(&format!(
"mapping {}{}:\n key as field.public;\n value as field.public;\n",
var_name, i
));
}
s
};
let gen_closure_string = |n: usize| -> String {
let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE);
for i in 0..n {
s.push_str(&format!("closure c{}:\n input r0 as u128;\n", i));
for j in 0..10 {
s.push_str(&format!(" add r0 r0 into r{};\n", j));
}
s.push_str(&format!(" output r{} as u128;\n", 4000));
}
s
};
let gen_function_string = |n: usize| -> String {
let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE);
for i in 0..n {
s.push_str(&format!("function f{}:\n add 1u128 1u128 into r0;\n", i));
for j in 0..10 {
s.push_str(&format!(" add r0 r0 into r{j};\n"));
}
}
s
};
let test_parse = |imports: &str, body: &str, should_succeed: bool| {
let program = format!("{imports}\nprogram to_parse.aleo;\n\n{body}");
let result = Program::<CurrentNetwork>::from_str(&program);
if result.is_ok() != should_succeed {
println!("Program failed to parse: {program}");
}
assert_eq!(result.is_ok(), should_succeed);
};
test_parse(&gen_import_string(CurrentNetwork::MAX_IMPORTS), &gen_struct_string(1), true);
test_parse(&gen_import_string(CurrentNetwork::MAX_IMPORTS + 1), &gen_struct_string(1), false);
test_parse("", &gen_struct_string(CurrentNetwork::MAX_STRUCTS), true);
test_parse("", &gen_struct_string(CurrentNetwork::MAX_STRUCTS + 1), false);
test_parse("", &gen_record_string(CurrentNetwork::MAX_RECORDS), true);
test_parse("", &gen_record_string(CurrentNetwork::MAX_RECORDS + 1), false);
test_parse("", &gen_mapping_string(CurrentNetwork::MAX_MAPPINGS), true);
test_parse("", &gen_mapping_string(CurrentNetwork::MAX_MAPPINGS + 1), false);
test_parse("", &gen_closure_string(CurrentNetwork::MAX_CLOSURES), true);
test_parse("", &gen_closure_string(CurrentNetwork::MAX_CLOSURES + 1), false);
test_parse("", &gen_function_string(CurrentNetwork::MAX_FUNCTIONS), true);
test_parse("", &gen_function_string(CurrentNetwork::MAX_FUNCTIONS + 1), false);
let program_too_big = format!(
"{} {} {} {} {}",
gen_struct_string(CurrentNetwork::MAX_STRUCTS),
gen_record_string(CurrentNetwork::MAX_RECORDS),
gen_mapping_string(CurrentNetwork::MAX_MAPPINGS),
gen_closure_string(CurrentNetwork::MAX_CLOSURES),
gen_function_string(CurrentNetwork::MAX_FUNCTIONS)
);
test_parse("", &program_too_big, false);
}
}