Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 277 named / type defined enums #404

Merged
merged 5 commits into from
Dec 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 84 additions & 4 deletions src/ast.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (c) 2020 Ghaith Hachem and Mathias Rieder
use crate::typesystem::DataTypeInformation;
use crate::{lexer::IdProvider, typesystem::DataTypeInformation};
use std::{
fmt::{Debug, Display, Formatter, Result},
iter,
Expand Down Expand Up @@ -442,7 +442,8 @@ pub enum DataType {
},
EnumType {
name: Option<String>, //maybe empty for inline enums
elements: Vec<String>,
numeric_type: String,
elements: AstStatement, //a single Ref, or an ExpressionList with Refs
},
SubRangeType {
name: Option<String>,
Expand Down Expand Up @@ -1150,6 +1151,41 @@ impl Display for Operator {
}
}

/// enum_elements should be the statement between then enum's brackets ( )
/// e.g. x : ( this, that, etc)
pub fn get_enum_element_names(enum_elements: &AstStatement) -> Vec<String> {
flatten_expression_list(enum_elements)
.into_iter()
.filter(|it| {
matches!(
it,
AstStatement::Reference { .. } | AstStatement::Assignment { .. }
)
})
.map(get_enum_element_name)
.collect()
}

/// expects a Reference or an Assignment
pub fn get_enum_element_name(enum_element: &AstStatement) -> String {
match enum_element {
AstStatement::Reference { name, .. } => name.to_string(),
AstStatement::Assignment { left, .. } => {
if let AstStatement::Reference { name, .. } = left.as_ref() {
name.to_string()
} else {
unreachable!("left of assignment not a reference")
}
}
_ => {
unreachable!(
"expected {:?} to be a Reference or Assignment",
enum_element
);
}
}
}

/// flattens expression-lists and MultipliedStatements into a vec of statements.
/// It can also handle nested structures like 2(3(4,5))
pub fn flatten_expression_list(condition: &AstStatement) -> Vec<&AstStatement> {
Expand Down Expand Up @@ -1214,8 +1250,52 @@ pub fn create_not_expression(operator: AstStatement, location: SourceRange) -> A
}
}

pub fn pre_process(unit: &mut CompilationUnit) {
pre_processor::pre_process(unit)
pub fn create_reference(name: &str, location: &SourceRange, id: AstId) -> AstStatement {
AstStatement::Reference {
id,
location: location.clone(),
name: name.to_string(),
}
}

pub fn create_literal_int(value: i128, location: &SourceRange, id: AstId) -> AstStatement {
AstStatement::LiteralInteger {
id,
location: location.clone(),
value,
}
}

pub fn create_binary_expression(
left: AstStatement,
operator: Operator,
right: AstStatement,
id: AstId,
) -> AstStatement {
AstStatement::BinaryExpression {
id,
left: Box::new(left),
operator,
right: Box::new(right),
}
}

pub fn create_cast_statement(
type_name: &str,
stmt: AstStatement,
location: &SourceRange,
id: AstId,
) -> AstStatement {
AstStatement::CastStatement {
id,
location: location.clone(),
type_name: type_name.to_string(),
target: Box::new(stmt),
}
}

pub fn pre_process(unit: &mut CompilationUnit, id_provider: IdProvider) {
pre_processor::pre_process(unit, id_provider)
}
impl Operator {
/// returns true, if this operator results in a bool value
Expand Down
96 changes: 92 additions & 4 deletions src/ast/pre_processor.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
// Copyright (c) 2020 Ghaith Hachem and Mathias Rieder

use crate::ast::DataTypeDeclaration;
use crate::{ast::DataTypeDeclaration, lexer::IdProvider};

use super::{
super::ast::{CompilationUnit, DataType, UserTypeDeclaration, Variable},
Pou, SourceRange,
super::ast::{CompilationUnit, UserTypeDeclaration, Variable},
create_binary_expression, create_cast_statement, create_literal_int, create_reference,
flatten_expression_list, AstStatement, DataType, Operator, Pou, SourceRange,
};
use std::{collections::HashMap, vec};

pub fn pre_process(unit: &mut CompilationUnit) {
pub fn pre_process(unit: &mut CompilationUnit, mut id_provider: IdProvider) {
//process all local variables from POUs
for mut pou in unit.units.iter_mut() {
//Find all generic types in that pou
Expand Down Expand Up @@ -89,13 +90,100 @@ pub fn pre_process(unit: &mut CompilationUnit) {
new_types.push(data_type);
}
}
DataType::EnumType { elements, .. }
if matches!(elements, AstStatement::EmptyStatement { .. }) =>
{
//avoid empty statements, just use an empty expression list to make it easier to work with
let _ = std::mem::replace(
elements,
AstStatement::ExpressionList {
expressions: vec![],
id: id_provider.next_id(),
},
);
}
DataType::EnumType {
elements: original_elements,
name: Some(enum_name),
..
} if !matches!(original_elements, AstStatement::EmptyStatement { .. }) => {
let mut last_name: Option<String> = None;
let initialized_enum_elements = flatten_expression_list(original_elements)
.iter()
.map(|it| match it {
AstStatement::Reference { name, .. } => {
(name.clone(), None, it.get_location())
}
AstStatement::Assignment { left, right, .. } => {
let name =
if let AstStatement::Reference { name, .. } = left.as_ref() {
name.clone()
} else {
unreachable!("expected reference, got {:?}", left.as_ref())
};
//<element-name, initializer, location>
(name, Some(*right.clone()), it.get_location())
}
_ => unreachable!("expected assignment, got {:?}", it),
})
.map(|(element_name, initializer, location)| {
let enum_literal = initializer.unwrap_or_else(|| {
build_enum_initializer(
&last_name,
&location,
&mut id_provider,
enum_name,
)
});
last_name = Some(element_name.clone());
AstStatement::Assignment {
id: id_provider.next_id(),
left: Box::new(AstStatement::Reference {
id: id_provider.next_id(),
name: element_name,
location,
}),
right: Box::new(enum_literal),
}
})
.collect::<Vec<AstStatement>>();
// if the enum is empty, we dont change anything
if !initialized_enum_elements.is_empty() {
//swap the expression list with our new Assignments
let expression = AstStatement::ExpressionList {
expressions: initialized_enum_elements,
id: id_provider.next_id(),
};
let _ = std::mem::replace(original_elements, expression);
}
}
_ => {}
}
}
}
unit.types.append(&mut new_types);
}

