add symbol-table module

This commit is contained in:
collin 2020-11-11 14:57:39 -08:00
parent d4b6a6d1f8
commit 667392237f
39 changed files with 2395 additions and 136 deletions

13
Cargo.lock generated
View File

@ -1312,6 +1312,7 @@ dependencies = [
"leo-input",
"leo-package",
"leo-state",
"leo-symbol-table",
"num-bigint",
"pest",
"rand",
@ -1481,6 +1482,18 @@ dependencies = [
"thiserror",
]
[[package]]
name = "leo-symbol-table"
version = "1.0.4"
dependencies = [
"leo-ast",
"leo-core",
"leo-grammar",
"leo-imports",
"serde",
"thiserror",
]
[[package]]
name = "libc"
version = "0.2.80"

View File

@ -36,7 +36,7 @@ members = [
"linter",
"package",
"state",
# "symbol-table",
"symbol-table",
# "type-inference",
]

View File

@ -49,9 +49,9 @@ version = "1.0.4"
path = "../state"
version = "1.0.4"
#[dependencies.leo-symbol-table]
#path = "../symbol-table"
#version = "1.0.4"
[dependencies.leo-symbol-table]
path = "../symbol-table"
version = "1.0.4"
#[dependencies.leo-type-inference]
#path = "../type-inference"

View File

@ -29,7 +29,7 @@ use leo_imports::ImportParser;
use leo_input::LeoInputParser;
use leo_package::inputs::InputPairs;
use leo_state::verify_local_data_commitment;
// use leo_symbol_table::SymbolTable;
use leo_symbol_table::SymbolTable;
// use leo_type_inference::TypeInference;
use snarkos_dpc::{base_dpc::instantiated::Components, SystemParameters};
@ -205,13 +205,13 @@ impl<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
/// catching type mismatch errors.
///
pub(crate) fn check_program(&self) -> Result<(), CompilerError> {
// // Create a new symbol table from the program, imported_programs, and program_input.
// let _symbol_table =
// SymbolTable::new(&self.program, &self.imported_programs, &self.program_input).map_err(|mut e| {
// e.set_path(&self.main_file_path);
//
// e
// })?;
// Create a new symbol table from the program, imported_programs, and program_input.
let _symbol_table =
SymbolTable::new(&self.program, &self.imported_programs, &self.program_input).map_err(|mut e| {
e.set_path(&self.main_file_path);
e
})?;
// // Run type inference check on program.
// TypeInference::new(&self.program, symbol_table).map_err(|mut e| {
@ -252,8 +252,8 @@ impl<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
// Parse and store all programs imported by the main program file.
self.imported_programs = ImportParser::parse(&self.program)?;
// // Create a new symbol table from the program, imported programs, and program input.
// let _symbol_table = SymbolTable::new(&self.program, &self.imported_programs, &self.program_input)?;
// Create a new symbol table from the program, imported programs, and program input.
let _symbol_table = SymbolTable::new(&self.program, &self.imported_programs, &self.program_input)?;
// // Run type inference check on program.
// TypeInference::new(&self.program, symbol_table)?;

View File

@ -19,7 +19,7 @@ use leo_grammar::ParserError;
use leo_imports::ImportParserError;
use leo_input::InputParserError;
use leo_state::LocalDataVerificationError;
// use leo_symbol_table::SymbolTableError;
use leo_symbol_table::SymbolTableError;
// use leo_type_inference::TypeInferenceError;
use bincode::Error as SerdeError;
@ -68,9 +68,9 @@ pub enum CompilerError {
#[error("{}", _0)]
SerdeError(#[from] SerdeError),
// #[error("{}", _0)]
// SymbolTableError(#[from] SymbolTableError),
#[error("{}", _0)]
SymbolTableError(#[from] SymbolTableError),
// #[error("{}", _0)]
// TypeInferenceError(#[from] TypeInferenceError),
}
@ -81,7 +81,7 @@ impl CompilerError {
CompilerError::InputParserError(error) => error.set_path(path),
CompilerError::FunctionError(error) => error.set_path(path),
CompilerError::OutputStringError(error) => error.set_path(path),
// CompilerError::SymbolTableError(error) => error.set_path(path),
CompilerError::SymbolTableError(error) => error.set_path(path),
// CompilerError::TypeInferenceError(error) => error.set_path(path),
_ => {}
}

View File

@ -1,67 +0,0 @@
// Copyright (C) 2019-2020 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 crate::errors::ImportError;
use leo_ast::{Package, Program};
use std::{collections::HashMap, env::current_dir};
/// Parses all relevant import files for a program.
/// Stores compiled program structs.
#[derive(Clone, Default)]
pub struct ImportParser {
imports: HashMap<String, Program>,
core_packages: Vec<Package>,
}
impl ImportParser {
pub fn new() -> Self {
Self::default()
}
pub(crate) fn insert_import(&mut self, file_name: String, program: Program) {
// todo: handle conflicting versions for duplicate imports here
let _res = self.imports.insert(file_name, program);
}
pub(crate) fn insert_core_package(&mut self, package: &Package) {
let _res = self.core_packages.push(package.clone());
}
pub fn get_import(&self, file_name: &str) -> Option<&Program> {
self.imports.get(file_name)
}
pub fn core_packages(&self) -> &Vec<Package> {
&self.core_packages
}
pub fn parse(program: &Program) -> Result<Self, ImportError> {
let mut imports = Self::new();
// Find all imports relative to current directory
let path = current_dir().map_err(ImportError::current_directory_error)?;
// Parse each imported file
program
.imports
.iter()
.map(|import| imports.parse_package(path.clone(), &import.package))
.collect::<Result<Vec<()>, ImportError>>()?;
Ok(imports)
}
}

View File

@ -17,7 +17,7 @@
use crate::{errors::ImportError, ConstrainedProgram, GroupType};
use leo_ast::ImportStatement;
use leo_imports::ImportParser;
// use leo_symbol_table::imported_symbols::ImportedSymbols;
use leo_symbol_table::imported_symbols::ImportedSymbols;
use snarkos_models::curves::{Field, PrimeField};
@ -37,21 +37,21 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
return Ok(());
}
// // Fetch dependencies for the current import
// let imported_symbols = ImportedSymbols::new(import);
//
// for (name, symbol) in imported_symbols.symbols {
// // Find imported program
// let program = imported_programs
// .get_import(&name)
// .ok_or_else(|| ImportError::unknown_package(import.package.name.clone()))?;
//
// // Parse imported program
// self.store_definitions(program.clone(), imported_programs)?;
//
// // Store the imported symbol
// self.store_symbol(scope, &name, &symbol, program)?;
// }
// Fetch dependencies for the current import
let imported_symbols = ImportedSymbols::new(import);
for (name, symbol) in imported_symbols.symbols {
// Find imported program
let program = imported_programs
.get_import(&name)
.ok_or_else(|| ImportError::unknown_package(import.package.name.clone()))?;
// Parse imported program
self.store_definitions(program.clone(), imported_programs)?;
// Store the imported symbol
self.store_symbol(scope, &name, &symbol, program)?;
}
Ok(())
}

View File

@ -16,39 +16,39 @@
pub mod packages;
use crate::{assert_satisfied, parse_program};
use crate::{assert_satisfied, expect_symbol_table_error, parse_program};
// #[test]
// fn test_core_circuit_invalid() {
// let program_bytes = include_bytes!("core_package_invalid.leo");
// let program = parse_program(program_bytes).err().unwrap();
//
// expect_symbol_table_error(program);
// }
//
// #[test]
// fn test_core_circuit_star_fail() {
// let program_bytes = include_bytes!("core_circuit_star_fail.leo");
// let error = parse_program(program_bytes).err().unwrap();
//
// expect_symbol_table_error(error);
// }
//
// #[test]
// fn test_core_package_invalid() {
// let program_bytes = include_bytes!("core_package_invalid.leo");
// let error = parse_program(program_bytes).err().unwrap();
//
// expect_symbol_table_error(error);
// }
//
// #[test]
// fn test_core_unstable_package_invalid() {
// let program_bytes = include_bytes!("core_unstable_package_invalid.leo");
// let error = parse_program(program_bytes).err().unwrap();
//
// expect_symbol_table_error(error);
// }
#[test]
fn test_core_circuit_invalid() {
let program_bytes = include_bytes!("core_package_invalid.leo");
let program = parse_program(program_bytes).err().unwrap();
expect_symbol_table_error(program);
}
#[test]
fn test_core_circuit_star_fail() {
let program_bytes = include_bytes!("core_circuit_star_fail.leo");
let error = parse_program(program_bytes).err().unwrap();
expect_symbol_table_error(error);
}
#[test]
fn test_core_package_invalid() {
let program_bytes = include_bytes!("core_package_invalid.leo");
let error = parse_program(program_bytes).err().unwrap();
expect_symbol_table_error(error);
}
#[test]
fn test_core_unstable_package_invalid() {
let program_bytes = include_bytes!("core_unstable_package_invalid.leo");
let error = parse_program(program_bytes).err().unwrap();
expect_symbol_table_error(error);
}
#[test]
fn test_unstable_blake2s_sanity() {

View File

@ -185,9 +185,9 @@ pub(crate) fn expect_compiler_error(program: EdwardsTestCompiler) -> CompilerErr
// assert!(matches!(error, CompilerError::TypeInferenceError(_)))
// }
// pub(crate) fn expect_symbol_table_error(error: CompilerError) {
// assert!(matches!(error, CompilerError::SymbolTableError(_)))
// }
pub(crate) fn expect_symbol_table_error(error: CompilerError) {
assert!(matches!(error, CompilerError::SymbolTableError(_)))
}
pub(crate) fn generate_main_input(input: Vec<(&str, Option<InputValue>)>) -> MainInput {
let mut main_input = MainInput::new();

40
symbol-table/Cargo.toml Normal file
View File

@ -0,0 +1,40 @@
[package]
name = "leo-symbol-table"
version = "1.0.4"
authors = [ "The Aleo Team <hello@aleo.org>" ]
description = "Stores user-defined variables during type resolution"
homepage = "https://aleo.org"
repository = "https://github.com/AleoHQ/leo"
keywords = [
"aleo",
"cryptography",
"leo",
"programming-language",
"zero-knowledge"
]
categories = [ "cryptography::croptocurrencies", "web-programming" ]
include = [ "Cargo.toml", "src", "README.md", "LICENSE.md" ]
license = "GPL-3.0"
edition = "2018"
[dependencies.leo-ast]
path = "../ast"
version = "1.0.4"
[dependencies.leo-core]
path = "../core"
version = "1.0.4"
[dependencies.leo-grammar]
path = "../grammar"
version = "1.0.4"
[dependencies.leo-imports]
path = "../imports"
version = "1.0.4"
[dependencies.serde]
version = "1.0"
[dependencies.thiserror]
version = "1.0"

View File

@ -0,0 +1,24 @@
// Copyright (C) 2019-2020 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 serde::{Deserialize, Serialize};
/// Indicates that a program variable has additional functionality.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Attribute {
Mutable,
Static,
}

View File

@ -0,0 +1,18 @@
// Copyright (C) 2019-2020 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 attribute;
pub use self::attribute::*;

View File

@ -0,0 +1,21 @@
// Copyright (C) 2019-2020 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 symbol_table;
pub use self::symbol_table::*;
pub mod type_;
pub use self::type_::*;

View File

@ -0,0 +1,100 @@
// Copyright (C) 2019-2020 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 crate::{TypeError, UserDefinedType};
use leo_ast::{Error as FormattedError, ImportSymbol, Program, Span};
use leo_core::{CorePackageListError, LeoCorePackageError};
use std::path::Path;
/// Errors encountered when tracking variable, function, and circuit names in a program.
#[derive(Debug, Error)]
pub enum SymbolTableError {
#[error("{}", _0)]
CorePackageListError(#[from] CorePackageListError),
#[error("{}", _0)]
Error(#[from] FormattedError),
#[error("{}", _0)]
LeoCorePackageError(#[from] LeoCorePackageError),
#[error("{}", _0)]
TypeError(#[from] TypeError),
}
impl SymbolTableError {
///
/// Sets the filepath for the error stacktrace.
///
pub fn set_path(&mut self, path: &Path) {
match self {
SymbolTableError::CorePackageListError(error) => error.set_path(path),
SymbolTableError::Error(error) => error.set_path(path),
SymbolTableError::LeoCorePackageError(error) => error.set_path(path),
SymbolTableError::TypeError(error) => error.set_path(path),
}
}
///
/// Returns a new formatted error with a given message and span information.
///
fn new_from_span(message: String, span: Span) -> Self {
SymbolTableError::Error(FormattedError::new_from_span(message, span))
}
///
/// Two circuits have been defined with the same name.
///
pub fn duplicate_circuit(variable: UserDefinedType) -> Self {
let message = format!("Duplicate circuit definition found for `{}`", variable.identifier);
Self::new_from_span(message, variable.identifier.span)
}
///
/// Two functions have been defined with the same name.
///
pub fn duplicate_function(variable: UserDefinedType) -> Self {
let message = format!("Duplicate function definition found for `{}`", variable.identifier);
Self::new_from_span(message, variable.identifier.span)
}
///
/// Attempted to access a package name that is not defined.
///
pub fn unknown_package(name: &str, span: &Span) -> Self {
let message = format!(
"Cannot find imported package `{}` in source files or import directory",
name
);
Self::new_from_span(message, span.to_owned())
}
///
/// Attempted to import a name that is not defined in the current file.
///
pub fn unknown_symbol(symbol: &ImportSymbol, program: &Program) -> Self {
let message = format!(
"Cannot find imported symbol `{}` in imported file `{}`",
symbol, program.name
);
Self::new_from_span(message, symbol.span.to_owned())
}
}

View File

@ -0,0 +1,86 @@
// Copyright (C) 2019-2020 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::{Error as FormattedError, Identifier, Span};
use std::path::Path;
/// Errors encountered when resolving types.
#[derive(Debug, Error)]
pub enum TypeError {
#[error("{}", _0)]
Error(#[from] FormattedError),
}
impl TypeError {
///
/// Set the filepath for the error stacktrace.
///
pub fn set_path(&mut self, path: &Path) {
match self {
TypeError::Error(error) => error.set_path(path),
}
}
///
/// Return a new formatted error with a given message and span information.
///
fn new_from_span(message: String, span: Span) -> Self {
TypeError::Error(FormattedError::new_from_span(message, span))
}
///
/// The `Self` keyword was used outside of a circuit.
///
pub fn self_not_available(span: Span) -> Self {
let message = "Type `Self` is only available in circuit definitions and circuit functions.".to_string();
Self::new_from_span(message, span)
}
///
/// Found an unknown circuit name.
///
pub fn undefined_circuit(identifier: Identifier) -> Self {
let message = format!(
"Type circuit `{}` must be defined before it is used in an expression.",
identifier.name
);
Self::new_from_span(message, identifier.span)
}
///
/// Found an unknown circuit member name.
///
pub fn undefined_circuit_member(identifier: Identifier) -> Self {
let message = format!("Circuit has no member `{}`.", identifier.name);
Self::new_from_span(message, identifier.span)
}
///
/// Found an unknown function name.
///
pub fn undefined_function(identifier: Identifier) -> Self {
let message = format!(
"Type function `{}` must be defined before it is used in an expression.",
identifier.name
);
Self::new_from_span(message, identifier.span)
}
}

View File

@ -0,0 +1,57 @@
// Copyright (C) 2019-2020 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::{ImportStatement, ImportSymbol, Package, PackageAccess};
/// Stores the the package file name and imported symbol from an import statement
#[derive(Debug)]
pub struct ImportedSymbols {
pub symbols: Vec<(String, ImportSymbol)>,
}
impl ImportedSymbols {
pub fn new(import: &ImportStatement) -> Self {
let mut imported_symbols = Self::default();
imported_symbols.push_package(&import.package);
imported_symbols
}
fn push_package(&mut self, package: &Package) {
self.push_package_access(package.name.name.clone(), &package.access);
}
fn push_package_access(&mut self, package: String, access: &PackageAccess) {
match access {
PackageAccess::SubPackage(package) => self.push_package(package),
PackageAccess::Star(span) => {
let star = ImportSymbol::star(span);
self.symbols.push((package, star));
}
PackageAccess::Symbol(symbol) => self.symbols.push((package, symbol.clone())),
PackageAccess::Multiple(packages) => packages
.iter()
.for_each(|access| self.push_package_access(package.clone(), access)),
}
}
}
impl Default for ImportedSymbols {
fn default() -> Self {
Self { symbols: Vec::new() }
}
}

View File

@ -0,0 +1,18 @@
// Copyright (C) 2019-2020 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 imported_symbols;
pub use self::imported_symbols::*;

41
symbol-table/src/lib.rs Normal file
View File

@ -0,0 +1,41 @@
// Copyright (C) 2019-2020 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/>.
//! The symbol table for a Leo program.
//!
//! This module contains the [`SymbolTable`] type, an abstract data type that tracks the current
//! bindings for functions and circuits in a Leo program.
//!
//! A new [`Symbol Table`] type can be created from a reference to a [`LeoAst`].
//! A [`Symbol Table`] type can be used to create a new [`TypeInference`] type.
#[macro_use]
extern crate thiserror;
pub mod attributes;
pub use self::attributes::*;
pub mod errors;
pub use self::errors::*;
pub mod imports;
pub use self::imports::*;
pub mod symbol_table;
pub use self::symbol_table::*;
pub mod types;
pub use self::types::*;

View File

@ -0,0 +1,502 @@
// Copyright (C) 2019-2020 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 crate::{CircuitType, CircuitVariableType, FunctionType, ImportedSymbols, SymbolTableError, UserDefinedType};
use leo_ast::{Circuit, Function, Identifier, ImportStatement, ImportSymbol, Input, Package, Program};
use leo_core::CorePackageList;
use leo_imports::ImportParser;
use std::collections::{HashMap, HashSet};
pub const INPUT_VARIABLE_NAME: &str = "input";
pub const RECORD_VARIABLE_NAME: &str = "record";
pub const REGISTERS_VARIABLE_NAME: &str = "registers";
pub const STATE_VARIABLE_NAME: &str = "state";
pub const STATE_LEAF_VARIABLE_NAME: &str = "state_leaf";
/// The symbol table for a Leo program.
///
/// A symbol table has access to all function and circuit names in its parent's symbol table.
/// A symbol table cannot access names in its child's symbol table.
/// A child symbol table cannot access names in another sibling's symbol table.
#[derive(Clone, Default)]
pub struct SymbolTable {
/// Maps name -> parameter type.
names: HashMap<String, UserDefinedType>,
/// Maps circuit name -> circuit type.
circuits: HashMap<String, CircuitType>,
/// Maps function name -> function type.
functions: HashMap<String, FunctionType>,
/// The parent of this symbol table.
parent: Option<Box<SymbolTable>>,
}
impl SymbolTable {
///
/// Returns a new `SymbolTable` from a given, program, imported programs, and program input.
///
/// Checks that each circuit or function name is unique.
/// Unique names are added to a table of name -> user defined type.
///
/// Checks that each circuit or function definition contains valid types.
///
pub fn new(
program: &Program,
import_parser: &ImportParser,
input: &Input,
) -> Result<SymbolTable, SymbolTableError> {
// Create a new symbol table.
let mut table = Self::default();
// Insert input types into symbol table.
table.insert_input(input)?;
// Check for duplicate program and import names.
table.check_names(program, import_parser)?;
// Check for unknown or invalid types.
table.check_types(program)?;
Ok(table)
}
///
/// Insert a function or circuit name into the symbol table from a given name and variable type.
///
/// If the symbol table did not have this name present, `None` is returned.
/// If the symbol table did have this name present, the variable type is updated, and the old
/// variable type is returned.
///
pub fn insert_name(&mut self, name: String, variable_type: UserDefinedType) -> Option<UserDefinedType> {
self.names.insert(name, variable_type)
}
///
/// Insert a circuit name into the symbol table from a given name and variable type.
///
/// Returns an error if the circuit name is a duplicate.
///
pub fn insert_circuit_name(
&mut self,
name: String,
variable_type: UserDefinedType,
) -> Result<(), SymbolTableError> {
// Check that the circuit name is unique.
match self.insert_name(name, variable_type) {
Some(duplicate) => Err(SymbolTableError::duplicate_circuit(duplicate)),
None => Ok(()),
}
}
///
/// Insert a function name into the symbol table from a given name and variable type.
///
/// Returns an error if the function name is a duplicate.
///
pub fn insert_function_name(
&mut self,
name: String,
variable_type: UserDefinedType,
) -> Result<(), SymbolTableError> {
// Check that the circuit name is unique.
match self.insert_name(name, variable_type) {
Some(duplicate) => Err(SymbolTableError::duplicate_function(duplicate)),
None => Ok(()),
}
}
///
/// Insert a circuit definition into the symbol table from a given circuit identifier and
/// circuit type.
///
/// If the symbol table did not have this name present, `None` is returned.
/// If the symbol table did have this name present, the circuit type is updated, and the old
/// circuit type is returned.
///
pub fn insert_circuit_type(&mut self, identifier: Identifier, circuit_type: CircuitType) -> Option<CircuitType> {
self.circuits.insert(identifier.name, circuit_type)
}
///
/// Insert a function definition into the symbol table from a given identifier and
/// function type.
///
/// If the symbol table did not have this name present, `None` is returned.
/// If the symbol table did have this name present, the function type is updated, and the old
/// function type is returned.
///
pub fn insert_function_type(
&mut self,
identifier: Identifier,
function_type: FunctionType,
) -> Option<FunctionType> {
self.functions.insert(identifier.name, function_type)
}
///
/// Returns a reference to the circuit type corresponding to the name.
///
/// If the symbol table did not have this name present, then the parent symbol table is checked.
/// If there is no parent symbol table, then `None` is returned.
///
pub fn get_circuit_type(&self, name: &str) -> Option<&CircuitType> {
// Lookup name in symbol table.
match self.circuits.get(name) {
Some(circuit) => Some(circuit),
None => {
// Lookup name in parent symbol table.
match &self.parent {
Some(parent) => parent.get_circuit_type(name),
None => None,
}
}
}
}
///
/// Returns a reference to the function type corresponding to the name.
///
/// If the symbol table did not have this name present, then the parent symbol table is checked.
/// If there is no parent symbol table, then `None` is returned.
///
pub fn get_function_type(&self, name: &str) -> Option<&FunctionType> {
// Lookup name in symbol table.
match self.functions.get(name) {
Some(circuit) => Some(circuit),
None => {
// Lookup name in parent symbol table
match &self.parent {
Some(parent) => parent.get_function_type(name),
None => None,
}
}
}
}
///
/// Checks for duplicate import, circuit, and function names given a program.
///
/// If a circuit or function name has no duplicates, then it is inserted into the symbol table.
/// Variables defined later in the unresolved program cannot have the same name.
///
pub fn check_names(&mut self, program: &Program, import_parser: &ImportParser) -> Result<(), SymbolTableError> {
// Check unresolved program import names.
self.check_import_names(&program.imports, import_parser)?;
// Check unresolved program circuit names.
self.check_circuit_names(&program.circuits)?;
// Check unresolved program function names.
self.check_function_names(&program.functions)?;
Ok(())
}
///
/// Checks for duplicate circuit names given a hashmap of circuits.
///
/// If a circuit name has no duplicates, then it is inserted into the symbol table.
/// Types defined later in the program cannot have the same name.
///
pub fn check_circuit_names(&mut self, circuits: &HashMap<Identifier, Circuit>) -> Result<(), SymbolTableError> {
// Iterate over circuit names and definitions.
for (identifier, circuit) in circuits.iter() {
// Attempt to insert the circuit name into the symbol table.
self.insert_circuit_name(identifier.to_string(), UserDefinedType::from(circuit.clone()))?;
}
Ok(())
}
///
/// Checks for duplicate function names given a hashmap of functions.
///
/// If a function name has no duplicates, then it is inserted into the symbol table.
/// Types defined later in the program cannot have the same name.
///
pub fn check_function_names(&mut self, functions: &HashMap<Identifier, Function>) -> Result<(), SymbolTableError> {
// Iterate over function names and definitions.
for (identifier, function) in functions.iter() {
// Attempt to insert the function name into the symbol table.
self.insert_function_name(identifier.to_string(), UserDefinedType::from(function.clone()))?;
}
Ok(())
}
///
/// Checks that all given imported names exist in the list of imported programs.
///
/// Additionally checks for duplicate imported names in the given vector of imports.
/// Types defined later in the program cannot have the same name.
///
pub fn check_import_names(
&mut self,
imports: &[ImportStatement],
import_parser: &ImportParser,
) -> Result<(), SymbolTableError> {
// Iterate over imported names.
for import in imports {
self.check_import_statement(import, import_parser)?;
}
Ok(())
}
///
/// Checks that a given import statement imports an existing package.
///
/// Additionally checks for duplicate imported names in the given vector of imports.
/// Types defined later in the program cannot have the same name.
///
pub fn check_import_statement(
&mut self,
import: &ImportStatement,
import_parser: &ImportParser,
) -> Result<(), SymbolTableError> {
// Check if the import name exists as core package.
let core_package = import_parser.get_core_package(&import.package);
// If the core package exists, then attempt to insert the import into the symbol table.
if let Some(package) = core_package {
return self.check_core_package(package);
}
// Attempt to insert the imported names into the symbol table.
self.check_package(import, import_parser)
}
///
/// Inserts imported core package circuit names and types into the symbol table.
///
/// Checks that the core package and all circuit names exist. Checks that imported circuit types
/// only contain known types.
///
pub fn check_core_package(&mut self, package: &Package) -> Result<(), SymbolTableError> {
// Create list of imported core packages.
let list = CorePackageList::from_package_access(package.access.to_owned())?;
// Fetch core package symbols from `leo-core`.
let symbol_list = list.to_symbols()?;
// Insert name and type information for each core package symbol.
for (name, circuit) in symbol_list.symbols() {
// Store name of symbol.
self.insert_circuit_name(name.to_string(), UserDefinedType::from(circuit.clone()))?;
// Create new circuit type for symbol.
let circuit_type = CircuitType::new(&self, circuit.to_owned())?;
// Insert circuit type of symbol.
self.insert_circuit_type(circuit_type.identifier.clone(), circuit_type);
}
Ok(())
}
///
/// Inserts one or more imported symbols for a given imported package.
///
/// Checks that the package and all circuit and function names exist. Checks that imported circuit
/// and function types only contain known types.
///
pub fn check_package(
&mut self,
import: &ImportStatement,
import_parser: &ImportParser,
) -> Result<(), SymbolTableError> {
// Get imported symbols from statement.
let imported_symbols = ImportedSymbols::new(import);
// Import all symbols from an imported file for now.
// Keep track of which import files have already been checked.
let mut checked = HashSet::new();
// Iterate over each imported symbol.
for (name, symbol) in imported_symbols.symbols {
// Find the imported program.
let program = import_parser
.get_import(&name)
.ok_or_else(|| SymbolTableError::unknown_package(&name, &symbol.span))?;
// Push the imported file's name to checked import files.
if !checked.insert(name) {
// Skip the imported symbol if we have already checked the file.
continue;
};
// Check the imported program for duplicate types.
self.check_names(program, import_parser)?;
// Check the imported program for undefined types.
self.check_types(program)?;
// Store the imported symbol.
// self.insert_import_symbol(symbol, program)?; // TODO (collinc97) uncomment this line when public/private import scopes are implemented.
}
Ok(())
}
///
/// Inserts the imported symbol into the symbol table if it is present in the given program.
///
pub fn insert_import_symbol(&mut self, symbol: ImportSymbol, program: &Program) -> Result<(), SymbolTableError> {
// Check for import *.
if symbol.is_star() {
// Insert all program circuits.
self.check_circuit_names(&program.circuits)?;
// Insert all program functions.
self.check_function_names(&program.functions)
} else {
// Check for a symbol alias.
let identifier = symbol.alias.to_owned().unwrap_or_else(|| symbol.symbol.to_owned());
// Check if the imported symbol is a circuit
match program.circuits.get(&symbol.symbol) {
Some(circuit) => {
// Insert imported circuit.
self.insert_circuit_name(identifier.to_string(), UserDefinedType::from(circuit.to_owned()))
}
None => {
// Check if the imported symbol is a function.
match program.functions.get(&symbol.symbol) {
Some(function) => {
// Insert the imported function.
self.insert_function_name(
identifier.to_string(),
UserDefinedType::from(function.to_owned()),
)
}
None => Err(SymbolTableError::unknown_symbol(&symbol, program)),
}
}
}
}
}
///
/// Checks for unknown types in circuit and function definitions given an unresolved program.
///
/// If a circuit or function definition only contains known types, then it is inserted into the
/// symbol table. Variables defined later in the unresolved program can lookup the definition and
/// refer to its expected types.
///
pub fn check_types(&mut self, program: &Program) -> Result<(), SymbolTableError> {
// Check unresolved program circuit definitions.
self.check_types_circuits(&program.circuits)?;
// Check unresolved program function definitions.
self.check_types_functions(&program.functions)?;
Ok(())
}
///
/// Checks for unknown types in a circuit given a hashmap of circuits.
///
/// If a circuit definition only contains known types, then it is inserted into the
/// symbol table. Variables defined later in the program can lookup the definition
/// and refer to its expected types
///
pub fn check_types_circuits(&mut self, circuits: &HashMap<Identifier, Circuit>) -> Result<(), SymbolTableError> {
// Iterate over circuit names and definitions.
for circuit in circuits.values() {
// Get the identifier of the circuit.
let identifier = circuit.circuit_name.clone();
// Resolve unknown types in the circuit definition.
let circuit_type = CircuitType::new(self, circuit.clone())?;
// Attempt to insert the circuit definition into the symbol table.
self.insert_circuit_type(identifier, circuit_type);
}
Ok(())
}
///
/// Checks for unknown types in a function given a hashmap of functions.
///
/// If a function definition only contains known types, then it is inserted into the
/// symbol table. Variables defined later in the program can lookup the definition
/// and refer to its expected types
///
pub fn check_types_functions(&mut self, functions: &HashMap<Identifier, Function>) -> Result<(), SymbolTableError> {
// Iterate over function names and definitions.
for function in functions.values() {
// Get the identifier of the function.
let identifier = function.identifier.clone();
// Resolve unknown types in the function definition.
let function_type = FunctionType::new(&self, function.clone())?;
// Attempt to insert the function definition into the symbol table.
self.insert_function_type(identifier, function_type);
}
Ok(())
}
///
/// Inserts function input types into the symbol table.
///
/// Creates a new `CircuitType` to represent the input values.
/// The new type contains register, record, state, and state leaf circuit variables.
/// This allows easy access to input types using dot syntax: `input.register.r0`.
///
pub fn insert_input(&mut self, input: &Input) -> Result<(), SymbolTableError> {
// Get values for each input section.
let registers_values = input.get_registers().values();
let record_values = input.get_record().values();
let state_values = input.get_state().values();
let state_leaf_values = input.get_state_leaf().values();
// Create a new `CircuitType` for each input section.
let registers_type =
CircuitType::from_input_section(&self, REGISTERS_VARIABLE_NAME.to_string(), registers_values)?;
let record_type = CircuitType::from_input_section(&self, RECORD_VARIABLE_NAME.to_string(), record_values)?;
let state_type = CircuitType::from_input_section(&self, STATE_VARIABLE_NAME.to_string(), state_values)?;
let state_leaf_type =
CircuitType::from_input_section(&self, STATE_LEAF_VARIABLE_NAME.to_string(), state_leaf_values)?;
// Create a new `CircuitVariableType` for each type.
let registers_variable = CircuitVariableType::from(&registers_type);
let record_variable = CircuitVariableType::from(&record_type);
let state_variable = CircuitVariableType::from(&state_type);
let state_leaf_variable = CircuitVariableType::from(&state_leaf_type);
// Create new `CircuitType` for input keyword.
let input_type = CircuitType {
identifier: Identifier::new(INPUT_VARIABLE_NAME.to_string()),
variables: vec![registers_variable, record_variable, state_variable, state_leaf_variable],
functions: Vec::new(),
};
// Insert each circuit type into the symbol table.
self.insert_circuit_type(registers_type.identifier.clone(), registers_type);
self.insert_circuit_type(record_type.identifier.clone(), record_type);
self.insert_circuit_type(state_type.identifier.clone(), state_type);
self.insert_circuit_type(state_leaf_type.identifier.clone(), state_leaf_type);
self.insert_circuit_type(input_type.identifier.clone(), input_type);
Ok(())
}
}

View File

@ -0,0 +1,194 @@
// Copyright (C) 2019-2020 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 crate::{
types::circuits::{CircuitFunctionType, CircuitVariableType},
Attribute,
FunctionType,
SymbolTable,
Type,
TypeError,
};
use leo_ast::{Circuit, CircuitMember, Identifier, InputValue, Parameter, Span};
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
hash::{Hash, Hasher},
};
/// Stores circuit definition details.
///
/// This type should be added to the circuit symbol table for a resolved syntax tree.
/// This is a user-defined type.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CircuitType {
/// The name of the circuit definition.
pub identifier: Identifier,
/// The circuit variables.
pub variables: Vec<CircuitVariableType>,
/// The circuit functions.
pub functions: Vec<CircuitFunctionType>,
}
impl CircuitType {
///
/// Return a new `CircuitType` from a given `Circuit` definition.
///
/// Performs a lookup in the given symbol table if the circuit definition contains
/// user-defined types.
///
pub fn new(table: &SymbolTable, unresolved: Circuit) -> Result<Self, TypeError> {
let circuit_identifier = unresolved.circuit_name;
let mut variables = vec![];
let mut functions = vec![];
// Resolve the type of every circuit member.
for member in unresolved.members {
match member {
CircuitMember::CircuitVariable(is_mutable, variable_identifier, type_) => {
// Resolve the type of the circuit member variable.
let type_ = Type::new_from_circuit(
table,
type_,
circuit_identifier.clone(),
circuit_identifier.span.clone(),
)?;
// Check if the circuit member variable is mutable.
let attribute = if is_mutable { Some(Attribute::Mutable) } else { None };
// Create a new circuit variable type.
let variable = CircuitVariableType {
identifier: variable_identifier,
type_,
attribute,
};
// Store the circuit variable type.
variables.push(variable);
}
CircuitMember::CircuitFunction(is_static, function) => {
// Resolve the type of the circuit member function.
let function_type = FunctionType::from_circuit(table, circuit_identifier.clone(), function)?;
// Check if the circuit member function is static.
let attribute = if is_static { Some(Attribute::Static) } else { None };
// Create a new circuit function type.
let function = CircuitFunctionType {
function: function_type,
attribute,
};
// Store the circuit function type.
functions.push(function);
}
}
}
// Return a new circuit type.
Ok(CircuitType {
identifier: circuit_identifier,
variables,
functions,
})
}
///
/// Returns the function type of a circuit member given an identifier.
///
pub fn member_function_type(&self, identifier: &Identifier) -> Option<&CircuitFunctionType> {
self.functions
.iter()
.find(|function| function.function.identifier.eq(identifier))
}
///
/// Returns the type of a circuit member.
///
/// If the member is a circuit variable, then the type of the variable is returned.
/// If the member is a circuit function, then the return type of the function is returned.
///
pub fn member_type(&self, identifier: &Identifier) -> Result<Type, TypeError> {
// Check if the circuit member is a circuit variable.
let matched_variable = self
.variables
.iter()
.find(|variable| variable.identifier.eq(identifier));
match matched_variable {
Some(variable) => Ok(variable.type_.to_owned()),
None => {
// Check if the circuit member is a circuit function.
let matched_function = self.member_function_type(identifier);
match matched_function {
Some(function) => Ok(Type::Function(function.function.identifier.to_owned())),
None => Err(TypeError::undefined_circuit_member(identifier.clone())),
}
}
}
}
///
/// Returns a new `CircuitType` from a given `Input` struct.
///
pub fn from_input_section(
table: &SymbolTable,
name: String,
section: HashMap<Parameter, Option<InputValue>>,
) -> Result<Self, TypeError> {
// Create a new `CircuitVariableType` for each section pair.
let mut variables = Vec::new();
for (parameter, _option) in section.into_iter() {
let variable = CircuitVariableType {
identifier: parameter.variable,
type_: Type::new(table, parameter.type_, Span::default())?,
attribute: None,
};
variables.push(variable);
}
// Create a new `Identifier` for the input section.
let identifier = Identifier::new(name);
// Return a new `CircuitType` with the given name.
Ok(Self {
identifier,
variables,
functions: Vec::new(),
})
}
}
impl PartialEq for CircuitType {
fn eq(&self, other: &Self) -> bool {
self.identifier.eq(&other.identifier)
}
}
impl Eq for CircuitType {}
impl Hash for CircuitType {
fn hash<H: Hasher>(&self, state: &mut H) {
self.identifier.hash(state);
}
}

View File

@ -0,0 +1,27 @@
// Copyright (C) 2019-2020 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 crate::{types::FunctionType, Attribute};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct CircuitFunctionType {
/// The function signature of the circuit function
pub function: FunctionType,
/// The attributes of the circuit function
pub attribute: Option<Attribute>,
}

View File

@ -0,0 +1,40 @@
// Copyright (C) 2019-2020 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 crate::{Attribute, CircuitType, Type};
use leo_ast::Identifier;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CircuitVariableType {
/// The name of the circuit variable
pub identifier: Identifier,
/// The type of the circuit variable
pub type_: Type,
/// The attribute of the circuit variable
pub attribute: Option<Attribute>,
}
impl From<&CircuitType> for CircuitVariableType {
fn from(type_: &CircuitType) -> Self {
Self {
identifier: type_.identifier.clone(),
type_: Type::Circuit(type_.identifier.clone()),
attribute: None,
}
}
}

View File

@ -0,0 +1,24 @@
// Copyright (C) 2019-2020 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 circuit;
pub use self::circuit::*;
pub mod circuit_function;
pub use self::circuit_function::*;
pub mod circuit_variable;
pub use self::circuit_variable::*;

View File

@ -0,0 +1,136 @@
// Copyright (C) 2019-2020 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 crate::{
types::functions::{FunctionInputType, FunctionOutputType},
SymbolTable,
TypeError,
};
use leo_ast::{Function, Identifier};
use serde::{Deserialize, Serialize};
use std::hash::{Hash, Hasher};
/// Stores function definition details.
///
/// This type should be added to the function symbol table for a resolved syntax tree.
/// This is a user-defined type.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FunctionType {
/// The name of the function definition.
pub identifier: Identifier,
/// The function inputs.
pub inputs: Vec<FunctionInputType>,
/// The function output.
pub output: FunctionOutputType,
}
impl FunctionType {
///
/// Return a new `FunctionType` from a given `Function` definition.
///
/// Performs a lookup in the given symbol table if the function definition contains
/// user-defined types.
///
pub fn new(table: &SymbolTable, unresolved: Function) -> Result<Self, TypeError> {
let mut inputs_resolved = Vec::with_capacity(unresolved.input.len());
// Type check function inputs
for input in unresolved.input {
let input = FunctionInputType::new(table, input)?;
inputs_resolved.push(input);
}
// Type check function output
let output = FunctionOutputType::new(table, unresolved.output, unresolved.span)?;
Ok(FunctionType {
identifier: unresolved.identifier,
inputs: inputs_resolved,
output,
})
}
///
/// Return a new `FunctionType` from a given `Function` definition.
///
/// Performs a lookup in the given symbol table if the function definition contains
/// user-defined types.
///
/// If the function definition contains the `Self` keyword, then the given circuit identifier
/// is used as the type.
///
pub fn from_circuit(
table: &SymbolTable,
circuit_name: Identifier,
unresolved_function: Function,
) -> Result<Self, TypeError> {
let function_identifier = unresolved_function.identifier;
let mut inputs = Vec::with_capacity(unresolved_function.input.len());
// Type check function inputs.
for unresolved_input in unresolved_function.input {
let input = FunctionInputType::new_from_circuit(table, unresolved_input, circuit_name.clone())?;
inputs.push(input);
}
// Type check function output.
let output = FunctionOutputType::new_from_circuit(
table,
circuit_name,
unresolved_function.output,
unresolved_function.span,
)?;
Ok(FunctionType {
identifier: function_identifier,
inputs,
output,
})
}
///
/// Resolve a function definition and insert it into the given symbol table.
///
pub fn insert_definition(table: &mut SymbolTable, unresolved_function: Function) -> Result<(), TypeError> {
// Get the identifier of the function.
let function_identifier = unresolved_function.identifier.clone();
// Resolve the function definition into a function type.
let function = Self::new(table, unresolved_function)?;
// Insert (function_identifier -> function_type) as a (key -> value) pair in the symbol table.
table.insert_function_type(function_identifier, function);
Ok(())
}
}
impl PartialEq for FunctionType {
fn eq(&self, other: &Self) -> bool {
self.identifier.eq(&other.identifier)
}
}
impl Eq for FunctionType {}
impl Hash for FunctionType {
fn hash<H: Hasher>(&self, state: &mut H) {
self.identifier.hash(state);
}
}

View File

@ -0,0 +1,90 @@
// Copyright (C) 2019-2020 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 crate::{FunctionInputVariableType, SymbolTable, Type, TypeError};
use leo_ast::{FunctionInput, Identifier};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum FunctionInputType {
InputKeyword(Identifier),
Variable(FunctionInputVariableType),
}
impl FunctionInputType {
///
/// Return the `Identifier` containing name and span information about the current function input.
///
pub fn identifier(&self) -> &Identifier {
match self {
FunctionInputType::InputKeyword(identifier) => identifier,
FunctionInputType::Variable(variable) => &variable.identifier,
}
}
///
/// Return the `Type` of the current function input.
///
pub fn type_(&self) -> Type {
match self {
FunctionInputType::InputKeyword(identifier) => Type::Circuit(identifier.to_owned()),
FunctionInputType::Variable(variable) => variable.type_.to_owned(),
}
}
///
/// Return a new `FunctionInputType` from a given `FunctionInput`.
///
/// Performs a lookup in the given symbol table if the function input contains
/// user-defined types.
///
pub fn new(table: &SymbolTable, unresolved: FunctionInput) -> Result<Self, TypeError> {
Ok(match unresolved {
FunctionInput::InputKeyword(identifier) => FunctionInputType::InputKeyword(identifier),
FunctionInput::Variable(variable) => {
let variable_resolved = FunctionInputVariableType::new(table, variable)?;
FunctionInputType::Variable(variable_resolved)
}
})
}
///
/// Return a new `FunctionInputType` from a given `FunctionInput`.
///
/// Performs a lookup in the given symbol table if the function input contains
/// user-defined types.
///
/// If the type of the function input is the `Self` keyword, then the given circuit identifier
/// is used as the type.
///
pub fn new_from_circuit(
table: &SymbolTable,
unresolved: FunctionInput,
circuit_name: Identifier,
) -> Result<Self, TypeError> {
Ok(match unresolved {
FunctionInput::InputKeyword(identifier) => FunctionInputType::InputKeyword(identifier),
FunctionInput::Variable(unresolved_function_input) => {
let function_input =
FunctionInputVariableType::new_from_circuit(table, unresolved_function_input, circuit_name)?;
FunctionInputType::Variable(function_input)
}
})
}
}

View File

@ -0,0 +1,119 @@
// Copyright (C) 2019-2020 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 crate::{Attribute, SymbolTable, Type, TypeError, UserDefinedType};
use leo_ast::{FunctionInputVariable, Identifier, Span};
use serde::{Deserialize, Serialize};
use std::hash::{Hash, Hasher};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FunctionInputVariableType {
/// Name of function input.
pub identifier: Identifier,
/// Type of function input.
pub type_: Type,
/// The attributes of the function input.
pub attribute: Option<Attribute>,
/// The span of the function input.
pub span: Span,
}
impl FunctionInputVariableType {
///
/// Return a new `FunctionInputVariableType` from a given `FunctionInputVariable`.
///
/// Performs a lookup in the given symbol table if the type is user-defined.
///
pub fn new(table: &SymbolTable, unresolved: FunctionInputVariable) -> Result<Self, TypeError> {
let type_ = Type::new(table, unresolved.type_, unresolved.span.clone())?;
let attribute = if unresolved.mutable {
Some(Attribute::Mutable)
} else {
None
};
Ok(FunctionInputVariableType {
identifier: unresolved.identifier,
type_,
attribute,
span: unresolved.span,
})
}
///
/// Return a new `FunctionInputVariableType` from a given `FunctionInputVariable`.
///
/// Performs a lookup in the given symbol table if the type is user-defined.
///
/// If the type of the function return type is the `Self` keyword, then the given circuit
/// identifier is used as the type.
///
pub fn new_from_circuit(
table: &SymbolTable,
unresolved_function_input: FunctionInputVariable,
circuit_name: Identifier,
) -> Result<Self, TypeError> {
let type_ = Type::new_from_circuit(
table,
unresolved_function_input.type_,
circuit_name,
unresolved_function_input.span.clone(),
)?;
let attribute = if unresolved_function_input.mutable {
Some(Attribute::Mutable)
} else {
None
};
Ok(FunctionInputVariableType {
identifier: unresolved_function_input.identifier,
type_,
attribute,
span: unresolved_function_input.span,
})
}
///
/// Insert the current function input variable type into the given symbol table.
///
/// If the symbol table did not have this name present, `None` is returned.
///
pub fn insert(&self, table: &mut SymbolTable) -> Option<UserDefinedType> {
let key = self.identifier.name.clone();
let value = UserDefinedType::from(self.clone());
table.insert_name(key, value)
}
}
impl PartialEq for FunctionInputVariableType {
fn eq(&self, other: &Self) -> bool {
self.identifier.eq(&other.identifier)
}
}
impl Eq for FunctionInputVariableType {}
impl Hash for FunctionInputVariableType {
fn hash<H: Hasher>(&self, state: &mut H) {
self.identifier.hash(state)
}
}

View File

@ -0,0 +1,69 @@
// Copyright (C) 2019-2020 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 crate::{SymbolTable, Type, TypeError};
use leo_ast::{Identifier, Span, Type as UnresolvedType};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct FunctionOutputType {
/// Type of function output.
pub type_: Type,
}
impl FunctionOutputType {
///
/// Return a new `FunctionOutputType` from a given optional function return type and span.
///
/// Performs a lookup in the given symbol table if the return type is user-defined.
///
pub(crate) fn new(
table: &SymbolTable,
function_output: Option<UnresolvedType>,
span: Span,
) -> Result<Self, TypeError> {
let type_ = match function_output {
None => Type::Tuple(vec![]), // functions with no return value return an empty tuple
Some(type_) => Type::new(table, type_, span)?,
};
Ok(FunctionOutputType { type_ })
}
///
/// Return a new `FunctionOutputType` from a given optional function return type and span.
///
/// Performs a lookup in the given symbol table if the return type is user-defined.
///
/// If the type of the function return type is the `Self` keyword, then the given circuit
/// identifier is used as the type.
///
pub fn new_from_circuit(
table: &SymbolTable,
circuit_name: Identifier,
unresolved: Option<UnresolvedType>,
span: Span,
) -> Result<Self, TypeError> {
let output_type = match unresolved {
None => Type::Tuple(vec![]),
Some(type_) => Type::new_from_circuit(table, type_, circuit_name, span)?,
};
Ok(FunctionOutputType { type_: output_type })
}
}

View File

@ -0,0 +1,27 @@
// Copyright (C) 2019-2020 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 function;
pub use self::function::*;
pub mod function_input;
pub use self::function_input::*;
pub mod function_input_variable;
pub use self::function_input_variable::*;
pub mod function_output;
pub use self::function_output::*;

View File

@ -0,0 +1,30 @@
// Copyright (C) 2019-2020 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 circuits;
pub use self::circuits::*;
pub mod functions;
pub use self::functions::*;
pub mod type_;
pub use self::type_::*;
pub mod type_variable;
pub use self::type_variable::*;
pub mod user_defined;
pub use self::user_defined::*;

View File

@ -0,0 +1,265 @@
// Copyright (C) 2019-2020 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 crate::{SymbolTable, TypeError, TypeVariable};
use leo_ast::{Identifier, IntegerType, Span, Type as UnresolvedType};
use serde::{Deserialize, Serialize};
use std::{
cmp::{Eq, PartialEq},
fmt,
};
/// A type in a Leo program.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Type {
// Data types
Address,
Boolean,
Field,
Group,
IntegerType(IntegerType),
// Data type wrappers
Array(Box<Type>),
Tuple(Vec<Type>),
// User defined types
Circuit(Identifier),
Function(Identifier),
// Unknown type variables
TypeVariable(TypeVariable),
}
impl Type {
///
/// Return a new type from the given unresolved type.
///
/// Performs a lookup in the given symbol table if the type is user-defined.
///
pub fn new(table: &SymbolTable, type_: UnresolvedType, span: Span) -> Result<Self, TypeError> {
Ok(match type_ {
UnresolvedType::Address => Type::Address,
UnresolvedType::Boolean => Type::Boolean,
UnresolvedType::Field => Type::Field,
UnresolvedType::Group => Type::Group,
UnresolvedType::IntegerType(integer) => Type::IntegerType(integer),
UnresolvedType::Array(type_, _) => {
let array_type = Type::new(table, *type_, span)?;
Type::Array(Box::new(array_type))
}
UnresolvedType::Tuple(types) => {
let tuple_types = types
.into_iter()
.map(|type_| Type::new(table, type_, span.clone()))
.collect::<Result<Vec<_>, _>>()?;
Type::Tuple(tuple_types)
}
UnresolvedType::Circuit(identifier) => {
// Lookup the circuit type in the symbol table
let circuit_type = table
.get_circuit_type(&identifier.name)
.ok_or_else(|| TypeError::undefined_circuit(identifier))?;
Type::Circuit(circuit_type.identifier.clone())
}
UnresolvedType::SelfType => {
// Throw an error for using `Self` outside of a circuit
return Err(TypeError::self_not_available(span));
}
})
}
///
/// Return a new type from the given unresolved type.
///
/// If this type is SelfType, return the circuit's type.
///
pub fn new_from_circuit(
table: &SymbolTable,
type_: UnresolvedType,
circuit_name: Identifier,
span: Span,
) -> Result<Self, TypeError> {
Ok(match type_ {
UnresolvedType::Array(type_, _) => {
let array_type = Type::new_from_circuit(table, *type_, circuit_name, span)?;
Type::Array(Box::new(array_type))
}
UnresolvedType::Tuple(types) => {
let tuple_types = types
.into_iter()
.map(|type_| Type::new_from_circuit(table, type_, circuit_name.clone(), span.clone()))
.collect::<Result<Vec<_>, _>>()?;
Type::Tuple(tuple_types)
}
UnresolvedType::SelfType => Type::Circuit(circuit_name),
// The unresolved type does not depend on the current circuit definition
unresolved => Type::new(table, unresolved, span)?,
})
}
/// Returns a list of signed integer types.
pub const fn signed_integer_types() -> [Type; 5] {
[
Type::IntegerType(IntegerType::I8),
Type::IntegerType(IntegerType::I16),
Type::IntegerType(IntegerType::I32),
Type::IntegerType(IntegerType::I64),
Type::IntegerType(IntegerType::I128),
]
}
/// Returns a list of unsigned integer types.
pub const fn unsigned_integer_types() -> [Type; 5] {
[
Type::IntegerType(IntegerType::U8),
Type::IntegerType(IntegerType::U16),
Type::IntegerType(IntegerType::U32),
Type::IntegerType(IntegerType::U64),
Type::IntegerType(IntegerType::U128),
]
}
/// Returns a list of positive integer types.
pub fn negative_integer_types() -> Vec<Type> {
let field_group = [Type::Field, Type::Group];
let mut types = Vec::new();
types.extend_from_slice(&field_group);
types.extend_from_slice(&Self::signed_integer_types());
types
}
/// Returns a list of integer types.
pub fn integer_types() -> Vec<Type> {
let mut types = Vec::new();
types.extend_from_slice(&Self::unsigned_integer_types());
types.extend_from_slice(&Self::negative_integer_types());
types
}
/// Returns a list of possible index types (u8, u16, u32).
pub fn index_types() -> Vec<Type> {
let index_types = [
Type::IntegerType(IntegerType::U8),
Type::IntegerType(IntegerType::U16),
Type::IntegerType(IntegerType::U32),
];
let mut types = Vec::new();
types.extend_from_slice(&index_types);
types
}
///
/// Replaces self with the given type if self is equal to the given `TypeVariable`.
///
pub fn substitute(&mut self, variable: &TypeVariable, type_: &Type) {
match self {
Type::TypeVariable(self_variable) => {
if self_variable == variable {
*self = type_.to_owned()
}
}
Type::Array(self_type) => {
self_type.substitute(variable, type_);
}
Type::Tuple(types) => types
.iter_mut()
.for_each(|tuple_type| tuple_type.substitute(variable, type_)),
_ => {}
}
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self {
Type::Address => write!(f, "address"),
Type::Boolean => write!(f, "bool"),
Type::Field => write!(f, "field"),
Type::Group => write!(f, "group"),
Type::IntegerType(integer_type) => write!(f, "{}", integer_type),
Type::Array(type_) => write!(f, "[{}]", *type_),
Type::Tuple(tuple) => {
let tuple_string = tuple.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", ");
write!(f, "({})", tuple_string)
}
Type::Circuit(identifier) => write!(f, "circuit {}", identifier),
Type::Function(identifier) => write!(f, "function {}", identifier),
Type::TypeVariable(type_variable) => write!(f, "{}", type_variable),
}
}
}
impl PartialEq for Type {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Type::Address, Type::Address) => true,
(Type::Boolean, Type::Boolean) => true,
(Type::Field, Type::Field) => true,
(Type::Group, Type::Group) => true,
(Type::IntegerType(integer_type1), Type::IntegerType(integer_type2)) => integer_type1.eq(integer_type2),
(Type::Array(array1), Type::Array(array2)) => {
// Get both array element types before comparison.
let array1_element = get_array_element_type(array1);
let array2_element = get_array_element_type(array2);
// Check that both arrays have the same element type.
array1_element.eq(array2_element)
}
(Type::Tuple(types1), Type::Tuple(types2)) => types1.eq(types2),
(Type::Circuit(identifier1), Type::Circuit(identifier2)) => identifier1.eq(identifier2),
(Type::Function(identifier1), Type::Function(identifier2)) => identifier1.eq(identifier2),
(Type::TypeVariable(variable1), Type::TypeVariable(variable2)) => variable1.eq(variable2),
_ => false,
}
}
}
impl Eq for Type {}
///
/// Returns the data type of the array element.
///
/// If the given `type_` is an array, call `get_array_element_type()` on the array element type.
/// If the given `type_` is any other type, return the `type_`.
///
pub fn get_array_element_type(type_: &Type) -> &Type {
if let Type::Array(element_type) = type_ {
get_array_element_type(element_type)
} else {
type_
}
}

