Resolve merge conflict

This commit is contained in:
howardwu 2020-08-02 18:51:44 -07:00
commit 5375d97b40
42 changed files with 791 additions and 184 deletions

3
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,3 @@
## 👉 [Please follow one of these issue templates](https://github.com/AleoHQ/leo/issues/new/choose) 👈
Note: to keep the backlog clean and actionable, issues may be immediately closed if they do not follow one of the above issue templates.

View File

@ -1,38 +0,0 @@
---
name: "\U0001F41B Bug report"
about: Create a bug report if something isn't working 🔧
title: "[Bug]"
labels: bug
assignees: ''
---
## 🐛 Describe the Bug
<!-- A clear and concise description of what the bug is. -->
<!-- To report a security issue, please email security@aleo.org. -->
## Steps to Reproduce
#### Code snippet to reproduce
```rust
# Add code here
```
#### Stack trace & error message
```
// Paste the output here
```
## Expected Behavior
<!-- A clear and concise description of what you expected to happen. -->
## System information
- <!-- Leo Version -->
- <!-- Rust Version -->
- <!-- Computer OS -->

View File

@ -1,34 +0,0 @@
---
name: "\U0001F680 Feature request"
about: Submit a new feature request 💡
title: "[Feature]"
labels: feature
assignees: ''
---
## 🚀 Describe the Feature
<!-- A clear and concise description of the feature you are requesting -->
## Motivation
**Is the feature request related to a problem? If so, please describe.**
<!-- A clear and concise description of what the problem is.
Please link to any relevant issues or other PRs! -->
**Is this something that currently cannot be done?**
## Solution
<!-- A clear and concise description of what you want to happen. -->
**Are you willing to open a pull request?** (See [CONTRIBUTING](../../CONTRIBUTING.md))
## Alternative Solutions
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
## Relevant Context
<!-- Add any other context or screenshots about the feature request here. -->

52
.github/ISSUE_TEMPLATE/bug.md vendored Normal file
View File

@ -0,0 +1,52 @@
---
name: 🐛 Bug Report
about: Submit a bug report if something isn't working
title: "[Bug]"
labels: bug
---
## 🐛 Bug Report
<!--
What's the bug in Leo that you found?
How serious is this bug and what is affected?
To report a security issue in Leo, please email security@aleo.org.
-->
(Write your description here)
## Steps to Reproduce
<!--
How do I reproduce this issue in Leo?
Is there a code snippet I can use to reproduce the issue?
Are there error messages or stack traces that would help debug this issue?
-->
#### Code snippet to reproduce
```
# Add code here
```
#### Stack trace & error message
```
// Paste the output here
```
## Expected Behavior
<!--
What was supposed to happen in Leo?
What happened instead?
-->
(Write what you expected to happen here)
## Your Environment
- <!-- Leo Version -->
- <!-- Rust Version -->
- <!-- Computer OS -->

View File

@ -1,5 +1,5 @@
blank_issues_enabled: true blank_issues_enabled: true
contact_links: contact_links:
- name: Help and Support Discord Channel - name: Q&A Technical Support Channel
url: https://discord.gg/dTw3wk9 url: https://discord.gg/dTw3wk9
about: Please ask and answer questions here. 🏥 about: For quick questions or technical troubleshooting, please ask them on our dedicated Discord channel.

15
.github/ISSUE_TEMPLATE/documentation.md vendored Normal file
View File

@ -0,0 +1,15 @@
---
name: 📚 Documentation
about: Report an issue related to documentation
title: "[Docs]"
labels: 'documentation'
---
## 📚 Documentation
<!--
Did you find a mistake in the Leo documentation?
Is there documentation about Leo that's missing?
-->
(Write your answer here.)

36
.github/ISSUE_TEMPLATE/feature.md vendored Normal file
View File

@ -0,0 +1,36 @@
---
name: 🚀 Feature
about: Submit a new feature request
title: "[Feature]"
labels: feature
---
## 🚀 Feature
<!--
What is the feature you would like to see in Leo?
-->
(Write your description here)
## Motivation
<!--
Why should this feature be implemented in Leo?
How would this feature be used in Leo?
Is this feature request related to a problem? If so, please describe.
Please link to any relevant issues or other PRs!
-->
(Outline your motivation here)
## Implementation
<!--
What needs to be built for the feature to be supported in Leo?
What components of Leo will be affected by this design (if any)?
How should this feature be implemented?
-->
**Are you willing to open a pull request?** (See [CONTRIBUTING](../../CONTRIBUTING.md))

