mirror of
https://github.com/AleoHQ/leo.git
synced 2024-08-17 10:50:29 +03:00
Refactor ST to account for fact that local records can have program None
This commit is contained in:
parent
0a4e5b3642
commit
c296f61f2f
@ -16,7 +16,7 @@
|
||||
|
||||
use crate::CodeGenerator;
|
||||
|
||||
use leo_ast::{functions, Composite, Function, Mapping, Mode, Program, ProgramScope, Type, Variant};
|
||||
use leo_ast::{Composite, Function, Mapping, Mode, Program, ProgramScope, Type, Variant};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use itertools::Itertools;
|
||||
@ -51,8 +51,21 @@ impl<'a> CodeGenerator<'a> {
|
||||
let order = self.struct_graph.post_order().unwrap();
|
||||
|
||||
// Create a mapping of symbols to references of structs so can perform constant-time lookups.
|
||||
let structs_map: IndexMap<Symbol, &Composite> =
|
||||
program_scope.structs.iter().map(|(name, struct_)| (*name, struct_)).collect();
|
||||
let structs_map: IndexMap<Symbol, &Composite> = self
|
||||
.symbol_table
|
||||
.structs
|
||||
.iter()
|
||||
.filter_map(|(name, struct_)| {
|
||||
// Only include structs and local records.
|
||||
if !(struct_.is_record
|
||||
&& struct_.external.map(|program| program != self.program_id.unwrap().name.name).unwrap_or(false))
|
||||
{
|
||||
Some((name.name, struct_))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Visit each `Struct` or `Record` in the post-ordering and produce an Aleo struct or record.
|
||||
program_string.push_str(
|
||||
@ -60,9 +73,9 @@ impl<'a> CodeGenerator<'a> {
|
||||
.into_iter()
|
||||
.map(|name| {
|
||||
match structs_map.get(&name) {
|
||||
// If the struct is found, it is a local struct.
|
||||
// If the struct is found, it is a struct or external record.
|
||||
Some(struct_) => self.visit_struct_or_record(struct_),
|
||||
// If the struct is not found, it is an imported struct.
|
||||
// If the struct is not found, it is an imported record.
|
||||
None => String::new(),
|
||||
}
|
||||
})
|
||||
@ -169,20 +182,12 @@ impl<'a> CodeGenerator<'a> {
|
||||
let register_string = format!("r{}", self.next_register);
|
||||
self.next_register += 1;
|
||||
|
||||
let type_string = match input {
|
||||
functions::Input::Internal(input) => {
|
||||
self.variable_mapping.insert(&input.identifier.name, register_string.clone());
|
||||
let visibility = match (self.is_transition_function, input.mode) {
|
||||
(true, Mode::None) => Mode::Private,
|
||||
_ => input.mode,
|
||||
};
|
||||
self.visit_type_with_visibility(&input.type_, visibility)
|
||||
}
|
||||
functions::Input::External(input) => {
|
||||
self.variable_mapping.insert(&input.identifier.name, register_string.clone());
|
||||
format!("{}.aleo/{}.record", input.program_name, input.record)
|
||||
}
|
||||
self.variable_mapping.insert(&input.identifier.name, register_string.clone());
|
||||
let visibility = match (self.is_transition_function, input.mode) {
|
||||
(true, Mode::None) => Mode::Private,
|
||||
_ => input.mode,
|
||||
};
|
||||
let type_string = self.visit_type_with_visibility(&input.type_, visibility);
|
||||
|
||||
writeln!(function_string, " input {register_string} as {type_string};",)
|
||||
.expect("failed to write to string");
|
||||
@ -223,23 +228,15 @@ impl<'a> CodeGenerator<'a> {
|
||||
let register_string = format!("r{}", self.next_register);
|
||||
self.next_register += 1;
|
||||
|
||||
// TODO: Dedup code.
|
||||
let type_string = match input {
|
||||
functions::Input::Internal(input) => {
|
||||
self.variable_mapping.insert(&input.identifier.name, register_string.clone());
|
||||
self.variable_mapping.insert(&input.identifier.name, register_string.clone());
|
||||
|
||||
let visibility = match (self.is_transition_function, input.mode) {
|
||||
(true, Mode::None) => Mode::Public,
|
||||
_ => input.mode,
|
||||
};
|
||||
self.visit_type_with_visibility(&input.type_, visibility)
|
||||
}
|
||||
functions::Input::External(input) => {
|
||||
self.variable_mapping.insert(&input.program_name.name, register_string.clone());
|
||||
format!("{}.aleo/{}.record", input.program_name, input.record)
|
||||
}
|
||||
let visibility = match (self.is_transition_function, input.mode) {
|
||||
(true, Mode::None) => Mode::Public,
|
||||
_ => input.mode,
|
||||
};
|
||||
|
||||
let type_string = self.visit_type_with_visibility(&input.type_, visibility);
|
||||
|
||||
writeln!(function_string, " input {register_string} as {type_string};",)
|
||||
.expect("failed to write to string");
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ use leo_ast::{
|
||||
ExpressionStatement,
|
||||
IterationStatement,
|
||||
Mode,
|
||||
Output,
|
||||
ReturnStatement,
|
||||
Statement,
|
||||
};
|
||||
@ -103,38 +102,28 @@ impl<'a> CodeGenerator<'a> {
|
||||
.iter()
|
||||
.zip_eq(output)
|
||||
.map(|(operand, output)| {
|
||||
match output {
|
||||
Output::Internal(output) => {
|
||||
let visibility = if self.is_transition_function {
|
||||
match self.in_finalize {
|
||||
// If in finalize block, the default visibility is public.
|
||||
true => match output.mode {
|
||||
Mode::None => Mode::Public,
|
||||
mode => mode,
|
||||
},
|
||||
// If not in finalize block, the default visibility is private.
|
||||
false => match output.mode {
|
||||
Mode::None => Mode::Private,
|
||||
mode => mode,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
// Only program functions have visibilities associated with their outputs.
|
||||
Mode::None
|
||||
};
|
||||
format!(
|
||||
" output {} as {};\n",
|
||||
operand,
|
||||
self.visit_type_with_visibility(&output.type_, visibility)
|
||||
)
|
||||
let visibility = if self.is_transition_function {
|
||||
match self.in_finalize {
|
||||
// If in finalize block, the default visibility is public.
|
||||
true => match output.mode {
|
||||
Mode::None => Mode::Public,
|
||||
mode => mode,
|
||||
},
|
||||
// If not in finalize block, the default visibility is private.
|
||||
false => match output.mode {
|
||||
Mode::None => Mode::Private,
|
||||
mode => mode,
|
||||
},
|
||||
}
|
||||
Output::External(output) => {
|
||||
format!(
|
||||
" output {} as {}.aleo/{}.record;\n",
|
||||
operand, output.program_name, output.record,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Only program functions have visibilities associated with their outputs.
|
||||
Mode::None
|
||||
};
|
||||
format!(
|
||||
" output {} as {};\n",
|
||||
operand,
|
||||
self.visit_type_with_visibility(&output.type_, visibility)
|
||||
)
|
||||
})
|
||||
.join("");
|
||||
|
||||
|
@ -28,7 +28,7 @@ use std::cell::RefCell;
|
||||
|
||||
use leo_ast::{normalize_json_value, remove_key_from_json, Composite, Function};
|
||||
use leo_errors::{AstError, Result};
|
||||
use leo_span::Span;
|
||||
use leo_span::{Span, Symbol};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -62,11 +62,10 @@ impl SymbolTable {
|
||||
pub fn check_shadowing(&self, location: &Location, span: Span) -> Result<()> {
|
||||
if self.functions.contains_key(location) {
|
||||
return Err(AstError::shadowed_function(location.name, span).into());
|
||||
} else if let Some(existing) = self.structs.get(location) {
|
||||
return match existing.is_record {
|
||||
true => Err(AstError::shadowed_record(location.name, span).into()),
|
||||
false => Err(AstError::shadowed_struct(location.name, span).into()),
|
||||
};
|
||||
} else if self.structs.get(location).is_some() {
|
||||
return Err(AstError::shadowed_record(location.name, span).into());
|
||||
} else if self.structs.get(&Location::new(None, location.name)).is_some() {
|
||||
return Err(AstError::shadowed_struct(location.name, span).into());
|
||||
} else if self.variables.contains_key(location) {
|
||||
return Err(AstError::shadowed_variable(location.name, span).into());
|
||||
}
|
||||
@ -92,13 +91,37 @@ impl SymbolTable {
|
||||
|
||||
/// Inserts a struct into the symbol table.
|
||||
pub fn insert_struct(&mut self, location: Location, insert: &Composite) -> Result<()> {
|
||||
match self.check_shadowing(&location, insert.span) {
|
||||
Ok(_) => {
|
||||
self.structs.insert(location, insert.clone());
|
||||
Ok(())
|
||||
// Check to see if the struct matches an existing one.
|
||||
if let Some(existing) = self.structs.get(&location) {
|
||||
if !self.check_eq_struct(insert, existing) {
|
||||
return Err(AstError::redefining_external_struct(location.name, existing.span).into());
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
return Ok(());
|
||||
} else {
|
||||
// Don't need to check shadowing if the struct is already inserted.
|
||||
self.check_shadowing(&location, insert.span)?;
|
||||
}
|
||||
|
||||
// Insert the struct into the symbol table.
|
||||
self.structs.insert(location, insert.clone());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Checks if two structs are equal.
|
||||
fn check_eq_struct(&self, new: &Composite, old: &Composite) -> bool {
|
||||
if new.is_record || old.is_record {
|
||||
return false;
|
||||
}
|
||||
if new.members.len() != old.members.len() {
|
||||
return false;
|
||||
}
|
||||
for (member1, member2) in new.members.iter().zip(old.members.iter()) {
|
||||
if member1.name() != member2.name() || !member1.type_.eq_flat(&member2.type_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Inserts a variable into the symbol table.
|
||||
@ -131,14 +154,15 @@ impl SymbolTable {
|
||||
}
|
||||
|
||||
/// Attempts to lookup a struct in the symbol table.
|
||||
pub fn lookup_struct(&self, location: Location) -> Option<&Composite> {
|
||||
pub fn lookup_struct(&self, location: Location, main_program: Option<Symbol>) -> Option<&Composite> {
|
||||
if let Some(struct_) = self.structs.get(&location) {
|
||||
Some(struct_)
|
||||
} else if let Some(parent) = self.parent.as_ref() {
|
||||
parent.lookup_struct(location)
|
||||
} else {
|
||||
None
|
||||
return Some(struct_);
|
||||
} else if location.program.is_none() {
|
||||
if let Some(struct_) = self.structs.get(&Location::new(main_program, location.name)) {
|
||||
return Some(struct_);
|
||||
}
|
||||
}
|
||||
if let Some(parent) = self.parent.as_ref() { parent.lookup_struct(location, main_program) } else { None }
|
||||
}
|
||||
|
||||
/// Attempts to lookup a variable in the symbol table.
|
||||
|
@ -14,8 +14,9 @@
|
||||
// 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 indexmap::IndexSet;
|
||||
use leo_ast::*;
|
||||
use leo_errors::emitter::Handler;
|
||||
use leo_errors::{emitter::Handler, AstError, LeoError};
|
||||
use leo_span::Symbol;
|
||||
|
||||
use crate::{Location, SymbolTable, VariableSymbol, VariableType};
|
||||
@ -32,11 +33,13 @@ pub struct SymbolTableCreator<'a> {
|
||||
program_name: Option<Symbol>,
|
||||
/// Whether or not traversing stub.
|
||||
is_stub: bool,
|
||||
/// The set of local structs that have been successfully visited.
|
||||
structs: IndexSet<Symbol>,
|
||||
}
|
||||
|
||||
impl<'a> SymbolTableCreator<'a> {
|
||||
pub fn new(handler: &'a Handler) -> Self {
|
||||
Self { symbol_table: Default::default(), handler, program_name: None, is_stub: false }
|
||||
Self { symbol_table: Default::default(), handler, program_name: None, is_stub: false, structs: IndexSet::new() }
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,7 +68,13 @@ impl<'a> ProgramVisitor<'a> for SymbolTableCreator<'a> {
|
||||
}
|
||||
|
||||
fn visit_struct(&mut self, input: &'a Composite) {
|
||||
if let Err(err) = self.symbol_table.insert_struct(Location::new(self.program_name, input.name()), input) {
|
||||
if !input.is_record {
|
||||
// Forbid redefinition of structs.
|
||||
if !self.structs.insert(input.name()) {
|
||||
return self.handler.emit_err::<LeoError>(AstError::shadowed_struct(input.name(), input.span).into());
|
||||
}
|
||||
}
|
||||
if let Err(err) = self.symbol_table.insert_struct(Location::new(input.external, input.name()), input) {
|
||||
self.handler.emit_err(err);
|
||||
}
|
||||
}
|
||||
@ -115,7 +124,7 @@ impl<'a> ProgramVisitor<'a> for SymbolTableCreator<'a> {
|
||||
}
|
||||
|
||||
fn visit_struct_stub(&mut self, input: &'a Composite) {
|
||||
if let Err(err) = self.symbol_table.insert_struct(Location::new(self.program_name, input.name()), input) {
|
||||
if let Err(err) = self.symbol_table.insert_struct(Location::new(input.external, input.name()), input) {
|
||||
self.handler.emit_err(err);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user