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 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.
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()))?)
}
/// Parses the Leo program string and constructs an abstract syntax tree.
pub fn parse_file<'a>(file_path: &'a PathBuf, program_string: &'a str) -> Result<files::File<'a>, ParserError> {
// Parse the file using leo.pest
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)
/// Returns a reference to the inner abstract syntax tree representation.
pub fn as_repr(&self) -> &files::File<'ast> {
&self.ast
}
/// Serializes a given abstract syntax tree into a JSON string.
pub fn to_json_string(syntax_tree: &files::File) -> Result<String, ParserError> {
Ok(serde_json::to_string_pretty(syntax_tree)?)
/// Serializes the abstract syntax tree into a JSON string.
pub fn to_json_string(&self) -> Result<String, ParserError> {
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};
fn to_leo_ast(filepath: &Path) -> Result<String, ParserError> {
// Loads the Leo code as a string from the given file path.
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.
let abstract_syntax_tree = LeoParser::parse_file(&program_filepath, &program_string)?;
// Parses the Leo file and constructs an abstract syntax tree.
let ast = LeoAst::new(&program_filepath, &program_string)?;
// 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)
}

View File

@ -7,9 +7,9 @@ use crate::{
GroupType,
ImportParser,
};
use leo_ast::LeoParser;
use leo_ast::LeoAst;
use leo_inputs::LeoInputsParser;
use leo_types::{InputValue, Inputs, Program};
use leo_types::{InputValue, Inputs, LeoTypedAst, Program};
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::{
@ -48,9 +48,8 @@ impl<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
let mut compiler = Self::new(package_name);
compiler.set_path(main_file_path);
// Generate the abstract syntax tree and assemble the program
let program_string = compiler.load_program()?;
compiler.parse_program(&program_string)?;
// Generate the abstract syntax tree and assemble the program.
compiler.parse_program()?;
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)
}
/// Loads the Leo code as a string from the given file path.
fn load_program(&mut self) -> Result<String, CompilerError> {
Ok(LeoParser::load_file(&self.main_file_path)?)
/// Parses the Leo program file, constructs a syntax tree, and generates a program.
pub fn parse_program(&mut self) -> Result<(), CompilerError> {
// 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.
pub fn parse_program(&mut self, program_string: &str) -> Result<(), CompilerError> {
// Parse the program syntax tree
let syntax_tree = LeoParser::parse_file(&self.main_file_path, program_string)?;
/// Used for testing only.
pub fn parse_program_from_string(&mut self, program_string: &str) -> Result<(), CompilerError> {
// 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();
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.imported_programs = ImportParser::parse(&self.program)?;

View File

@ -1,5 +1,5 @@
use crate::{errors::ImportError, ImportParser};
use leo_ast::LeoParser;
use leo_ast::LeoAst;
use leo_types::{ImportSymbol, Program, Span};
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
let input_file = &LeoParser::load_file(&file_path)?;
let syntax_tree = LeoParser::parse_file(&file_path, input_file)?;
// Builds the abstract syntax tree.
let program_string = &LeoAst::load_file(&file_path)?;
let ast = &LeoAst::new(&file_path, &program_string)?;
// Generate aleo program from file
Ok(Program::from(syntax_tree, file_name.clone()))
// Generates the Leo program from file.
Ok(Program::from(&file_name, ast.as_repr()))
}
impl ImportParser {

View File

@ -59,7 +59,7 @@ pub(crate) fn parse_program(bytes: &[u8]) -> Result<EdwardsTestCompiler, Compile
let mut compiler = new_compiler();
let program_string = String::from_utf8_lossy(bytes);
compiler.parse_program(&program_string)?;
compiler.parse_program_from_string(&program_string)?;
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 use self::circuits::*;
@ -30,3 +33,22 @@ pub use self::statements::*;
pub mod 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 {
//! 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
let imports = file
let imports = program_ast
.imports
.to_owned()
.into_iter()
.map(|import| Import::from(import))
.collect::<Vec<Import>>();
@ -33,23 +34,23 @@ impl<'ast> Program {
let mut tests = HashMap::new();
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));
});
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);
if function.function_name.name.eq("main") {
expected_inputs = function.inputs.clone();
}
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);
tests.insert(test.0.function_name.clone(), test);
});
Self {
name,
name: program_name.to_string(),
expected_inputs,
imports,
circuits,