16
.github/ISSUE_TEMPLATE/proposal.md vendored Normal file
View File

@ -0,0 +1,16 @@
---
name: 💥 Proposal
about: Propose a non-trivial change to Leo
title: "[Proposal]"
labels: 'proposal'
---
## 💥 Proposal
<!--
What is your proposal for Leo?
What are the implications of this proposal to Leo?
Does your proposal affect other aspects of Aleo as well?
-->
(Write your proposal here)

31
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,31 @@
<!--
Thank you for submitting the PR! We appreciate you spending the time to work on these changes.
Help us understand your motivation by explaining why you decided to make this change.
Happy contributing!
-->
## Motivation
(Write your motivation here)
## Test Plan
<!--
If you changed any code,
please provide us with clear instructions on how you verified your changes work.
Bonus points for screenshots and videos!
-->
(Write your test plan here)
## Related PRs
<!--
If this PR adds or changes functionality,
please take some time to update the docs at https://github.com/AleoHQ/leo,
and link to your PR here.
-->
(Link your related PRs here)

View File

@ -42,8 +42,8 @@ jobs:
test: test:
name: Test name: Test
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: # env:
RUSTFLAGS: -Dwarnings # RUSTFLAGS: -Dwarnings
strategy: strategy:
matrix: matrix:
rust: rust:
@ -68,27 +68,27 @@ jobs:
toolchain: ${{ matrix.rust }} toolchain: ${{ matrix.rust }}
override: true override: true
- name: Check examples # - name: Check examples
uses: actions-rs/cargo@v1 # uses: actions-rs/cargo@v1
env: # env:
CARGO_NET_GIT_FETCH_WITH_CLI: true # CARGO_NET_GIT_FETCH_WITH_CLI: true
with: # with:
command: check # command: check
args: --examples --all # args: --examples --all
#
- name: Check examples with all features on stable # - name: Check examples with all features on stable
uses: actions-rs/cargo@v1 # uses: actions-rs/cargo@v1
with: # with:
command: check # command: check
args: --examples --all-features --all # args: --examples --all-features --all
if: matrix.rust == 'stable' # if: matrix.rust == 'stable'
#
- name: Check benchmarks on nightly # - name: Check benchmarks on nightly
uses: actions-rs/cargo@v1 # uses: actions-rs/cargo@v1
with: # with:
command: check # command: check
args: --all-features --examples --all --benches # args: --all-features --examples --all --benches
if: matrix.rust == 'nightly' # if: matrix.rust == 'nightly'
- name: Test - name: Test
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1

5
Cargo.lock generated
View File

@ -649,6 +649,10 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "leo-linter"
version = "0.1.0"
[[package]] [[package]]
name = "leo-types" name = "leo-types"
version = "0.1.0" version = "0.1.0"
@ -657,6 +661,7 @@ dependencies = [
"leo-input", "leo-input",
"pest", "pest",
"serde", "serde",
"serde_json",
"snarkos-errors", "snarkos-errors",
"snarkos-models", "snarkos-models",
] ]

View File

@ -13,7 +13,7 @@ name = "leo"
path = "leo/main.rs" path = "leo/main.rs"
[workspace] [workspace]
members = [ "ast", "compiler", "gadgets", "leo-input", "types" ] members = [ "ast", "compiler", "gadgets", "leo-input", "linter", "types" ]
[dependencies] [dependencies]
leo-compiler = { path = "compiler", version = "0.1.0" } leo-compiler = { path = "compiler", version = "0.1.0" }

View File

@ -18,3 +18,5 @@ pest_derive = { version = "2.0" }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" } serde_json = { version = "1.0" }
thiserror = { version = "1.0" } thiserror = { version = "1.0" }
[dev-dependencies]

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)
} }

1
ast/tests/mod.rs Normal file
View File

@ -0,0 +1 @@
mod serialization;

View File

