mirror of
https://github.com/AleoHQ/leo.git
synced 2024-12-26 11:06:00 +03:00
Resolve merge conflict
This commit is contained in:
commit
5375d97b40
3
.github/ISSUE_TEMPLATE.md
vendored
Normal file
3
.github/ISSUE_TEMPLATE.md
vendored
Normal 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.
|
38
.github/ISSUE_TEMPLATE/01_bug_report.md
vendored
38
.github/ISSUE_TEMPLATE/01_bug_report.md
vendored
@ -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 -->
|
|
34
.github/ISSUE_TEMPLATE/02_bug_report.md
vendored
34
.github/ISSUE_TEMPLATE/02_bug_report.md
vendored
@ -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
52
.github/ISSUE_TEMPLATE/bug.md
vendored
Normal 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 -->
|
4
.github/ISSUE_TEMPLATE/config.yml
vendored
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -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
15
.github/ISSUE_TEMPLATE/documentation.md
vendored
Normal 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
36
.github/ISSUE_TEMPLATE/feature.md
vendored
Normal 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
16
.github/ISSUE_TEMPLATE/proposal.md
vendored
Normal 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
31
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal 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)
|
46
.github/workflows/ci.yml
vendored
46
.github/workflows/ci.yml
vendored
@ -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
5
Cargo.lock
generated
@ -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",
|
||||||
]
|
]
|
||||||
|
@ -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" }
|
||||||
|
@ -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]
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
1
ast/tests/mod.rs
Normal file
1
ast/tests/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
mod serialization;
|
91
ast/tests/serialization/expected_ast.json
Normal file
91
ast/tests/serialization/expected_ast.json
Normal 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
|
||||||
|
}
|
||||||
|
}
|
22
ast/tests/serialization/json.rs
Normal file
22
ast/tests/serialization/json.rs
Normal 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);
|
||||||
|
}
|
3
ast/tests/serialization/main.leo
Normal file
3
ast/tests/serialization/main.leo
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
function main() {
|
||||||
|
return 1 + 1
|
||||||
|
}
|
1
ast/tests/serialization/mod.rs
Normal file
1
ast/tests/serialization/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
mod json;
|
@ -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
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -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 {
|
||||||
|
@ -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
56
leo/commands/lint.rs
Normal 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(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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::*;
|
||||||
|
|
||||||
|
18
leo/main.rs
18
leo/main.rs
@ -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
7
linter/Cargo.toml
Normal 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
3
linter/src/main.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
@ -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" }
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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() {
|
||||||
|
@ -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,
|
||||||
|
@ -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>),
|
||||||
|
@ -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
56
types/src/main.rs
Normal 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(())
|
||||||
|
}
|
@ -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
1
types/tests/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
mod serialization;
|
66
types/tests/serialization/expected_typed_ast.json
Normal file
66
types/tests/serialization/expected_typed_ast.json
Normal 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": {}
|
||||||
|
}
|
76
types/tests/serialization/json.rs
Normal file
76
types/tests/serialization/json.rs
Normal 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);
|
||||||
|
}
|
3
types/tests/serialization/main.leo
Normal file
3
types/tests/serialization/main.leo
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
function main() {
|
||||||
|
return 1 + 1
|
||||||
|
}
|
1
types/tests/serialization/mod.rs
Normal file
1
types/tests/serialization/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
mod json;
|
Loading…
Reference in New Issue
Block a user