Skip to content

Commit

Permalink
Fix build pathway for tests
Browse files Browse the repository at this point in the history
  • Loading branch information
d0cd committed Nov 27, 2024
1 parent 17c8ad3 commit c6e49ad
Show file tree
Hide file tree
Showing 13 changed files with 183 additions and 153 deletions.
11 changes: 11 additions & 0 deletions compiler/ast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ use leo_errors::{AstError, Result};
///
/// The [`Ast`] type represents a Leo program as a series of recursive data types.
/// These data types form a tree that begins from a [`Program`] type root.
// TODO: Clean up by removing the `Ast` type and renaming the exiting `Program` type to `Ast`.
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Ast {
pub ast: Program,
Expand All @@ -84,6 +85,16 @@ impl Ast {
Self { ast: program }
}

/// Combines the two ASTs into a single AST.
/// The ASTs are combined by extending the components of the first AST with the components of the second AST.
pub fn combine(&mut self, other: Self) {
let Program { imports, stubs, program_scopes, tests } = other.ast;
self.ast.imports.extend(imports);
self.ast.stubs.extend(stubs);
self.ast.program_scopes.extend(program_scopes);
self.ast.tests.extend(tests);
}

/// Returns a reference to the inner program AST representation.
pub fn as_repr(&self) -> &Program {
&self.ast
Expand Down
4 changes: 2 additions & 2 deletions compiler/ast/src/tst/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
//! A Leo test consists of struct, record, function, transition, and mapping definitions.
//! Anything that can be defined within a program scope can be defined in a test.
use crate::{Composite, ConstDeclaration, Function, Mapping, ProgramId, Stub};
use crate::{Composite, ConstDeclaration, Function, Mapping};

use leo_span::{Span, Symbol};
use leo_span::Symbol;
use serde::{Deserialize, Serialize};
use std::fmt;

Expand Down
116 changes: 67 additions & 49 deletions compiler/compiler/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,19 @@ use leo_span::{Symbol, source_map::FileName, symbol::with_session_globals};
use snarkvm::prelude::Network;

use indexmap::{IndexMap, IndexSet};
use sha2::{Digest, Sha256};
use std::{fs, path::PathBuf};
use std::path::PathBuf;

/// The primary entry point of the Leo compiler.
#[derive(Clone)]
pub struct Compiler<'a, N: Network> {
/// A name used to identify the instance of the compiler.
pub name: String,
/// The handler is used for error and warning emissions.
handler: &'a Handler,
/// The path to the main leo file.
main_file_path: PathBuf,
/// The source files and their content.
sources: Vec<(FileName, String)>,
/// The path to where the compiler outputs all generated files.
output_directory: PathBuf,
/// The program name,
pub program_name: String,
/// The AST for the program.
pub ast: Ast,
/// Options configuring compilation.
Expand All @@ -63,23 +62,23 @@ pub struct Compiler<'a, N: Network> {
impl<'a, N: Network> Compiler<'a, N> {
/// Returns a new Leo compiler.
pub fn new(
program_name: String,
name: String,
handler: &'a Handler,
main_file_path: PathBuf,
sources: Vec<(FileName, String)>,
output_directory: PathBuf,
compiler_options: Option<CompilerOptions>,
compiler_options: CompilerOptions,
import_stubs: IndexMap<Symbol, Stub>,
) -> Self {
let node_builder = NodeBuilder::default();
let assigner = Assigner::default();
let type_table = TypeTable::default();
Self {
name,
handler,
main_file_path,
sources,
output_directory,
program_name,
ast: Ast::new(Program::default()),
compiler_options: compiler_options.unwrap_or_default(),
compiler_options,
node_builder,
assigner,
import_stubs,
Expand All @@ -88,44 +87,52 @@ impl<'a, N: Network> Compiler<'a, N> {
}
}

/// Returns a SHA256 checksum of the program file.
pub fn checksum(&self) -> Result<String> {
// Read in the main file as string
let unparsed_file = fs::read_to_string(&self.main_file_path)
.map_err(|e| CompilerError::file_read_error(self.main_file_path.clone(), e))?;

// Hash the file contents
let mut hasher = Sha256::new();
hasher.update(unparsed_file.as_bytes());
let hash = hasher.finalize();

Ok(format!("{hash:x}"))
// TODO: Rethink build caching.
// /// Returns a SHA256 checksum of the program file.
// pub fn checksum(&self) -> Result<String> {
// // Read in the main file as string
// let unparsed_file = fs::read_to_string(&self.main_file_path)
// .map_err(|e| CompilerError::file_read_error(self.main_file_path.clone(), e))?;
//
// // Hash the file contents
// let mut hasher = Sha256::new();
// hasher.update(unparsed_file.as_bytes());
// let hash = hasher.finalize();
//
// Ok(format!("{hash:x}"))
// }

/// Reset the compiler with new sources.
pub fn reset(&mut self, sources: Vec<(FileName, String)>) {
// Reset the sources and AST.
self.sources = sources;
self.ast = Ast::new(Program::default());
// Reset the internal state.
self.node_builder = NodeBuilder::default();
self.assigner = Assigner::default();
self.type_table = TypeTable::default();
}

/// Parses and stores a program file content from a string, constructs a syntax tree, and generates a program.
pub fn parse_program_from_string(&mut self, program_string: &str, name: FileName) -> Result<()> {
// Register the source (`program_string`) in the source map.
let prg_sf = with_session_globals(|s| s.source_map.new_source(program_string, name));

// Use the parser to construct the abstract syntax tree (ast).
self.ast = leo_parser::parse_ast::<N>(self.handler, &self.node_builder, &prg_sf.src, prg_sf.start_pos)?;

/// Parses and stores the source information, constructs the AST, and optionally outputs it.
pub fn parse(&mut self) -> Result<()> {
// Initialize the AST.
let mut ast = Ast::default();
// Parse the sources.
for (name, program_string) in &self.sources {
// Register the source (`program_string`) in the source map.
let prg_sf = with_session_globals(|s| s.source_map.new_source(program_string, name.clone()));
// Use the parser to construct the abstract syntax tree (ast).
ast.combine(leo_parser::parse_ast::<N>(self.handler, &self.node_builder, &prg_sf.src, prg_sf.start_pos)?);
}
// Store the AST.
self.ast = ast;
// Write the AST to a JSON file.
if self.compiler_options.output.initial_ast {
self.write_ast_to_json("initial_ast.json")?;
}

Ok(())
}

/// Parses and stores the main program file, constructs a syntax tree, and generates a program.
pub fn parse_program(&mut self) -> Result<()> {
// Load the program file.
let program_string = fs::read_to_string(&self.main_file_path)
.map_err(|e| CompilerError::file_read_error(&self.main_file_path, e))?;

self.parse_program_from_string(&program_string, FileName::Real(self.main_file_path.clone()))
}

/// Runs the symbol table pass.
pub fn symbol_table_pass(&self) -> Result<SymbolTable> {
let symbol_table = SymbolTableCreator::do_pass((&self.ast, self.handler))?;
Expand Down Expand Up @@ -289,7 +296,20 @@ impl<'a, N: Network> Compiler<'a, N> {
/// Returns a compiled Leo program.
pub fn compile(&mut self) -> Result<String> {
// Parse the program.
self.parse_program()?;
self.parse()?;
// Copy the dependencies specified in `program.json` into the AST.
self.add_import_stubs()?;
// Run the intermediate compiler stages.
let (symbol_table, struct_graph, call_graph) = self.compiler_stages()?;
// Run code generation.
let bytecode = self.code_generation_pass(&symbol_table, &struct_graph, &call_graph)?;
Ok(bytecode)
}

/// Returns the compiled Leo tests.
pub fn compile_tests(&mut self) -> Result<String> {
// Parse the program.
self.parse()?;
// Copy the dependencies specified in `program.json` into the AST.
self.add_import_stubs()?;
// Run the intermediate compiler stages.
Expand All @@ -303,11 +323,11 @@ impl<'a, N: Network> Compiler<'a, N> {
fn write_ast_to_json(&self, file_suffix: &str) -> Result<()> {
// Remove `Span`s if they are not enabled.
if self.compiler_options.output.ast_spans_enabled {
self.ast.to_json_file(self.output_directory.clone(), &format!("{}.{file_suffix}", self.program_name))?;
self.ast.to_json_file(self.output_directory.clone(), &format!("{}.{file_suffix}", self.name))?;
} else {
self.ast.to_json_file_without_keys(
self.output_directory.clone(),
&format!("{}.{file_suffix}", self.program_name),
&format!("{}.{file_suffix}", self.name),
&["_span", "span"],
)?;
}
Expand All @@ -318,12 +338,11 @@ impl<'a, N: Network> Compiler<'a, N> {
fn write_symbol_table_to_json(&self, file_suffix: &str, symbol_table: &SymbolTable) -> Result<()> {
// Remove `Span`s if they are not enabled.
if self.compiler_options.output.symbol_table_spans_enabled {
symbol_table
.to_json_file(self.output_directory.clone(), &format!("{}.{file_suffix}", self.program_name))?;
symbol_table.to_json_file(self.output_directory.clone(), &format!("{}.{file_suffix}", self.name))?;
} else {
symbol_table.to_json_file_without_keys(
self.output_directory.clone(),
&format!("{}.{file_suffix}", self.program_name),
&format!("{}.{file_suffix}", self.name),
&["_span", "span"],
)?;
}
Expand All @@ -349,7 +368,6 @@ impl<'a, N: Network> Compiler<'a, N> {
}
} else {
return Err(CompilerError::imported_program_not_found(
self.program_name.clone(),
*program_name,
self.ast.ast.imports[program_name].1,
)
Expand Down
11 changes: 5 additions & 6 deletions compiler/compiler/tests/integration/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,11 @@ fn run_test(test: Test, handler: &Handler, buf: &BufferEmitter) -> Result<Value,
handler,
program_string,
cwd.clone(),
Some(compiler_options.clone()),
compiler_options.clone(),
import_stubs.clone(),
))?;

// Compile the program to bytecode.
let program_name = parsed.program_name.to_string();
let bytecode = handler.extend_if_error(compile_and_process(&mut parsed))?;

// Parse the bytecode as an Aleo program.
Expand All @@ -130,17 +129,17 @@ fn run_test(test: Test, handler: &Handler, buf: &BufferEmitter) -> Result<Value,

// Add the bytecode to the import stubs.
let stub = handler.extend_if_error(
disassemble_from_str::<CurrentNetwork>(&program_name, &bytecode).map_err(|err| err.into()),
disassemble_from_str::<CurrentNetwork>(program_name, &bytecode).map_err(|err| err.into()),
)?;
import_stubs.insert(Symbol::intern(&program_name), stub);
import_stubs.insert(Symbol::intern(program_name), stub);

// Hash the ast files.
let (initial_ast, unrolled_ast, ssa_ast, flattened_ast, destructured_ast, inlined_ast, dce_ast) =
hash_asts(&program_name);
hash_asts(program_name);

// Hash the symbol tables.
let (initial_symbol_table, type_checked_symbol_table, unrolled_symbol_table) =
hash_symbol_tables(&program_name);
hash_symbol_tables(program_name);

// Clean up the output directory.
if fs::read_dir("/tmp/output").is_ok() {
Expand Down
11 changes: 5 additions & 6 deletions compiler/compiler/tests/integration/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,11 @@ fn run_test(test: Test, handler: &Handler, buf: &BufferEmitter) -> Result<Value,
handler,
program_string,
cwd.clone(),
Some(compiler_options.clone()),
compiler_options.clone(),
import_stubs.clone(),
))?;

// Compile the program to bytecode.
let program_name = parsed.program_name.to_string();
let bytecode = handler.extend_if_error(compile_and_process(&mut parsed))?;

// Parse the bytecode as an Aleo program.
Expand All @@ -172,17 +171,17 @@ fn run_test(test: Test, handler: &Handler, buf: &BufferEmitter) -> Result<Value,

// Add the bytecode to the import stubs.
let stub = handler.extend_if_error(
disassemble_from_str::<CurrentNetwork>(&program_name, &bytecode).map_err(|err| err.into()),
disassemble_from_str::<CurrentNetwork>(program_name, &bytecode).map_err(|err| err.into()),
)?;
import_stubs.insert(Symbol::intern(&program_name), stub);
import_stubs.insert(Symbol::intern(program_name), stub);

// Hash the ast files.
let (initial_ast, unrolled_ast, ssa_ast, flattened_ast, destructured_ast, inlined_ast, dce_ast) =
hash_asts(&program_name);
hash_asts(program_name);

// Hash the symbol tables.
let (initial_symbol_table, type_checked_symbol_table, unrolled_symbol_table) =
hash_symbol_tables(&program_name);
hash_symbol_tables(program_name);

// Clean up the output directory.
if fs::read_dir("/tmp/output").is_ok() {
Expand Down
25 changes: 10 additions & 15 deletions compiler/compiler/tests/integration/utilities/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,35 +152,30 @@ pub fn setup_build_directory(
}

pub fn new_compiler(
program_name: String,
name: String,
handler: &Handler,
main_file_path: PathBuf,
compiler_options: Option<CompilerOptions>,
sources: Vec<(FileName, String)>,
compiler_options: CompilerOptions,
import_stubs: IndexMap<Symbol, Stub>,
) -> Compiler<'_, CurrentNetwork> {
let output_dir = PathBuf::from("/tmp/output/");
fs::create_dir_all(output_dir.clone()).unwrap();

Compiler::new(program_name, handler, main_file_path, output_dir, compiler_options, import_stubs)
Compiler::new(name, handler, sources, output_dir, compiler_options, import_stubs)
}

pub fn parse_program<'a>(
program_name: String,
name: String,
handler: &'a Handler,
program_string: &str,
cwd: Option<PathBuf>,
compiler_options: Option<CompilerOptions>,
compiler_options: CompilerOptions,
import_stubs: IndexMap<Symbol, Stub>,
) -> Result<Compiler<'a, CurrentNetwork>, LeoError> {
let mut compiler = new_compiler(
program_name,
handler,
cwd.clone().unwrap_or_else(|| "compiler-test".into()),
compiler_options,
import_stubs,
);
let name = cwd.map_or_else(|| FileName::Custom("compiler-test".into()), FileName::Real);
compiler.parse_program_from_string(program_string, name)?;
let file_name = cwd.map_or_else(|| FileName::Custom("compiler-test".into()), FileName::Real);
let mut compiler =
new_compiler(name, handler, vec![(file_name, program_string.into())], compiler_options, import_stubs);
compiler.parse()?;

CheckUniqueNodeIds::new().visit_program(&compiler.ast.ast);

Expand Down
5 changes: 2 additions & 3 deletions compiler/parser/src/parser/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

use super::*;

use leo_errors::{ParserError, Result};
use leo_errors::Result;
use leo_span::Symbol;

impl<N: Network> ParserContext<'_, N> {
/// Parses a test file.
Expand Down Expand Up @@ -65,5 +66,3 @@ impl<N: Network> ParserContext<'_, N> {
Ok(Test { consts, functions, structs, mappings })
}
}

use leo_span::{Symbol, sym};
6 changes: 3 additions & 3 deletions errors/src/errors/compiler/compiler_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ create_messages!(

@formatted
imported_program_not_found {
args: (main_program_name: impl Display, dependency_name: impl Display),
msg: format!("`{main_program_name}` imports `{dependency_name}.aleo`, but `{dependency_name}.aleo` is not found in program manifest. Use `leo add --help` for more information on how to add a dependency."),
help: None,
args: (dependency_name: impl Display),
msg: format!("`{dependency_name}.aleo` is not found in program manifest."),
help: Some("Use `leo add --help` for more information on how to add a dependency.".to_string()),
}
);
Loading

0 comments on commit c6e49ad

Please sign in to comment.