mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-12-18 15:31:32 +03:00
Refactor compiler to have a separate typed ast infrastucture
This commit is contained in:
parent
8767d01abc
commit
5bd6ab78b9
@ -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)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)?;
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user