Produce a valid record as input

This commit is contained in:
Pranav Gaddamadugu 2022-07-20 15:02:33 -07:00
parent 0570a421f0
commit a6feb314a7
6 changed files with 121 additions and 70 deletions

1
Cargo.lock generated
View File

@ -1210,6 +1210,7 @@ dependencies = [
"dirs",
"indexmap",
"lazy_static",
"leo-ast",
"leo-compiler",
"leo-errors",
"leo-package",

View File

@ -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"

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
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<String> {
pub fn program_inputs(&self, program_name: &str, circuits: IndexMap<Symbol, Circuit>) -> Vec<String> {
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::<Vec<_>>()
.join(", ")
)
}
_ => panic!("Input error: Expected a circuit expression."),
},
},
}
}
_ => definition.value.to_string(),
})
})
.collect::<Vec<_>>()
}

View File

@ -1,4 +1,13 @@
// The program input for token/src/main.leo
[mint]
owner: address = aleo1ht2a9q0gsd38j0se4t9lsfulxgqrens2vgzgry3pkvs93xrrzu8s892zn7;
amount: u64 = 100u64;
amount: u64 = 100u64;
[transfer]
token: Token = Token {
owner: aleo1ht2a9q0gsd38j0se4t9lsfulxgqrens2vgzgry3pkvs93xrrzu8s892zn7,
gates: 0u64,
amount: 100u64,
};
to: address = aleo1mgfq6g40l6zkhsm063n3uhr43qk5e0zsua5aszeq5080dsvlcvxsn0rrau;
amount: u64 = 50u64;

View File

@ -15,25 +15,26 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
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<InputAst>;
type Output = (Option<InputAst>, IndexMap<Symbol, Circuit>);
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<IndexMap<Symbol, Circuit>> {
// 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)
}

View File

@ -59,14 +59,16 @@ impl Command for Run {
fn apply(self, context: Context, input: Self::Input) -> Result<Self::Output> {
// 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)?;