@ -0,0 +1,91 @@
{
"imports": [],
"circuits": [],
"functions": [
{
"function_name": {
"value": "main",
"span": {
"input": "main",
"start": 9,
"end": 13
}
},
"parameters": [],
"returns": [],
"statements": [
{
"Return": {
"return_": {
"Single": {
"Binary": {
"operation": "Add",
"left": {
"Value": {
"Implicit": {
"number": {
"value": "1",
"span": {
"input": "1",
"start": 29,
"end": 30
}
},
"span": {
"input": "1",
"start": 29,
"end": 30
}
}
}
},
"right": {
"Value": {
"Implicit": {
"number": {
"value": "1",
"span": {
"input": "1",
"start": 33,
"end": 34
}
},
"span": {
"input": "1",
"start": 33,
"end": 34
}
}
}
},
"span": {
"input": "1 + 1",
"start": 29,
"end": 34
}
}
}
},
"span": {
"input": "return 1 + 1",
"start": 22,
"end": 34
}
}
}
],
"span": {
"input": "function main() {\n return 1 + 1\n}\n",
"start": 0,
"end": 37
}
}
],
"tests": [],
"eoi": null,
"span": {
"input": "function main() {\n return 1 + 1\n}\n",
"start": 0,
"end": 37
}
}

View File

@ -0,0 +1,22 @@
use leo_ast::LeoAst;
use std::path::PathBuf;
#[test]
fn test_serialization() {
let mut program_filepath = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
program_filepath.push("tests/serialization/main.leo");
let expected = include_str!("./expected_ast.json");
// Loads the Leo code as a string from the given file path.
let program_string = LeoAst::load_file(&program_filepath).unwrap();
// Parses the Leo file and constructs an abstract syntax tree.
let ast = LeoAst::new(&program_filepath, &program_string).unwrap();
// Serializes the abstract syntax tree into JSON format.
let serialized_ast = LeoAst::to_json_string(&ast).unwrap();
assert_eq!(expected, serialized_ast);
}

View File

@ -0,0 +1,3 @@
function main() {
return 1 + 1
}

View File

@ -0,0 +1 @@
mod json;

View File

