// Copyright (C) 2019-2021 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 .
use crate::{AsgConvertError, Circuit, Function, Input, Type, Variable};
use indexmap::IndexMap;
use std::{cell::RefCell, sync::Arc};
use uuid::Uuid;
/// An abstract data type that track the current bindings for variables, functions, and circuits.
#[derive(Debug)]
pub struct InnerScope {
/// The unique id of the scope.
pub id: Uuid,
/// The parent scope that this scope inherits.
pub parent_scope: Option,
/// The function definition that this scope occurs in.
pub function: Option>,
/// The circuit definition that this scope occurs in.
pub circuit_self: Option>,
/// Maps variable name => variable.
pub variables: IndexMap,
/// Maps function name => function.
pub functions: IndexMap>,
/// Maps circuit name => circuit.
pub circuits: IndexMap>,
/// The main input to the program.
pub input: Option,
}
pub type Scope = Arc>;
impl InnerScope {
///
/// Returns a reference to the variable corresponding to the name.
///
/// If the current scope did not have this name present, then the parent scope is checked.
/// If there is no parent scope, then `None` is returned.
///
pub fn resolve_variable(&self, name: &str) -> Option {
if let Some(resolved) = self.variables.get(name) {
Some(resolved.clone())
} else if let Some(resolved) = self.parent_scope.as_ref() {
if let Some(resolved) = resolved.borrow().resolve_variable(name) {
Some(resolved)
} else {
None
}
} else {
None
}
}
///
/// Returns a reference to the current function.
///
/// If the current scope did not have a function present, then the parent scope is checked.
/// If there is no parent scope, then `None` is returned.
///
pub fn resolve_current_function(&self) -> Option> {
if let Some(resolved) = self.function.as_ref() {
Some(resolved.clone())
} else if let Some(resolved) = self.parent_scope.as_ref() {
if let Some(resolved) = resolved.borrow().resolve_current_function() {
Some(resolved)
} else {
None
}
} else {
None
}
}
///
/// Returns a reference to the current input.
///
/// If the current scope did not have an input present, then the parent scope is checked.
/// If there is no parent scope, then `None` is returned.
///
pub fn resolve_input(&self) -> Option {
if let Some(input) = self.input.as_ref() {
Some(input.clone())
} else if let Some(resolved) = self.parent_scope.as_ref() {
if let Some(resolved) = resolved.borrow().resolve_input() {
Some(resolved)
} else {
None
}
} else {
None
}
}
///
/// Returns a reference to the function corresponding to the name.
///
/// If the current scope did not have this name present, then the parent scope is checked.
/// If there is no parent scope, then `None` is returned.
///
pub fn resolve_function(&self, name: &str) -> Option> {
if let Some(resolved) = self.functions.get(name) {
Some(resolved.clone())
} else if let Some(resolved) = self.parent_scope.as_ref() {
if let Some(resolved) = resolved.borrow().resolve_function(name) {
Some(resolved)
} else {
None
}
} else {
None
}
}
///
/// Returns a reference to the circuit corresponding to the name.
///
/// If the current scope did not have this name present, then the parent scope is checked.
/// If there is no parent scope, then `None` is returned.
///
pub fn resolve_circuit(&self, name: &str) -> Option> {
if let Some(resolved) = self.circuits.get(name) {
Some(resolved.clone())
} else if name == "Self" && self.circuit_self.is_some() {
self.circuit_self.clone()
} else if let Some(resolved) = self.parent_scope.as_ref() {
if let Some(resolved) = resolved.borrow().resolve_circuit(name) {
Some(resolved)
} else {
None
}
} else {
None
}
}
///
/// Returns a reference to the current circuit.
///
/// If the current scope did not have a circuit self present, then the parent scope is checked.
/// If there is no parent scope, then `None` is returned.
///
pub fn resolve_circuit_self(&self) -> Option> {
if let Some(resolved) = self.circuit_self.as_ref() {
Some(resolved.clone())
} else if let Some(resolved) = self.parent_scope.as_ref() {
if let Some(resolved) = resolved.borrow().resolve_circuit_self() {
Some(resolved)
} else {
None
}
} else {
None
}
}
///
/// Returns a new scope given a parent scope.
///
pub fn make_subscope(scope: &Scope) -> Scope {
Arc::new(RefCell::new(InnerScope {
id: Uuid::new_v4(),
parent_scope: Some(scope.clone()),
circuit_self: None,
variables: IndexMap::new(),
functions: IndexMap::new(),
circuits: IndexMap::new(),
function: None,
input: None,
}))
}
///
/// Returns the type returned by the current scope.
///
pub fn resolve_ast_type(&self, type_: &leo_ast::Type) -> Result {
use leo_ast::Type::*;
Ok(match type_ {
Address => Type::Address,
Boolean => Type::Boolean,
Field => Type::Field,
Group => Type::Group,
IntegerType(int_type) => Type::Integer(int_type.clone()),
Array(sub_type, dimensions) => {
let mut item = Box::new(self.resolve_ast_type(&*sub_type)?);
for dimension in dimensions.0.iter().rev() {
let dimension = dimension
.value
.parse::()
.map_err(|_| AsgConvertError::parse_index_error())?;
item = Box::new(Type::Array(item, dimension));
}
*item
}
Tuple(sub_types) => Type::Tuple(
sub_types
.iter()
.map(|x| self.resolve_ast_type(x))
.collect::, AsgConvertError>>()?,
),
Circuit(name) if name.name == "Self" => Type::Circuit(
self.circuit_self
.clone()
.ok_or_else(|| AsgConvertError::unresolved_circuit(&name.name, &name.span))?,
),
Circuit(name) => Type::Circuit(
self.circuits
.get(&name.name)
.ok_or_else(|| AsgConvertError::unresolved_circuit(&name.name, &name.span))?
.clone(),
),
SelfType => Type::Circuit(
self.circuit_self
.clone()
.ok_or_else(AsgConvertError::reference_self_outside_circuit)?,
),
})
}
}