mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-12-24 18:52:58 +03:00
some clean up to compiler to be able to better test
This commit is contained in:
parent
bd356e1f31
commit
0fc6d17289
@ -25,9 +25,11 @@
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
pub use leo_ast::Ast;
|
||||
use leo_ast::Program;
|
||||
pub use leo_ast::{Ast, ParsedInputFile};
|
||||
use leo_errors::emitter::Handler;
|
||||
use leo_errors::{CompilerError, Result};
|
||||
pub use leo_passes::SymbolTable;
|
||||
use leo_passes::*;
|
||||
use leo_span::symbol::create_session_if_not_set_then;
|
||||
|
||||
@ -41,7 +43,7 @@ pub struct Compiler<'a> {
|
||||
handler: &'a Handler,
|
||||
main_file_path: PathBuf,
|
||||
output_directory: PathBuf,
|
||||
input_file_path: Option<PathBuf>,
|
||||
pub ast: Ast,
|
||||
}
|
||||
|
||||
impl<'a> Compiler<'a> {
|
||||
@ -53,7 +55,7 @@ impl<'a> Compiler<'a> {
|
||||
handler,
|
||||
main_file_path,
|
||||
output_directory,
|
||||
input_file_path: None,
|
||||
ast: Ast::new(Program::new("Initial".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,26 +75,28 @@ impl<'a> Compiler<'a> {
|
||||
Ok(format!("{:x}", hash))
|
||||
}
|
||||
|
||||
///
|
||||
/// Runs the compiler stages.
|
||||
///
|
||||
fn compiler_stages(&self, program_string: &str) -> Result<leo_ast::Ast> {
|
||||
//load the input file if it exists.
|
||||
if let Some(input_file_path) = self.input_file_path.as_ref() {
|
||||
let _input_ast = if input_file_path.exists() {
|
||||
let input_string = fs::read_to_string(&input_file_path)
|
||||
.map_err(|e| CompilerError::file_read_error(input_file_path.clone(), e))?;
|
||||
|
||||
Some(leo_parser::parse_input(
|
||||
// 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) -> Result<()> {
|
||||
// Use the parser to construct the abstract syntax tree (ast).
|
||||
let ast: leo_ast::Ast = leo_parser::parse_ast(
|
||||
self.handler,
|
||||
input_file_path.to_str().unwrap_or_default(),
|
||||
input_string,
|
||||
)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.main_file_path.to_str().unwrap_or_default(),
|
||||
program_string,
|
||||
)?;
|
||||
// Write the AST snapshot post parsing.
|
||||
ast.to_json_file_without_keys(self.output_directory.clone(), "initial_ast.json", &["span"])?;
|
||||
|
||||
self.ast = ast;
|
||||
|
||||
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.clone(), e))?;
|
||||
|
||||
// Use the parser to construct the abstract syntax tree (ast).
|
||||
let ast: leo_ast::Ast = leo_parser::parse_ast(
|
||||
self.handler,
|
||||
@ -102,31 +106,43 @@ impl<'a> Compiler<'a> {
|
||||
// Write the AST snapshot post parsing.
|
||||
ast.to_json_file_without_keys(self.output_directory.clone(), "initial_ast.json", &["span"])?;
|
||||
|
||||
// Canonicalize the AST.
|
||||
// ast = leo_ast_passes::Canonicalizer::do_pass(Default::default(), ast.into_repr())?;
|
||||
// Write the AST snapshot post parsing
|
||||
// ast.to_json_file_without_keys(self.output_directory, "canonicalization_ast.json", &["span"])?;
|
||||
self.ast = ast;
|
||||
|
||||
let _symbol_table = CreateSymbolTable::do_pass((&ast, self.handler));
|
||||
|
||||
Ok(ast)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses and stores the main program file, constructs a syntax tree, and generates a program.
|
||||
///
|
||||
/// Parses and stores all programs imported by the main program file.
|
||||
pub fn parse_program(&self) -> Result<Ast> {
|
||||
// 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.clone(), e))?;
|
||||
// Loads the given input file if it exists.
|
||||
fn parse_input(&self, input_file_path: PathBuf) -> Result<Option<ParsedInputFile>> {
|
||||
// Load the input file if it exists.
|
||||
if input_file_path.exists() {
|
||||
let input_string = fs::read_to_string(&input_file_path)
|
||||
.map_err(|e| CompilerError::file_read_error(input_file_path.clone(), e))?;
|
||||
|
||||
self.compiler_stages(&program_string)
|
||||
let input_ast =
|
||||
leo_parser::parse_input(self.handler, input_file_path.to_str().unwrap_or_default(), input_string)?;
|
||||
|
||||
input_ast.to_json_file_without_keys(self.output_directory.clone(), "inital_input_ast.json", &["span"])?;
|
||||
|
||||
Ok(Some(input_ast))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Runs the compiler stages.
|
||||
///
|
||||
fn compiler_stages(&mut self, input_file_path: PathBuf) -> Result<(Option<ParsedInputFile>, SymbolTable<'_>)> {
|
||||
let input_ast = self.parse_input(input_file_path)?;
|
||||
let symbol_table = CreateSymbolTable::do_pass((&self.ast, self.handler));
|
||||
|
||||
Ok((input_ast, symbol_table))
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a compiled Leo program.
|
||||
///
|
||||
pub fn compile(&self) -> Result<leo_ast::Ast> {
|
||||
create_session_if_not_set_then(|_| self.parse_program())
|
||||
pub fn compile(&mut self, input_file_path: PathBuf) -> Result<(Option<ParsedInputFile>, SymbolTable<'_>)> {
|
||||
create_session_if_not_set_then(|_| self.compiler_stages(input_file_path))
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ use leo_errors::{
|
||||
emitter::{Buffer, Emitter, Handler},
|
||||
LeoError,
|
||||
};
|
||||
use leo_passes::SymbolTable;
|
||||
use leo_span::symbol::create_session_if_not_set_then;
|
||||
use leo_test_framework::{
|
||||
runner::{Namespace, ParseType, Runner},
|
||||
@ -49,22 +50,25 @@ fn parse_program<'a>(
|
||||
cwd: Option<PathBuf>,
|
||||
) -> Result<Compiler<'a>, LeoError> {
|
||||
let mut compiler = new_compiler(handler, cwd.unwrap_or_else(|| "compiler-test".into()));
|
||||
|
||||
compiler.compile()?;
|
||||
compiler.parse_program_from_string(program_string)?;
|
||||
|
||||
Ok(compiler)
|
||||
}
|
||||
|
||||
fn hash_file(path: &str) -> String {
|
||||
fn hash_content(content: &str) -> String {
|
||||
use sha2::{Digest, Sha256};
|
||||
let mut file = fs::File::open(&Path::new(path)).unwrap();
|
||||
let mut hasher = Sha256::new();
|
||||
std::io::copy(&mut file, &mut hasher).unwrap();
|
||||
hasher.update(content);
|
||||
let hash = hasher.finalize();
|
||||
|
||||
format!("{:x}", hash)
|
||||
}
|
||||
|
||||
fn hash_file(path: &str) -> String {
|
||||
let file = fs::read_to_string(&Path::new(path)).unwrap();
|
||||
hash_content(&file)
|
||||
}
|
||||
|
||||
struct CompileNamespace;
|
||||
|
||||
impl Namespace for CompileNamespace {
|
||||
@ -85,6 +89,8 @@ impl Namespace for CompileNamespace {
|
||||
#[derive(Deserialize, PartialEq, Serialize)]
|
||||
struct OutputItem {
|
||||
pub input_file: String,
|
||||
pub initial_input_ast: String,
|
||||
pub symbol_table: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, PartialEq, Serialize)]
|
||||
@ -93,44 +99,18 @@ struct CompileOutput {
|
||||
pub initial_ast: String,
|
||||
}
|
||||
|
||||
type Input = (String, String);
|
||||
type Input = (PathBuf, String);
|
||||
|
||||
/// Collect all inputs into `list` from `field`, if possible.
|
||||
fn collect_inputs_list(list: &mut Vec<Input>, field: &[Value]) -> Result<(), String> {
|
||||
for map in field {
|
||||
for (name, value) in map.as_mapping().unwrap().iter() {
|
||||
// Try to parse string from 'inputs' map, else fail
|
||||
let value = if let serde_yaml::Value::String(value) = value {
|
||||
value
|
||||
} else {
|
||||
return Err("Expected string in 'inputs' map".to_string());
|
||||
};
|
||||
|
||||
list.push((name.as_str().unwrap().to_string(), value.clone()));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read contents of `input_file` given in `input` into `list`.
|
||||
fn read_input_file(list: &mut Vec<Input>, test: &Test, input: &Value) {
|
||||
/// Get the path of the `input_file` given in `input` into `list`.
|
||||
fn get_input_file_paths(list: &mut Vec<Input>, test: &Test, input: &Value) {
|
||||
let input_file: PathBuf = test.path.parent().expect("no test parent dir").into();
|
||||
if let Some(name) = input.as_str() {
|
||||
if let Some(_) = input.as_str() {
|
||||
let mut input_file = input_file;
|
||||
input_file.push(input.as_str().expect("input_file was not a string or array"));
|
||||
list.push((
|
||||
name.to_string(),
|
||||
input_file.clone(),
|
||||
fs::read_to_string(&input_file).expect("failed to read test input file"),
|
||||
));
|
||||
} else if let Some(seq) = input.as_sequence() {
|
||||
for name in seq {
|
||||
let mut input_file = input_file.clone();
|
||||
input_file.push(name.as_str().expect("input_file was not a string"));
|
||||
list.push((
|
||||
name.as_str().expect("input_file item was not a string").to_string(),
|
||||
fs::read_to_string(&input_file).expect("failed to read test input file"),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,22 +118,19 @@ fn read_input_file(list: &mut Vec<Input>, test: &Test, input: &Value) {
|
||||
fn collect_all_inputs(test: &Test) -> Result<Vec<Input>, String> {
|
||||
let mut list = vec![];
|
||||
|
||||
if let Some(Value::Sequence(field)) = test.config.get("inputs") {
|
||||
collect_inputs_list(&mut list, field)?;
|
||||
if let Some(input) = test.config.get("input_file") {
|
||||
get_input_file_paths(&mut list, &test, input);
|
||||
}
|
||||
|
||||
if let Some(input) = test.config.get("input_file") {
|
||||
read_input_file(&mut list, &test, input);
|
||||
}
|
||||
if list.is_empty() {
|
||||
list.push(("empty".to_string(), "".to_string()));
|
||||
}
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
fn compile_and_process(parsed: Compiler<'_>, input: &Input) -> Result<(), LeoError> {
|
||||
// ParsedInputFile
|
||||
Ok(())
|
||||
fn compile_and_process<'a>(
|
||||
parsed: &'a mut Compiler<'a>,
|
||||
input_file_path: PathBuf,
|
||||
) -> Result<(Option<ParsedInputFile>, SymbolTable<'a>), LeoError> {
|
||||
let compiled = parsed.compiler_stages(input_file_path)?;
|
||||
Ok(compiled)
|
||||
}
|
||||
|
||||
// Errors used in this module.
|
||||
@ -212,14 +189,18 @@ fn run_test(test: Test, handler: &Handler, err_buf: &BufferEmitter) -> Result<Va
|
||||
let mut output_items = Vec::with_capacity(inputs.len());
|
||||
|
||||
for input in inputs {
|
||||
let parsed = parsed.clone();
|
||||
handler.extend_if_error(compile_and_process(parsed, &input))?;
|
||||
let mut parsed = parsed.clone();
|
||||
let (_, symbol_table) = handler.extend_if_error(compile_and_process(&mut parsed, input.0))?;
|
||||
let initial_input_ast = hash_file("/tmp/output/inital_input_ast.json");
|
||||
|
||||
output_items.push(OutputItem { input_file: input.0 });
|
||||
output_items.push(OutputItem {
|
||||
input_file: input.1,
|
||||
initial_input_ast,
|
||||
symbol_table: hash_content(&symbol_table.to_string()),
|
||||
});
|
||||
}
|
||||
|
||||
let initial_ast = hash_file("/tmp/output/initial_ast.json");
|
||||
let initial_input = hash_file("/tmp/output/input_ast.json");
|
||||
|
||||
if fs::read_dir("/tmp/output").is_ok() {
|
||||
fs::remove_dir_all(Path::new("/tmp/output")).expect("Error failed to clean up output dir.");
|
||||
|
@ -14,6 +14,8 @@
|
||||
// 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 std::fmt::Display;
|
||||
|
||||
use leo_ast::Function;
|
||||
use leo_errors::{AstError, Result};
|
||||
use leo_span::Symbol;
|
||||
@ -48,3 +50,9 @@ impl<'a> SymbolTable<'a> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for SymbolTable<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{commands::Command, context::Context};
|
||||
use leo_compiler::{Ast, Compiler};
|
||||
use leo_compiler::{Ast, Compiler, ParsedInputFile};
|
||||
use leo_errors::{CliError, Result};
|
||||
use leo_package::{
|
||||
inputs::InputFile,
|
||||
@ -101,7 +101,7 @@ pub struct Build {
|
||||
|
||||
impl Command for Build {
|
||||
type Input = ();
|
||||
type Output = (Ast, bool);
|
||||
type Output = (Option<ParsedInputFile>, Ast, bool);
|
||||
|
||||
fn log_span(&self) -> Span {
|
||||
tracing::span!(tracing::Level::INFO, "Build")
|
||||
@ -180,13 +180,14 @@ impl Command for Build {
|
||||
// Initialize error handler
|
||||
let handler = leo_errors::emitter::Handler::default();
|
||||
|
||||
let program = Compiler::new(&handler, main_file_path, output_directory);
|
||||
let mut program = Compiler::new(&handler, main_file_path, output_directory);
|
||||
program.parse_program()?;
|
||||
|
||||
// Compute the current program checksum
|
||||
let program_checksum = program.checksum()?;
|
||||
|
||||
// Compile the program
|
||||
let program_compiled = program.compile()?;
|
||||
let (input_ast, _) = program.compile(input_path.to_path_buf())?;
|
||||
|
||||
// Generate the program on the constraint system and verify correctness
|
||||
{
|
||||
@ -240,6 +241,6 @@ impl Command for Build {
|
||||
|
||||
tracing::info!("Complete");
|
||||
|
||||
Ok((program_compiled, checksum_differs))
|
||||
Ok((input_ast, program.ast, checksum_differs))
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user