@ -8,9 +8,9 @@ use crate::{
OutputBytes, OutputBytes,
OutputFile, OutputFile,
}; };
use leo_ast::LeoParser; use leo_ast::LeoAst;
use leo_input::LeoInputParser; use leo_input::LeoInputParser;
use leo_types::{Input, MainInput, Program}; use leo_types::{Input, LeoTypedAst, MainInput, Program};
use snarkos_errors::gadgets::SynthesisError; use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::{ use snarkos_models::{
@ -47,6 +47,18 @@ impl<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
} }
} }
/// Parse the input and state files.
/// Stores a typed ast of all input variables to the program.
pub fn parse_input(&mut self, input_string: &str, state_string: &str) -> Result<(), CompilerError> {
let input_syntax_tree = LeoInputParser::parse_file(&input_string)?;
let state_syntax_tree = LeoInputParser::parse_file(&state_string)?;
self.program_input.parse_input(input_syntax_tree)?;
self.program_input.parse_state(state_syntax_tree)?;
Ok(())
}
/// Parses program files. /// Parses program files.
/// Returns a compiler struct that stores the typed program abstract syntax trees (ast). /// Returns a compiler struct that stores the typed program abstract syntax trees (ast).
pub fn parse_program_without_input( pub fn parse_program_without_input(
@ -56,8 +68,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
) -> Result<Self, CompilerError> { ) -> Result<Self, CompilerError> {
let mut compiler = Self::new(package_name, main_file_path, output_directory); let mut compiler = Self::new(package_name, main_file_path, output_directory);
let program_string = compiler.load_program()?; compiler.parse_program()?;
compiler.parse_program(&program_string)?;
Ok(compiler) Ok(compiler)
} }
@ -74,34 +85,33 @@ impl<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
let mut compiler = Self::new(package_name, main_file_path, output_directory); let mut compiler = Self::new(package_name, main_file_path, output_directory);
compiler.parse_input(input_string, state_string)?; compiler.parse_input(input_string, state_string)?;
compiler.parse_program()?;
let program_string = compiler.load_program()?;
compiler.parse_program(&program_string)?;
Ok(compiler) Ok(compiler)
} }
/// Parse the input and state files. /// Parses the Leo program file, constructs a syntax tree, and generates a program.
/// Stores a typed ast of all input variables to the program. pub(crate) fn parse_program(&mut self) -> Result<(), CompilerError> {
pub fn parse_input(&mut self, input_string: &str, state_string: &str) -> Result<(), CompilerError> { // Use the parser to construct the abstract syntax tree.
let input_syntax_tree = LeoInputParser::parse_file(&input_string)?; let program_string = LeoAst::load_file(&self.main_file_path)?;
let state_syntax_tree = LeoInputParser::parse_file(&state_string)?;
self.program_input.parse_input(input_syntax_tree)?; self.parse_program_from_string(&program_string)
self.program_input.parse_state(state_syntax_tree)?;
Ok(())
} }
/// Parse the program file and all associated import files. /// 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 #[deprecated(note = "Please use the 'parse_program' method instead.")]
let syntax_tree = LeoParser::parse_file(&self.main_file_path, program_string)?; 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(); 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.imported_programs = ImportParser::parse(&self.program)?; self.imported_programs = ImportParser::parse(&self.program)?;
log::debug!("Program parsing complete\n{:#?}", self.program); log::debug!("Program parsing complete\n{:#?}", self.program);
@ -109,11 +119,6 @@ impl<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
Ok(()) Ok(())
} }
/// Loads the program file at `main_file_path`.
fn load_program(&mut self) -> Result<String, CompilerError> {
Ok(LeoParser::load_file(&self.main_file_path)?)
}
/// Manually sets main function input /// Manually sets main function input
pub fn set_main_input(&mut self, input: MainInput) { pub fn set_main_input(&mut self, input: MainInput) {
self.program_input.set_main_input(input); self.program_input.set_main_input(input);
@ -158,7 +163,6 @@ impl<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
generate_constraints::<_, G, _>(cs, self.program, self.program_input, &self.imported_programs).map_err( generate_constraints::<_, G, _>(cs, self.program, self.program_input, &self.imported_programs).map_err(
|mut error| { |mut error| {
error.set_path(path); error.set_path(path);
error error
}, },
) )

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

@ -45,7 +45,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)
} }
@ -90,8 +90,8 @@ pub fn parse_program_with_input(
let program_string = String::from_utf8_lossy(program_bytes); let program_string = String::from_utf8_lossy(program_bytes);
let input_string = String::from_utf8_lossy(input_bytes); let input_string = String::from_utf8_lossy(input_bytes);
compiler.parse_input(&input_string, EMPTY_FILE)?; compiler.parse_inputs(&inputs_string, EMPTY_FILE)?;
compiler.parse_program(&program_string)?; compiler.parse_program_from_string(&program_string)?;
Ok(compiler) Ok(compiler)
} }
@ -105,8 +105,8 @@ pub fn parse_program_with_state(
let program_string = String::from_utf8_lossy(program_bytes); let program_string = String::from_utf8_lossy(program_bytes);
let state_string = String::from_utf8_lossy(state_bytes); let state_string = String::from_utf8_lossy(state_bytes);
compiler.parse_input(EMPTY_FILE, &state_string)?; compiler.parse_inputs(EMPTY_FILE, &state_string)?;
compiler.parse_program(&program_string)?; compiler.parse_program_from_string(&program_string)?;
Ok(compiler) Ok(compiler)
} }
@ -122,8 +122,8 @@ pub fn parse_program_with_input_and_state(
let input_string = String::from_utf8_lossy(input_bytes); let input_string = String::from_utf8_lossy(input_bytes);
let state_string = String::from_utf8_lossy(state_bytes); let state_string = String::from_utf8_lossy(state_bytes);
compiler.parse_input(&input_string, &state_string)?; compiler.parse_inputs(&inputs_string, &state_string)?;
compiler.parse_program(&program_string)?; compiler.parse_program_from_string(&program_string)?;
Ok(compiler) Ok(compiler)
} }

56
leo/commands/lint.rs Normal file
View File

