Refactor compiler to have a separate typed ast infrastucture

This commit is contained in:
howardwu 2020-07-29 01:12:17 -07:00
parent 8767d01abc
commit 5bd6ab78b9
7 changed files with 102 additions and 48 deletions

View File

@ -29,29 +29,41 @@ pub(crate) use span::*;
use from_pest::FromPest; use from_pest::FromPest;
use std::{fs, path::PathBuf}; use std::{fs, path::PathBuf};
pub struct LeoParser; pub struct LeoAst<'ast> {
ast: files::File<'ast>,
}
impl LeoParser { impl<'ast> LeoAst<'ast> {
/// Creates a new abstract syntax tree given the file path.
pub fn new(file_path: &'ast PathBuf, program_string: &'ast str) -> Result<Self, ParserError> {
// TODO (howardwu): Turn this check back on after fixing the testing module.
// assert_eq!(program_string, fs::read_to_string(file_path).map_err(|_| ParserError::FileReadError(file_path.clone()))?);
// Parse the file using leo.pest
let file = &mut ast::parse(&program_string)
.map_err(|error| ParserError::from(error.with_path(file_path.to_str().unwrap())))?;
// Builds the abstract syntax tree using pest derivation.
let ast = files::File::<'ast>::from_pest(file).map_err(|_| ParserError::SyntaxTreeError)?;
log::debug!("{:#?}", ast);
Ok(Self { ast })
}
// TODO (howardwu): Remove this in favor of a dedicated file loader to verify checksums
// and maintain a global cache of program strings during the compilation process.
/// Loads the Leo code as a string from the given file path. /// Loads the Leo code as a string from the given file path.
pub fn load_file(file_path: &PathBuf) -> Result<String, ParserError> { pub fn load_file(file_path: &'ast PathBuf) -> Result<String, ParserError> {
Ok(fs::read_to_string(file_path).map_err(|_| ParserError::FileReadError(file_path.clone()))?) Ok(fs::read_to_string(file_path).map_err(|_| ParserError::FileReadError(file_path.clone()))?)
} }
/// Parses the Leo program string and constructs an abstract syntax tree. /// Returns a reference to the inner abstract syntax tree representation.
pub fn parse_file<'a>(file_path: &'a PathBuf, program_string: &'a str) -> Result<files::File<'a>, ParserError> { pub fn as_repr(&self) -> &files::File<'ast> {
// Parse the file using leo.pest &self.ast
let mut file = ast::parse(program_string)
.map_err(|error| ParserError::from(error.with_path(file_path.to_str().unwrap())))?;
// Build the abstract syntax tree
let syntax_tree = files::File::from_pest(&mut file).map_err(|_| ParserError::SyntaxTreeError)?;
log::debug!("{:#?}", syntax_tree);
Ok(syntax_tree)
} }
/// Serializes a given abstract syntax tree into a JSON string. /// Serializes the abstract syntax tree into a JSON string.
pub fn to_json_string(syntax_tree: &files::File) -> Result<String, ParserError> { pub fn to_json_string(&self) -> Result<String, ParserError> {
Ok(serde_json::to_string_pretty(syntax_tree)?) Ok(serde_json::to_string_pretty(&self.ast)?)
} }
} }

View File

@ -1,16 +1,16 @@
use leo_ast::{LeoParser, ParserError}; use leo_ast::{LeoAst, ParserError};
use std::{env, fs, path::Path}; use std::{env, fs, path::Path};
fn to_leo_ast(filepath: &Path) -> Result<String, ParserError> { fn to_leo_ast(filepath: &Path) -> Result<String, ParserError> {
// Loads the Leo code as a string from the given file path. // Loads the Leo code as a string from the given file path.
let program_filepath = filepath.to_path_buf(); let program_filepath = filepath.to_path_buf();
let program_string = LeoParser::load_file(&program_filepath)?; let program_string = LeoAst::load_file(&program_filepath)?;
// Parses the Leo program string and constructs an abstract syntax tree. // Parses the Leo file and constructs an abstract syntax tree.
let abstract_syntax_tree = LeoParser::parse_file(&program_filepath, &program_string)?; let ast = LeoAst::new(&program_filepath, &program_string)?;
// Serializes the abstract syntax tree into JSON format. // Serializes the abstract syntax tree into JSON format.
let serialized_ast = LeoParser::to_json_string(&abstract_syntax_tree)?; let serialized_ast = LeoAst::to_json_string(&ast)?;
Ok(serialized_ast) Ok(serialized_ast)
} }

