Skip to content

Commit

Permalink
map types
Browse files Browse the repository at this point in the history
  • Loading branch information
SkymanOne committed Feb 19, 2024
1 parent 3172cb7 commit a9a5aa0
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 25 deletions.
27 changes: 8 additions & 19 deletions crates/semantics/src/ast.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use derive_node::Node;
use folidity_parser::{
ast::{BinaryExpression, Identifier, UnaryExpression},
ast::{BinaryExpression, Identifier, MappingRelation, UnaryExpression},
Span,
};
use indexmap::IndexMap;

use crate::global_symbol::SymbolInfo;

#[derive(Clone, Debug, PartialEq, Node)]
pub struct Type {
pub loc: Span,
Expand All @@ -25,10 +27,11 @@ pub enum TypeVariant {
Set(Set),
List(List),
Mapping(Mapping),
Function(Box<FuncReturnType>),
Struct(usize),
Model(usize),
State(usize),
Function(SymbolInfo),
Struct(SymbolInfo),
Model(SymbolInfo),
Enum(SymbolInfo),
State(SymbolInfo),
}

#[derive(Clone, Debug, PartialEq)]
Expand All @@ -47,20 +50,6 @@ pub struct List {
pub ty: Box<Type>,
}

#[derive(Clone, Debug, PartialEq, Node)]
pub struct MappingRelation {
pub loc: Span,
pub injective: bool,
pub partial: bool,
pub surjective: bool,
}

impl MappingRelation {
pub fn is_bijective(&self) -> bool {
self.injective && self.surjective
}
}

#[derive(Clone, Debug, PartialEq, Node)]
pub struct Mapping {
pub from_ty: Box<Type>,
Expand Down
49 changes: 44 additions & 5 deletions crates/semantics/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ use folidity_parser::{ast::Source, Span};
use indexmap::IndexMap;

use crate::ast::{
EnumDeclaration, FunctionDeclaration, ModelDeclaration, StateDeclaration, StructDeclaration,
EnumDeclaration, FunctionDeclaration, ModelDeclaration, Param, StateDeclaration,
StructDeclaration,
};
use crate::decls::DelayedFields;
use crate::global_symbol::SymbolInfo;
Expand All @@ -23,6 +24,7 @@ const RESERVED_TYPE_NAMES: &[&str] = &[

/// Semantically analysed contract definition.
/// Ready for the the next stage of compilation.
#[derive(Debug, Clone, Default)]
pub struct ContractDefinition {
/// List of all enums in the contract.
pub enums: Vec<EnumDeclaration>,
Expand Down Expand Up @@ -69,6 +71,7 @@ impl ContractDefinition {
delay
}

/// Resolves fields during the second pass.
pub fn resolve_fields(&mut self, delay: &DelayedDeclarations) {}

fn analyze_enum(&mut self, item: &parsed_ast::EnumDeclaration) {
Expand Down Expand Up @@ -113,7 +116,43 @@ impl ContractDefinition {
);
}

pub fn analyze_struct(
fn analyze_fields(&mut self, fields: &[&parsed_ast::Param], ident: &Identifier) {
let analyzed_fields: Vec<Param> = Vec::new();
if fields.is_empty() {
self.diagnostics.push(Report::semantic_error(
ident.loc.clone(),
format!("`{}` has no fields", &ident.name),
));
return;
}

for field in fields {
let duplicates: Vec<&parsed_ast::Param> = fields
.iter()
.filter(|f| f.name.name == field.name.name)
.copied()
.collect();
if !duplicates.is_empty() {
let start = duplicates
.iter()
.min_by(|x, y| x.loc.start.cmp(&y.loc.start))
.map(|p| p.loc.start)
.unwrap();
let end = duplicates
.iter()
.max_by(|x, y| x.loc.end.cmp(&y.loc.end))
.map(|p| p.loc.end)
.unwrap();

self.diagnostics.push(Report::semantic_error(
Span { start, end },
format!("`{}` is duplicated", field.name.name),
));
}
}
}

fn analyze_struct(
&mut self,
item: &parsed_ast::StructDeclaration,
delay: &mut DelayedDeclarations,
Expand All @@ -140,7 +179,7 @@ impl ContractDefinition {
}
}

pub fn analyze_model(
fn analyze_model(
&mut self,
item: &parsed_ast::ModelDeclaration,
delay: &mut DelayedDeclarations,
Expand Down Expand Up @@ -169,7 +208,7 @@ impl ContractDefinition {
}
}

pub fn analyze_state(
fn analyze_state(
&mut self,
item: &parsed_ast::StateDeclaration,
delay: &mut DelayedDeclarations,
Expand Down Expand Up @@ -203,7 +242,7 @@ impl ContractDefinition {
/// # Errors
/// - The symbol table is already in use.
/// - The symbol name is a reserved word.
fn add_global_symbol(&mut self, ident: &Identifier, symbol: GlobalSymbol) -> bool {
pub fn add_global_symbol(&mut self, ident: &Identifier, symbol: GlobalSymbol) -> bool {
if RESERVED_TYPE_NAMES.contains(&ident.name.as_str()) {
self.diagnostics.push(Report::semantic_error(
ident.loc.clone(),
Expand Down
2 changes: 1 addition & 1 deletion crates/semantics/src/global_symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub enum GlobalSymbol {
}

/// Global user defined symbol info.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct SymbolInfo {
/// Locations of the global symbol.
pub loc: Span,
Expand Down
17 changes: 17 additions & 0 deletions crates/semantics/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
use contract::ContractDefinition;
use folidity_parser::ast::Source;

mod ast;
mod contract;
mod decls;
mod global_symbol;
mod symtable;
mod types;

#[cfg(test)]
mod tests;

/// Resolves the contract's parsed tree into the semantically analysed and typed-checked definition.
///
/// # Errors
/// [`ContractDefinition`] may contain errors stored in the `diagnostics` field.
pub fn resolve_semantics(source: &Source) -> ContractDefinition {
let mut definition = ContractDefinition::default();
let delay = definition.resolve_declarations(source);

definition
}
38 changes: 38 additions & 0 deletions crates/semantics/src/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use crate::resolve_semantics;
use folidity_parser::parse;

const DECL_SRC: &str = r#"
struct MyStruct {
a: int,
b: address
}
enum MyEnum {
A,
B
}
model MyModel: ParentModel {
c: int,
b: string,
}
state NoState(MyModel)
"#;

#[test]
fn test_first_pass() {
let tree = parse(DECL_SRC).unwrap();

let def = resolve_semantics(&tree);
assert_eq!(def.structs.len(), 1);
assert_eq!(def.enums.len(), 1);
assert_eq!(def.models.len(), 1);
assert_eq!(def.states.len(), 1);

let e = &def.enums[0];
assert_eq!(e.variants.len(), 2);

assert!(e.variants.contains_key("A"));
assert!(e.variants.contains_key("B"));
}
62 changes: 62 additions & 0 deletions crates/semantics/src/types.rs
Original file line number Diff line number Diff line change
@@ -1 +1,63 @@
use crate::ast::{List, Mapping, Set, Type, TypeVariant};
use crate::contract::ContractDefinition;
use crate::global_symbol::GlobalSymbol;
use folidity_diagnostics::Report;
use folidity_parser::ast as parsed_ast;
use folidity_parser::ast::Identifier;

/// Maps type from parsed AST to semantically resolved type.
/// - Primitive types are simply mapped 1-1.
/// - User defined types (e.g. structs, enums) are looked up in the global symbol table.
/// - List types are recursively mapped.
pub fn map_type(contract: &mut ContractDefinition, ty: &parsed_ast::Type) -> Result<Type, ()> {
let variant = match &ty.ty {
parsed_ast::TypeVariant::Int => TypeVariant::Int,
parsed_ast::TypeVariant::Uint => TypeVariant::Uint,
parsed_ast::TypeVariant::Float => TypeVariant::Float,
parsed_ast::TypeVariant::Char => TypeVariant::Char,
parsed_ast::TypeVariant::String => TypeVariant::String,
parsed_ast::TypeVariant::Hex => TypeVariant::Hex,
parsed_ast::TypeVariant::Address => TypeVariant::Address,
parsed_ast::TypeVariant::Unit => TypeVariant::Address,
parsed_ast::TypeVariant::Bool => TypeVariant::Bool,
parsed_ast::TypeVariant::Set(s) => {
let set_ty = map_type(contract, &s.ty)?;
TypeVariant::Set(Set::new(Box::new(set_ty)))
}
parsed_ast::TypeVariant::List(l) => {
let list_ty = map_type(contract, &l.ty)?;
TypeVariant::List(List::new(Box::new(list_ty)))
}
parsed_ast::TypeVariant::Mapping(m) => {
let m_from_ty = map_type(contract, &m.from_ty)?;
let m_to_ty = map_type(contract, &m.to_ty)?;
TypeVariant::Mapping(Mapping::new(
Box::new(m_from_ty),
m.relation.clone(),
Box::new(m_to_ty),
))
}
parsed_ast::TypeVariant::Custom(user_ty) => {
if let Some(symbol) = contract.declaration_symbols.get(&user_ty.name) {
match symbol {
GlobalSymbol::Struct(info) => TypeVariant::Struct(info.clone()),
GlobalSymbol::Model(info) => TypeVariant::Model(info.clone()),
GlobalSymbol::Enum(info) => TypeVariant::Enum(info.clone()),
GlobalSymbol::State(info) => TypeVariant::State(info.clone()),
GlobalSymbol::Function(info) => TypeVariant::Function(info.clone()),
}
} else {
contract.diagnostics.push(Report::semantic_error(
user_ty.loc.clone(),
format!("`{}` is not defined", user_ty.name),
));
return Err(());
}
}
};

Ok(Type {
loc: ty.loc.clone(),
ty: variant,
})
}

0 comments on commit a9a5aa0

Please sign in to comment.