@ -0,0 +1,56 @@
use crate::{
cli::*,
cli_types::*,
commands::BuildCommand,
directories::SOURCE_DIRECTORY_NAME,
errors::{CLIError, RunError},
files::{Manifest, MAIN_FILE_NAME},
};
use clap::ArgMatches;
use std::{convert::TryFrom, env::current_dir};
#[derive(Debug)]
pub struct LintCommand;
impl CLI for LintCommand {
type Options = ();
type Output = ();
const ABOUT: AboutType = "Lints the Leo files in the package (*)";
const ARGUMENTS: &'static [ArgumentType] = &[];
const FLAGS: &'static [FlagType] = &[];
const NAME: NameType = "lint";
const OPTIONS: &'static [OptionType] = &[];
const SUBCOMMANDS: &'static [SubCommandType] = &[];
#[cfg_attr(tarpaulin, skip)]
fn parse(_arguments: &ArgMatches) -> Result<Self::Options, CLIError> {
Ok(())
}
#[cfg_attr(tarpaulin, skip)]
fn output(options: Self::Options) -> Result<Self::Output, CLIError> {
let path = current_dir()?;
match BuildCommand::output(options)? {
Some((_program, _checksum_differs)) => {
// Get the package name
let _package_name = Manifest::try_from(&path)?.get_package_name();
log::info!("Unimplemented - `leo lint`");
Ok(())
}
None => {
let mut main_file_path = path.clone();
main_file_path.push(SOURCE_DIRECTORY_NAME);
main_file_path.push(MAIN_FILE_NAME);
Err(CLIError::RunError(RunError::MainFileDoesNotExist(
main_file_path.into_os_string(),
)))
}
}
}
}

View File

@ -10,6 +10,9 @@ pub use self::deploy::*;
pub mod init; pub mod init;
pub use self::init::*; pub use self::init::*;
pub mod lint;
pub use self::lint::*;
pub mod load; pub mod load;
pub use self::load::*; pub use self::load::*;

View File

@ -24,14 +24,15 @@ fn main() -> Result<(), CLIError> {
InitCommand::new().display_order(1), InitCommand::new().display_order(1),
BuildCommand::new().display_order(2), BuildCommand::new().display_order(2),
TestCommand::new().display_order(3), TestCommand::new().display_order(3),
LoadCommand::new().display_order(4), LintCommand::new().display_order(4),
UnloadCommand::new().display_order(5), LoadCommand::new().display_order(5),
SetupCommand::new().display_order(6), UnloadCommand::new().display_order(6),
ProveCommand::new().display_order(7), SetupCommand::new().display_order(7),
RunCommand::new().display_order(8), ProveCommand::new().display_order(8),
PublishCommand::new().display_order(9), RunCommand::new().display_order(9),
DeployCommand::new().display_order(10), PublishCommand::new().display_order(10),
CleanCommand::new().display_order(11), DeployCommand::new().display_order(11),
CleanCommand::new().display_order(12),
]) ])
.set_term_width(0) .set_term_width(0)
.get_matches(); .get_matches();
@ -41,6 +42,7 @@ fn main() -> Result<(), CLIError> {
("init", Some(arguments)) => InitCommand::process(arguments), ("init", Some(arguments)) => InitCommand::process(arguments),
("build", Some(arguments)) => BuildCommand::process(arguments), ("build", Some(arguments)) => BuildCommand::process(arguments),
("test", Some(arguments)) => TestCommand::process(arguments), ("test", Some(arguments)) => TestCommand::process(arguments),
("lint", Some(arguments)) => LintCommand::process(arguments),
("load", Some(arguments)) => LoadCommand::process(arguments), ("load", Some(arguments)) => LoadCommand::process(arguments),
("unload", Some(arguments)) => UnloadCommand::process(arguments), ("unload", Some(arguments)) => UnloadCommand::process(arguments),
("setup", Some(arguments)) => SetupCommand::process(arguments), ("setup", Some(arguments)) => SetupCommand::process(arguments),

7
linter/Cargo.toml Normal file
View File

@ -0,0 +1,7 @@
[package]
name = "leo-linter"
version = "0.1.0"
authors = ["The Aleo Team <hello@aleo.org>"]
edition = "2018"
[dependencies]

3
linter/src/main.rs Normal file
View File

@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}

View File

