mirror of
https://github.com/AleoHQ/leo.git
synced 2024-12-18 14:31:31 +03:00
Merge pull request #1774 from AleoHQ/feature/create-symbol-table
[Impl] Symbol Table
This commit is contained in:
commit
cd606ac578
3
.gitattributes
vendored
3
.gitattributes
vendored
@ -1,4 +1,5 @@
|
||||
# Force certain files to be LF
|
||||
*.leo 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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "leo-ast-passes"
|
||||
version = "1.5.3"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"leo-ast",
|
||||
"leo-errors",
|
||||
"leo-parser",
|
||||
"leo-span",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "leo-compiler"
|
||||
version = "1.5.3"
|
||||
dependencies = [
|
||||
"leo-ast",
|
||||
"leo-ast-passes",
|
||||
"leo-errors",
|
||||
"leo-parser",
|
||||
"leo-passes",
|
||||
"leo-span",
|
||||
"leo-test-framework",
|
||||
"serde",
|
||||
"serde_yaml",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
@ -1273,6 +1265,7 @@ dependencies = [
|
||||
"leo-compiler",
|
||||
"leo-errors",
|
||||
"leo-package",
|
||||
"leo-span",
|
||||
"notify",
|
||||
"rand",
|
||||
"rand_core",
|
||||
@ -1325,6 +1318,17 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "leo-passes"
|
||||
version = "1.5.3"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"leo-ast",
|
||||
"leo-errors",
|
||||
"leo-parser",
|
||||
"leo-span",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "leo-span"
|
||||
version = "1.5.3"
|
||||
|
20
Cargo.toml
20
Cargo.toml
@ -46,10 +46,17 @@ version = "1.5.3"
|
||||
path = "./leo/package"
|
||||
version = "1.5.3"
|
||||
|
||||
[dependencies.leo-span]
|
||||
path = "./leo/span"
|
||||
version = "1.5.3"
|
||||
|
||||
[dependencies.snarkvm-utilities]
|
||||
git = "https://github.com/AleoHQ/snarkVM.git"
|
||||
rev = "51633e2"
|
||||
|
||||
[dependencies.backtrace]
|
||||
version = "0.3.65"
|
||||
|
||||
[dependencies.clap]
|
||||
version = "3.1"
|
||||
|
||||
@ -59,18 +66,12 @@ version = "0.5.1"
|
||||
[dependencies.colored]
|
||||
version = "2.0"
|
||||
|
||||
[dependencies.backtrace]
|
||||
version = "0.3.65"
|
||||
|
||||
[dependencies.sys-info]
|
||||
version = "0.9.1"
|
||||
[dependencies.console]
|
||||
version = "0.15.0"
|
||||
|
||||
[dependencies.dirs]
|
||||
version = "4.0.0"
|
||||
|
||||
[dependencies.console]
|
||||
version = "0.15.0"
|
||||
|
||||
[dependencies.indexmap]
|
||||
version = "1.8"
|
||||
features = ["serde"]
|
||||
@ -105,6 +106,9 @@ version = "1.0"
|
||||
[dependencies.structopt]
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.sys-info]
|
||||
version = "0.9.1"
|
||||
|
||||
[dependencies.toml]
|
||||
version = "0.5"
|
||||
|
||||
|
@ -18,9 +18,6 @@ license = "GPL-3.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.56"
|
||||
|
||||
[dependencies]
|
||||
smallvec = { version = "1.8.0", features = ["serde"] }
|
||||
|
||||
[dependencies.leo-errors]
|
||||
path = "../../leo/errors"
|
||||
version = "1.5.3"
|
||||
@ -41,6 +38,10 @@ features = [ "derive", "rc" ]
|
||||
version = "1.0"
|
||||
features = [ "preserve_order" ]
|
||||
|
||||
[dependencies.smallvec]
|
||||
version = "1.8.0"
|
||||
features = ["serde"]
|
||||
|
||||
[dev-dependencies.criterion]
|
||||
version = "0.3"
|
||||
|
||||
|
@ -14,8 +14,10 @@
|
||||
// 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 crate::{normalize_json_value, remove_key_from_json};
|
||||
|
||||
use super::*;
|
||||
use leo_errors::AstError;
|
||||
use leo_errors::{AstError, Result};
|
||||
|
||||
/// Input data which includes [`ProgramInput`] and [`ProgramState`].
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
@ -24,16 +26,44 @@ pub struct Input {
|
||||
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 {
|
||||
/// Serializes the ast into a JSON 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))?)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
// 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 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()),
|
||||
})
|
||||
}
|
||||
|
@ -23,9 +23,9 @@ pub struct ProgramInput {
|
||||
pub registers: Definitions,
|
||||
}
|
||||
|
||||
impl TryFrom<ParsedInputFile> for ProgramInput {
|
||||
impl TryFrom<InputAst> for ProgramInput {
|
||||
type Error = LeoError;
|
||||
fn try_from(input: ParsedInputFile) -> Result<Self> {
|
||||
fn try_from(input: InputAst) -> Result<Self> {
|
||||
let mut main = IndexMap::new();
|
||||
let mut registers = IndexMap::new();
|
||||
|
||||
|
@ -22,9 +22,9 @@ pub struct ProgramState {
|
||||
pub state: Definitions,
|
||||
}
|
||||
|
||||
impl TryFrom<ParsedInputFile> for ProgramState {
|
||||
impl TryFrom<InputAst> for ProgramState {
|
||||
type Error = LeoError;
|
||||
fn try_from(input: ParsedInputFile) -> Result<Self> {
|
||||
fn try_from(input: InputAst) -> Result<Self> {
|
||||
let mut state = IndexMap::new();
|
||||
|
||||
for section in input.sections {
|
||||
|
@ -40,9 +40,6 @@ pub use self::groups::*;
|
||||
pub mod input;
|
||||
pub use self::input::*;
|
||||
|
||||
pub mod pass;
|
||||
pub use self::pass::*;
|
||||
|
||||
pub mod passes;
|
||||
pub use self::passes::*;
|
||||
|
||||
@ -147,7 +144,7 @@ impl AsRef<Program> for Ast {
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
serde_json::Value::Object(map) => serde_json::Value::Object(
|
||||
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
|
||||
/// 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
|
||||
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 {
|
||||
serde_json::Value::Array(vec) => {
|
||||
let orig_length = vec.len();
|
||||
|
@ -29,84 +29,84 @@ impl Default for VisitResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExpressionVisitor {
|
||||
fn visit_expression(&mut self, _input: &Expression) -> VisitResult {
|
||||
pub trait ExpressionVisitor<'a> {
|
||||
fn visit_expression(&mut self, _input: &'a Expression) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_identifer(&mut self, _input: &Identifier) -> VisitResult {
|
||||
fn visit_identifier(&mut self, _input: &'a Identifier) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_value(&mut self, _input: &ValueExpression) -> VisitResult {
|
||||
fn visit_value(&mut self, _input: &'a ValueExpression) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_binary(&mut self, _input: &BinaryExpression) -> VisitResult {
|
||||
fn visit_binary(&mut self, _input: &'a BinaryExpression) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_unary(&mut self, _input: &UnaryExpression) -> VisitResult {
|
||||
fn visit_unary(&mut self, _input: &'a UnaryExpression) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_ternary(&mut self, _input: &TernaryExpression) -> VisitResult {
|
||||
fn visit_ternary(&mut self, _input: &'a TernaryExpression) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_call(&mut self, _input: &CallExpression) -> VisitResult {
|
||||
fn visit_call(&mut self, _input: &'a CallExpression) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_err(&mut self, _input: &ErrExpression) -> VisitResult {
|
||||
fn visit_err(&mut self, _input: &'a ErrExpression) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait StatementVisitor {
|
||||
fn visit_statement(&mut self, _input: &Statement) -> VisitResult {
|
||||
pub trait StatementVisitor<'a> {
|
||||
fn visit_statement(&mut self, _input: &'a Statement) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_return(&mut self, _input: &ReturnStatement) -> VisitResult {
|
||||
fn visit_return(&mut self, _input: &'a ReturnStatement) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_definition(&mut self, _input: &DefinitionStatement) -> VisitResult {
|
||||
fn visit_definition(&mut self, _input: &'a DefinitionStatement) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_assign(&mut self, _input: &AssignStatement) -> VisitResult {
|
||||
fn visit_assign(&mut self, _input: &'a AssignStatement) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_conditional(&mut self, _input: &ConditionalStatement) -> VisitResult {
|
||||
fn visit_conditional(&mut self, _input: &'a ConditionalStatement) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_iteration(&mut self, _input: &IterationStatement) -> VisitResult {
|
||||
fn visit_iteration(&mut self, _input: &'a IterationStatement) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_console(&mut self, _input: &ConsoleStatement) -> VisitResult {
|
||||
fn visit_console(&mut self, _input: &'a ConsoleStatement) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_expression_statement(&mut self, _input: &ExpressionStatement) -> VisitResult {
|
||||
fn visit_expression_statement(&mut self, _input: &'a ExpressionStatement) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_block(&mut self, _input: &Block) -> VisitResult {
|
||||
fn visit_block(&mut self, _input: &'a Block) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ProgramVisitor {
|
||||
fn visit_program(&mut self, _input: &Program) -> VisitResult {
|
||||
pub trait ProgramVisitor<'a> {
|
||||
fn visit_program(&mut self, _input: &'a Program) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_function(&mut self, _input: &Function) -> VisitResult {
|
||||
fn visit_function(&mut self, _input: &'a Function) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
@ -18,25 +18,28 @@
|
||||
//! It implements default methods for each node to be made
|
||||
//! given the type of node its visiting.
|
||||
|
||||
// temporary till we use in another pr.
|
||||
#![allow(dead_code)]
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::*;
|
||||
|
||||
pub struct VisitorDirector<V: ExpressionVisitor> {
|
||||
pub struct VisitorDirector<'a, V: ExpressionVisitor<'a>> {
|
||||
visitor: V,
|
||||
lifetime: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<V: ExpressionVisitor> VisitorDirector<V> {
|
||||
impl<'a, V: ExpressionVisitor<'a>> VisitorDirector<'a, V> {
|
||||
pub fn new(visitor: V) -> Self {
|
||||
Self { visitor }
|
||||
Self {
|
||||
visitor,
|
||||
lifetime: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visitor(self) -> V {
|
||||
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) {
|
||||
match input {
|
||||
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) {
|
||||
self.visit_expression(&input.left);
|
||||
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) {
|
||||
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) {
|
||||
self.visit_expression(&input.condition);
|
||||
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) {
|
||||
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> {
|
||||
fn visit_statement(&mut self, input: &Statement) {
|
||||
impl<'a, V: ExpressionVisitor<'a> + StatementVisitor<'a>> VisitorDirector<'a, V> {
|
||||
pub fn visit_statement(&mut self, input: &'a Statement) {
|
||||
if let VisitResult::VisitChildren = self.visitor.visit_statement(input) {
|
||||
match input {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
self.visit_expression(&input.condition);
|
||||
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) {
|
||||
self.visit_expression(&input.start);
|
||||
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 ConsoleFunction::Assert(expr) = &input.function {
|
||||
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) {
|
||||
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) {
|
||||
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> {
|
||||
fn visit_program(&mut self, input: &Program) {
|
||||
impl<'a, V: ExpressionVisitor<'a> + ProgramVisitor<'a> + StatementVisitor<'a>> VisitorDirector<'a, V> {
|
||||
pub fn visit_program(&mut self, input: &'a Program) {
|
||||
if let VisitResult::VisitChildren = self.visitor.visit_program(input) {
|
||||
input
|
||||
.functions
|
||||
.values()
|
||||
.for_each(|function| self.visit_function(function));
|
||||
for function in input.functions.values() {
|
||||
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) {
|
||||
self.visit_block(&input.block);
|
||||
}
|
||||
|
@ -22,24 +22,36 @@ rust-version = "1.56.1"
|
||||
path = "../ast"
|
||||
version = "1.5.3"
|
||||
|
||||
[dependencies.leo-ast-passes]
|
||||
path = "../ast-passes"
|
||||
version = "1.5.3"
|
||||
|
||||
[dependencies.leo-errors]
|
||||
path = "../../leo/errors"
|
||||
version = "1.5.3"
|
||||
|
||||
[dependencies.leo-passes]
|
||||
path = "../passes"
|
||||
version = "1.5.3"
|
||||
|
||||
[dependencies.leo-parser]
|
||||
path = "../parser"
|
||||
version = "1.5.3"
|
||||
|
||||
[dependencies.leo-span]
|
||||
[dependencies.sha2]
|
||||
version = "0.10"
|
||||
|
||||
[dev-dependencies.leo-span]
|
||||
path = "../../leo/span"
|
||||
version = "1.5.3"
|
||||
|
||||
[dependencies.sha2]
|
||||
version = "0.10"
|
||||
[dev-dependencies.leo-test-framework]
|
||||
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]
|
||||
default = [ ]
|
||||
|
@ -22,38 +22,41 @@
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
#![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::{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 std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Clone)]
|
||||
/// The primary entry point of the Leo compiler.
|
||||
pub struct Compiler<'a> {
|
||||
handler: &'a Handler,
|
||||
main_file_path: PathBuf,
|
||||
output_directory: PathBuf,
|
||||
input_file_path: PathBuf,
|
||||
pub ast: Ast,
|
||||
pub input_ast: Option<InputAst>,
|
||||
}
|
||||
|
||||
impl<'a> Compiler<'a> {
|
||||
///
|
||||
/// Returns a new Leo compiler.
|
||||
///
|
||||
pub fn new(
|
||||
handler: &'a Handler,
|
||||
main_file_path: PathBuf,
|
||||
output_directory: PathBuf,
|
||||
input_file_path: PathBuf,
|
||||
) -> Self {
|
||||
pub fn new(handler: &'a Handler, main_file_path: PathBuf, output_directory: PathBuf) -> Self {
|
||||
Self {
|
||||
handler,
|
||||
main_file_path,
|
||||
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))
|
||||
}
|
||||
|
||||
///
|
||||
/// Runs the compiler stages.
|
||||
///
|
||||
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))?;
|
||||
|
||||
// Parses and stores a program file content from a string, constructs a syntax tree, and generates a program.
|
||||
pub fn parse_program_from_string(&mut self, program_string: &str) -> Result<()> {
|
||||
// Use the parser to construct the abstract syntax tree (ast).
|
||||
let ast: leo_ast::Ast = leo_parser::parse_ast(
|
||||
self.handler,
|
||||
@ -102,20 +85,58 @@ impl<'a> Compiler<'a> {
|
||||
program_string,
|
||||
)?;
|
||||
// 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.
|
||||
// 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"])?;
|
||||
self.ast = ast;
|
||||
|
||||
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.
|
||||
///
|
||||
pub fn compile(self) -> Result<leo_ast::Ast> {
|
||||
create_session_if_not_set_then(|_| self.compiler_stages())
|
||||
pub fn compile(&self) -> Result<SymbolTable<'_>> {
|
||||
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"
|
||||
harness = false
|
||||
|
||||
[dependencies]
|
||||
smallvec = "1.8"
|
||||
|
||||
[dependencies.leo-ast]
|
||||
path = "../ast"
|
||||
version = "1.5.3"
|
||||
@ -52,6 +49,9 @@ version = "1.3.0"
|
||||
version = "1.0"
|
||||
features = [ "derive" ]
|
||||
|
||||
[dependencies.smallvec]
|
||||
version = "1.8"
|
||||
|
||||
[dependencies.tracing]
|
||||
version = "0.1"
|
||||
|
||||
|
@ -20,7 +20,7 @@ use leo_errors::{ParserError, Result};
|
||||
|
||||
impl ParserContext<'_> {
|
||||
/// 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();
|
||||
|
||||
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.
|
||||
@ -67,7 +67,7 @@ impl ParserContext<'_> {
|
||||
self.expect(&Token::Colon)?;
|
||||
let (type_, span) = self.parse_non_ident_types()?;
|
||||
self.expect(&Token::Assign)?;
|
||||
let value = self.parse_primary_expression()?;
|
||||
let value = self.parse_unary_expression()?;
|
||||
self.expect(&Token::Semicolon)?;
|
||||
|
||||
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.
|
||||
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())?);
|
||||
|
||||
tokens.parse_input()
|
||||
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "leo-ast-passes"
|
||||
name = "leo-passes"
|
||||
version = "1.5.3"
|
||||
authors = [ "The Aleo Team <hello@aleo.org>" ]
|
||||
description = "The Leo programming language"
|
||||
@ -39,7 +39,3 @@ version = "1.5.3"
|
||||
[dependencies.leo-span]
|
||||
path = "../../leo/span"
|
||||
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.
|
||||
/* pub mod 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
|
||||
// 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`.
|
||||
pub trait AstPass {
|
||||
fn do_pass(self, ast: Program) -> Result<Ast>;
|
||||
pub trait Pass<'a> {
|
||||
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