View File

@ -7,9 +7,9 @@ use crate::{
GroupType, GroupType,
ImportParser, ImportParser,
}; };
use leo_ast::LeoParser; use leo_ast::LeoAst;
use leo_inputs::LeoInputsParser; use leo_inputs::LeoInputsParser;
use leo_types::{InputValue, Inputs, Program}; use leo_types::{InputValue, Inputs, LeoTypedAst, Program};
use snarkos_errors::gadgets::SynthesisError; use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::{ use snarkos_models::{
@ -48,9 +48,8 @@ impl<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
let mut compiler = Self::new(package_name); let mut compiler = Self::new(package_name);
compiler.set_path(main_file_path); compiler.set_path(main_file_path);
// Generate the abstract syntax tree and assemble the program // Generate the abstract syntax tree and assemble the program.
let program_string = compiler.load_program()?; compiler.parse_program()?;
compiler.parse_program(&program_string)?;
Ok(compiler) Ok(compiler)
} }
@ -94,20 +93,40 @@ impl<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
generate_test_constraints::<F, G>(cs, self.program, &self.imported_programs) generate_test_constraints::<F, G>(cs, self.program, &self.imported_programs)
} }
/// Loads the Leo code as a string from the given file path. /// Parses the Leo program file, constructs a syntax tree, and generates a program.
fn load_program(&mut self) -> Result<String, CompilerError> { pub fn parse_program(&mut self) -> Result<(), CompilerError> {
Ok(LeoParser::load_file(&self.main_file_path)?) // Use the parser to construct the abstract syntax tree.
let program_string = LeoAst::load_file(&self.main_file_path)?;
let ast = LeoAst::new(&self.main_file_path, &program_string)?;
// Derive the package name.
let package_name = self.package_name.clone();
// Use the typed parser to construct the typed syntax tree.
let typed_tree = LeoTypedAst::new(&package_name, &ast);
self.program = typed_tree.into_repr();
self.program_inputs.set_inputs_size(self.program.expected_inputs.len());
self.imported_programs = ImportParser::parse(&self.program)?;
log::debug!("Program parsing complete\n{:#?}", self.program);
Ok(())
} }
/// Parses the Leo program string, constructs a syntax tree, and generates a program. /// Parses the Leo program string, constructs a syntax tree, and generates a program.
pub fn parse_program(&mut self, program_string: &str) -> Result<(), CompilerError> { /// Used for testing only.
// Parse the program syntax tree pub fn parse_program_from_string(&mut self, program_string: &str) -> Result<(), CompilerError> {
let syntax_tree = LeoParser::parse_file(&self.main_file_path, program_string)?; // Use the given bytes to construct the abstract syntax tree.
let ast = LeoAst::new(&self.main_file_path, &program_string)?;
// Build program from syntax tree // Derive the package name.
let package_name = self.package_name.clone(); let package_name = self.package_name.clone();
self.program = Program::from(syntax_tree, package_name); // Use the typed parser to construct the typed syntax tree.
let typed_tree = LeoTypedAst::new(&package_name, &ast);
self.program = typed_tree.into_repr();
self.program_inputs.set_inputs_size(self.program.expected_inputs.len()); self.program_inputs.set_inputs_size(self.program.expected_inputs.len());
self.imported_programs = ImportParser::parse(&self.program)?; self.imported_programs = ImportParser::parse(&self.program)?;

View File

@ -1,5 +1,5 @@
use crate::{errors::ImportError, ImportParser}; use crate::{errors::ImportError, ImportParser};
use leo_ast::LeoParser; use leo_ast::LeoAst;
use leo_types::{ImportSymbol, Program, Span}; use leo_types::{ImportSymbol, Program, Span};
use std::{ffi::OsString, fs::DirEntry, path::PathBuf}; use std::{ffi::OsString, fs::DirEntry, path::PathBuf};
@ -30,12 +30,12 @@ fn parse_import_file(entry: &DirEntry, span: &Span) -> Result<Program, ImportErr
} }
} }
// Build the abstract syntax tree // Builds the abstract syntax tree.
let input_file = &LeoParser::load_file(&file_path)?; let program_string = &LeoAst::load_file(&file_path)?;
let syntax_tree = LeoParser::parse_file(&file_path, input_file)?; let ast = &LeoAst::new(&file_path, &program_string)?;
// Generate aleo program from file // Generates the Leo program from file.
Ok(Program::from(syntax_tree, file_name.clone())) Ok(Program::from(&file_name, ast.as_repr()))
} }
impl ImportParser { impl ImportParser {

View File

@ -59,7 +59,7 @@ pub(crate) fn parse_program(bytes: &[u8]) -> Result<EdwardsTestCompiler, Compile
let mut compiler = new_compiler(); let mut compiler = new_compiler();
let program_string = String::from_utf8_lossy(bytes); let program_string = String::from_utf8_lossy(bytes);
compiler.parse_program(&program_string)?; compiler.parse_program_from_string(&program_string)?;
Ok(compiler) Ok(compiler)
} }