@ -4,6 +4,10 @@ version = "0.1.0"
authors = ["The Aleo Team <hello@aleo.org>"] authors = ["The Aleo Team <hello@aleo.org>"]
edition = "2018" edition = "2018"
[[bin]]
name = "leo_types_ast"
path = "src/main.rs"
[dependencies] [dependencies]
leo-ast = { path = "../ast", version = "0.1.0" } leo-ast = { path = "../ast", version = "0.1.0" }
leo-input = { path = "../leo-input", version = "0.1.0" } leo-input = { path = "../leo-input", version = "0.1.0" }
@ -13,3 +17,4 @@ snarkos-models = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", package = "
pest = { version = "2.0" } pest = { version = "2.0" }
serde = { version = "1.0" } serde = { version = "1.0" }
serde_json = { version = "1.0" }

View File

@ -2,11 +2,21 @@ use crate::Span;
use leo_ast::common::Identifier as AstIdentifier; use leo_ast::common::Identifier as AstIdentifier;
use leo_input::common::Identifier as InputAstIdentifier; use leo_input::common::Identifier as InputAstIdentifier;
use serde::{Deserialize, Serialize}; use serde::{
use std::fmt; de::{self, Visitor},
Deserialize,
Deserializer,
Serialize,
Serializer,
};
use std::{collections::BTreeMap, fmt};
/// An identifier in the constrained program. /// An identifier in the constrained program.
#[derive(Clone, Hash, Serialize, Deserialize)] ///
/// Attention - When adding or removing fields from this struct,
/// please remember to update it's Serialize and Deserialize implementation
/// to reflect the new struct instantiation.
#[derive(Clone, Hash)]
pub struct Identifier { pub struct Identifier {
pub name: String, pub name: String,
pub span: Span, pub span: Span,
@ -54,3 +64,61 @@ impl PartialEq for Identifier {
} }
impl Eq for Identifier {} impl Eq for Identifier {}
impl Serialize for Identifier {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
// Converts an element that implements Serialize into a string.
fn to_json_string<E: Serialize, Error: serde::ser::Error>(element: &E) -> Result<String, Error> {
serde_json::to_string(&element).map_err(|e| Error::custom(e.to_string()))
}
// Load the struct elements into a BTreeMap (to preserve serialized ordering of keys).
let mut key: BTreeMap<String, String> = BTreeMap::new();
key.insert("name".to_string(), self.name.clone());
key.insert("span".to_string(), to_json_string(&self.span)?);
// Convert the serialized object into a string for use as a key.
serializer.serialize_str(&to_json_string(&key)?)
}
}
impl<'de> Deserialize<'de> for Identifier {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct IdentifierVisitor;
impl<'de> Visitor<'de> for IdentifierVisitor {
type Value = Identifier;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string encoding the typed Identifier struct")
}
/// Implementation for recovering a string that serializes Identifier.
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
// Converts a serialized string into an element that implements Deserialize.
fn to_json_string<'a, D: Deserialize<'a>, Error: serde::de::Error>(
serialized: &'a str,
) -> Result<D, Error> {
serde_json::from_str::<'a>(serialized).map_err(|e| Error::custom(e.to_string()))
}
// Convert the serialized string into a BTreeMap to recover Identifier.
let key: BTreeMap<String, String> = to_json_string(value)?;
let name = match key.get("name") {
Some(name) => name.clone(),
None => return Err(E::custom("missing 'name' in serialized Identifier struct")),
};
let span: Span = match key.get("span") {
Some(span) => to_json_string(span)?,
None => return Err(E::custom("missing 'span' in serialized Identifier struct")),
};
Ok(Identifier { name, span })
}
}
deserializer.deserialize_str(IdentifierVisitor)
}
}

View File

