Skip to content

Commit

Permalink
Issue 375 add support for any type (#380)
Browse files Browse the repository at this point in the history
* Add generics type to the AST

* Add support for parsing generic types

* Codegen tests

* Add indexing support for generics

* Add resolver support for generics

* Code generation for generics

Avoid generating generic methods

* Add validation for generics

* Add intermdiate index

* Create implementation entries for resolved generics 

* Make sure types are generated, add correctness tests

* additional tests for generic-parameter annotation

added additional tests that test if generic function parameters
can be provided in an arbitrary order:

it should make no difference if you write
   foo(a := x, b := y);
or
   foo(b := y, a := x);

for a foo<T, V>

Co-authored-by: Mathias Rieder <mathias.rieder@gmail.com>
  • Loading branch information
ghaith and riederm committed Dec 2, 2021
1 parent 901304e commit ca2ebac
Show file tree
Hide file tree
Showing 36 changed files with 2,830 additions and 332 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "rusty"
version = "0.2.0"
authors = ["Ghaith Hachem <ghaith.hachem@gmail.com>", "Mathias Rieder <mathias.rieder@gmail.com>"]
edition = "2018"
edition = "2021"
readme = "README.md"
repository = "https://github.com/ghaith/rusty/"
license = "LGPL-3.0-or-later"
Expand Down
201 changes: 124 additions & 77 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ mod pre_processor;

pub type AstId = usize;

#[derive(Clone, Debug, PartialEq)]
pub struct GenericBinding {
pub name: String,
pub nature: TypeNature,
}

#[derive(PartialEq)]
pub struct Pou {
pub name: String,
Expand All @@ -18,6 +24,7 @@ pub struct Pou {
pub return_type: Option<DataTypeDeclaration>,
pub location: SourceRange,
pub poly_mode: Option<PolymorphismMode>,
pub generics: Vec<GenericBinding>,
}

#[derive(Debug, PartialEq)]
Expand All @@ -35,6 +42,95 @@ pub enum DirectAccessType {
DWord,
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum TypeNature {
Any,
Derived,
Elementary,
Magnitude,
Num,
Real,
Int,
Signed,
Unsigned,
Duration,
Bit,
Chars,
String,
Char,
Date,
}

impl TypeNature {
pub fn derives(self, other: TypeNature) -> bool {
if other == self {
true
} else {
match self {
TypeNature::Any => true,
TypeNature::Derived => matches!(other, TypeNature::Any),
TypeNature::Elementary => matches!(other, TypeNature::Any),
TypeNature::Magnitude => matches!(other, TypeNature::Elementary | TypeNature::Any),
TypeNature::Num => matches!(
other,
TypeNature::Magnitude | TypeNature::Elementary | TypeNature::Any
),
TypeNature::Real => matches!(
other,
TypeNature::Num
| TypeNature::Magnitude
| TypeNature::Elementary
| TypeNature::Any
),
TypeNature::Int => matches!(
other,
TypeNature::Num
| TypeNature::Magnitude
| TypeNature::Elementary
| TypeNature::Any
),
TypeNature::Signed => matches!(
other,
TypeNature::Int
| TypeNature::Num
| TypeNature::Magnitude
| TypeNature::Elementary
| TypeNature::Any
),
TypeNature::Unsigned => matches!(
other,
TypeNature::Int
| TypeNature::Num
| TypeNature::Magnitude
| TypeNature::Elementary
| TypeNature::Any
),
TypeNature::Duration => matches!(
other,
TypeNature::Num
| TypeNature::Magnitude
| TypeNature::Elementary
| TypeNature::Any
),
TypeNature::Bit => matches!(
other,
TypeNature::Magnitude | TypeNature::Elementary | TypeNature::Any
),
TypeNature::Chars => matches!(other, TypeNature::Elementary | TypeNature::Any),
TypeNature::String => matches!(
other,
TypeNature::Chars | TypeNature::Elementary | TypeNature::Any
),
TypeNature::Char => matches!(
other,
TypeNature::Chars | TypeNature::Elementary | TypeNature::Any
),
TypeNature::Date => matches!(other, TypeNature::Elementary | TypeNature::Any),
}
}
}
}

impl DirectAccessType {
/// Returns true if the current index is in the range for the given type
pub fn is_in_range(&self, index: u64, data_type: &DataTypeInformation) -> bool {
Expand Down Expand Up @@ -64,12 +160,15 @@ impl DirectAccessType {

impl Debug for Pou {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_struct("POU")
.field("name", &self.name)
let mut str = f.debug_struct("POU");
str.field("name", &self.name)
.field("variable_blocks", &self.variable_blocks)
.field("pou_type", &self.pou_type)
.field("return_type", &self.return_type)
.finish()
.field("return_type", &self.return_type);
if !self.generics.is_empty() {
str.field("generics", &self.generics);
}
str.finish()
}
}

Expand Down Expand Up @@ -335,7 +434,7 @@ impl Debug for UserTypeDeclaration {
}
}

#[derive(Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub enum DataType {
StructType {
name: Option<String>, //maybe None for inline structs
Expand Down Expand Up @@ -367,88 +466,36 @@ pub enum DataType {
VarArgs {
referenced_type: Option<Box<DataTypeDeclaration>>,
},
}

impl Debug for DataType {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
DataType::StructType { name, variables } => f
.debug_struct("StructType")
.field("name", name)
.field("variables", variables)
.finish(),
DataType::EnumType { name, elements } => f
.debug_struct("EnumType")
.field("name", name)
.field("elements", elements)
.finish(),
DataType::SubRangeType {
name,
referenced_type,
bounds,
} => f
.debug_struct("SubRangeType")
.field("name", name)
.field("referenced_type", referenced_type)
.field("bounds", bounds)
.finish(),
DataType::ArrayType {
name,
bounds,
referenced_type,
} => f
.debug_struct("ArrayType")
.field("name", name)
.field("bounds", bounds)
.field("referenced_type", referenced_type)
.finish(),
DataType::PointerType {
name,
referenced_type,
} => f
.debug_struct("PointerType")
.field("name", name)
.field("referenced_type", referenced_type)
.finish(),
DataType::StringType {
name,
is_wide,
size,
} => f
.debug_struct("StringType")
.field("name", name)
.field("is_wide", is_wide)
.field("size", size)
.finish(),
DataType::VarArgs { referenced_type } => f
.debug_struct("VarArgs")
.field("referenced_type", referenced_type)
.finish(),
}
}
GenericType {
name: String,
generic_symbol: String,
nature: TypeNature,
},
}

impl DataType {
pub fn set_name(&mut self, new_name: String) {
match self {
DataType::StructType { name, variables: _ } => *name = Some(new_name),
DataType::EnumType { name, elements: _ } => *name = Some(new_name),
DataType::SubRangeType { name, .. } => *name = Some(new_name),
DataType::ArrayType { name, .. } => *name = Some(new_name),
DataType::PointerType { name, .. } => *name = Some(new_name),
DataType::StringType { name, .. } => *name = Some(new_name),
DataType::StructType { name, .. }
| DataType::EnumType { name, .. }
| DataType::SubRangeType { name, .. }
| DataType::ArrayType { name, .. }
| DataType::PointerType { name, .. }
| DataType::StringType { name, .. } => *name = Some(new_name),
DataType::GenericType { name, .. } => *name = new_name,
DataType::VarArgs { .. } => {} //No names on varargs
}
}

pub fn get_name(&self) -> Option<&str> {
match self {
DataType::StructType { name, variables: _ } => name.as_ref().map(|x| x.as_str()),
DataType::EnumType { name, elements: _ } => name.as_ref().map(|x| x.as_str()),
DataType::ArrayType { name, .. } => name.as_ref().map(|x| x.as_str()),
DataType::PointerType { name, .. } => name.as_ref().map(|x| x.as_str()),
DataType::StringType { name, .. } => name.as_ref().map(|x| x.as_str()),
DataType::SubRangeType { name, .. } => name.as_ref().map(|x| x.as_str()),
match &self {
DataType::StructType { name, .. }
| DataType::EnumType { name, .. }
| DataType::ArrayType { name, .. }
| DataType::PointerType { name, .. }
| DataType::StringType { name, .. }
| DataType::SubRangeType { name, .. } => name.as_ref().map(|x| x.as_str()),
DataType::GenericType { name, .. } => Some(name.as_str()),
DataType::VarArgs { .. } => None,
}
}
Expand Down
67 changes: 64 additions & 3 deletions src/ast/pre_processor.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
// Copyright (c) 2020 Ghaith Hachem and Mathias Rieder

use crate::ast::DataTypeDeclaration;

use super::{
super::ast::{CompilationUnit, DataType, DataTypeDeclaration, UserTypeDeclaration, Variable},
super::ast::{CompilationUnit, DataType, UserTypeDeclaration, Variable},
Pou, SourceRange,
};
use std::vec;
use std::{collections::HashMap, vec};

pub fn pre_process(unit: &mut CompilationUnit) {
//process all local variables from POUs
for mut pou in unit.units.iter_mut() {
//Find all generic types in that pou
let generic_types = preprocess_generic_structs(&mut pou);
unit.types.extend(generic_types);

let all_variables = pou
.variable_blocks
.iter_mut()
Expand Down Expand Up @@ -39,7 +45,9 @@ pub fn pre_process(unit: &mut CompilationUnit) {
for dt in unit.types.iter_mut() {
{
match &mut dt.data_type {
DataType::StructType { name, variables } => {
DataType::StructType {
name, variables, ..
} => {
let name: &str = name.as_ref().map(|it| it.as_str()).unwrap_or("undefined");
variables
.iter_mut()
Expand Down Expand Up @@ -88,6 +96,38 @@ pub fn pre_process(unit: &mut CompilationUnit) {
unit.types.append(&mut new_types);
}

fn preprocess_generic_structs(pou: &mut Pou) -> Vec<UserTypeDeclaration> {
let mut generic_types = HashMap::new();
let mut types = vec![];
for binding in &pou.generics {
let new_name = format!("__{}__{}", pou.name, binding.name);
//Generate a type for the generic
let data_type = UserTypeDeclaration {
data_type: DataType::GenericType {
name: new_name.clone(),
generic_symbol: binding.name.clone(),
nature: binding.nature,
},
initializer: None,
scope: Some(pou.name.clone()),
location: pou.location.clone(),
};
types.push(data_type);
generic_types.insert(binding.name.clone(), new_name);
}
for var in pou
.variable_blocks
.iter_mut()
.flat_map(|it| it.variables.iter_mut())
{
replace_generic_type_name(&mut var.data_type, &generic_types);
}
if let Some(datatype) = pou.return_type.as_mut() {
replace_generic_type_name(datatype, &generic_types);
};
types
}

fn preprocess_return_type(pou: &mut Pou, types: &mut Vec<UserTypeDeclaration>) {
if let Some(return_type) = &pou.return_type {
if should_generate_implicit(return_type) {
Expand Down Expand Up @@ -185,3 +225,24 @@ fn add_nested_datatypes(
});
}
}

fn replace_generic_type_name(dt: &mut DataTypeDeclaration, generics: &HashMap<String, String>) {
match dt {
DataTypeDeclaration::DataTypeDefinition { data_type, .. } => match data_type {
DataType::ArrayType {
referenced_type, ..
}
| DataType::PointerType {
referenced_type, ..
} => replace_generic_type_name(referenced_type.as_mut(), generics),
_ => {}
},
DataTypeDeclaration::DataTypeReference {
referenced_type, ..
} => {
if let Some(type_name) = generics.get(referenced_type) {
*referenced_type = type_name.clone();
}
}
}
}
8 changes: 6 additions & 2 deletions src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,12 @@ impl<'ink> CodeGen<'ink> {

//Generate the POU stubs in the first go to make sure they can be referenced.
for implementation in &unit.implementations {
//Don't generate external functions
if implementation.linkage != LinkageType::External {
//Don't generate external or generic functions
if implementation.linkage != LinkageType::External
&& !global_index
.get_type_information_or_void(&implementation.type_name)
.is_generic()
{
pou_generator.generate_implementation(implementation)?;
}
}
Expand Down
Loading

0 comments on commit ca2ebac

Please sign in to comment.