Passing ST serialization test

This commit is contained in:
evan-schott 2024-03-27 10:24:01 -07:00
parent 7a741dbde4
commit 75f3647879
4 changed files with 95 additions and 80 deletions

View File

@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize};
use crate::SymbolTable;
/// Metadata associated with the finalize block.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct FinalizeData {
/// The inputs to the finalize block.
pub(crate) input: Vec<Input>,
@ -31,7 +31,7 @@ pub struct FinalizeData {
}
/// An entry for a function in the symbol table.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct FunctionSymbol {
/// The index associated with the scope in the parent symbol table.
pub(crate) id: usize,

View File

@ -20,13 +20,13 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
// Create custom struct to wrap (Symbol, Symbol) so that it can be serialized and deserialized.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Location {
pub program: Symbol,
pub program: Option<Symbol>,
pub name: Symbol,
}
impl Location {
// Create new Location instance.
pub fn new(program: Symbol, name: Symbol) -> Location {
pub fn new(program: Option<Symbol>, name: Symbol) -> Location {
Location { program, name }
}
}
@ -36,7 +36,11 @@ impl Serialize for Location {
where
S: Serializer,
{
serializer.serialize_str(&format!("{}/{}", self.program, self.name))
let condensed_str = match self.program {
Some(program) => format!("{}/{}", program, self.name),
None => format!("{}", self.name),
};
serializer.serialize_str(&condensed_str)
}
}
@ -46,9 +50,13 @@ impl<'de> Deserialize<'de> for Location {
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let mut parts = s.split('/');
let program = Symbol::intern(parts.next().unwrap());
let name = Symbol::intern(parts.next().unwrap());
let mut parts: Vec<&str> = s.split('/').collect();
let program = if parts.len() == 1 {
None
} else {
Some(Symbol::intern(parts.remove(0)))
};
let name = Symbol::intern(parts.get(0).unwrap());
Ok(Location::new(program, name))
}
}

View File