@ -6,7 +6,7 @@ use leo_ast::imports::Import as AstImport;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Import { pub struct Import {
pub package: Package, pub package: Package,
pub span: Span, pub span: Span,

View File

@ -4,7 +4,7 @@ use leo_ast::imports::ImportSymbol as AstImportSymbol;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct ImportSymbol { pub struct ImportSymbol {
pub symbol: Identifier, pub symbol: Identifier,
pub alias: Option<Identifier>, pub alias: Option<Identifier>,
@ -31,7 +31,7 @@ impl fmt::Display for ImportSymbol {
} }
} }
// todo: remove this // TODO (collin): remove this
impl fmt::Debug for ImportSymbol { impl fmt::Debug for ImportSymbol {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.alias.is_some() { if self.alias.is_some() {

View File

@ -4,7 +4,7 @@ use leo_ast::imports::Package as AstPackage;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Package { pub struct Package {
pub name: Identifier, pub name: Identifier,
pub access: PackageAccess, pub access: PackageAccess,

View File

@ -4,7 +4,7 @@ use leo_ast::imports::PackageAccess as AstPackageAccess;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum PackageAccess { pub enum PackageAccess {
Star(Span), Star(Span),
SubPackage(Box<Package>), SubPackage(Box<Package>),

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,37 @@ pub use self::statements::*;
pub mod types; pub mod types;
pub use self::types::*; pub use self::types::*;
use leo_ast::LeoAst;
use serde_json;
#[derive(Debug, Eq, PartialEq)]
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()),
}
}
/// Returns a reference to the inner typed syntax tree representation.
pub fn into_repr(self) -> Program {
self.typed_ast
}
/// Serializes the typed syntax tree into a JSON string.
pub fn to_json_string(&self) -> Result<String, serde_json::Error> {
Ok(serde_json::to_string_pretty(&self.typed_ast)?)
}
/// Deserializes the JSON string into a typed syntax tree.
pub fn from_json_string(json: &str) -> Result<Self, serde_json::Error> {
let typed_ast: Program = serde_json::from_str(json)?;
Ok(Self { typed_ast })
}
}

56
types/src/main.rs Normal file
View File

@ -0,0 +1,56 @@
use leo_ast::{LeoAst, ParserError};
use leo_types::LeoTypedAst;
use std::{env, fs, path::Path};
fn to_leo_types_tree(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 = LeoAst::load_file(&program_filepath)?;
// Parses the Leo file and constructs an abstract syntax tree.
let ast = LeoAst::new(&program_filepath, &program_string)?;
// Parse the abstract syntax tree and constructs a typed syntax tree.
let typed_ast = LeoTypedAst::new("leo_types_tree", &ast);
// Serializes the typed syntax tree into JSON format.
let serialized_typed_tree = LeoTypedAst::to_json_string(&typed_ast)?;
Ok(serialized_typed_tree)
}
fn main() -> Result<(), ParserError> {
// Parse the command-line arguments as strings.
let cli_arguments = env::args().collect::<Vec<String>>();
// Check that the correct number of command-line arguments were passed in.
if cli_arguments.len() < 2 || cli_arguments.len() > 3 {
eprintln!("Warning - an invalid number of command-line arguments were provided.");
println!(
"\nCommand-line usage:\n\n\tleo_types_ast {{PATH/TO/INPUT_FILENAME}}.leo {{PATH/TO/OUTPUT_DIRECTORY (optional)}}\n"
);
return Ok(()); // Exit innocently
}
// Construct the input filepath.
let input_filepath = Path::new(&cli_arguments[1]);
// Construct the serialized typed syntax tree.
let serialized_typed_tree = to_leo_types_tree(&input_filepath)?;
println!("{}", serialized_typed_tree);
// Determine the output directory.
let output_directory = match cli_arguments.len() == 3 {
true => format!(
"{}/{}.json",
cli_arguments[2],
input_filepath.file_stem().unwrap().to_str().unwrap()
),
false => format!("./{}.json", input_filepath.file_stem().unwrap().to_str().unwrap()),
};
// Write the serialized abstract syntax tree to the output directory.
fs::write(Path::new(&output_directory), serialized_typed_tree)?;
Ok(())
}

View File

@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
/// A simple program with statement expressions, program arguments and program returns. /// A simple program with statement expressions, program arguments and program returns.
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Program { pub struct Program {
pub name: String, pub name: String,
pub expected_input: Vec<InputVariable>, pub expected_input: Vec<InputVariable>,
@ -22,10 +22,11 @@ const MAIN_FUNCTION_NAME: &str = "main";
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>>();
@ -35,23 +36,23 @@ impl<'ast> Program {
let mut tests = HashMap::new(); let mut tests = HashMap::new();
let mut expected_input = vec![]; let mut expected_input = 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_FUNCTION_NAME) { if function.function_name.name.eq(MAIN_FUNCTION_NAME) {
expected_input = function.input.clone(); expected_input = function.input.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_input, expected_input,
imports, imports,
circuits, circuits,

1
types/tests/mod.rs Normal file
View File

@ -0,0 +1 @@
mod serialization;

View File

@ -0,0 +1,66 @@
{
"name": "leo_types_tree",
"expected_inputs": [],
"imports": [],
"circuits": {},
"functions": {
"{\"name\":\"main\",\"span\":\"{\\\"text\\\":\\\" function main() {\\\",\\\"line\\\":1,\\\"start\\\":10,\\\"end\\\":14}\"}": {
"function_name": "{\"name\":\"main\",\"span\":\"{\\\"text\\\":\\\" function main() {\\\",\\\"line\\\":1,\\\"start\\\":10,\\\"end\\\":14}\"}",
"inputs": [],
"returns": [],
"statements": [
{
"Return": [
[
{
"Add": [
{
"Implicit": [
"1",
{
"text": " return 1 + 1",
"line": 2,
"start": 12,
"end": 13
}
]
},
{
"Implicit": [
"1",
{
"text": " return 1 + 1",
"line": 2,
"start": 16,
"end": 17
}
]
},
{
"text": " return 1 + 1",
"line": 2,
"start": 12,
"end": 17
}
]
}
],
{
"text": " return 1 + 1",
"line": 2,
"start": 5,
"end": 17
}
]
}
],
"span": {
"text": " function main() {",
"line": 1,
"start": 1,
"end": 1
}
}
},
"tests": {}
}

View File

@ -0,0 +1,76 @@
use leo_ast::LeoAst;
use leo_types::LeoTypedAst;
use std::path::PathBuf;
fn to_typed_ast(program_filepath: &PathBuf) -> LeoTypedAst {
// Loads the Leo code as a string from the given file path.
let program_string = LeoAst::load_file(program_filepath).unwrap();
// Parses the Leo file and constructs an abstract syntax tree.
let ast = LeoAst::new(&program_filepath, &program_string).unwrap();
// Parse the abstract syntax tree and constructs a typed syntax tree.
let typed_ast = LeoTypedAst::new("leo_types_tree", &ast);
typed_ast
}
#[test]
fn test_serialize() {
// Construct a typed syntax tree from the given test file.
let typed_ast = {
let mut program_filepath = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
program_filepath.push("tests/serialization/main.leo");
to_typed_ast(&program_filepath)
};
// Serializes the typed syntax tree into JSON format.
let serialized_typed_ast = typed_ast.to_json_string().unwrap();
// Load the expected typed syntax tree.
let expected = include_str!("expected_typed_ast.json");
println!("{}", serialized_typed_ast);
assert_eq!(expected, serialized_typed_ast);
}
#[test]
fn test_deserialize() {
// Load the expected typed syntax tree.
let expected_typed_ast = {
let mut program_filepath = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
program_filepath.push("tests/serialization/main.leo");
to_typed_ast(&program_filepath)
};
// Construct a typed syntax tree by deserializing a typed syntax tree JSON file.
let serialized_typed_ast = include_str!("expected_typed_ast.json");
let typed_ast = LeoTypedAst::from_json_string(serialized_typed_ast).unwrap();
assert_eq!(expected_typed_ast, typed_ast);
}
#[test]
fn test_serialize_deserialize_serialize() {
// Construct a typed syntax tree from the given test file.
let typed_ast = {
let mut program_filepath = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
program_filepath.push("tests/serialization/main.leo");
to_typed_ast(&program_filepath)
};
// Serializes the typed syntax tree into JSON format.
let serialized_typed_ast = typed_ast.to_json_string().unwrap();
// Deserializes the typed syntax tree into a LeoTypedAst.
let typed_ast = LeoTypedAst::from_json_string(&serialized_typed_ast).unwrap();
// Reserializes the typed syntax tree into JSON format.
let reserialized_typed_ast = typed_ast.to_json_string().unwrap();
assert_eq!(serialized_typed_ast, reserialized_typed_ast);
}

View File

@ -0,0 +1,3 @@
function main() {
return 1 + 1
}

View File

@ -0,0 +1 @@
mod json;