Skip to content
Open
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
4 changes: 4 additions & 0 deletions compiler/plc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,10 @@ impl VariableBlock {
pub fn is_temp(&self) -> bool {
matches!(self.kind, VariableBlockType::Temp)
}

pub fn is_input_by_val(&self) -> bool {
matches!(self.kind, VariableBlockType::Input(ArgumentProperty::ByVal))
}
}

impl Default for VariableBlock {
Expand Down
128 changes: 109 additions & 19 deletions compiler/plc_ast/src/ser.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,55 @@
use crate::{
ast::{
Allocation, Assignment, AstNode, AstStatement, BinaryExpression, CallStatement, CompilationUnit,
ConfigVariable, DataType, DataTypeDeclaration, DefaultValue, DirectAccess, EmptyStatement,
HardwareAccess, Implementation, Interface, JumpStatement, LabelStatement, MultipliedStatement, Pou,
PropertyBlock, RangeStatement, ReferenceAccess, ReferenceExpr, UnaryExpression, UserTypeDeclaration,
Variable, VariableBlock,
Allocation, ArgumentProperty, Assignment, AstNode, AstStatement, AutoDerefType, BinaryExpression,
CallStatement, CompilationUnit, ConfigVariable, DataType, DataTypeDeclaration, DefaultValue,
DirectAccess, EmptyStatement, HardwareAccess, Implementation, Interface, JumpStatement,
LabelStatement, MultipliedStatement, Pou, PropertyBlock, RangeStatement, ReferenceAccess,
ReferenceExpr, UnaryExpression, UserTypeDeclaration, Variable, VariableBlock, VariableBlockType,
},
control_statements::{AstControlStatement, ReturnStatement},
literals::AstLiteral,
visitor::{AstVisitor, Walker},
};

pub struct AstSerializer {
pub struct AstSerializer<'a> {
result: String,
indent: usize,
unit: Option<&'a CompilationUnit>,
user_type_context: Option<&'a UserTypeDeclaration>,
}

impl AstSerializer {
impl AstSerializer<'_> {
pub fn format(node: &AstNode) -> String {
let mut serializer = AstSerializer { result: String::new(), indent: 0 };
let mut serializer =
AstSerializer { result: String::new(), indent: 0, unit: None, user_type_context: None };
serializer.visit(node);

serializer.result
}

pub fn format_nodes(nodes: &[AstNode]) -> String {
let mut serializer =
AstSerializer { result: String::new(), indent: 0, unit: None, user_type_context: None };

for (index, node) in nodes.iter().enumerate() {
if index > 0 {
serializer.result.push('\n');
}
serializer.visit(node);
serializer.result.push(';');
}

serializer.result
}

pub fn format_variable_block(variable_block: &VariableBlock, unit: &CompilationUnit) -> String {
let mut serializer =
AstSerializer { result: String::new(), indent: 0, unit: Some(unit), user_type_context: None };
serializer.visit_variable_block(variable_block);

serializer.result
}

/// Serializes a list of statements, each on its own indented line.
fn serialize_statement_list(&mut self, stmts: &[AstNode]) {
self.indent += 1;
Expand All @@ -41,9 +67,18 @@ impl AstSerializer {
self.result.push_str(" ");
}
}

fn set_user_type_declaration_context(&mut self, type_name: &str) {
let Some(unit) = self.unit else {
panic!("cannot retrieve user type declaration without a compilation unit")
};

self.user_type_context =
unit.user_types.iter().find(|it| it.data_type.get_name().is_some_and(|name| name == type_name));
}
}

impl AstVisitor for AstSerializer {
impl AstVisitor for AstSerializer<'_> {
fn visit(&mut self, node: &AstNode) {
node.walk(self)
}
Expand All @@ -56,12 +91,32 @@ impl AstVisitor for AstSerializer {
unimplemented!("for now only interested in individual nodes located in a POU body")
}

fn visit_variable_block(&mut self, _: &VariableBlock) {
unimplemented!("for now only interested in individual nodes located in a POU body")
fn visit_variable_block(&mut self, variable_block: &VariableBlock) {
let var_start = match variable_block.kind {
VariableBlockType::InOut => "VAR_IN_OUT",
VariableBlockType::Input(ArgumentProperty::ByVal) => "VAR_INPUT",
VariableBlockType::Input(ArgumentProperty::ByRef) => "VAR_INPUT {ref}",
VariableBlockType::Output => "VAR_OUTPUT",
VariableBlockType::Temp => "VAR_TEMP",
VariableBlockType::Global => "VAR_GLOBAL",
_ => "VAR",
};
let var_end: &str = "END_VAR";

self.result.push_str(var_start);
self.indent += 1;
variable_block.variables.iter().for_each(|v| {
self.push_indent();
self.result.push_str(&format!("{} : ", v.name));
v.walk(self);
self.result.push(';');
});
self.indent -= 1;
self.result.push_str(&format!("\n{var_end}"));
}

fn visit_variable(&mut self, _: &Variable) {
unimplemented!("for now only interested in individual nodes located in a POU body")
fn visit_variable(&mut self, variable: &Variable) {
variable.data_type_declaration.walk(self);
}

fn visit_config_variable(&mut self, _: &ConfigVariable) {
Expand All @@ -80,16 +135,51 @@ impl AstVisitor for AstSerializer {
element.walk(self);
}

fn visit_data_type_declaration(&mut self, _: &DataTypeDeclaration) {
unimplemented!("for now only interested in individual nodes located in a POU body")
fn visit_data_type_declaration(&mut self, data_type_declaration: &DataTypeDeclaration) {
match data_type_declaration {
DataTypeDeclaration::Reference { referenced_type, .. } => {
self.set_user_type_declaration_context(referenced_type);

if let Some(user_type_declaration) = self.user_type_context {
self.visit_user_type_declaration(user_type_declaration);
} else {
self.result.push_str(referenced_type);
}
}
DataTypeDeclaration::Definition { data_type, .. } => {
data_type.as_ref().walk(self);
}
DataTypeDeclaration::Aggregate { referenced_type, .. } => {
self.result.push_str(referenced_type);
}
}
}

fn visit_user_type_declaration(&mut self, _: &UserTypeDeclaration) {
unimplemented!("for now only interested in individual nodes located in a POU body")
fn visit_user_type_declaration(&mut self, user_type_declaration: &UserTypeDeclaration) {
self.visit_data_type(&user_type_declaration.data_type);
}

fn visit_data_type(&mut self, _: &DataType) {
unimplemented!("for now only interested in individual nodes located in a POU body")
fn visit_data_type(&mut self, data_type: &DataType) {
if let DataType::PointerType { referenced_type, auto_deref, type_safe, .. } = data_type {
match auto_deref {
Some(AutoDerefType::Reference) => {
self.result.push_str("REFERENCE TO ");
}
// TODO: We also want to handle these cases at some point
Some(AutoDerefType::Alias) | Some(AutoDerefType::Default) => (),
_ => {
if *type_safe {
self.result.push_str("REF_TO ");
} else {
self.result.push_str("POINTER TO ");
}
}
}

self.visit_data_type_declaration(referenced_type.as_ref());
}

// TODO: For now we aren't interested in non-pointer types, but this should be expanded
}

fn visit_pou(&mut self, _: &Pou) {
Expand Down
3 changes: 2 additions & 1 deletion compiler/plc_driver/src/pipelines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use plc_header_generator::{
use plc_index::GlobalContext;
use plc_lowering::{
control_statement::ControlStatementParticipant, inheritance::InheritanceLowerer,
retain::RetainParticipant,
reference_to_return::ReferenceToReturnParticipant, retain::RetainParticipant,
};
use project::{
object::Object,
Expand Down Expand Up @@ -305,6 +305,7 @@ impl<T: SourceContainer> BuildPipeline<T> {
self.context.should_generate_external_constructors(),
)),
Box::new(ControlStatementParticipant::new(self.context.provider())),
Box::new(ReferenceToReturnParticipant::new(self.context.provider())),
Box::new(RetainParticipant::new(self.context.provider())),
Box::new(AggregateTypeLowerer::new(self.context.provider())),
Box::new(InheritanceLowerer::new(self.context.provider())),
Expand Down
16 changes: 10 additions & 6 deletions compiler/plc_driver/src/pipelines/participant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,8 @@ use plc::{
};
use plc_diagnostics::diagnostics::Diagnostic;
use plc_lowering::{
array_lowering,
retain::RetainParticipant,
{
control_statement::ControlStatementParticipant, inheritance::InheritanceLowerer,
initializer::Initializer,
},
array_lowering, control_statement::ControlStatementParticipant, inheritance::InheritanceLowerer,
initializer::Initializer, reference_to_return::ReferenceToReturnParticipant, retain::RetainParticipant,
};
use project::{object::Object, project::LibraryInformation};
use source_code::SourceContainer;
Expand Down Expand Up @@ -364,3 +360,11 @@ impl PipelineParticipantMut for ControlStatementParticipant {
ParsedProject { units }
}
}

impl PipelineParticipantMut for ReferenceToReturnParticipant {
fn pre_index(&mut self, parsed_project: ParsedProject) -> ParsedProject {
let ParsedProject { mut units } = parsed_project;
self.lower_reference_to_return(&mut units);
ParsedProject { units }
}
}
2 changes: 2 additions & 0 deletions compiler/plc_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ pub mod control_statement;
pub(crate) mod helper;
pub mod inheritance;
pub mod initializer;
pub mod reference_to_return;
pub mod retain;

#[cfg(test)]
mod tests;

Expand Down
Loading
Loading