Skip to content

Commit

Permalink
Ast simplification (PLC-lang#963)
Browse files Browse the repository at this point in the history
simplilfy AST by introducing AstNode and Structs for AstStatements

the AstStatement-Enum no longer defines the data-structs in the enum itself,
we now offer dedicated structs for every AstStatement to allow trait-impls.

The AstId and the location are now stored in its own AstNode and no longer
in a stores it as part of the AstStatement.

renamed ast structs to AstNode and AstStatement
  • Loading branch information
riederm authored Sep 8, 2023
1 parent 87663fb commit a28506f
Show file tree
Hide file tree
Showing 54 changed files with 2,306 additions and 2,392 deletions.
964 changes: 506 additions & 458 deletions compiler/plc_ast/src/ast.rs

Large diffs are not rendered by default.

26 changes: 13 additions & 13 deletions compiler/plc_ast/src/control_statements.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
use std::fmt::{Debug, Formatter};

use crate::ast::AstStatement;
use crate::ast::AstNode;

#[derive(Clone, PartialEq)]
pub struct IfStatement {
pub blocks: Vec<ConditionalBlock>,
pub else_block: Vec<AstStatement>,
pub else_block: Vec<AstNode>,
}

#[derive(Clone, PartialEq)]
pub struct ForLoopStatement {
pub counter: Box<AstStatement>,
pub start: Box<AstStatement>,
pub end: Box<AstStatement>,
pub by_step: Option<Box<AstStatement>>,
pub body: Vec<AstStatement>,
pub counter: Box<AstNode>,
pub start: Box<AstNode>,
pub end: Box<AstNode>,
pub by_step: Option<Box<AstNode>>,
pub body: Vec<AstNode>,
}

#[derive(Clone, PartialEq)]
/// used for While and Repeat loops
pub struct LoopStatement {
pub condition: Box<AstStatement>,
pub body: Vec<AstStatement>,
pub condition: Box<AstNode>,
pub body: Vec<AstNode>,
}

#[derive(Clone, PartialEq)]
pub struct CaseStatement {
pub selector: Box<AstStatement>,
pub selector: Box<AstNode>,
pub case_blocks: Vec<ConditionalBlock>,
pub else_block: Vec<AstStatement>,
pub else_block: Vec<AstNode>,
}

#[derive(Clone, PartialEq)]
Expand All @@ -42,8 +42,8 @@ pub enum AstControlStatement {

#[derive(Clone, PartialEq)]
pub struct ConditionalBlock {
pub condition: Box<AstStatement>,
pub body: Vec<AstStatement>,
pub condition: Box<AstNode>,
pub body: Vec<AstNode>,
}

impl Debug for ConditionalBlock {
Expand Down
8 changes: 4 additions & 4 deletions compiler/plc_ast/src/literals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::fmt::{Debug, Formatter};

use chrono::NaiveDate;

use crate::ast::AstStatement;
use crate::ast::AstNode;

macro_rules! impl_getters {
($type:ty, [$($name:ident),+], [$($out:ty),+]) => {
Expand Down Expand Up @@ -84,7 +84,7 @@ pub struct StringValue {

#[derive(Clone, PartialEq)]
pub struct Array {
pub elements: Option<Box<AstStatement>>, // expression-list
pub elements: Option<Box<AstNode>>, // expression-list
}

/// calculates the nanoseconds since 1970-01-01-00:00:00 for the given
Expand Down Expand Up @@ -170,14 +170,14 @@ impl Time {
}

impl Array {
pub fn elements(&self) -> Option<&AstStatement> {
pub fn elements(&self) -> Option<&AstNode> {
self.elements.as_ref().map(|it| it.as_ref())
}
}

impl AstLiteral {
/// Creates a new literal array
pub fn new_array(elements: Option<Box<AstStatement>>) -> Self {
pub fn new_array(elements: Option<Box<AstNode>>) -> Self {
AstLiteral::Array(Array { elements })
}
/// Creates a new literal integer
Expand Down
53 changes: 28 additions & 25 deletions compiler/plc_ast/src/pre_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use plc_util::convention::internal_type_name;

use crate::{
ast::{
flatten_expression_list, AstFactory, AstStatement, CompilationUnit, DataType, DataTypeDeclaration,
Operator, Pou, UserTypeDeclaration, Variable,
flatten_expression_list, Assignment, AstFactory, AstNode, AstStatement, CompilationUnit, DataType,
DataTypeDeclaration, Operator, Pou, UserTypeDeclaration, Variable,
},
literals::AstLiteral,
provider::IdProvider,
Expand Down Expand Up @@ -80,27 +80,24 @@ pub fn pre_process(unit: &mut CompilationUnit, mut id_provider: IdProvider) {
}
}
DataType::EnumType { elements, .. }
if matches!(elements, AstStatement::EmptyStatement { .. }) =>
if matches!(elements.stmt, 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() },
);
let _ = std::mem::replace(&mut elements.stmt, AstStatement::ExpressionList(vec![]));
}
DataType::EnumType { elements: original_elements, name: Some(enum_name), .. }
if !matches!(original_elements, AstStatement::EmptyStatement { .. }) =>
if !matches!(original_elements.stmt, AstStatement::EmptyStatement { .. }) =>
{
let mut last_name: Option<String> = None;

fn extract_flat_ref_name(statement: &AstStatement) -> &str {
fn extract_flat_ref_name(statement: &AstNode) -> &str {
statement.get_flat_reference_name().expect("expected assignment")
}

let initialized_enum_elements = flatten_expression_list(original_elements)
.iter()
.map(|it| match it {
AstStatement::Assignment { left, right, .. } => {
.map(|it| match &it.stmt {
AstStatement::Assignment(Assignment { left, right }) => {
//<element-name, initializer, location>
(
extract_flat_ref_name(left.as_ref()),
Expand All @@ -115,28 +112,34 @@ pub fn pre_process(unit: &mut CompilationUnit, mut id_provider: IdProvider) {
build_enum_initializer(&last_name, &location, &mut id_provider, enum_name)
});
last_name = Some(element_name.to_string());
AstStatement::Assignment {
id: id_provider.next_id(),
left: Box::new(AstFactory::create_member_reference(
AstFactory::create_assignment(
AstFactory::create_member_reference(
AstFactory::create_identifier(
element_name,
&location,
id_provider.next_id(),
),
None,
id_provider.next_id(),
)),
right: Box::new(enum_literal),
}
),
enum_literal,
id_provider.next_id(),
)
})
.collect::<Vec<AstStatement>>();
.collect::<Vec<AstNode>>();
// if the enum is empty, we dont change anything
if !initialized_enum_elements.is_empty() {
// we can safely unwrap because we checked the vec
let start_loc =
initialized_enum_elements.first().expect("non empty vec").get_location();
let end_loc =
initialized_enum_elements.iter().last().expect("non empty vec").get_location();
//swap the expression list with our new Assignments
let expression = AstStatement::ExpressionList {
expressions: initialized_enum_elements,
id: id_provider.next_id(),
};
let expression = AstFactory::create_expression_list(
initialized_enum_elements,
start_loc.span(&end_loc),
id_provider.next_id(),
);
let _ = std::mem::replace(original_elements, expression);
}
}
Expand All @@ -152,7 +155,7 @@ fn build_enum_initializer(
location: &SourceLocation,
id_provider: &mut IdProvider,
enum_name: &mut str,
) -> AstStatement {
) -> AstNode {
if let Some(last_element) = last_name.as_ref() {
// generate a `enum#last + 1` statement
let enum_ref = AstFactory::create_identifier(last_element, location, id_provider.next_id());
Expand All @@ -164,11 +167,11 @@ fn build_enum_initializer(
AstFactory::create_binary_expression(
AstFactory::create_cast_statement(type_element, enum_ref, location, id_provider.next_id()),
Operator::Plus,
AstStatement::new_literal(AstLiteral::new_integer(1), id_provider.next_id(), location.clone()),
AstNode::new_literal(AstLiteral::new_integer(1), id_provider.next_id(), location.clone()),
id_provider.next_id(),
)
} else {
AstStatement::new_literal(AstLiteral::new_integer(0), id_provider.next_id(), location.clone())
AstNode::new_literal(AstLiteral::new_integer(0), id_provider.next_id(), location.clone())
}
}

Expand Down
4 changes: 2 additions & 2 deletions compiler/plc_diagnostics/src/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{error::Error, ops::Range};

use plc_ast::ast::{AstStatement, DataTypeDeclaration, DiagnosticInfo, PouType};
use plc_ast::ast::{AstNode, DataTypeDeclaration, DiagnosticInfo, PouType};
use plc_source::source_location::SourceLocation;

use crate::errno::ErrNo;
Expand Down Expand Up @@ -669,7 +669,7 @@ impl Diagnostic {
}
}

pub fn invalid_range_statement(entity: &AstStatement, range: SourceLocation) -> Diagnostic {
pub fn invalid_range_statement(entity: &AstNode, range: SourceLocation) -> Diagnostic {
Diagnostic::SyntaxError {
message: format!("Expected a range statement, got {entity:?} instead"),
range: vec![range],
Expand Down
2 changes: 1 addition & 1 deletion compiler/plc_driver/src/pipelines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ impl IndexedProject {

/// A project that has been annotated with information about different types and used units
pub struct AnnotatedProject {
units: Vec<(CompilationUnit, IndexSet<Dependency>, StringLiterals)>,
pub units: Vec<(CompilationUnit, IndexSet<Dependency>, StringLiterals)>,
index: Index,
annotations: AstAnnotations,
}
Expand Down
1 change: 1 addition & 0 deletions compiler/plc_driver/src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub fn compile<T: Compilable>(context: &CodegenContext, source: T) -> GeneratedM
..Default::default()
};

dbg!(&annotated_project.units[0].0);
annotated_project.generate_single_module(context, &compile_options).unwrap().unwrap()
}

Expand Down
9 changes: 5 additions & 4 deletions compiler/plc_xml/src/xml_parser.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use ast::{
ast::{AstId, AstStatement, CompilationUnit, Implementation, LinkageType, PouType as AstPouType},
ast::{AstId, AstNode, CompilationUnit, Implementation, LinkageType, PouType as AstPouType},
provider::IdProvider,
};
use plc::{lexer, parser::expressions_parser::parse_expression};
Expand Down Expand Up @@ -119,14 +119,15 @@ impl<'parse> ParseSession<'parse> {
))
}

fn parse_expression(&self, expr: &str, local_id: usize, execution_order: Option<usize>) -> AstStatement {
let exp = parse_expression(&mut lexer::lex_with_ids(
fn parse_expression(&self, expr: &str, local_id: usize, execution_order: Option<usize>) -> AstNode {
let mut exp = parse_expression(&mut lexer::lex_with_ids(
html_escape::decode_html_entities_to_string(expr, &mut String::new()),
self.id_provider.clone(),
self.range_factory.clone(),
));
let loc = exp.get_location();
exp.set_location(self.range_factory.create_block_location(local_id, execution_order).span(&loc))
exp.set_location(self.range_factory.create_block_location(local_id, execution_order).span(&loc));
exp
}

fn parse_model(&self) -> Vec<Implementation> {
Expand Down
4 changes: 2 additions & 2 deletions compiler/plc_xml/src/xml_parser/action.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use ast::ast::{AstStatement, Implementation, PouType as AstPouType};
use ast::ast::{AstNode, Implementation, PouType as AstPouType};

use crate::model::action::Action;

use super::ParseSession;

impl Action {
pub(crate) fn transform(&self, _session: &ParseSession) -> Vec<AstStatement> {
pub(crate) fn transform(&self, _session: &ParseSession) -> Vec<AstNode> {
todo!()
}

Expand Down
4 changes: 2 additions & 2 deletions compiler/plc_xml/src/xml_parser/block.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use ast::ast::{AstFactory, AstStatement};
use ast::ast::{AstFactory, AstNode};

use crate::model::{block::Block, fbd::NodeIndex};

use super::ParseSession;

impl Block {
pub(crate) fn transform(&self, session: &ParseSession, index: &NodeIndex) -> AstStatement {
pub(crate) fn transform(&self, session: &ParseSession, index: &NodeIndex) -> AstNode {
let parameters = self
.variables
.iter()
Expand Down
19 changes: 6 additions & 13 deletions compiler/plc_xml/src/xml_parser/fbd.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use ast::ast::AstStatement;
use ast::ast::{AstFactory, AstNode, AstStatement};
use indexmap::IndexMap;

use crate::model::fbd::{FunctionBlockDiagram, Node, NodeId};
Expand All @@ -8,7 +8,7 @@ use super::ParseSession;
impl FunctionBlockDiagram {
/// Transforms the body of a function block diagram to their AST-equivalent, in order of execution.
/// Only statements that are necessary for execution logic will be selected.
pub(crate) fn transform(&self, session: &ParseSession) -> Vec<AstStatement> {
pub(crate) fn transform(&self, session: &ParseSession) -> Vec<AstNode> {
let mut ast_association = IndexMap::new();
// transform each node to an ast-statement. since we might see and transform a node multiple times, we use an
// ast-association map to keep track of the latest statement for each id
Expand All @@ -34,8 +34,8 @@ impl FunctionBlockDiagram {
&self,
id: NodeId,
session: &ParseSession,
ast_association: &IndexMap<usize, AstStatement>,
) -> (AstStatement, Option<NodeId>) {
ast_association: &IndexMap<usize, AstNode>,
) -> (AstNode, Option<NodeId>) {
let Some(current_node) = self.nodes.get(&id) else { unreachable!() };

match current_node {
Expand All @@ -51,22 +51,15 @@ impl FunctionBlockDiagram {
let (rhs, remove_id) = ast_association
.get(&ref_id)
.map(|stmt| {
if matches!(stmt, AstStatement::CallStatement { .. }) {
if matches!(stmt.get_stmt(), AstStatement::CallStatement(..)) {
(stmt.clone(), Some(ref_id))
} else {
self.transform_node(ref_id, session, ast_association)
}
})
.expect("Expected AST statement, found None");

(
AstStatement::Assignment {
left: Box::new(lhs),
right: Box::new(rhs),
id: session.next_id(),
},
remove_id,
)
(AstFactory::create_assignment(lhs, rhs, session.next_id()), remove_id)
}
Node::Control(_) => todo!(),
Node::Connector(_) => todo!(),
Expand Down
4 changes: 2 additions & 2 deletions compiler/plc_xml/src/xml_parser/pou.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use ast::ast::{AstStatement, Implementation};
use ast::ast::{AstNode, Implementation};

use crate::model::pou::Pou;

use super::ParseSession;

impl Pou {
fn transform(&self, session: &ParseSession) -> Vec<AstStatement> {
fn transform(&self, session: &ParseSession) -> Vec<AstNode> {
let Some(fbd) = &self.body.function_block_diagram else {
// empty body
return vec![];
Expand Down
19 changes: 11 additions & 8 deletions compiler/plc_xml/src/xml_parser/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use ast::{
ast::{flatten_expression_list, AstStatement, CompilationUnit, LinkageType},
ast::{
flatten_expression_list, Assignment, AstNode, AstStatement, CallStatement, CompilationUnit,
LinkageType,
},
provider::IdProvider,
};
use insta::assert_debug_snapshot;
Expand Down Expand Up @@ -129,18 +132,18 @@ fn ast_generates_locations() {
let (units, diagnostics) = xml_parser::parse(&source_code, LinkageType::Internal, IdProvider::default());
let impl1 = &units.implementations[0];
//Deconstruct assignment and get locations
let AstStatement::Assignment { left, right, .. } = &impl1.statements[0] else {
panic!("Not an assignment");
};
let AstStatement::Assignment (Assignment{ left, right, .. })= &impl1.statements[0].get_stmt() else {
panic!("Not an assignment");
};
assert_debug_snapshot!(left.get_location());
assert_debug_snapshot!(right.get_location());
//Deconstruct call statement and get locations
let AstStatement::CallStatement { operator, parameters, location, .. } = &impl1.statements[1] else {
panic!("Not a call statement");
};
let AstNode { stmt: AstStatement::CallStatement (CallStatement{ operator, parameters, .. }), location, ..} = &impl1.statements[1] else {
panic!("Not a call statement");
};
assert_debug_snapshot!(location);
assert_debug_snapshot!(operator.get_location());
let parameters = parameters.as_ref().as_ref().unwrap();
let parameters = parameters.as_deref().unwrap();
let parameters = flatten_expression_list(parameters);
for param in parameters {
assert_debug_snapshot!(param.get_location());
Expand Down
Loading

0 comments on commit a28506f

Please sign in to comment.