View File

@ -0,0 +1,46 @@
// Copyright (C) 2019-2020 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::Identifier;
use serde::{Deserialize, Serialize};
use std::fmt;
/// An unknown type in a Leo program.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TypeVariable {
identifier: Identifier,
}
impl fmt::Display for TypeVariable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.identifier)
}
}
impl From<Identifier> for TypeVariable {
fn from(identifier: Identifier) -> Self {
Self { identifier }
}
}
/// Compare the type variable `Identifier` and `Span`.
impl PartialEq for TypeVariable {
fn eq(&self, other: &Self) -> bool {
self.identifier.name.eq(&other.identifier.name) || self.identifier.span.eq(&other.identifier.span)
}
}
impl Eq for TypeVariable {}

View File

@ -0,0 +1,18 @@
// Copyright (C) 2019-2020 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 user_defined_type;
pub use self::user_defined_type::*;

View File

@ -0,0 +1,86 @@
// Copyright (C) 2019-2020 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 crate::{Attribute, FunctionInputVariableType, Type};
use leo_ast::{Circuit, Function, Identifier};
use std::{
fmt,
hash::{Hash, Hasher},
};
/// Stores information for a user defined type.
///
/// User defined types include circuits and functions in a Leo program.
#[derive(Clone, Debug)]
pub struct UserDefinedType {
pub identifier: Identifier,
pub type_: Type,
pub attribute: Option<Attribute>,
}
impl From<Circuit> for UserDefinedType {
fn from(value: Circuit) -> Self {
let identifier = value.circuit_name;
UserDefinedType {
identifier: identifier.clone(),
type_: Type::Circuit(identifier),
attribute: None,
}
}
}
impl From<Function> for UserDefinedType {
fn from(value: Function) -> Self {
let identifier = value.identifier;
UserDefinedType {
identifier: identifier.clone(),
type_: Type::Function(identifier),
attribute: None,
}
}
}
impl From<FunctionInputVariableType> for UserDefinedType {
fn from(value: FunctionInputVariableType) -> Self {
UserDefinedType {
identifier: value.identifier,
type_: value.type_,
attribute: value.attribute,
}
}
}
impl fmt::Display for UserDefinedType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.identifier)
}
}
impl PartialEq for UserDefinedType {
fn eq(&self, other: &Self) -> bool {
self.identifier.eq(&other.identifier)
}
}
impl Eq for UserDefinedType {}
impl Hash for UserDefinedType {
fn hash<H: Hasher>(&self, state: &mut H) {
self.identifier.hash(state);
}
}

