diff --git a/Cargo.lock b/Cargo.lock index c05879dedb..11f072e055 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1210,6 +1210,7 @@ dependencies = [ "dirs", "indexmap", "lazy_static", + "leo-ast", "leo-compiler", "leo-errors", "leo-package", diff --git a/Cargo.toml b/Cargo.toml index 389d181fd2..81454ee5c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ path = "leo/main.rs" [workspace] members = [ + "compiler/ast", "compiler/compiler", "compiler/parser", "docs/grammar", @@ -35,6 +36,10 @@ members = [ "tests/test-framework", ] +[dependencies.leo-ast] +path = "./compiler/ast" +version = "1.5.3" + [dependencies.leo-compiler] path = "./compiler/compiler" version = "1.5.3" diff --git a/compiler/ast/src/input/input_ast.rs b/compiler/ast/src/input/input_ast.rs index 4eec431645..ad8922305b 100644 --- a/compiler/ast/src/input/input_ast.rs +++ b/compiler/ast/src/input/input_ast.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::{normalize_json_value, remove_key_from_json}; +use crate::{normalize_json_value, remove_key_from_json, Circuit, Expression, Type}; use super::*; use leo_errors::{AstError, Result}; @@ -41,15 +41,41 @@ pub struct InputAst { impl InputAst { /// Returns all values of the input AST for execution with `leo run`. - pub fn program_inputs(&self, program_name: &str) -> Vec { + pub fn program_inputs(&self, program_name: &str, circuits: IndexMap) -> Vec { self.sections .iter() .filter(|section| section.name() == program_name) .flat_map(|section| { - section - .definitions - .iter() - .map(|definition| definition.value.to_string()) + section.definitions.iter().map(|definition| match &definition.type_ { + // Handle case where the input may be record. + Type::Identifier(identifier) => { + match circuits.get(&identifier.name) { + // TODO: Better error handling. + None => panic!( + "Input error: A circuit or record declaration does not exist for {}.", + identifier.name + ), + Some(circuit) => match circuit.is_record { + false => definition.value.to_string(), + true => match &definition.value { + Expression::Circuit(circuit_expression) => { + format!( + "{{{}}}", + circuit_expression + .members + .iter() + .map(|x| format!("{}.private", x)) + .collect::>() + .join(", ") + ) + } + _ => panic!("Input error: Expected a circuit expression."), + }, + }, + } + } + _ => definition.value.to_string(), + }) }) .collect::>() } diff --git a/examples/token/inputs/token.in b/examples/token/inputs/token.in index adc7fc5638..026e9c28b6 100644 --- a/examples/token/inputs/token.in +++ b/examples/token/inputs/token.in @@ -1,4 +1,13 @@ // The program input for token/src/main.leo [mint] owner: address = aleo1ht2a9q0gsd38j0se4t9lsfulxgqrens2vgzgry3pkvs93xrrzu8s892zn7; -amount: u64 = 100u64; \ No newline at end of file +amount: u64 = 100u64; + +[transfer] +token: Token = Token { + owner: aleo1ht2a9q0gsd38j0se4t9lsfulxgqrens2vgzgry3pkvs93xrrzu8s892zn7, + gates: 0u64, + amount: 100u64, +}; +to: address = aleo1mgfq6g40l6zkhsm063n3uhr43qk5e0zsua5aszeq5080dsvlcvxsn0rrau; +amount: u64 = 50u64; \ No newline at end of file diff --git a/leo/commands/build.rs b/leo/commands/build.rs index 3a0e83e2fb..08001db49b 100644 --- a/leo/commands/build.rs +++ b/leo/commands/build.rs @@ -15,25 +15,26 @@ // along with the Leo library. If not, see . use crate::{commands::Command, context::Context}; + +use leo_ast::Circuit; use leo_compiler::{Compiler, InputAst, OutputOptions}; use leo_errors::{CliError, CompilerError, PackageError, Result}; use leo_package::source::{SourceDirectory, MAIN_FILENAME}; -use leo_package::{ - inputs::InputFile, - outputs::{ChecksumFile, OutputsDirectory}, -}; +use leo_package::{inputs::InputFile, outputs::OutputsDirectory}; use leo_span::symbol::with_session_globals; use aleo::commands::Build as AleoBuild; use clap::StructOpt; use colored::Colorize; +use indexmap::IndexMap; use std::io::Write; use std::path::{Path, PathBuf}; use leo_errors::emitter::Handler; use leo_package::build::BuildDirectory; use leo_package::imports::ImportsDirectory; +use leo_span::Symbol; use tracing::span::Span; /// Compiler Options wrapper for Build command. Also used by other commands which @@ -85,7 +86,7 @@ pub struct Build { impl Command for Build { type Input = (); - type Output = Option; + type Output = (Option, IndexMap); fn log_span(&self) -> Span { tracing::span!(tracing::Level::INFO, "Build") @@ -114,9 +115,12 @@ impl Command for Build { // Fetch paths to all .leo files in the source directory. let source_files = SourceDirectory::files(&package_path)?; + // Store all circuits declarations made in the source files. + let mut circuits = IndexMap::new(); + // Compile all .leo files into .aleo files. for file_path in source_files.into_iter() { - compile_leo_file( + circuits.extend(compile_leo_file( file_path, &package_path, &package_name, @@ -124,7 +128,7 @@ impl Command for Build { &build_directory, &handler, self.compiler_options.clone(), - )?; + )?); } if !ImportsDirectory::is_empty(&package_path)? { @@ -136,7 +140,7 @@ impl Command for Build { // Compile all .leo files into .aleo files. for file_path in import_files.into_iter() { - compile_leo_file( + circuits.extend(compile_leo_file( file_path, &package_path, &package_name, @@ -144,7 +148,7 @@ impl Command for Build { &build_imports_directory, &handler, self.compiler_options.clone(), - )?; + )?); } } @@ -157,7 +161,10 @@ impl Command for Build { let input_sf = with_session_globals(|s| s.source_map.load_file(&input_file_path)) .map_err(|e| CompilerError::file_read_error(&input_file_path, e))?; - leo_parser::parse_input(&handler, &input_sf.src, input_sf.start_pos).ok() + // TODO: This is a hack to notify the user that something is wrong with the input file. Redesign. + leo_parser::parse_input(&handler, &input_sf.src, input_sf.start_pos) + .map_err(|_e| println!("Warning: Failed to parse input file")) + .ok() } else { None }; @@ -172,19 +179,19 @@ impl Command for Build { // Log the result of the build tracing::info!("{}", result); - Ok(input_ast) + Ok((input_ast, circuits)) } } fn compile_leo_file( file_path: PathBuf, - package_path: &PathBuf, + _package_path: &Path, package_name: &String, outputs: &Path, build: &Path, handler: &Handler, options: BuildOptions, -) -> Result<()> { +) -> Result> { // Construct the Leo file name with extension `foo.leo`. let file_name = file_path .file_name() @@ -214,58 +221,59 @@ fn compile_leo_file( Some(options.into()), ); - // Check if we need to compile the Leo program. - let checksum_differs = { - // Compute the current program checksum. - let program_checksum = program.checksum()?; + // TODO: Temporarily removing checksum files. Need to redesign this scheme. + // // Check if we need to compile the Leo program. + // let checksum_differs = { + // // Compute the current program checksum. + // let program_checksum = program.checksum()?; + // + // // Get the current program checksum. + // let checksum_file = ChecksumFile::new(program_name); + // + // // If a checksum file exists, check if it differs from the new checksum. + // let checksum_differs = if checksum_file.exists_at(package_path) { + // let previous_checksum = checksum_file.read_from(package_path)?; + // program_checksum != previous_checksum + // } else { + // // By default, the checksum differs if there is no checksum to compare against. + // true + // }; + // + // // If checksum differs, compile the program + // if checksum_differs { + // // Write the new checksum to the output directory + // checksum_file.write_to(package_path, program_checksum)?; + // + // tracing::debug!("Checksum saved ({:?})", package_path); + // } + // + // checksum_differs + // }; - // Get the current program checksum. - let checksum_file = ChecksumFile::new(program_name); + // if checksum_differs { + // Compile the Leo program into Aleo instructions. + let (symbol_table, instructions) = program.compile_and_generate_instructions()?; - // If a checksum file exists, check if it differs from the new checksum. - let checksum_differs = if checksum_file.exists_at(package_path) { - let previous_checksum = checksum_file.read_from(package_path)?; - program_checksum != previous_checksum - } else { - // By default, the checksum differs if there is no checksum to compare against. - true - }; + // Create the path to the Aleo file. + let mut aleo_file_path = build.to_path_buf(); + aleo_file_path.push(format!("{}.aleo", program_name)); - // If checksum differs, compile the program - if checksum_differs { - // Write the new checksum to the output directory - checksum_file.write_to(package_path, program_checksum)?; + // Write the instructions. + std::fs::File::create(&aleo_file_path) + .map_err(CliError::failed_to_load_instructions)? + .write_all(instructions.as_bytes()) + .map_err(CliError::failed_to_load_instructions)?; - tracing::debug!("Checksum saved ({:?})", package_path); - } + // Prepare the path string. + let path_string = format!("(in \"{}\")", aleo_file_path.display()); - checksum_differs - }; + // Log the build as successful. + tracing::info!( + "✅ Compiled '{}' into Aleo instructions {}", + file_name, + path_string.dimmed() + ); + // } - if checksum_differs { - // Compile the Leo program into Aleo instructions. - let (_, instructions) = program.compile_and_generate_instructions()?; - - // Create the path to the Aleo file. - let mut aleo_file_path = build.to_path_buf(); - aleo_file_path.push(format!("{}.aleo", program_name)); - - // Write the instructions. - std::fs::File::create(&aleo_file_path) - .map_err(CliError::failed_to_load_instructions)? - .write_all(instructions.as_bytes()) - .map_err(CliError::failed_to_load_instructions)?; - - // Prepare the path string. - let path_string = format!("(in \"{}\")", aleo_file_path.display()); - - // Log the build as successful. - tracing::info!( - "✅ Compiled '{}' into Aleo instructions {}", - file_name, - path_string.dimmed() - ); - } - - Ok(()) + Ok(symbol_table.circuits) } diff --git a/leo/commands/run.rs b/leo/commands/run.rs index fed6e74fa7..b4d8779bfb 100644 --- a/leo/commands/run.rs +++ b/leo/commands/run.rs @@ -59,14 +59,16 @@ impl Command for Run { fn apply(self, context: Context, input: Self::Input) -> Result { // Get the input values. let mut inputs = match input { - Some(input_ast) => input_ast.program_inputs(&self.name), - None => Vec::new(), + (Some(input_ast), circuits) => input_ast.program_inputs(&self.name, circuits), + _ => Vec::new(), }; // Compose the `aleo run` command. let mut arguments = vec![ALEO_CLI_COMMAND.to_string(), self.name]; arguments.append(&mut inputs); + println!("Arguments: {:?}", arguments); + // Open the Leo build/ directory let path = context.dir()?; let build_directory = BuildDirectory::open(&path)?;