fn build_enum_initializer(
last_name: &Option<String>,
location: &SourceRange,
id_provider: &mut IdProvider,
enum_name: &mut String,
) -> AstStatement {
if let Some(last_element) = last_name.as_ref() {
// generate a `enum#last + 1` statement
let enum_ref = create_reference(last_element, location, id_provider.next_id());
create_binary_expression(
create_cast_statement(enum_name, enum_ref, location, id_provider.next_id()),
Operator::Plus,
create_literal_int(1, location, id_provider.next_id()),
id_provider.next_id(),
)
} else {
create_literal_int(0, location, id_provider.next_id())
}
}

fn preprocess_generic_structs(pou: &mut Pou) -> Vec<UserTypeDeclaration> {
let mut generic_types = HashMap::new();
let mut types = vec![];
Expand Down
24 changes: 21 additions & 3 deletions src/codegen/generators/data_type_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,27 @@ impl<'ink, 'b> DataTypeGenerator<'ink, 'b> {
DataTypeInformation::Integer { size, .. } => {
get_llvm_int_type(self.llvm.context, *size, name).map(|it| it.into())
}
DataTypeInformation::Enum { name, .. } => {
let enum_size = information.get_size();
get_llvm_int_type(self.llvm.context, enum_size, name).map(|it| it.into())
DataTypeInformation::Enum {
name,
referenced_type,
..
} => {
let effective_type = self
.index
.get_effective_type_by_name(referenced_type)
.get_type_information();
if let DataTypeInformation::Integer {
size: enum_size, ..
} = effective_type
{
get_llvm_int_type(self.llvm.context, *enum_size, name).map(|it| it.into())
} else {
Err(Diagnostic::invalid_type_nature(
effective_type.get_name(),
"ANY_INT",
SourceRange::undefined(),
))
}
}
DataTypeInformation::Float { size, .. } => {
get_llvm_float_type(self.llvm.context, *size, name).map(|it| it.into())
Expand Down
8 changes: 4 additions & 4 deletions src/codegen/llvm_typesystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,12 @@ pub fn cast_if_needed<'ctx>(
) -> Result<BasicValueEnum<'ctx>, Diagnostic> {
let builder = &llvm.builder;
let target_type = index
.find_effective_type_info(target_type.get_name())
.unwrap_or_else(|| index.get_void_type().get_type_information());
.get_intrinsic_type_by_name(target_type.get_name())
.get_type_information();

let value_type = index
.find_effective_type_info(value_type.get_name())
.unwrap_or_else(|| index.get_void_type().get_type_information());
.get_intrinsic_type_by_name(value_type.get_name())
.get_type_information();

match target_type {
DataTypeInformation::Integer {
Expand Down
71 changes: 71 additions & 0 deletions src/codegen/tests/code_gen_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3992,6 +3992,77 @@ source_filename = "main"
assert_eq!(result, expected);
}

#[test]
fn typed_enums_are_generated() {
let result = codegen(
"
TYPE MyEnum: BYTE(red, yellow, green);
END_TYPE

TYPE MyEnum2: UINT(red, yellow, green);
END_TYPE

TYPE MyEnum3: DINT(red, yellow, green);
END_TYPE

VAR_GLOBAL
x : MyEnum;
y : MyEnum2;
z : MyEnum3;
END_VAR
",
);

insta::assert_snapshot!(result);
}

#[test]
fn typed_enums_with_initializers_are_generated() {
let result = codegen(
"
TYPE MyEnum: BYTE(red := 1, yellow := 2, green := 3);
END_TYPE

TYPE MyEnum2: UINT(red := 10, yellow := 11, green := 12);
END_TYPE

TYPE MyEnum3: DINT(red := 22, yellow := 33, green := 44);
END_TYPE

VAR_GLOBAL
x : MyEnum;
y : MyEnum2;
z : MyEnum3;
END_VAR
",
);

insta::assert_snapshot!(result);
}

#[test]
fn typed_enums_with_partly_initializers_are_generated() {
let result = codegen(
"
VAR_GLOBAL CONSTANT
twenty : INT := 20;
END_VAR

TYPE MyEnum: BYTE(red := 7, yellow, green);
END_TYPE

TYPE MyEnum: BYTE(a,b,c:=7,d,e,f:=twenty,g);
END_TYPE

VAR_GLOBAL
x : MyEnum;
END_VAR
",
);

insta::assert_snapshot!(result);
}

#[test]
fn enums_custom_type_are_generated() {
let result = codegen(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
source: src/codegen/tests/code_gen_tests.rs
expression: result

---
; ModuleID = 'main'
source_filename = "main"

@x = global i8 0
@y = global i16 0
@z = global i32 0
@red = global i8 0
@yellow = global i8 1
@green = global i8 2
@red.1 = global i16 0
@yellow.2 = global i16 1
@green.3 = global i16 2
@red.4 = global i32 0
@yellow.5 = global i32 1
@green.6 = global i32 2

Loading