122
symbol-table/tests/mod.rs Normal file
View File

@ -0,0 +1,122 @@
// Copyright (C) 2019-2020 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 symbol_table;
use leo_ast::{Input, LeoAst};
use leo_grammar::Grammar;
use leo_symbol_table::{SymbolTable, SymbolTableError};
use leo_imports::ImportParser;
use std::path::PathBuf;
const TEST_PROGRAM_PATH: &str = "";
/// A helper struct to test a `SymbolTable`.
pub struct TestSymbolTable {
ast: LeoAst,
}
impl TestSymbolTable {
///
/// Returns a Leo syntax tree given a Leo program.
///
pub fn new(bytes: &[u8]) -> Self {
// Get file string from bytes.
let file_string = String::from_utf8_lossy(bytes);
// Get test file path.
let file_path = PathBuf::from(TEST_PROGRAM_PATH);
// Get parser syntax tree
let grammar = Grammar::new(&file_path, &*file_string).unwrap();
// Get Leo syntax tree
let ast = LeoAst::new(TEST_PROGRAM_PATH, &grammar);
Self { ast }
}
///
/// Parse the Leo syntax tree into a symbol table.
///
/// Expect no errors during parsing.
///
pub fn expect_success(self) {
// Get program.
let program = self.ast.into_repr();
// Create empty import parser.
let import_parser = ImportParser::default();
// Create empty input.
let input = Input::new();
// Create new symbol table.
let _symbol_table = SymbolTable::new(&program, &import_parser, &input).unwrap();
}
///
/// Parse the Leo syntax tree into a symbol table.
///
/// Expect an error involving entries in the symbol table.
///
pub fn expect_pass_one_error(self) {
// Get program.
let program = self.ast.into_repr();
// Create new symbol table.
let static_check = &mut SymbolTable::default();
// Create empty import parser.
let import_parser = ImportParser::default();
// Run pass one and expect an error.
let error = static_check.check_names(&program, &import_parser).unwrap_err();
match error {
SymbolTableError::Error(_) => {} // Ok
error => panic!("Expected a symbol table error found `{}`", error),
}
}
///
/// Parse the Leo syntax tree into a symbol table.
///
/// Expect an error involving types in the symbol table.
///
pub fn expect_pass_two_error(self) {
// Get program.
let program = self.ast.into_repr();
// Create a new symbol table.
let static_check = &mut SymbolTable::default();
// Create empty import parser.
let import_parser = ImportParser::default();
// Run the pass one and expect no errors.
static_check.check_names(&program, &import_parser).unwrap();
// Run the pass two and expect and error.
let error = static_check.check_types(&program).unwrap_err();
match error {
SymbolTableError::TypeError(_) => {} //Ok
error => panic!("Expected a type error found `{}`", error),
}
}
}

