Skip to content

Commit

Permalink
Address #16 for contract and struct calls and for initialisation
Browse files Browse the repository at this point in the history
 - Checks call arity and provided types, and produces more helpful error messages if not matched
 - New `scope_or_default()` on contexts will always produce a usable scope (replaces `.scope_context.as_ref().unwrap_or_default()`)
 - More conversions from wasteful `.collect()`s to plain `Iterators`, and optimisations to a more functional style
  • Loading branch information
mrRachar committed Aug 19, 2020
1 parent 77dc850 commit 93d1fcc
Show file tree
Hide file tree
Showing 17 changed files with 252 additions and 154 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 14 additions & 17 deletions src/ast/declarations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::ast_processor::Target;
use crate::context::*;
use crate::visitor::Visitor;
use hex::encode;
use nom::lib::std::fmt::Formatter;
use std::fmt::Formatter;

#[derive(Debug, Clone, PartialEq)]
pub enum TopLevelDeclaration {
Expand Down Expand Up @@ -641,30 +641,23 @@ impl FunctionSignatureDeclaration {
self.modifiers.contains(&Modifier::Public)
}

pub fn parameter_identifiers(&self) -> Vec<Identifier> {
self.parameters
.clone()
.into_iter()
.map(|p| p.identifier)
.collect()
pub fn parameter_identifiers<'a>(&'a self) -> impl Iterator<Item=&'a Identifier> + 'a {
self.parameters.iter().map(|p| &p.identifier)
}

pub fn parameter_types(&self) -> Vec<Type> {
self.parameters
.clone()
.into_iter()
.map(|p| p.type_assignment)
.collect()
pub fn parameter_types<'a>(&'a self) -> impl Iterator<Item=&'a Type> + 'a {
self.parameters.iter().map(|p| &p.type_assignment)
}

pub fn is_equal(&self, against: FunctionSignatureDeclaration) -> bool {
let modifiers_match = self.modifiers == against.modifiers;
let attibutes_match = self.attributes == against.attributes;
let parameter_names_match = self.parameter_identifiers() == against.parameter_identifiers();
let parameter_types = self.parameter_types() == against.parameter_types();
let attributes_match = self.attributes == against.attributes;
let parameter_names_match = self.parameter_identifiers()
.eq(against.parameter_identifiers());
let parameter_types = self.parameter_types().eq(against.parameter_types());
if self.identifier.token == against.identifier.token
&& modifiers_match
&& attibutes_match
&& attributes_match
&& parameter_names_match
&& parameter_types
{
Expand Down Expand Up @@ -810,6 +803,10 @@ impl SpecialSignatureDeclaration {
pub fn has_parameters(&self) -> bool {
!self.parameters.is_empty()
}

pub fn parameter_types<'a>(&'a self) -> impl Iterator<Item=&'a Type> + 'a {
self.parameters.iter().map(|p| &p.type_assignment)
}
}

impl Visitable for SpecialSignatureDeclaration {
Expand Down
2 changes: 1 addition & 1 deletion src/ast/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ impl Visitable for BinaryExpression {
ctx.external_call_context = old_context;
ctx.is_enclosing = false;

let scope = ctx.scope_context.as_ref().unwrap_or_default();
let scope = ctx.scope_or_default();

let enclosing = ctx
.enclosing_type_identifier()
Expand Down
30 changes: 22 additions & 8 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,14 @@ impl SpecialInformation {
.map(|p| p.type_assignment)
.collect()
}

pub fn name(&self) -> &str {
&self.declaration.head.special_token
}

pub fn get_parameter_types<'a>(&'a self) -> impl Iterator<Item=&'a Type> + 'a {
self.declaration.head.parameter_types()
}
}

#[derive(Default, Debug, Clone)]
Expand All @@ -230,21 +238,27 @@ impl FunctionInformation {
self.declaration.get_result_type()
}

pub fn get_parameter_types(&self) -> Vec<Type> {
pub fn get_parameter_types<'a>(&'a self) -> impl Iterator<Item=&'a Type> + 'a {
self.declaration.head.parameter_types()
}

pub fn parameter_identifiers(&self) -> Vec<Identifier> {
pub fn parameter_identifiers<'a>(&'a self) -> impl Iterator<Item=&'a Identifier> + 'a {
self.declaration.head.parameter_identifiers()
}

pub fn required_parameter_identifiers(&self) -> Vec<Identifier> {
let identifiers = self.declaration.head.parameters.clone();
identifiers
.into_iter()
pub fn required_parameter_identifiers(&self) -> impl Iterator<Item=&Identifier> {
self.declaration.head.parameters
.iter()
.filter(|i| i.expression.is_none())
.map(|p| p.identifier)
.collect()
.map(|p| &p.identifier)
}

pub fn identifier(&self) -> &Identifier {
&self.declaration.head.identifier
}

pub fn line_info(&self) -> &LineInfo {
&self.declaration.head.identifier.line_info
}
}

Expand Down
17 changes: 17 additions & 0 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,23 @@ impl Context {
.map(|c| &*c.caller_protections)
.unwrap_or(&[])
}

pub fn scope_or_default(&self) -> &ScopeContext {
self.scope_context.as_ref().unwrap_or_default()
}

pub fn declaration_context_type_id(&self) -> Option<&str> {
self.contract_behaviour_declaration_context.as_ref()
.map(|c| &*c.identifier.token)
.or_else(
|| self.struct_declaration_context.as_ref()
.map(|c| &*c.identifier.token)
.or_else(
|| self.asset_context.as_ref()
.map(|c| &*c.identifier.token)
)
)
}
}

#[derive(Debug)]
Expand Down
2 changes: 1 addition & 1 deletion src/environment/conflicts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl Environment {
if let Some(first_signature) = funcs.get(0) {
let first_parameter = &first_signature.declaration.head;
let is_first_signature = |func: &FunctionInformation| {
func.get_parameter_types() == first_signature.get_parameter_types()
func.get_parameter_types().eq(first_signature.get_parameter_types())
&& func.declaration.head.is_equal(first_parameter.clone())
};
if funcs.iter().any(is_first_signature) {
Expand Down
86 changes: 34 additions & 52 deletions src/environment/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,28 @@ impl Environment {
caller_protections: Vec<CallerProtection>,
type_states: Vec<TypeState>,
) {
let name = f.head.identifier.token.clone();
let name = &f.head.identifier.token;
let mutating = f.is_mutating();
let function_information = FunctionInformation {
declaration: f,
mutating,
caller_protections,
type_states,
..Default::default()
};
let type_info = if let Some(type_info) = self.types.get_mut(type_id) {
type_info
} else {
self.types.insert(type_id.to_string(), TypeInfo::new());
self.types.get_mut(type_id).unwrap()
};

if let Some(function_set) = type_info.functions.get_mut(&name) {
function_set.push(function_information);
// Allow further references without premature moving or code duplication
let function_information = |f| FunctionInformation {
declaration: f,
mutating,
caller_protections,
type_states,
..Default::default()
};

if let Some(function_set) = type_info.functions.get_mut(name) {
function_set.push(function_information(f));
} else {
type_info.functions.insert(name, vec![function_information]);
type_info.functions.insert(name.clone(), vec![function_information(f)]);
}
}

Expand Down Expand Up @@ -107,12 +109,6 @@ impl Environment {
) -> FunctionCallMatchResult {
let mut candidates = Vec::new();

let argument_types: Vec<Type> = call
.arguments
.iter()
.map(|a| self.get_expression_type(&a.expression, type_id, &[], &[], &scope))
.collect();

if let Some(type_info) = self.types.get(type_id) {
if let Some(functions) = type_info.all_functions().get(&call.identifier.token) {
for function in functions {
Expand All @@ -136,22 +132,12 @@ impl Environment {
}
}

let argument_types: Vec<_> = self.argument_types(call, type_id, scope).collect();

let matched_candidates: Vec<FunctionInformation> = candidates
.clone()
.into_iter()
.filter(|c| {
let p_types = c.get_parameter_types();
if p_types.len() != argument_types.len() {
return false;
}
let mut arg_types = argument_types.clone();
for p in p_types {
if p != arg_types.remove(0) {
return false;
}
}
true
})
.filter(|c| c.get_parameter_types().eq(argument_types.iter()))
.collect();

let matched_candidates: Vec<CallableInformation> = matched_candidates
Expand Down Expand Up @@ -204,12 +190,7 @@ impl Environment {
if let Some(type_info) = self.types.get(&call.identifier.token) {
for initialiser in &type_info.initialisers {
let parameter_types = initialiser.parameter_types();
let mut equal_types = true;
for argument_type in argument_types {
if !parameter_types.contains(argument_type) {
equal_types = false;
}
}
let equal_types = *&parameter_types == argument_types;

if equal_types
&& self
Expand Down Expand Up @@ -242,10 +223,10 @@ impl Environment {
if let Some(type_info) = self.types.get(&"Quartz_Global".to_string()) {
if let Some(functions) = type_info.functions.get(&call.identifier.token) {
for function in functions {
let parameter_types = function.get_parameter_types();
let parameter_types: Vec<_> = function.get_parameter_types().collect();
let equal_types = argument_types
.iter()
.all(|argument_type| parameter_types.contains(argument_type));
.all(|argument_type| parameter_types.contains(&argument_type));
if equal_types
&& self.compatible_caller_protections(
protections,
Expand All @@ -270,6 +251,12 @@ impl Environment {
FunctionCallMatchResult::Failure(Candidates { candidates })
}

pub fn argument_types<'a>(&'a self, call: &'a FunctionCall, type_id: &'a str, scope: &'a ScopeContext) -> impl Iterator<Item=Type> + 'a {
call.arguments
.iter()
.map(move |a| self.get_expression_type(&a.expression, type_id, &[], &[], scope))
}

pub fn is_runtime_function_call(function_call: &FunctionCall) -> bool {
function_call.identifier.token.starts_with("Quartz_")
}
Expand All @@ -281,9 +268,7 @@ impl Environment {
caller_protections: &[CallerProtection],
scope: &ScopeContext,
) -> FunctionCallMatchResult {
let result = FunctionCallMatchResult::Failure(Candidates {
..Default::default()
});
let result = FunctionCallMatchResult::Failure(Candidates { ..Default::default() });

let argument_types: Vec<_> = call
.arguments
Expand All @@ -298,9 +283,7 @@ impl Environment {

let global_match = self.match_global_function(call, &argument_types, caller_protections);

let result = result.merge(regular_match);
let result = result.merge(initaliser_match);
result.merge(global_match)
result.merge(regular_match).merge(initaliser_match).merge(global_match)
}

fn compatible_caller_protections(
Expand Down Expand Up @@ -337,8 +320,8 @@ impl Environment {
type_id: &str,
scope: &ScopeContext,
) -> bool {
let no_self_declaration_type =
Environment::replace_self(source.get_parameter_types(), type_id);
let no_self_declaration_type: Vec<_> =
Environment::replace_self(source.get_parameter_types(), type_id).collect();

let parameters: Vec<_> = source
.declaration
Expand All @@ -348,8 +331,8 @@ impl Environment {
.map(|p| p.as_variable_declaration())
.collect();

if target.arguments.len() <= source.parameter_identifiers().len()
&& target.arguments.len() >= source.required_parameter_identifiers().len()
if target.arguments.len() <= source.parameter_identifiers().count()
&& target.arguments.len() >= source.required_parameter_identifiers().count()
{
self.check_parameter_compatibility(
&target.arguments,
Expand Down Expand Up @@ -458,9 +441,8 @@ impl Environment {
true
}

pub fn replace_self(list: Vec<Type>, enclosing: &str) -> Vec<Type> {
list.into_iter()
.map(|t| t.replacing_self(enclosing))
.collect()
pub fn replace_self<'a, I: 'a + Iterator<Item=&'a Type>>(iterator: I, enclosing: &'a str) -> impl Iterator<Item=Type> + 'a {
iterator
.map(move |t| t.replacing_self(enclosing))
}
}
Loading

0 comments on commit 93d1fcc

Please sign in to comment.