mirror of
https://github.com/AleoHQ/leo.git
synced 2024-12-19 23:52:02 +03:00
Merge pull request #1774 from AleoHQ/feature/create-symbol-table
[Impl] Symbol Table
This commit is contained in:
commit
cd606ac578
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -2,3 +2,4 @@
|
|||||||
*.leo text eol=lf
|
*.leo text eol=lf
|
||||||
*.out text eol=lf
|
*.out text eol=lf
|
||||||
*.rs text eol=lf
|
*.rs text eol=lf
|
||||||
|
*.in text eol=lf
|
||||||
|
28
Cargo.lock
generated
28
Cargo.lock
generated
@ -1220,26 +1220,18 @@ dependencies = [
|
|||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "leo-ast-passes"
|
|
||||||
version = "1.5.3"
|
|
||||||
dependencies = [
|
|
||||||
"indexmap",
|
|
||||||
"leo-ast",
|
|
||||||
"leo-errors",
|
|
||||||
"leo-parser",
|
|
||||||
"leo-span",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "leo-compiler"
|
name = "leo-compiler"
|
||||||
version = "1.5.3"
|
version = "1.5.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"leo-ast",
|
"leo-ast",
|
||||||
"leo-ast-passes",
|
|
||||||
"leo-errors",
|
"leo-errors",
|
||||||
"leo-parser",
|
"leo-parser",
|
||||||
|
"leo-passes",
|
||||||
"leo-span",
|
"leo-span",
|
||||||
|
"leo-test-framework",
|
||||||
|
"serde",
|
||||||
|
"serde_yaml",
|
||||||
"sha2",
|
"sha2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1273,6 +1265,7 @@ dependencies = [
|
|||||||
"leo-compiler",
|
"leo-compiler",
|
||||||
"leo-errors",
|
"leo-errors",
|
||||||
"leo-package",
|
"leo-package",
|
||||||
|
"leo-span",
|
||||||
"notify",
|
"notify",
|
||||||
"rand",
|
"rand",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
@ -1325,6 +1318,17 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "leo-passes"
|
||||||
|
version = "1.5.3"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"leo-ast",
|
||||||
|
"leo-errors",
|
||||||
|
"leo-parser",
|
||||||
|
"leo-span",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "leo-span"
|
name = "leo-span"
|
||||||
version = "1.5.3"
|
version = "1.5.3"
|
||||||
|
20
Cargo.toml
20
Cargo.toml
@ -46,10 +46,17 @@ version = "1.5.3"
|
|||||||
path = "./leo/package"
|
path = "./leo/package"
|
||||||
version = "1.5.3"
|
version = "1.5.3"
|
||||||
|
|
||||||
|
[dependencies.leo-span]
|
||||||
|
path = "./leo/span"
|
||||||
|
version = "1.5.3"
|
||||||
|
|
||||||
[dependencies.snarkvm-utilities]
|
[dependencies.snarkvm-utilities]
|
||||||
git = "https://github.com/AleoHQ/snarkVM.git"
|
git = "https://github.com/AleoHQ/snarkVM.git"
|
||||||
rev = "51633e2"
|
rev = "51633e2"
|
||||||
|
|
||||||
|
[dependencies.backtrace]
|
||||||
|
version = "0.3.65"
|
||||||
|
|
||||||
[dependencies.clap]
|
[dependencies.clap]
|
||||||
version = "3.1"
|
version = "3.1"
|
||||||
|
|
||||||
@ -59,18 +66,12 @@ version = "0.5.1"
|
|||||||
[dependencies.colored]
|
[dependencies.colored]
|
||||||
version = "2.0"
|
version = "2.0"
|
||||||
|
|
||||||
[dependencies.backtrace]
|
[dependencies.console]
|
||||||
version = "0.3.65"
|
version = "0.15.0"
|
||||||
|
|
||||||
[dependencies.sys-info]
|
|
||||||
version = "0.9.1"
|
|
||||||
|
|
||||||
[dependencies.dirs]
|
[dependencies.dirs]
|
||||||
version = "4.0.0"
|
version = "4.0.0"
|
||||||
|
|
||||||
[dependencies.console]
|
|
||||||
version = "0.15.0"
|
|
||||||
|
|
||||||
[dependencies.indexmap]
|
[dependencies.indexmap]
|
||||||
version = "1.8"
|
version = "1.8"
|
||||||
features = ["serde"]
|
features = ["serde"]
|
||||||
@ -105,6 +106,9 @@ version = "1.0"
|
|||||||
[dependencies.structopt]
|
[dependencies.structopt]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
|
|
||||||
|
[dependencies.sys-info]
|
||||||
|
version = "0.9.1"
|
||||||
|
|
||||||
[dependencies.toml]
|
[dependencies.toml]
|
||||||
version = "0.5"
|
version = "0.5"
|
||||||
|
|
||||||
|
@ -18,9 +18,6 @@ license = "GPL-3.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.56"
|
rust-version = "1.56"
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
smallvec = { version = "1.8.0", features = ["serde"] }
|
|
||||||
|
|
||||||
[dependencies.leo-errors]
|
[dependencies.leo-errors]
|
||||||
path = "../../leo/errors"
|
path = "../../leo/errors"
|
||||||
version = "1.5.3"
|
version = "1.5.3"
|
||||||
@ -41,6 +38,10 @@ features = [ "derive", "rc" ]
|
|||||||
version = "1.0"
|
version = "1.0"
|
||||||
features = [ "preserve_order" ]
|
features = [ "preserve_order" ]
|
||||||
|
|
||||||
|
[dependencies.smallvec]
|
||||||
|
version = "1.8.0"
|
||||||
|
features = ["serde"]
|
||||||
|
|
||||||
[dev-dependencies.criterion]
|
[dev-dependencies.criterion]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
|
|
||||||
|
@ -14,8 +14,10 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use crate::{normalize_json_value, remove_key_from_json};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use leo_errors::AstError;
|
use leo_errors::{AstError, Result};
|
||||||
|
|
||||||
/// Input data which includes [`ProgramInput`] and [`ProgramState`].
|
/// Input data which includes [`ProgramInput`] and [`ProgramState`].
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
@ -24,16 +26,44 @@ pub struct Input {
|
|||||||
pub program_state: ProgramState,
|
pub program_state: ProgramState,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A raw unprocessed input or state file data. Used for future conversion
|
|
||||||
/// into [`ProgramInput`] or [`ProgramState`].
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct ParsedInputFile {
|
|
||||||
pub sections: Vec<Section>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
/// Serializes the ast into a JSON string.
|
/// Serializes the ast into a JSON string.
|
||||||
pub fn to_json_string(&self) -> Result<String> {
|
pub fn to_json_string(&self) -> Result<String> {
|
||||||
Ok(serde_json::to_string_pretty(&self).map_err(|e| AstError::failed_to_convert_ast_to_json_string(&e))?)
|
Ok(serde_json::to_string_pretty(&self).map_err(|e| AstError::failed_to_convert_ast_to_json_string(&e))?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A raw unprocessed input or state file data. Used for future conversion
|
||||||
|
/// into [`ProgramInput`] or [`ProgramState`].
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct InputAst {
|
||||||
|
pub sections: Vec<Section>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputAst {
|
||||||
|
/// Serializes the `Input` into a JSON Value.
|
||||||
|
pub fn to_json_value(&self) -> Result<serde_json::Value> {
|
||||||
|
Ok(serde_json::to_value(&self).map_err(|e| AstError::failed_to_convert_ast_to_json_value(&e))?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serializes the `Input` into a JSON value and removes keys from object mappings before writing to a file.
|
||||||
|
pub fn to_json_file_without_keys(
|
||||||
|
&self,
|
||||||
|
mut path: std::path::PathBuf,
|
||||||
|
file_name: &str,
|
||||||
|
excluded_keys: &[&str],
|
||||||
|
) -> Result<()> {
|
||||||
|
path.push(file_name);
|
||||||
|
let file = std::fs::File::create(&path).map_err(|e| AstError::failed_to_create_ast_json_file(&path, &e))?;
|
||||||
|
let writer = std::io::BufWriter::new(file);
|
||||||
|
|
||||||
|
let mut value = self.to_json_value().unwrap();
|
||||||
|
for key in excluded_keys {
|
||||||
|
value = remove_key_from_json(value, key);
|
||||||
|
}
|
||||||
|
value = normalize_json_value(value);
|
||||||
|
|
||||||
|
Ok(serde_json::to_writer_pretty(writer, &value)
|
||||||
|
.map_err(|e| AstError::failed_to_write_ast_to_json_file(&path, &e))?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::{CharValue, Expression, GroupValue, IntegerType, Node, Type, ValueExpression};
|
use crate::{CharValue, Expression, GroupValue, IntegerType, Node, Type, UnaryOperation, ValueExpression};
|
||||||
use leo_errors::{InputError, LeoError, ParserError, Result};
|
use leo_errors::{InputError, LeoError, ParserError, Result};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -59,6 +59,9 @@ impl TryFrom<(Type, Expression)> for InputValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
(type_, Expression::Unary(unary)) if unary.op == UnaryOperation::Negate => {
|
||||||
|
InputValue::try_from((type_, *unary.inner))?
|
||||||
|
}
|
||||||
(_type_, expr) => return Err(InputError::illegal_expression(&expr, expr.span()).into()),
|
(_type_, expr) => return Err(InputError::illegal_expression(&expr, expr.span()).into()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,9 @@ pub struct ProgramInput {
|
|||||||
pub registers: Definitions,
|
pub registers: Definitions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<ParsedInputFile> for ProgramInput {
|
impl TryFrom<InputAst> for ProgramInput {
|
||||||
type Error = LeoError;
|
type Error = LeoError;
|
||||||
fn try_from(input: ParsedInputFile) -> Result<Self> {
|
fn try_from(input: InputAst) -> Result<Self> {
|
||||||
let mut main = IndexMap::new();
|
let mut main = IndexMap::new();
|
||||||
let mut registers = IndexMap::new();
|
let mut registers = IndexMap::new();
|
||||||
|
|
||||||
|
@ -22,9 +22,9 @@ pub struct ProgramState {
|
|||||||
pub state: Definitions,
|
pub state: Definitions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<ParsedInputFile> for ProgramState {
|
impl TryFrom<InputAst> for ProgramState {
|
||||||
type Error = LeoError;
|
type Error = LeoError;
|
||||||
fn try_from(input: ParsedInputFile) -> Result<Self> {
|
fn try_from(input: InputAst) -> Result<Self> {
|
||||||
let mut state = IndexMap::new();
|
let mut state = IndexMap::new();
|
||||||
|
|
||||||
for section in input.sections {
|
for section in input.sections {
|
||||||
|
@ -40,9 +40,6 @@ pub use self::groups::*;
|
|||||||
pub mod input;
|
pub mod input;
|
||||||
pub use self::input::*;
|
pub use self::input::*;
|
||||||
|
|
||||||
pub mod pass;
|
|
||||||
pub use self::pass::*;
|
|
||||||
|
|
||||||
pub mod passes;
|
pub mod passes;
|
||||||
pub use self::passes::*;
|
pub use self::passes::*;
|
||||||
|
|
||||||
@ -147,7 +144,7 @@ impl AsRef<Program> for Ast {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to recursively filter keys from AST JSON
|
/// Helper function to recursively filter keys from AST JSON
|
||||||
fn remove_key_from_json(value: serde_json::Value, key: &str) -> serde_json::Value {
|
pub(crate) fn remove_key_from_json(value: serde_json::Value, key: &str) -> serde_json::Value {
|
||||||
match value {
|
match value {
|
||||||
serde_json::Value::Object(map) => serde_json::Value::Object(
|
serde_json::Value::Object(map) => serde_json::Value::Object(
|
||||||
map.into_iter()
|
map.into_iter()
|
||||||
@ -168,7 +165,7 @@ fn remove_key_from_json(value: serde_json::Value, key: &str) -> serde_json::Valu
|
|||||||
/// 1. Remove empty object mappings from JSON arrays
|
/// 1. Remove empty object mappings from JSON arrays
|
||||||
/// 2. If there are two elements in a JSON array and one is an empty object
|
/// 2. If there are two elements in a JSON array and one is an empty object
|
||||||
/// mapping and the other is not, then lift up the one that isn't
|
/// mapping and the other is not, then lift up the one that isn't
|
||||||
fn normalize_json_value(value: serde_json::Value) -> serde_json::Value {
|
pub(crate) fn normalize_json_value(value: serde_json::Value) -> serde_json::Value {
|
||||||
match value {
|
match value {
|
||||||
serde_json::Value::Array(vec) => {
|
serde_json::Value::Array(vec) => {
|
||||||
let orig_length = vec.len();
|
let orig_length = vec.len();
|
||||||
|
@ -29,84 +29,84 @@ impl Default for VisitResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ExpressionVisitor {
|
pub trait ExpressionVisitor<'a> {
|
||||||
fn visit_expression(&mut self, _input: &Expression) -> VisitResult {
|
fn visit_expression(&mut self, _input: &'a Expression) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_identifer(&mut self, _input: &Identifier) -> VisitResult {
|
fn visit_identifier(&mut self, _input: &'a Identifier) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_value(&mut self, _input: &ValueExpression) -> VisitResult {
|
fn visit_value(&mut self, _input: &'a ValueExpression) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_binary(&mut self, _input: &BinaryExpression) -> VisitResult {
|
fn visit_binary(&mut self, _input: &'a BinaryExpression) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_unary(&mut self, _input: &UnaryExpression) -> VisitResult {
|
fn visit_unary(&mut self, _input: &'a UnaryExpression) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_ternary(&mut self, _input: &TernaryExpression) -> VisitResult {
|
fn visit_ternary(&mut self, _input: &'a TernaryExpression) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_call(&mut self, _input: &CallExpression) -> VisitResult {
|
fn visit_call(&mut self, _input: &'a CallExpression) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_err(&mut self, _input: &ErrExpression) -> VisitResult {
|
fn visit_err(&mut self, _input: &'a ErrExpression) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait StatementVisitor {
|
pub trait StatementVisitor<'a> {
|
||||||
fn visit_statement(&mut self, _input: &Statement) -> VisitResult {
|
fn visit_statement(&mut self, _input: &'a Statement) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_return(&mut self, _input: &ReturnStatement) -> VisitResult {
|
fn visit_return(&mut self, _input: &'a ReturnStatement) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_definition(&mut self, _input: &DefinitionStatement) -> VisitResult {
|
fn visit_definition(&mut self, _input: &'a DefinitionStatement) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_assign(&mut self, _input: &AssignStatement) -> VisitResult {
|
fn visit_assign(&mut self, _input: &'a AssignStatement) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_conditional(&mut self, _input: &ConditionalStatement) -> VisitResult {
|
fn visit_conditional(&mut self, _input: &'a ConditionalStatement) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_iteration(&mut self, _input: &IterationStatement) -> VisitResult {
|
fn visit_iteration(&mut self, _input: &'a IterationStatement) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_console(&mut self, _input: &ConsoleStatement) -> VisitResult {
|
fn visit_console(&mut self, _input: &'a ConsoleStatement) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_expression_statement(&mut self, _input: &ExpressionStatement) -> VisitResult {
|
fn visit_expression_statement(&mut self, _input: &'a ExpressionStatement) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_block(&mut self, _input: &Block) -> VisitResult {
|
fn visit_block(&mut self, _input: &'a Block) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ProgramVisitor {
|
pub trait ProgramVisitor<'a> {
|
||||||
fn visit_program(&mut self, _input: &Program) -> VisitResult {
|
fn visit_program(&mut self, _input: &'a Program) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_function(&mut self, _input: &Function) -> VisitResult {
|
fn visit_function(&mut self, _input: &'a Function) -> VisitResult {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,25 +18,28 @@
|
|||||||
//! It implements default methods for each node to be made
|
//! It implements default methods for each node to be made
|
||||||
//! given the type of node its visiting.
|
//! given the type of node its visiting.
|
||||||
|
|
||||||
// temporary till we use in another pr.
|
use std::marker::PhantomData;
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub struct VisitorDirector<V: ExpressionVisitor> {
|
pub struct VisitorDirector<'a, V: ExpressionVisitor<'a>> {
|
||||||
visitor: V,
|
visitor: V,
|
||||||
|
lifetime: PhantomData<&'a ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: ExpressionVisitor> VisitorDirector<V> {
|
impl<'a, V: ExpressionVisitor<'a>> VisitorDirector<'a, V> {
|
||||||
pub fn new(visitor: V) -> Self {
|
pub fn new(visitor: V) -> Self {
|
||||||
Self { visitor }
|
Self {
|
||||||
|
visitor,
|
||||||
|
lifetime: PhantomData,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn visitor(self) -> V {
|
pub fn visitor(self) -> V {
|
||||||
self.visitor
|
self.visitor
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn visit_expression(&mut self, input: &Expression) {
|
pub fn visit_expression(&mut self, input: &'a Expression) {
|
||||||
if let VisitResult::VisitChildren = self.visitor.visit_expression(input) {
|
if let VisitResult::VisitChildren = self.visitor.visit_expression(input) {
|
||||||
match input {
|
match input {
|
||||||
Expression::Identifier(_) => {}
|
Expression::Identifier(_) => {}
|
||||||
@ -50,20 +53,20 @@ impl<V: ExpressionVisitor> VisitorDirector<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn visit_binary(&mut self, input: &BinaryExpression) {
|
pub fn visit_binary(&mut self, input: &'a BinaryExpression) {
|
||||||
if let VisitResult::VisitChildren = self.visitor.visit_binary(input) {
|
if let VisitResult::VisitChildren = self.visitor.visit_binary(input) {
|
||||||
self.visit_expression(&input.left);
|
self.visit_expression(&input.left);
|
||||||
self.visit_expression(&input.right);
|
self.visit_expression(&input.right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn visit_unary(&mut self, input: &UnaryExpression) {
|
pub fn visit_unary(&mut self, input: &'a UnaryExpression) {
|
||||||
if let VisitResult::VisitChildren = self.visitor.visit_unary(input) {
|
if let VisitResult::VisitChildren = self.visitor.visit_unary(input) {
|
||||||
self.visit_expression(&input.inner);
|
self.visit_expression(&input.inner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn visit_ternary(&mut self, input: &TernaryExpression) {
|
pub fn visit_ternary(&mut self, input: &'a TernaryExpression) {
|
||||||
if let VisitResult::VisitChildren = self.visitor.visit_ternary(input) {
|
if let VisitResult::VisitChildren = self.visitor.visit_ternary(input) {
|
||||||
self.visit_expression(&input.condition);
|
self.visit_expression(&input.condition);
|
||||||
self.visit_expression(&input.if_true);
|
self.visit_expression(&input.if_true);
|
||||||
@ -71,16 +74,18 @@ impl<V: ExpressionVisitor> VisitorDirector<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn visit_call(&mut self, input: &CallExpression) {
|
pub fn visit_call(&mut self, input: &'a CallExpression) {
|
||||||
if let VisitResult::VisitChildren = self.visitor.visit_call(input) {
|
if let VisitResult::VisitChildren = self.visitor.visit_call(input) {
|
||||||
self.visit_expression(&input.function);
|
self.visit_expression(&input.function);
|
||||||
input.arguments.iter().for_each(|expr| self.visit_expression(expr));
|
for expr in input.arguments.iter() {
|
||||||
|
self.visit_expression(expr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: ExpressionVisitor + StatementVisitor> VisitorDirector<V> {
|
impl<'a, V: ExpressionVisitor<'a> + StatementVisitor<'a>> VisitorDirector<'a, V> {
|
||||||
fn visit_statement(&mut self, input: &Statement) {
|
pub fn visit_statement(&mut self, input: &'a Statement) {
|
||||||
if let VisitResult::VisitChildren = self.visitor.visit_statement(input) {
|
if let VisitResult::VisitChildren = self.visitor.visit_statement(input) {
|
||||||
match input {
|
match input {
|
||||||
Statement::Return(stmt) => self.visit_return(stmt),
|
Statement::Return(stmt) => self.visit_return(stmt),
|
||||||
@ -95,25 +100,25 @@ impl<V: ExpressionVisitor + StatementVisitor> VisitorDirector<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_return(&mut self, input: &ReturnStatement) {
|
pub fn visit_return(&mut self, input: &'a ReturnStatement) {
|
||||||
if let VisitResult::VisitChildren = self.visitor.visit_return(input) {
|
if let VisitResult::VisitChildren = self.visitor.visit_return(input) {
|
||||||
self.visit_expression(&input.expression);
|
self.visit_expression(&input.expression);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_definition(&mut self, input: &DefinitionStatement) {
|
pub fn visit_definition(&mut self, input: &'a DefinitionStatement) {
|
||||||
if let VisitResult::VisitChildren = self.visitor.visit_definition(input) {
|
if let VisitResult::VisitChildren = self.visitor.visit_definition(input) {
|
||||||
self.visit_expression(&input.value);
|
self.visit_expression(&input.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_assign(&mut self, input: &AssignStatement) {
|
pub fn visit_assign(&mut self, input: &'a AssignStatement) {
|
||||||
if let VisitResult::VisitChildren = self.visitor.visit_assign(input) {
|
if let VisitResult::VisitChildren = self.visitor.visit_assign(input) {
|
||||||
self.visit_expression(&input.value);
|
self.visit_expression(&input.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_conditional(&mut self, input: &ConditionalStatement) {
|
pub fn visit_conditional(&mut self, input: &'a ConditionalStatement) {
|
||||||
if let VisitResult::VisitChildren = self.visitor.visit_conditional(input) {
|
if let VisitResult::VisitChildren = self.visitor.visit_conditional(input) {
|
||||||
self.visit_expression(&input.condition);
|
self.visit_expression(&input.condition);
|
||||||
self.visit_block(&input.block);
|
self.visit_block(&input.block);
|
||||||
@ -123,7 +128,7 @@ impl<V: ExpressionVisitor + StatementVisitor> VisitorDirector<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_iteration(&mut self, input: &IterationStatement) {
|
pub fn visit_iteration(&mut self, input: &'a IterationStatement) {
|
||||||
if let VisitResult::VisitChildren = self.visitor.visit_iteration(input) {
|
if let VisitResult::VisitChildren = self.visitor.visit_iteration(input) {
|
||||||
self.visit_expression(&input.start);
|
self.visit_expression(&input.start);
|
||||||
self.visit_expression(&input.stop);
|
self.visit_expression(&input.stop);
|
||||||
@ -131,7 +136,7 @@ impl<V: ExpressionVisitor + StatementVisitor> VisitorDirector<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_console(&mut self, input: &ConsoleStatement) {
|
pub fn visit_console(&mut self, input: &'a ConsoleStatement) {
|
||||||
if let VisitResult::VisitChildren = self.visitor.visit_console(input) {
|
if let VisitResult::VisitChildren = self.visitor.visit_console(input) {
|
||||||
if let ConsoleFunction::Assert(expr) = &input.function {
|
if let ConsoleFunction::Assert(expr) = &input.function {
|
||||||
self.visit_expression(expr);
|
self.visit_expression(expr);
|
||||||
@ -139,30 +144,31 @@ impl<V: ExpressionVisitor + StatementVisitor> VisitorDirector<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_expression_statement(&mut self, input: &ExpressionStatement) {
|
pub fn visit_expression_statement(&mut self, input: &'a ExpressionStatement) {
|
||||||
if let VisitResult::VisitChildren = self.visitor.visit_expression_statement(input) {
|
if let VisitResult::VisitChildren = self.visitor.visit_expression_statement(input) {
|
||||||
self.visit_expression(&input.expression);
|
self.visit_expression(&input.expression);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_block(&mut self, input: &Block) {
|
pub fn visit_block(&mut self, input: &'a Block) {
|
||||||
if let VisitResult::VisitChildren = self.visitor.visit_block(input) {
|
if let VisitResult::VisitChildren = self.visitor.visit_block(input) {
|
||||||
input.statements.iter().for_each(|stmt| self.visit_statement(stmt));
|
for stmt in input.statements.iter() {
|
||||||
|
self.visit_statement(stmt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: ExpressionVisitor + ProgramVisitor + StatementVisitor> VisitorDirector<V> {
|
impl<'a, V: ExpressionVisitor<'a> + ProgramVisitor<'a> + StatementVisitor<'a>> VisitorDirector<'a, V> {
|
||||||
fn visit_program(&mut self, input: &Program) {
|
pub fn visit_program(&mut self, input: &'a Program) {
|
||||||
if let VisitResult::VisitChildren = self.visitor.visit_program(input) {
|
if let VisitResult::VisitChildren = self.visitor.visit_program(input) {
|
||||||
input
|
for function in input.functions.values() {
|
||||||
.functions
|
self.visit_function(function);
|
||||||
.values()
|
}
|
||||||
.for_each(|function| self.visit_function(function));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_function(&mut self, input: &Function) {
|
pub fn visit_function(&mut self, input: &'a Function) {
|
||||||
if let VisitResult::VisitChildren = self.visitor.visit_function(input) {
|
if let VisitResult::VisitChildren = self.visitor.visit_function(input) {
|
||||||
self.visit_block(&input.block);
|
self.visit_block(&input.block);
|
||||||
}
|
}
|
||||||
|
@ -22,24 +22,36 @@ rust-version = "1.56.1"
|
|||||||
path = "../ast"
|
path = "../ast"
|
||||||
version = "1.5.3"
|
version = "1.5.3"
|
||||||
|
|
||||||
[dependencies.leo-ast-passes]
|
|
||||||
path = "../ast-passes"
|
|
||||||
version = "1.5.3"
|
|
||||||
|
|
||||||
[dependencies.leo-errors]
|
[dependencies.leo-errors]
|
||||||
path = "../../leo/errors"
|
path = "../../leo/errors"
|
||||||
version = "1.5.3"
|
version = "1.5.3"
|
||||||
|
|
||||||
|
[dependencies.leo-passes]
|
||||||
|
path = "../passes"
|
||||||
|
version = "1.5.3"
|
||||||
|
|
||||||
[dependencies.leo-parser]
|
[dependencies.leo-parser]
|
||||||
path = "../parser"
|
path = "../parser"
|
||||||
version = "1.5.3"
|
version = "1.5.3"
|
||||||
|
|
||||||
[dependencies.leo-span]
|
[dependencies.sha2]
|
||||||
|
version = "0.10"
|
||||||
|
|
||||||
|
[dev-dependencies.leo-span]
|
||||||
path = "../../leo/span"
|
path = "../../leo/span"
|
||||||
version = "1.5.3"
|
version = "1.5.3"
|
||||||
|
|
||||||
[dependencies.sha2]
|
[dev-dependencies.leo-test-framework]
|
||||||
version = "0.10"
|
path = "../../tests/test-framework"
|
||||||
|
version = "1.4.0"
|
||||||
|
|
||||||
|
[dev-dependencies.serde]
|
||||||
|
version = "1.0.136"
|
||||||
|
features = ["derive"]
|
||||||
|
|
||||||
|
[dev-dependencies.serde_yaml]
|
||||||
|
version = "0.8.23"
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = [ ]
|
default = [ ]
|
||||||
|
@ -22,38 +22,41 @@
|
|||||||
#![allow(clippy::upper_case_acronyms)]
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
pub use leo_ast::Ast;
|
#[cfg(test)]
|
||||||
|
mod test;
|
||||||
|
|
||||||
|
use leo_ast::Program;
|
||||||
|
pub use leo_ast::{Ast, InputAst};
|
||||||
use leo_errors::emitter::Handler;
|
use leo_errors::emitter::Handler;
|
||||||
use leo_errors::{CompilerError, Result};
|
use leo_errors::{CompilerError, Result};
|
||||||
use leo_span::symbol::create_session_if_not_set_then;
|
pub use leo_passes::SymbolTable;
|
||||||
|
use leo_passes::*;
|
||||||
|
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
/// The primary entry point of the Leo compiler.
|
/// The primary entry point of the Leo compiler.
|
||||||
pub struct Compiler<'a> {
|
pub struct Compiler<'a> {
|
||||||
handler: &'a Handler,
|
handler: &'a Handler,
|
||||||
main_file_path: PathBuf,
|
main_file_path: PathBuf,
|
||||||
output_directory: PathBuf,
|
output_directory: PathBuf,
|
||||||
input_file_path: PathBuf,
|
pub ast: Ast,
|
||||||
|
pub input_ast: Option<InputAst>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Compiler<'a> {
|
impl<'a> Compiler<'a> {
|
||||||
///
|
///
|
||||||
/// Returns a new Leo compiler.
|
/// Returns a new Leo compiler.
|
||||||
///
|
///
|
||||||
pub fn new(
|
pub fn new(handler: &'a Handler, main_file_path: PathBuf, output_directory: PathBuf) -> Self {
|
||||||
handler: &'a Handler,
|
|
||||||
main_file_path: PathBuf,
|
|
||||||
output_directory: PathBuf,
|
|
||||||
input_file_path: PathBuf,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
handler,
|
handler,
|
||||||
main_file_path,
|
main_file_path,
|
||||||
output_directory,
|
output_directory,
|
||||||
input_file_path,
|
ast: Ast::new(Program::new("Initial".to_string())),
|
||||||
|
input_ast: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,28 +76,8 @@ impl<'a> Compiler<'a> {
|
|||||||
Ok(format!("{:x}", hash))
|
Ok(format!("{:x}", hash))
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
// Parses and stores a program file content from a string, constructs a syntax tree, and generates a program.
|
||||||
/// Runs the compiler stages.
|
pub fn parse_program_from_string(&mut self, program_string: &str) -> Result<()> {
|
||||||
///
|
|
||||||
fn compiler_stages(self) -> Result<leo_ast::Ast> {
|
|
||||||
//load the input file if it exists.
|
|
||||||
let _input_ast = if self.input_file_path.exists() {
|
|
||||||
let input_string = fs::read_to_string(&self.input_file_path)
|
|
||||||
.map_err(|e| CompilerError::file_read_error(self.main_file_path.clone(), e))?;
|
|
||||||
|
|
||||||
Some(leo_parser::parse_input(
|
|
||||||
self.handler,
|
|
||||||
self.input_file_path.to_str().unwrap_or_default(),
|
|
||||||
input_string,
|
|
||||||
)?)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load the program file.
|
|
||||||
let program_string = fs::read_to_string(&self.main_file_path)
|
|
||||||
.map_err(|e| CompilerError::file_read_error(self.main_file_path.clone(), e))?;
|
|
||||||
|
|
||||||
// Use the parser to construct the abstract syntax tree (ast).
|
// Use the parser to construct the abstract syntax tree (ast).
|
||||||
let ast: leo_ast::Ast = leo_parser::parse_ast(
|
let ast: leo_ast::Ast = leo_parser::parse_ast(
|
||||||
self.handler,
|
self.handler,
|
||||||
@ -102,20 +85,58 @@ impl<'a> Compiler<'a> {
|
|||||||
program_string,
|
program_string,
|
||||||
)?;
|
)?;
|
||||||
// Write the AST snapshot post parsing.
|
// Write the AST snapshot post parsing.
|
||||||
ast.to_json_file_without_keys(self.output_directory, "initial_ast.json", &["span"])?;
|
ast.to_json_file_without_keys(self.output_directory.clone(), "initial_ast.json", &["span"])?;
|
||||||
|
|
||||||
// Canonicalize the AST.
|
self.ast = ast;
|
||||||
// ast = leo_ast_passes::Canonicalizer::do_pass(Default::default(), ast.into_repr())?;
|
|
||||||
// Write the AST snapshot post parsing
|
|
||||||
// ast.to_json_file_without_keys(self.output_directory, "canonicalization_ast.json", &["span"])?;
|
|
||||||
|
|
||||||
Ok(ast)
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses and stores the main program file, constructs a syntax tree, and generates a program.
|
||||||
|
pub fn parse_program(&mut self) -> Result<()> {
|
||||||
|
// Load the program file.
|
||||||
|
let program_string = fs::read_to_string(&self.main_file_path)
|
||||||
|
.map_err(|e| CompilerError::file_read_error(self.main_file_path.clone(), e))?;
|
||||||
|
|
||||||
|
self.parse_program_from_string(&program_string)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses and stores the input file, constructs a syntax tree, and generates a program input.
|
||||||
|
pub fn parse_input_from_string(&mut self, input_file_path: PathBuf, input_string: &str) -> Result<()> {
|
||||||
|
let input_ast =
|
||||||
|
leo_parser::parse_input(self.handler, input_file_path.to_str().unwrap_or_default(), input_string)?;
|
||||||
|
input_ast.to_json_file_without_keys(self.output_directory.clone(), "inital_input_ast.json", &["span"])?;
|
||||||
|
|
||||||
|
self.input_ast = Some(input_ast);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses and stores the input file, constructs a syntax tree, and generates a program input.
|
||||||
|
pub fn parse_input(&mut self, input_file_path: PathBuf) -> Result<()> {
|
||||||
|
// Load the input file if it exists.
|
||||||
|
if input_file_path.exists() {
|
||||||
|
let input_string = fs::read_to_string(&input_file_path)
|
||||||
|
.map_err(|e| CompilerError::file_read_error(input_file_path.clone(), e))?;
|
||||||
|
|
||||||
|
self.parse_input_from_string(input_file_path, &input_string)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Runs the compiler stages.
|
||||||
|
///
|
||||||
|
fn compiler_stages(&self) -> Result<SymbolTable<'_>> {
|
||||||
|
let symbol_table = CreateSymbolTable::do_pass((&self.ast, self.handler))?;
|
||||||
|
|
||||||
|
Ok(symbol_table)
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Returns a compiled Leo program.
|
/// Returns a compiled Leo program.
|
||||||
///
|
///
|
||||||
pub fn compile(self) -> Result<leo_ast::Ast> {
|
pub fn compile(&self) -> Result<SymbolTable<'_>> {
|
||||||
create_session_if_not_set_then(|_| self.compiler_stages())
|
self.compiler_stages()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
224
compiler/compiler/src/test.rs
Normal file
224
compiler/compiler/src/test.rs
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
// Copyright (C) 2019-2022 Aleo Systems Inc.
|
||||||
|
// This file is part of the Leo library.
|
||||||
|
|
||||||
|
// The Leo library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// The Leo library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
fmt, fs,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::Compiler;
|
||||||
|
|
||||||
|
use leo_errors::{
|
||||||
|
emitter::{Buffer, Emitter, Handler},
|
||||||
|
LeoError, LeoWarning,
|
||||||
|
};
|
||||||
|
use leo_passes::SymbolTable;
|
||||||
|
use leo_span::symbol::create_session_if_not_set_then;
|
||||||
|
use leo_test_framework::{
|
||||||
|
runner::{Namespace, ParseType, Runner},
|
||||||
|
Test,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_yaml::Value;
|
||||||
|
|
||||||
|
fn new_compiler(handler: &Handler, main_file_path: PathBuf) -> Compiler<'_> {
|
||||||
|
let output_dir = PathBuf::from("/tmp/output/");
|
||||||
|
fs::create_dir_all(output_dir.clone()).unwrap();
|
||||||
|
|
||||||
|
Compiler::new(handler, main_file_path, output_dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_program<'a>(
|
||||||
|
handler: &'a Handler,
|
||||||
|
program_string: &str,
|
||||||
|
cwd: Option<PathBuf>,
|
||||||
|
) -> Result<Compiler<'a>, LeoError> {
|
||||||
|
let mut compiler = new_compiler(handler, cwd.unwrap_or_else(|| "compiler-test".into()));
|
||||||
|
compiler.parse_program_from_string(program_string)?;
|
||||||
|
|
||||||
|
Ok(compiler)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_content(content: &str) -> String {
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(content);
|
||||||
|
let hash = hasher.finalize();
|
||||||
|
|
||||||
|
format!("{:x}", hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_file(path: &str) -> String {
|
||||||
|
let file = fs::read_to_string(&Path::new(path)).unwrap();
|
||||||
|
hash_content(&file)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CompileNamespace;
|
||||||
|
|
||||||
|
impl Namespace for CompileNamespace {
|
||||||
|
fn parse_type(&self) -> ParseType {
|
||||||
|
ParseType::Whole
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_test(&self, test: Test) -> Result<Value, String> {
|
||||||
|
let buf = BufferEmitter(Rc::default(), Rc::default());
|
||||||
|
let handler = Handler::new(Box::new(buf.clone()));
|
||||||
|
|
||||||
|
create_session_if_not_set_then(|_| run_test(test, &handler, &buf).map_err(|()| buf.0.take().to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, PartialEq, Serialize)]
|
||||||
|
struct OutputItem {
|
||||||
|
pub initial_input_ast: String,
|
||||||
|
pub symbol_table: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, PartialEq, Serialize)]
|
||||||
|
struct CompileOutput {
|
||||||
|
pub output: Vec<OutputItem>,
|
||||||
|
pub initial_ast: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the path of the `input_file` given in `input` into `list`.
|
||||||
|
fn get_input_file_paths(list: &mut Vec<PathBuf>, test: &Test, input: &Value) {
|
||||||
|
let input_file: PathBuf = test.path.parent().expect("no test parent dir").into();
|
||||||
|
if input.as_str().is_some() {
|
||||||
|
let mut input_file = input_file;
|
||||||
|
input_file.push(input.as_str().expect("input_file was not a string or array"));
|
||||||
|
list.push(input_file.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collect and return all inputs, if possible.
|
||||||
|
fn collect_all_inputs(test: &Test) -> Result<Vec<PathBuf>, String> {
|
||||||
|
let mut list = vec![];
|
||||||
|
|
||||||
|
if let Some(input) = test.config.get("input_file") {
|
||||||
|
get_input_file_paths(&mut list, test, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_and_process<'a>(
|
||||||
|
parsed: &'a mut Compiler<'a>,
|
||||||
|
input_file_path: PathBuf,
|
||||||
|
) -> Result<SymbolTable<'a>, LeoError> {
|
||||||
|
parsed.parse_input(input_file_path)?;
|
||||||
|
parsed.compiler_stages()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errors used in this module.
|
||||||
|
enum LeoOrString {
|
||||||
|
Leo(LeoError),
|
||||||
|
String(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for LeoOrString {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Leo(x) => x.fmt(f),
|
||||||
|
Self::String(x) => x.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A buffer used to emit errors into.
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct BufferEmitter(Rc<RefCell<Buffer<LeoOrString>>>, Rc<RefCell<Buffer<LeoWarning>>>);
|
||||||
|
|
||||||
|
impl Emitter for BufferEmitter {
|
||||||
|
fn emit_err(&mut self, err: LeoError) {
|
||||||
|
self.0.borrow_mut().push(LeoOrString::Leo(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn last_emited_err(&self) -> Option<LeoError> {
|
||||||
|
let temp = &*self.0.borrow();
|
||||||
|
temp.last_entry().map(|entry| match entry {
|
||||||
|
LeoOrString::Leo(err) => err.clone(),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_warning(&mut self, warning: leo_errors::LeoWarning) {
|
||||||
|
self.1.borrow_mut().push(warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn buffer_if_err<T>(buf: &BufferEmitter, res: Result<T, String>) -> Result<T, ()> {
|
||||||
|
res.map_err(|err| buf.0.borrow_mut().push(LeoOrString::String(err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_test(test: Test, handler: &Handler, err_buf: &BufferEmitter) -> Result<Value, ()> {
|
||||||
|
// Check for CWD option:
|
||||||
|
// ``` cwd: import ```
|
||||||
|
// When set, uses different working directory for current file.
|
||||||
|
// If not, uses file path as current working directory.
|
||||||
|
let cwd = test.config.get("cwd").map(|val| {
|
||||||
|
let mut cwd = test.path.clone();
|
||||||
|
cwd.pop();
|
||||||
|
cwd.join(&val.as_str().unwrap())
|
||||||
|
});
|
||||||
|
|
||||||
|
let parsed = handler.extend_if_error(parse_program(handler, &test.content, cwd))?;
|
||||||
|
|
||||||
|
// (name, content)
|
||||||
|
let inputs = buffer_if_err(err_buf, collect_all_inputs(&test))?;
|
||||||
|
|
||||||
|
let mut output_items = Vec::with_capacity(inputs.len());
|
||||||
|
|
||||||
|
for input in inputs {
|
||||||
|
let mut parsed = parsed.clone();
|
||||||
|
let symbol_table = handler.extend_if_error(compile_and_process(&mut parsed, input))?;
|
||||||
|
let initial_input_ast = hash_file("/tmp/output/inital_input_ast.json");
|
||||||
|
|
||||||
|
output_items.push(OutputItem {
|
||||||
|
initial_input_ast,
|
||||||
|
symbol_table: hash_content(&symbol_table.to_string()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let initial_ast = hash_file("/tmp/output/initial_ast.json");
|
||||||
|
|
||||||
|
if fs::read_dir("/tmp/output").is_ok() {
|
||||||
|
fs::remove_dir_all(Path::new("/tmp/output")).expect("Error failed to clean up output dir.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let final_output = CompileOutput {
|
||||||
|
output: output_items,
|
||||||
|
initial_ast,
|
||||||
|
};
|
||||||
|
Ok(serde_yaml::to_value(&final_output).expect("serialization failed"))
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestRunner;
|
||||||
|
|
||||||
|
impl Runner for TestRunner {
|
||||||
|
fn resolve_namespace(&self, name: &str) -> Option<Box<dyn Namespace>> {
|
||||||
|
Some(match name {
|
||||||
|
"Compile" => Box::new(CompileNamespace),
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn compiler_tests() {
|
||||||
|
leo_test_framework::run_tests(&TestRunner, "compiler");
|
||||||
|
}
|
@ -23,9 +23,6 @@ name = "leo_ast"
|
|||||||
path = "benches/leo_ast.rs"
|
path = "benches/leo_ast.rs"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
smallvec = "1.8"
|
|
||||||
|
|
||||||
[dependencies.leo-ast]
|
[dependencies.leo-ast]
|
||||||
path = "../ast"
|
path = "../ast"
|
||||||
version = "1.5.3"
|
version = "1.5.3"
|
||||||
@ -52,6 +49,9 @@ version = "1.3.0"
|
|||||||
version = "1.0"
|
version = "1.0"
|
||||||
features = [ "derive" ]
|
features = [ "derive" ]
|
||||||
|
|
||||||
|
[dependencies.smallvec]
|
||||||
|
version = "1.8"
|
||||||
|
|
||||||
[dependencies.tracing]
|
[dependencies.tracing]
|
||||||
version = "0.1"
|
version = "0.1"
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ use leo_errors::{ParserError, Result};
|
|||||||
|
|
||||||
impl ParserContext<'_> {
|
impl ParserContext<'_> {
|
||||||
/// Returns a [`ParsedInputFile`] struct filled with the data acquired in the file.
|
/// Returns a [`ParsedInputFile`] struct filled with the data acquired in the file.
|
||||||
pub fn parse_input(&mut self) -> Result<ParsedInputFile> {
|
pub fn parse_input(&mut self) -> Result<InputAst> {
|
||||||
let mut sections = Vec::new();
|
let mut sections = Vec::new();
|
||||||
|
|
||||||
while self.has_next() {
|
while self.has_next() {
|
||||||
@ -31,7 +31,7 @@ impl ParserContext<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ParsedInputFile { sections })
|
Ok(InputAst { sections })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses particular section in the Input or State file.
|
/// Parses particular section in the Input or State file.
|
||||||
@ -67,7 +67,7 @@ impl ParserContext<'_> {
|
|||||||
self.expect(&Token::Colon)?;
|
self.expect(&Token::Colon)?;
|
||||||
let (type_, span) = self.parse_non_ident_types()?;
|
let (type_, span) = self.parse_non_ident_types()?;
|
||||||
self.expect(&Token::Assign)?;
|
self.expect(&Token::Assign)?;
|
||||||
let value = self.parse_primary_expression()?;
|
let value = self.parse_unary_expression()?;
|
||||||
self.expect(&Token::Semicolon)?;
|
self.expect(&Token::Semicolon)?;
|
||||||
|
|
||||||
Ok(Definition {
|
Ok(Definition {
|
||||||
|
@ -57,7 +57,7 @@ pub fn parse(handler: &Handler, path: &str, source: &str) -> Result<Program> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parses an input file at the given file `path` and `source` code text.
|
/// Parses an input file at the given file `path` and `source` code text.
|
||||||
pub fn parse_input<T: AsRef<str>, Y: AsRef<str>>(handler: &Handler, path: T, source: Y) -> Result<ParsedInputFile> {
|
pub fn parse_input<T: AsRef<str>, Y: AsRef<str>>(handler: &Handler, path: T, source: Y) -> Result<InputAst> {
|
||||||
let mut tokens = ParserContext::new(handler, crate::tokenize(path.as_ref(), source.as_ref())?);
|
let mut tokens = ParserContext::new(handler, crate::tokenize(path.as_ref(), source.as_ref())?);
|
||||||
|
|
||||||
tokens.parse_input()
|
tokens.parse_input()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "leo-ast-passes"
|
name = "leo-passes"
|
||||||
version = "1.5.3"
|
version = "1.5.3"
|
||||||
authors = [ "The Aleo Team <hello@aleo.org>" ]
|
authors = [ "The Aleo Team <hello@aleo.org>" ]
|
||||||
description = "The Leo programming language"
|
description = "The Leo programming language"
|
||||||
@ -39,7 +39,3 @@ version = "1.5.3"
|
|||||||
[dependencies.leo-span]
|
[dependencies.leo-span]
|
||||||
path = "../../leo/span"
|
path = "../../leo/span"
|
||||||
version = "1.5.3"
|
version = "1.5.3"
|
||||||
|
|
||||||
#[dependencies.leo-stdlib]
|
|
||||||
#path = "../stdlib"
|
|
||||||
#version = "1.5.3"
|
|
@ -25,3 +25,9 @@ pub use canonicalization::*;
|
|||||||
// until we migrate stdlib and then import resolution.
|
// until we migrate stdlib and then import resolution.
|
||||||
/* pub mod import_resolution;
|
/* pub mod import_resolution;
|
||||||
pub use import_resolution::*; */
|
pub use import_resolution::*; */
|
||||||
|
|
||||||
|
pub mod pass;
|
||||||
|
pub use self::pass::*;
|
||||||
|
|
||||||
|
pub mod symbol_table;
|
||||||
|
pub use symbol_table::*;
|
@ -14,10 +14,10 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::{Ast, Program};
|
|
||||||
use leo_errors::Result;
|
|
||||||
|
|
||||||
/// A pass consuming a `Program` and possibly returning an `Ast`.
|
/// A pass consuming a `Program` and possibly returning an `Ast`.
|
||||||
pub trait AstPass {
|
pub trait Pass<'a> {
|
||||||
fn do_pass(self, ast: Program) -> Result<Ast>;
|
type Input;
|
||||||
|
type Output;
|
||||||
|
|
||||||
|
fn do_pass(input: Self::Input) -> Self::Output;
|
||||||
}
|
}
|
50
compiler/passes/src/symbol_table/create.rs
Normal file
50
compiler/passes/src/symbol_table/create.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (C) 2019-2022 Aleo Systems Inc.
|
||||||
|
// This file is part of the Leo library.
|
||||||
|
|
||||||
|
// The Leo library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// The Leo library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use leo_ast::*;
|
||||||
|
use leo_errors::emitter::Handler;
|
||||||
|
|
||||||
|
use crate::SymbolTable;
|
||||||
|
|
||||||
|
pub struct CreateSymbolTable<'a> {
|
||||||
|
symbol_table: SymbolTable<'a>,
|
||||||
|
handler: &'a Handler,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CreateSymbolTable<'a> {
|
||||||
|
pub fn new(handler: &'a Handler) -> Self {
|
||||||
|
Self {
|
||||||
|
symbol_table: SymbolTable::default(),
|
||||||
|
handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn symbol_table(self) -> SymbolTable<'a> {
|
||||||
|
self.symbol_table
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ExpressionVisitor<'a> for CreateSymbolTable<'a> {}
|
||||||
|
|
||||||
|
impl<'a> StatementVisitor<'a> for CreateSymbolTable<'a> {}
|
||||||
|
|
||||||
|
impl<'a> ProgramVisitor<'a> for CreateSymbolTable<'a> {
|
||||||
|
fn visit_function(&mut self, input: &'a Function) -> VisitResult {
|
||||||
|
if let Err(err) = self.symbol_table.insert_fn(input.name(), input) {
|
||||||
|
self.handler.emit_err(err);
|
||||||
|
}
|
||||||
|
VisitResult::SkipChildren
|
||||||
|
}
|
||||||
|
}
|
42
compiler/passes/src/symbol_table/mod.rs
Normal file
42
compiler/passes/src/symbol_table/mod.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (C) 2019-2022 Aleo Systems Inc.
|
||||||
|
// This file is part of the Leo library.
|
||||||
|
|
||||||
|
// The Leo library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// The Leo library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
pub mod create;
|
||||||
|
pub use create::*;
|
||||||
|
|
||||||
|
pub mod table;
|
||||||
|
pub use table::*;
|
||||||
|
|
||||||
|
pub mod variable_symbol;
|
||||||
|
pub use variable_symbol::*;
|
||||||
|
|
||||||
|
use crate::Pass;
|
||||||
|
|
||||||
|
use leo_ast::{Ast, VisitorDirector};
|
||||||
|
use leo_errors::{emitter::Handler, Result};
|
||||||
|
|
||||||
|
impl<'a> Pass<'a> for CreateSymbolTable<'a> {
|
||||||
|
type Input = (&'a Ast, &'a Handler);
|
||||||
|
type Output = Result<SymbolTable<'a>>;
|
||||||
|
|
||||||
|
fn do_pass((ast, handler): Self::Input) -> Self::Output {
|
||||||
|
let mut visitor = VisitorDirector::new(CreateSymbolTable::new(handler));
|
||||||
|
visitor.visit_program(ast.as_repr());
|
||||||
|
handler.last_err()?;
|
||||||
|
|
||||||
|
Ok(visitor.visitor().symbol_table())
|
||||||
|
}
|
||||||
|
}
|
68
compiler/passes/src/symbol_table/table.rs
Normal file
68
compiler/passes/src/symbol_table/table.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// Copyright (C) 2019-2022 Aleo Systems Inc.
|
||||||
|
// This file is part of the Leo library.
|
||||||
|
|
||||||
|
// The Leo library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// The Leo library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use leo_ast::Function;
|
||||||
|
use leo_errors::{AstError, Result};
|
||||||
|
use leo_span::Symbol;
|
||||||
|
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
|
||||||
|
use crate::VariableSymbol;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||||
|
pub struct SymbolTable<'a> {
|
||||||
|
/// Functions represents the name of each function mapped to the Ast's function definition.
|
||||||
|
/// This field is populated at a first pass.
|
||||||
|
functions: IndexMap<Symbol, &'a Function>,
|
||||||
|
/// Variables represents functions variable definitions and input variables.
|
||||||
|
/// This field is not populated till necessary.
|
||||||
|
variables: VariableSymbol<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SymbolTable<'a> {
|
||||||
|
pub fn check_shadowing(&self, symbol: &Symbol) -> Result<()> {
|
||||||
|
if let Some(function) = self.functions.get(symbol) {
|
||||||
|
Err(AstError::shadowed_function(symbol, &function.span).into())
|
||||||
|
} else {
|
||||||
|
self.variables.check_shadowing(symbol)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_variables(&mut self) {
|
||||||
|
self.variables.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_fn(&mut self, symbol: Symbol, function: &'a Function) -> Result<()> {
|
||||||
|
self.check_shadowing(&symbol)?;
|
||||||
|
self.functions.insert(symbol, function);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for SymbolTable<'a> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "SymbolTable")?;
|
||||||
|
|
||||||
|
for func in self.functions.values() {
|
||||||
|
write!(f, "{func}")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "{}", self.variables)
|
||||||
|
}
|
||||||
|
}
|
91
compiler/passes/src/symbol_table/variable_symbol.rs
Normal file
91
compiler/passes/src/symbol_table/variable_symbol.rs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// Copyright (C) 2019-2022 Aleo Systems Inc.
|
||||||
|
// This file is part of the Leo library.
|
||||||
|
|
||||||
|
// The Leo library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// The Leo library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
use leo_ast::{DefinitionStatement, FunctionInput, FunctionInputVariable};
|
||||||
|
use leo_errors::{AstError, Result};
|
||||||
|
use leo_span::Symbol;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||||
|
pub struct VariableSymbol<'a> {
|
||||||
|
/// The parent scope of variables if it exists.
|
||||||
|
/// For example if we are in a if block inside a function.
|
||||||
|
/// The parent would be the functions variables and inputs.
|
||||||
|
/// This field is populated as necessary.
|
||||||
|
parent: Option<Box<VariableSymbol<'a>>>,
|
||||||
|
/// The input variables defined in a scope.
|
||||||
|
/// This field is populated as necessary.
|
||||||
|
inputs: IndexMap<Symbol, &'a FunctionInputVariable>,
|
||||||
|
/// The variables defined in a scope.
|
||||||
|
/// This field is populated as necessary.
|
||||||
|
variables: IndexMap<Symbol, &'a DefinitionStatement>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> VariableSymbol<'a> {
|
||||||
|
pub fn new(parent: Option<Box<VariableSymbol<'a>>>, inputs: Vec<&'a FunctionInput>) -> Self {
|
||||||
|
Self {
|
||||||
|
parent,
|
||||||
|
inputs: inputs
|
||||||
|
.iter()
|
||||||
|
.map(|input| {
|
||||||
|
let inner = input.get_variable();
|
||||||
|
(inner.identifier.name, inner)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
variables: IndexMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_shadowing(&self, symbol: &Symbol) -> Result<()> {
|
||||||
|
if let Some(input) = self.inputs.get(symbol) {
|
||||||
|
Err(AstError::shadowed_function_input(symbol, &input.span).into())
|
||||||
|
} else if let Some(var) = self.variables.get(symbol) {
|
||||||
|
Err(AstError::shadowed_variable(symbol, &var.span).into())
|
||||||
|
} else if let Some(parent) = &self.parent {
|
||||||
|
parent.check_shadowing(symbol)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.parent = None;
|
||||||
|
self.inputs.clear();
|
||||||
|
self.variables.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for VariableSymbol<'a> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "VariableSymbol")?;
|
||||||
|
self.parent
|
||||||
|
.as_ref()
|
||||||
|
.map(|parent| write!(f, "parent {parent}"))
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
|
for input in self.inputs.values() {
|
||||||
|
write!(f, "{input}")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for var in self.variables.values() {
|
||||||
|
write!(f, "{var}")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user