Skip to content

Commit

Permalink
partially resolve lists
Browse files Browse the repository at this point in the history
  • Loading branch information
SkymanOne committed Mar 2, 2024
1 parent 9a0aa64 commit ac78b1c
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 42 deletions.
4 changes: 2 additions & 2 deletions crates/parser/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,13 +303,15 @@ pub struct StructInit {
pub enum Expression {
Variable(Identifier),

// Literals
Number(UnaryExpression<String>),
Boolean(UnaryExpression<bool>),
Float(UnaryExpression<String>),
String(UnaryExpression<String>),
Char(UnaryExpression<char>),
Hex(UnaryExpression<String>),
Address(UnaryExpression<String>),
List(UnaryExpression<Vec<Expression>>),

// Maths operations.
Multiply(BinaryExpression),
Expand All @@ -336,8 +338,6 @@ pub enum Expression {
MemberAccess(MemberAccess),
Pipe(BinaryExpression),
StructInit(UnaryExpression<StructInit>),

List(UnaryExpression<Vec<Expression>>),
}

#[derive(Clone, Debug, PartialEq, Node)]
Expand Down
58 changes: 29 additions & 29 deletions crates/semantics/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,33 @@ pub struct Type {
pub ty: TypeVariant,
}

impl Type {
#[derive(Clone, Debug, PartialEq, Default)]
pub enum TypeVariant {
#[default]
Int,
Uint,
Float,
Char,
String,
Hex,
Address,
Unit,
Bool,
Set(Box<TypeVariant>),
List(Box<TypeVariant>),
Mapping(Mapping),
Function(SymbolInfo),
Struct(SymbolInfo),
Model(SymbolInfo),
Enum(SymbolInfo),
State(SymbolInfo),
}

impl TypeVariant {
/// Is data type primitive.
pub fn is_primitive(&self) -> bool {
matches!(
&self.ty,
&self,
TypeVariant::Int
| TypeVariant::Uint
| TypeVariant::Float
Expand All @@ -37,9 +59,9 @@ impl Type {

/// Find the set of dependent user defined types that are encapsulated by this type.
pub fn custom_type_dependencies(&self) -> HashSet<usize> {
match &self.ty {
TypeVariant::Set(s) => s.ty.custom_type_dependencies(),
TypeVariant::List(s) => s.ty.custom_type_dependencies(),
match &self {
TypeVariant::Set(ty) => ty.custom_type_dependencies(),
TypeVariant::List(ty) => ty.custom_type_dependencies(),
TypeVariant::Mapping(m) => {
let mut set = m.from_ty.custom_type_dependencies();
set.extend(m.to_ty.custom_type_dependencies());
Expand All @@ -51,28 +73,6 @@ impl Type {
}
}

#[derive(Clone, Debug, PartialEq, Default)]
pub enum TypeVariant {
#[default]
Int,
Uint,
Float,
Char,
String,
Hex,
Address,
Unit,
Bool,
Set(Set),
List(List),
Mapping(Mapping),
Function(SymbolInfo),
Struct(SymbolInfo),
Model(SymbolInfo),
Enum(SymbolInfo),
State(SymbolInfo),
}

#[derive(Clone, Debug, PartialEq)]
pub struct FunctionType {
params: Vec<TypeVariant>,
Expand All @@ -91,9 +91,9 @@ pub struct List {

#[derive(Clone, Debug, PartialEq, Node, Default)]
pub struct Mapping {
pub from_ty: Box<Type>,
pub from_ty: Box<TypeVariant>,
pub relation: MappingRelation,
pub to_ty: Box<Type>,
pub to_ty: Box<TypeVariant>,
}

/// Parameter declaration of the state.
Expand Down
2 changes: 1 addition & 1 deletion crates/semantics/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ impl ContractDefinition {
item: &parsed_ast::StateDeclaration,
delay: &mut DelayedDeclarations,
) {
let state_len = self.models.len();
let state_len = self.states.len();
// if we successfully add a symbol to the symbol table,
// then we can proceed with creating the delayed fields for the second pass.
if self.add_global_symbol(
Expand Down
86 changes: 85 additions & 1 deletion crates/semantics/src/expression/literals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ use folidity_parser::{ast as parsed_ast, Span};
use crate::{
ast::{Expression, TypeVariant},
contract::ContractDefinition,
symtable::SymTable,
types::{report_type_mismatch, ExpectedType},
};

use super::dynamic_to_concrete_type;
use super::{dynamic_to_concrete_type, expression};

/// Resolve bool to an expression.
///
Expand Down Expand Up @@ -200,3 +201,86 @@ pub fn resolve_address(
}
}
}

/// Resolve list and set of expression to an AST expressions
pub fn resolve_lists(
exprs: &[parsed_ast::Expression],
loc: Span,
contract: &mut ContractDefinition,
symtable: &mut SymTable,
expected_ty: ExpectedType,
) -> Result<Expression, ()> {
let mut derive_expr = |ty: &TypeVariant, loc: Span| -> Result<Expression, ()> {
let mut error = false;
let eval_exprs: Vec<Expression> = exprs
.iter()
.filter_map(|e| {
if let Ok(e) = expression(e, ExpectedType::Concrete(ty.clone()), symtable, contract)
{
Some(e)
} else {
error = true;
None
}
})
.collect();

if error {
Err(())
} else {
Ok(Expression::List(parsed_ast::UnaryExpression {
loc,
element: eval_exprs,
}))
}
};
match &expected_ty {
ExpectedType::Concrete(ty) => match ty {
TypeVariant::Set(ty) => derive_expr(ty, loc),
TypeVariant::List(ty) => derive_expr(ty, loc),
a_ty => {
report_type_mismatch(&expected_ty, a_ty, &loc, contract);
Err(())
}
},
ExpectedType::Dynamic(tys) => {
if tys.is_empty() {
// if there are no expected types, then we derive it from the first element in the list.
if exprs.is_empty() {
contract.diagnostics.push(Report::type_error(
loc,
String::from(
"Cannot derive type from the empty list without the type annotation.",
),
));
}
let list_expr = &exprs[0];
//todo: write a derivation function.
Err(())
} else {
// we need to manually inspect the type.
let allowed_tys: Vec<TypeVariant> = tys
.iter()
.filter(|ty| matches!(ty, TypeVariant::List(_) | TypeVariant::Set(_)))
.cloned()
.collect();
if allowed_tys.is_empty() {
contract.diagnostics.push(Report::semantic_error(
loc,
format!("Expected list or set, found {:?}", tys),
));
return Err(());
}
let concrete = ExpectedType::Concrete(allowed_tys.first().unwrap().clone());
resolve_lists(exprs, loc, contract, symtable, concrete)
}
}
ExpectedType::Empty => {
contract.diagnostics.push(Report::semantic_error(
loc,
String::from("List literals can only be used in expressions."),
));
Err(())
}
}
}
10 changes: 8 additions & 2 deletions crates/semantics/src/expression/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod literals;
mod nums;
#[cfg(test)]
mod tests;

use folidity_parser::ast as parsed_ast;

Expand All @@ -11,7 +13,9 @@ use crate::{
};

use self::{
literals::{resolve_address, resolve_bool, resolve_char, resolve_hex, resolve_string},
literals::{
resolve_address, resolve_bool, resolve_char, resolve_hex, resolve_lists, resolve_string,
},
nums::{resolve_float, resolve_integer},
};

Expand Down Expand Up @@ -44,6 +48,9 @@ pub fn expression(
parsed_ast::Expression::Address(a) => {
resolve_address(&a.element, a.loc.clone(), contract, expected_ty)
}
parsed_ast::Expression::List(l) => {
resolve_lists(&l.element, l.loc.clone(), contract, symtable, expected_ty)
}
parsed_ast::Expression::Variable(_) => todo!(),
parsed_ast::Expression::Multiply(_) => todo!(),
parsed_ast::Expression::Divide(_) => todo!(),
Expand All @@ -64,7 +71,6 @@ pub fn expression(
parsed_ast::Expression::MemberAccess(_) => todo!(),
parsed_ast::Expression::Pipe(_) => todo!(),
parsed_ast::Expression::StructInit(_) => todo!(),
parsed_ast::Expression::List(_) => todo!(),
}
}

Expand Down
46 changes: 46 additions & 0 deletions crates/semantics/src/expression/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use folidity_parser::{ast as parsed_ast, Span};

use crate::{
ast::TypeVariant, contract::ContractDefinition, symtable::SymTable, types::ExpectedType,
};

use super::expression;

#[test]
fn test_list() {
let loc = Span { start: 0, end: 0 };
let mut contract = ContractDefinition::default();
let mut symtable = SymTable::default();
let parsed_list = parsed_ast::Expression::List(parsed_ast::UnaryExpression {
loc: loc.clone(),
element: vec![
parsed_ast::Expression::Number(parsed_ast::UnaryExpression {
loc: loc.clone(),
element: "1".to_string(),
}),
parsed_ast::Expression::Number(parsed_ast::UnaryExpression {
loc: loc.clone(),
element: "2".to_string(),
}),
parsed_ast::Expression::Number(parsed_ast::UnaryExpression {
loc: loc.clone(),
element: "3".to_string(),
}),
],
});
let resolved_expr = expression(
&parsed_list,
ExpectedType::Concrete(TypeVariant::List(Box::new(TypeVariant::Int))),
&mut symtable,
&mut contract,
);
assert!(resolved_expr.is_ok());

let resolved_expr_err = expression(
&parsed_list,
ExpectedType::Concrete(TypeVariant::List(Box::new(TypeVariant::Float))),
&mut symtable,
&mut contract,
);
assert!(resolved_expr_err.is_err())
}
14 changes: 7 additions & 7 deletions crates/semantics/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::HashSet;
use std::fmt::Display;

use crate::ast::{List, Mapping, Param, Set, StateBody, StateDeclaration, Type, TypeVariant};
use crate::ast::{Mapping, Param, StateBody, Type, TypeVariant};
use crate::contract::ContractDefinition;
use crate::global_symbol::GlobalSymbol;
use folidity_diagnostics::Report;
Expand Down Expand Up @@ -79,19 +79,19 @@ pub fn map_type(contract: &mut ContractDefinition, ty: &parsed_ast::Type) -> Res
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)))
TypeVariant::Set(Box::new(set_ty.ty))
}
parsed_ast::TypeVariant::List(l) => {
let list_ty = map_type(contract, &l.ty)?;
TypeVariant::List(List::new(Box::new(list_ty)))
TypeVariant::List(Box::new(list_ty.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),
Box::new(m_from_ty.ty),
m.relation.clone(),
Box::new(m_to_ty),
Box::new(m_to_ty.ty),
))
}
parsed_ast::TypeVariant::Custom(user_ty) => {
Expand Down Expand Up @@ -157,7 +157,7 @@ pub fn find_user_type_recursion(contract: &mut ContractDefinition) {
/// Collect field dependencies into the graph edges.
fn collect_edges(edges: &mut HashSet<(usize, usize, usize)>, fields: &[Param], struct_no: usize) {
for (no, field) in fields.iter().enumerate() {
for dependency in field.ty.custom_type_dependencies() {
for dependency in field.ty.ty.custom_type_dependencies() {
if edges.insert((no, dependency, struct_no)) {
collect_edges(edges, fields, struct_no)
}
Expand Down Expand Up @@ -277,7 +277,7 @@ fn detect_model_cycle(contract: &mut ContractDefinition) {
0,
None,
) {
for (a, b) in simple_path.windows(2).map(|p| (p[0], p[1])) {
for (a, _) in simple_path.windows(2).map(|p| (p[0], p[1])) {
contract.models[a.index()].recursive_parent = true;
}
}
Expand Down

0 comments on commit ac78b1c

Please sign in to comment.