@ -36,7 +36,7 @@ use serde_json;
// TODO (@d0cd) Consider a safe interface for the symbol table.
// TODO (@d0cd) Cleanup API
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct SymbolTable {
/// The parent scope if it exists.
/// For example, the parent scope of a then-block is the scope containing the associated ConditionalStatement.
@ -49,7 +49,7 @@ pub struct SymbolTable {
pub structs: IndexMap<Location, Composite>,
/// The variables defined in a scope.
/// This field is populated as necessary.
pub(crate) variables: IndexMap<Symbol, VariableSymbol>,
pub(crate) variables: IndexMap<Location, VariableSymbol>,
/// The index of the current scope.
pub(crate) scope_index: usize,
/// The sub-scopes of this scope.
@ -59,21 +59,22 @@ pub struct SymbolTable {
impl SymbolTable {
/// Recursively checks if the symbol table contains an entry for the given symbol.
/// Leo does not allow any variable shadowing or overlap between different symbols.
pub fn check_shadowing(&self, program: Option<Symbol>, symbol: Symbol, span: Span) -> Result<()> {
if let Some(program) = program {
if self.functions.contains_key(&Location::new(program, symbol)) {
return Err(AstError::shadowed_function(symbol, span).into());
} else if let Some(existing) = self.structs.get(&Location::new(program, symbol)) {
pub fn check_shadowing(&self, location: &Location, span: Span) -> Result<()> {
if let Some(_) = location.program {
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(symbol, span).into()),
false => Err(AstError::shadowed_struct(symbol, span).into()),
true => Err(AstError::shadowed_record(location.name, span).into()),
false => Err(AstError::shadowed_struct(location.name, span).into()),
};
}
}
if self.variables.contains_key(&symbol) {
Err(AstError::shadowed_variable(symbol, span).into())
} else if let Some(parent) = self.parent.as_ref() {
parent.check_shadowing(program, symbol, span)
else if self.variables.contains_key(location) {
return Err(AstError::shadowed_variable(location.name, span).into())
}
}
if let Some(parent) = self.parent.as_ref() {
return parent.check_shadowing(location, span)
} else {
Ok(())
}
@ -88,19 +89,19 @@ impl SymbolTable {
}
/// Inserts a function into the symbol table.
pub fn insert_fn(&mut self, program: Symbol, symbol: Symbol, insert: &Function) -> Result<()> {
pub fn insert_fn(&mut self, location: Location, insert: &Function) -> Result<()> {
let id = self.scope_index();
self.check_shadowing(Some(program), symbol, insert.span)?;
self.functions.insert(Location::new(program, symbol), Self::new_function_symbol(id, insert));
self.check_shadowing(&location, insert.span)?;
self.functions.insert(location, Self::new_function_symbol(id, insert));
self.scopes.push(Default::default());
Ok(())
}
/// Inserts a struct into the symbol table.
pub fn insert_struct(&mut self, program: Symbol, symbol: Symbol, insert: &Composite) -> Result<()> {
match self.check_shadowing(Some(program), symbol, insert.span) {
pub fn insert_struct(&mut self, location: Location, insert: &Composite) -> Result<()> {
match self.check_shadowing(&location, insert.span) {
Ok(_) => {
self.structs.insert(Location::new(program, symbol), insert.clone());
self.structs.insert(location, insert.clone());
Ok(())
}
Err(e) => Err(e),
@ -108,15 +109,15 @@ impl SymbolTable {
}
/// Inserts a variable into the symbol table.
pub fn insert_variable(&mut self, symbol: Symbol, insert: VariableSymbol) -> Result<()> {
self.check_shadowing(None, symbol, insert.span)?;
self.variables.insert(symbol, insert);
pub fn insert_variable(&mut self, location: Location, insert: VariableSymbol) -> Result<()> {
self.check_shadowing(&location, insert.span)?;
self.variables.insert(location, insert);
Ok(())
}
/// Removes a variable from the symbol table.
pub fn remove_variable_from_current_scope(&mut self, symbol: Symbol) {
self.variables.remove(&symbol);
pub fn remove_variable_from_current_scope(&mut self, location: Location) {
self.variables.remove(&location);
}
/// Creates a new scope for the block and stores it in the symbol table.
@ -126,58 +127,33 @@ impl SymbolTable {
}
/// Attempts to lookup a function in the symbol table.
pub fn lookup_fn_symbol(&self, program: Symbol, symbol: Symbol) -> Option<&FunctionSymbol> {
if let Some(func) = self.functions.get(&Location::new(program, symbol)) {
pub fn lookup_fn_symbol(&self, location: Location) -> Option<&FunctionSymbol> {
if let Some(func) = self.functions.get(&location) {
Some(func)
} else if let Some(parent) = self.parent.as_ref() {
parent.lookup_fn_symbol(program, symbol)
parent.lookup_fn_symbol(location)
} else {
None
}
}
/// Attempts to lookup a struct in the symbol table.
pub fn lookup_struct(&self, program: Symbol, symbol: Symbol) -> Option<&Composite> {
if let Some(struct_) = self.structs.get(&Location::new(program, symbol)) {
pub fn lookup_struct(&self, location: Location) -> Option<&Composite> {
if let Some(struct_) = self.structs.get(&location) {
Some(struct_)
} else if let Some(parent) = self.parent.as_ref() {
parent.lookup_struct(program, symbol)
parent.lookup_struct(location)
} else {
None
}
}
/// Attempts to lookup a variable in the symbol table.
pub fn lookup_variable(&self, symbol: Symbol) -> Option<&VariableSymbol> {
if let Some(var) = self.variables.get(&symbol) {
pub fn lookup_variable(&self, location: Location) -> Option<&VariableSymbol> {
if let Some(var) = self.variables.get(&location) {
Some(var)
} else if let Some(parent) = self.parent.as_ref() {
parent.lookup_variable(symbol)
} else {
None
}
}
/// Returns true if the variable exists in the local scope
pub fn variable_in_local_scope(&self, symbol: Symbol) -> bool {
self.variables.contains_key(&symbol)
}
/// Returns true if the variable exists in any parent scope
pub fn variable_in_parent_scope(&self, symbol: Symbol) -> bool {
if let Some(parent) = self.parent.as_ref() {
if parent.variables.contains_key(&symbol) { true } else { parent.variable_in_parent_scope(symbol) }
} else {
false
}
}
/// Returns a mutable reference to the `VariableSymbol` if it exists in the symbol table.
pub fn lookup_variable_mut(&mut self, symbol: Symbol) -> Option<&mut VariableSymbol> {
if let Some(var) = self.variables.get_mut(&symbol) {
Some(var)
} else if let Some(parent) = self.parent.as_mut() {
parent.lookup_variable_mut(symbol)
parent.lookup_variable(location)
} else {
None
}
@ -251,12 +227,10 @@ mod tests {
use leo_ast::{Identifier, Type, Variant};
use leo_span::symbol::create_session_if_not_set_then;
#[test]
#[ignore]
fn serialization_test() {
create_session_if_not_set_then(|_| {
let mut symbol_table = SymbolTable::default();
let program = Symbol::intern("credits");
let function = Symbol::intern("transfer_public");
let func_loc = Location::new(Some(Symbol::intern("credits")), Symbol::intern("transfer_public"));
let insert = Function {
annotations: Vec::new(),
id: 0,
@ -269,11 +243,34 @@ mod tests {
output: vec![],
block: Default::default(),
};
symbol_table.insert_fn(program, function, &insert).unwrap();
symbol_table.insert_fn(func_loc, &insert).unwrap();
symbol_table.insert_variable(
Location::new(Some(Symbol::intern("credits")), Symbol::intern("accounts")),
VariableSymbol {
type_: Type::Address,
span: Default::default(),
declaration: VariableType::Const,
}).unwrap();
symbol_table.insert_struct(
Location::new(Some(Symbol::intern("credits")), Symbol::intern("token")),
&Composite {
is_record: false,
span: Default::default(),
id: 0,
identifier: Identifier::new(Symbol::intern("token"), Default::default()),
members: Vec::new(),
external: None,
}).unwrap();
symbol_table.insert_variable(
Location::new(None, Symbol::intern("foo")),
VariableSymbol {
type_: Type::Address,
span: Default::default(),
declaration: VariableType::Const,
}).unwrap();
let json = symbol_table.to_json_string().unwrap();
dbg!(json.clone());
let deserialized = SymbolTable::from_json_string(&json).unwrap();
dbg!(deserialized);
assert_eq!(symbol_table, deserialized);
});
}
}

View File

@ -18,7 +18,7 @@ use leo_ast::*;
use leo_errors::emitter::Handler;
use leo_span::Symbol;
use crate::{SymbolTable, VariableSymbol, VariableType};
use crate::{Location, SymbolTable, VariableSymbol, VariableType};
/// A compiler pass during which the `SymbolTable` is created.
/// Note that this pass only creates the initial entries for functions, structs, and records.
@ -30,11 +30,13 @@ pub struct SymbolTableCreator<'a> {
handler: &'a Handler,
/// The current program name.
program_name: Option<Symbol>,
/// Whether or not traversing stub.
is_stub: bool,
}
impl<'a> SymbolTableCreator<'a> {
pub fn new(handler: &'a Handler) -> Self {
Self { symbol_table: Default::default(), handler, program_name: None }
Self { symbol_table: Default::default(), handler, program_name: None, is_stub: false }
}
}
@ -49,6 +51,7 @@ impl<'a> ProgramVisitor<'a> for SymbolTableCreator<'a> {
fn visit_program_scope(&mut self, input: &'a ProgramScope) {
// Set current program name
self.program_name = Some(input.program_id.name.name);
self.is_stub = false;
// Visit the program scope
input.structs.iter().for_each(|(_, c)| (self.visit_struct(c)));
@ -62,17 +65,23 @@ impl<'a> ProgramVisitor<'a> for SymbolTableCreator<'a> {
}
fn visit_struct(&mut self, input: &'a Composite) {
if let Err(err) = self.symbol_table.insert_struct(self.program_name.unwrap(), input.name(), input) {
if let Err(err) = self.symbol_table.insert_struct(Location::new(self.program_name, input.name()), input) {
self.handler.emit_err(err);
}
}
fn visit_mapping(&mut self, input: &'a Mapping) {
// Check if mapping is external.
let program = match self.is_stub {
true => self.program_name,
false => None,
};
// Add the variable associated with the mapping to the symbol table.
if let Err(err) = self.symbol_table.insert_variable(input.identifier.name, VariableSymbol {
if let Err(err) = self.symbol_table.insert_variable(Location::new(program, input.identifier.name), VariableSymbol {
type_: Type::Mapping(MappingType {
key: Box::new(input.key_type.clone()),
value: Box::new(input.value_type.clone()),
program: self.program_name.unwrap(),
}),
span: input.span,
declaration: VariableType::Mut,
@ -82,12 +91,13 @@ impl<'a> ProgramVisitor<'a> for SymbolTableCreator<'a> {
}
fn visit_function(&mut self, input: &'a Function) {
if let Err(err) = self.symbol_table.insert_fn(self.program_name.unwrap(), input.name(), input) {
if let Err(err) = self.symbol_table.insert_fn(Location::new(self.program_name, input.name()), input) {
self.handler.emit_err(err);
}
}
fn visit_stub(&mut self, input: &'a Stub) {
self.is_stub = true;
self.program_name = Some(input.stub_id.name.name);
input.functions.iter().for_each(|(_, c)| (self.visit_function_stub(c)));
input.structs.iter().for_each(|(_, c)| (self.visit_struct_stub(c)));
@ -95,14 +105,14 @@ impl<'a> ProgramVisitor<'a> for SymbolTableCreator<'a> {
fn visit_function_stub(&mut self, input: &'a FunctionStub) {
if let Err(err) =
self.symbol_table.insert_fn(self.program_name.unwrap(), input.name(), &Function::from(input.clone()))
self.symbol_table.insert_fn(Location::new(self.program_name, input.name()), &Function::from(input.clone()))
{
self.handler.emit_err(err);
}
}
fn visit_struct_stub(&mut self, input: &'a Composite) {
if let Err(err) = self.symbol_table.insert_struct(self.program_name.unwrap(), input.name(), input) {
if let Err(err) = self.symbol_table.insert_struct(Location::new(self.program_name, input.name()), input) {
self.handler.emit_err(err);
}
}