View File

@ -0,0 +1,10 @@
///
/// Defines a circuit `Foo {}`.
/// Attempts to define a second circuit `Foo {}`.
///
/// Expected output: SymbolTableError
/// Message: "Duplicate circuit definition found for `Foo`."
///
circuit Foo {}
circuit Foo {}

View File

@ -0,0 +1,10 @@
///
/// Defines a function `main() {}`.
/// Attempts to define a second function `main() {}`.
///
/// Expected output: SymbolTableError
/// Message: "Duplicate function definition found for `main`."
///
function main() {}
function main() {}

View File

@ -0,0 +1,75 @@
// Copyright (C) 2019-2020 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 crate::TestSymbolTable;
///
/// Defines a circuit `Foo {}`.
/// Attempts to define a second circuit `Foo {}`.
///
/// Expected output: SymbolTableError
/// Message: "Duplicate circuit definition found for `Foo`."
///
#[test]
fn test_duplicate_circuit() {
let program_bytes = include_bytes!("duplicate_circuit.leo");
let resolver = TestSymbolTable::new(program_bytes);
resolver.expect_pass_one_error();
}
///
/// Defines a function `main() {}`.
/// Attempts to define a second function `main() {}`.
///
/// Expected output: SymbolTableError
/// Message: "Duplicate function definition found for `main`."
///
#[test]
fn test_duplicate_function() {
let program_bytes = include_bytes!("duplicate_function.leo");
let resolver = TestSymbolTable::new(program_bytes);
resolver.expect_pass_one_error();
}
///
/// Defines a function that returns `Self`.
///
/// Expected output: TypeError
/// Message: "Type `Self` is only available in circuit definitions and circuit functions."
///
#[test]
fn test_self_not_available() {
let program_bytes = include_bytes!("self_not_available.leo");
let resolver = TestSymbolTable::new(program_bytes);
resolver.expect_pass_two_error();
}
///
/// Defines a circuit with variable whose type is `Bar`, an undefined circuit.
///
/// Expected output: TypeError
/// Message: "Type circuit `Bar` must be defined before it is used in an expression."
///
#[test]
fn test_undefined_circuit() {
let program_bytes = include_bytes!("undefined_circuit.leo");
let resolver = TestSymbolTable::new(program_bytes);
resolver.expect_pass_two_error();
}

View File

@ -0,0 +1,8 @@
///
/// Defines a function that returns `Self`.
///
/// Expected output: TypeError
/// Message: "Type `Self` is only available in circuit definitions and circuit functions."
///
function main() -> Self {}

View File

@ -0,0 +1,10 @@
///
/// Defines a circuit with variable whose type is `Bar`, an undefined circuit.
///
/// Expected output: TypeError
/// Message: "Type circuit `Bar` must be defined before it is used in an expression."
///
circuit Foo {
b: Bar
}