View File

@ -1,3 +1,6 @@
//! A typed syntax tree is represented as a `Program` and consists of import, circuit, and function definitions.
//! Each defined type consists of typed statements and expressions.
pub mod circuits; pub mod circuits;
pub use self::circuits::*; pub use self::circuits::*;
@ -30,3 +33,22 @@ pub use self::statements::*;
pub mod types; pub mod types;
pub use self::types::*; pub use self::types::*;
use leo_ast::LeoAst;
pub struct LeoTypedAst {
typed_ast: Program,
}
impl LeoTypedAst {
/// Creates a new typed syntax tree from a given program name and abstract syntax tree.
pub fn new<'ast>(program_name: &str, ast: &LeoAst<'ast>) -> Self {
Self {
typed_ast: Program::from(program_name, ast.as_repr()),
}
}
pub fn into_repr(self) -> Program {
self.typed_ast
}
}

View File

@ -20,10 +20,11 @@ pub struct Program {
impl<'ast> Program { impl<'ast> Program {
//! Logic to convert from an abstract syntax tree (ast) representation to a Leo program. //! Logic to convert from an abstract syntax tree (ast) representation to a Leo program.
pub fn from(file: File<'ast>, name: String) -> Self { pub fn from(program_name: &str, program_ast: &File<'ast>) -> Self {
// Compiled ast -> aleo program representation // Compiled ast -> aleo program representation
let imports = file let imports = program_ast
.imports .imports
.to_owned()
.into_iter() .into_iter()
.map(|import| Import::from(import)) .map(|import| Import::from(import))
.collect::<Vec<Import>>(); .collect::<Vec<Import>>();
@ -33,23 +34,23 @@ impl<'ast> Program {
let mut tests = HashMap::new(); let mut tests = HashMap::new();
let mut expected_inputs = vec![]; let mut expected_inputs = vec![];
file.circuits.into_iter().for_each(|circuit| { program_ast.circuits.to_owned().into_iter().for_each(|circuit| {
circuits.insert(Identifier::from(circuit.identifier.clone()), Circuit::from(circuit)); circuits.insert(Identifier::from(circuit.identifier.clone()), Circuit::from(circuit));
}); });
file.functions.into_iter().for_each(|function_def| { program_ast.functions.to_owned().into_iter().for_each(|function_def| {
let function = Function::from(function_def); let function = Function::from(function_def);
if function.function_name.name.eq("main") { if function.function_name.name.eq("main") {
expected_inputs = function.inputs.clone(); expected_inputs = function.inputs.clone();
} }
functions.insert(function.function_name.clone(), function); functions.insert(function.function_name.clone(), function);
}); });
file.tests.into_iter().for_each(|test_def| { program_ast.tests.to_owned().into_iter().for_each(|test_def| {
let test = TestFunction::from(test_def); let test = TestFunction::from(test_def);
tests.insert(test.0.function_name.clone(), test); tests.insert(test.0.function_name.clone(), test);
}); });
Self { Self {
name, name: program_name.to_string(),
expected_inputs, expected_inputs,
imports, imports,
circuits, circuits,