Skip to content

Commit b23232d

Browse files
committed
feat: Desugaring of polymorphic method calls
Methods calls which must make use of the virtual table are now desugared. For example a method call within a method such as `foo(1, 2)` will now be desugared into `__vtable_{FB_NAME}#(THIS^.__vtable^).foo^(THIS^, 1, 2)`. Similarly, a variable like `refInstance: POINTER TO FbA` making a method call such as `refInstance^.foo(1, 2)` will be lowered into some similar form, except not making use of THIS, rather of the operator name (excluding the method name), i.e. `__vtable_FbA#(refInstance^.__vtable^).foo(refInstance^, 1, 2)`.
1 parent d9d58f8 commit b23232d

34 files changed

+1370
-164
lines changed

.vscode/launch.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,15 @@
6464
"args": [
6565
"build",
6666
"--bin=plc",
67-
"--package=plc_driver"
67+
"--package=plc_driver",
6868
],
6969
"filter": {
7070
"name": "plc",
7171
"kind": "bin"
7272
}
7373
},
7474
"args": [
75+
"-j=1",
7576
"target/demo.st",
7677
"tests/lit/util/printf.pli",
7778
"-j=1",

compiler/plc_ast/src/ast.rs

Lines changed: 319 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::{
1616
literals::{AstLiteral, StringValue},
1717
pre_processor,
1818
provider::IdProvider,
19+
visitor::{AstVisitor, Walker},
1920
};
2021

2122
use plc_source::source_location::*;
@@ -1318,6 +1319,10 @@ impl AstNode {
13181319
matches!(node.get_stmt_peeled(), AstStatement::Super(Some(_)))
13191320
}
13201321

1322+
pub fn is_super_or_super_deref(&self) -> bool {
1323+
self.is_super() || self.is_super_deref()
1324+
}
1325+
13211326
pub fn has_super_metadata(&self) -> bool {
13221327
self.get_metadata()
13231328
.or_else(|| self.get_identifier().and_then(|it| it.get_metadata()))
@@ -1424,6 +1429,30 @@ impl AstNode {
14241429
AstNode { metadata: Some(metadata), ..self }
14251430
}
14261431

1432+
pub fn is_deref(&self) -> bool {
1433+
matches!(
1434+
self,
1435+
AstNode {
1436+
stmt: AstStatement::ReferenceExpr(ReferenceExpr { access: ReferenceAccess::Deref, .. }),
1437+
..
1438+
}
1439+
)
1440+
}
1441+
1442+
pub fn get_call_operator(&self) -> Option<&AstNode> {
1443+
match &self.stmt {
1444+
AstStatement::CallStatement(CallStatement { operator, .. }) => Some(operator),
1445+
_ => None,
1446+
}
1447+
}
1448+
1449+
pub fn get_ref_expr_mut(&mut self) -> Option<&mut ReferenceExpr> {
1450+
match &mut self.stmt {
1451+
AstStatement::ReferenceExpr(expr) => Some(expr),
1452+
_ => None,
1453+
}
1454+
}
1455+
14271456
pub fn get_deref_expr(&self) -> Option<&ReferenceExpr> {
14281457
match &self.stmt {
14291458
AstStatement::ReferenceExpr(expr) => match expr {
@@ -1433,6 +1462,255 @@ impl AstNode {
14331462
_ => None,
14341463
}
14351464
}
1465+
1466+
pub fn get_base(&self) -> Option<&AstNode> {
1467+
match &self.stmt {
1468+
AstStatement::ReferenceExpr(ReferenceExpr { base: Some(base), .. }) => Some(base.as_ref()),
1469+
_ => None,
1470+
}
1471+
}
1472+
1473+
pub fn get_base_mut(&mut self) -> Option<&mut AstNode> {
1474+
match &mut self.stmt {
1475+
AstStatement::ReferenceExpr(ReferenceExpr { base: Some(base), .. }) => Some(base.as_mut()),
1476+
_ => None,
1477+
}
1478+
}
1479+
1480+
pub fn as_string(&self) -> String {
1481+
let mut formatter = StringFormatter { result: String::new() };
1482+
formatter.visit(self);
1483+
formatter.result
1484+
}
1485+
1486+
pub fn as_string_mut(&mut self) -> String {
1487+
let mut formatter = StringFormatter { result: String::new() };
1488+
formatter.visit(self);
1489+
formatter.result
1490+
}
1491+
}
1492+
1493+
// TODO: Move into own file, rename to AstSerializer and implement some tests
1494+
struct StringFormatter {
1495+
result: String,
1496+
}
1497+
1498+
impl AstVisitor for StringFormatter {
1499+
fn visit(&mut self, node: &AstNode) {
1500+
node.walk(self)
1501+
}
1502+
1503+
fn visit_compilation_unit(&mut self, _: &CompilationUnit) {
1504+
unimplemented!("for now only interested in individual nodes located in a POU body")
1505+
}
1506+
1507+
fn visit_implementation(&mut self, _: &Implementation) {
1508+
unimplemented!("for now only interested in individual nodes located in a POU body")
1509+
}
1510+
1511+
fn visit_variable_block(&mut self, _: &VariableBlock) {
1512+
unimplemented!("for now only interested in individual nodes located in a POU body")
1513+
}
1514+
1515+
fn visit_variable(&mut self, _: &Variable) {
1516+
unimplemented!("for now only interested in individual nodes located in a POU body")
1517+
}
1518+
1519+
fn visit_config_variable(&mut self, _: &ConfigVariable) {
1520+
unimplemented!("for now only interested in individual nodes located in a POU body")
1521+
}
1522+
1523+
fn visit_interface(&mut self, _: &Interface) {
1524+
unimplemented!("for now only interested in individual nodes located in a POU body")
1525+
}
1526+
1527+
fn visit_property(&mut self, _: &PropertyBlock) {
1528+
unimplemented!("for now only interested in individual nodes located in a POU body")
1529+
}
1530+
1531+
fn visit_enum_element(&mut self, element: &AstNode) {
1532+
element.walk(self);
1533+
}
1534+
1535+
fn visit_data_type_declaration(&mut self, _: &DataTypeDeclaration) {
1536+
unimplemented!("for now only interested in individual nodes located in a POU body")
1537+
}
1538+
1539+
fn visit_user_type_declaration(&mut self, _: &UserTypeDeclaration) {
1540+
unimplemented!("for now only interested in individual nodes located in a POU body")
1541+
}
1542+
1543+
fn visit_data_type(&mut self, _: &DataType) {
1544+
unimplemented!("for now only interested in individual nodes located in a POU body")
1545+
}
1546+
1547+
fn visit_pou(&mut self, _: &Pou) {
1548+
unimplemented!("for now only interested in individual nodes located in a POU body")
1549+
}
1550+
1551+
fn visit_empty_statement(&mut self, _stmt: &EmptyStatement, _node: &AstNode) {}
1552+
1553+
fn visit_default_value(&mut self, _stmt: &DefaultValue, _node: &AstNode) {}
1554+
1555+
fn visit_literal(&mut self, stmt: &AstLiteral, _node: &AstNode) {
1556+
use crate::literals::AstLiteral;
1557+
match stmt {
1558+
AstLiteral::Integer(value) => self.result.push_str(&value.to_string()),
1559+
AstLiteral::Real(value) => self.result.push_str(value),
1560+
AstLiteral::Bool(value) => self.result.push_str(&value.to_string().to_uppercase()),
1561+
AstLiteral::String(string_value) => {
1562+
if string_value.is_wide {
1563+
self.result.push_str(&format!("\"{}\"", string_value.value));
1564+
} else {
1565+
self.result.push_str(&format!("'{}'", string_value.value));
1566+
}
1567+
}
1568+
AstLiteral::Null => self.result.push_str("NULL"),
1569+
_ => stmt.walk(self), // Let other literals use their default walking behavior
1570+
}
1571+
}
1572+
1573+
fn visit_multiplied_statement(&mut self, stmt: &MultipliedStatement, _node: &AstNode) {
1574+
stmt.walk(self)
1575+
}
1576+
1577+
fn visit_reference_expr(&mut self, stmt: &ReferenceExpr, _node: &AstNode) {
1578+
if let Some(base) = &stmt.base {
1579+
base.walk(self);
1580+
}
1581+
1582+
match &stmt.access {
1583+
ReferenceAccess::Global(reference) => {
1584+
self.result.push('.');
1585+
reference.walk(self);
1586+
}
1587+
ReferenceAccess::Member(reference) => {
1588+
if stmt.base.is_some() {
1589+
self.result.push('.');
1590+
}
1591+
reference.walk(self);
1592+
}
1593+
ReferenceAccess::Index(index) => {
1594+
self.result.push('[');
1595+
index.walk(self);
1596+
self.result.push(']');
1597+
}
1598+
ReferenceAccess::Cast(reference) => {
1599+
self.result.push('#');
1600+
reference.walk(self);
1601+
}
1602+
ReferenceAccess::Deref => {
1603+
self.result.push('^');
1604+
}
1605+
ReferenceAccess::Address => {
1606+
self.result.insert_str(0, "ADR(");
1607+
self.result.push(')');
1608+
}
1609+
}
1610+
}
1611+
1612+
fn visit_identifier(&mut self, stmt: &str, _node: &AstNode) {
1613+
self.result.push_str(stmt);
1614+
}
1615+
1616+
fn visit_direct_access(&mut self, stmt: &DirectAccess, _node: &AstNode) {
1617+
stmt.walk(self)
1618+
}
1619+
1620+
fn visit_hardware_access(&mut self, stmt: &HardwareAccess, _node: &AstNode) {
1621+
stmt.walk(self)
1622+
}
1623+
1624+
fn visit_binary_expression(&mut self, stmt: &BinaryExpression, _node: &AstNode) {
1625+
stmt.left.walk(self);
1626+
self.result.push(' ');
1627+
self.result.push_str(&stmt.operator.to_string());
1628+
self.result.push(' ');
1629+
stmt.right.walk(self);
1630+
}
1631+
1632+
fn visit_unary_expression(&mut self, stmt: &UnaryExpression, _node: &AstNode) {
1633+
self.result.push_str(&stmt.operator.to_string());
1634+
stmt.value.walk(self);
1635+
}
1636+
1637+
fn visit_expression_list(&mut self, stmt: &Vec<AstNode>, _node: &AstNode) {
1638+
for (i, node) in stmt.iter().enumerate() {
1639+
if i > 0 {
1640+
self.result.push_str(", ");
1641+
}
1642+
node.walk(self);
1643+
}
1644+
}
1645+
1646+
fn visit_paren_expression(&mut self, inner: &AstNode, _node: &AstNode) {
1647+
self.result.push('(');
1648+
inner.walk(self);
1649+
self.result.push(')');
1650+
}
1651+
1652+
fn visit_range_statement(&mut self, stmt: &RangeStatement, _node: &AstNode) {
1653+
stmt.walk(self)
1654+
}
1655+
1656+
fn visit_vla_range_statement(&mut self, _node: &AstNode) {}
1657+
1658+
fn visit_assignment(&mut self, stmt: &Assignment, _node: &AstNode) {
1659+
stmt.left.walk(self);
1660+
self.result.push_str(" := ");
1661+
stmt.right.walk(self);
1662+
}
1663+
1664+
fn visit_output_assignment(&mut self, stmt: &Assignment, _node: &AstNode) {
1665+
stmt.left.walk(self);
1666+
self.result.push_str(" => ");
1667+
stmt.right.walk(self);
1668+
}
1669+
1670+
fn visit_ref_assignment(&mut self, stmt: &Assignment, _node: &AstNode) {
1671+
stmt.left.walk(self);
1672+
self.result.push_str(" REF= ");
1673+
stmt.right.walk(self);
1674+
}
1675+
1676+
fn visit_call_statement(&mut self, stmt: &CallStatement, _node: &AstNode) {
1677+
stmt.operator.walk(self);
1678+
self.result.push_str("(");
1679+
stmt.parameters.as_ref().map(|opt| opt.walk(self));
1680+
self.result.push_str(")");
1681+
}
1682+
1683+
fn visit_control_statement(&mut self, stmt: &AstControlStatement, _node: &AstNode) {
1684+
stmt.walk(self)
1685+
}
1686+
1687+
fn visit_case_condition(&mut self, child: &AstNode, _node: &AstNode) {
1688+
child.walk(self)
1689+
}
1690+
1691+
fn visit_exit_statement(&mut self, _node: &AstNode) {}
1692+
1693+
fn visit_continue_statement(&mut self, _node: &AstNode) {}
1694+
1695+
fn visit_return_statement(&mut self, stmt: &ReturnStatement, _node: &AstNode) {
1696+
stmt.walk(self)
1697+
}
1698+
1699+
fn visit_jump_statement(&mut self, stmt: &JumpStatement, _node: &AstNode) {
1700+
stmt.walk(self)
1701+
}
1702+
1703+
fn visit_label_statement(&mut self, _stmt: &LabelStatement, _node: &AstNode) {}
1704+
1705+
fn visit_allocation(&mut self, _stmt: &Allocation, _node: &AstNode) {}
1706+
1707+
fn visit_super(&mut self, _stmt: &AstStatement, _node: &AstNode) {
1708+
self.result.push_str("SUPER");
1709+
}
1710+
1711+
fn visit_this(&mut self, _stmt: &AstStatement, _node: &AstNode) {
1712+
self.result.push_str("THIS");
1713+
}
14361714
}
14371715

14381716
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -1463,9 +1741,17 @@ impl Display for Operator {
14631741
Operator::Multiplication => "*",
14641742
Operator::Division => "/",
14651743
Operator::Equal => "=",
1744+
Operator::NotEqual => "<>",
14661745
Operator::Modulo => "MOD",
1746+
Operator::Less => "<",
1747+
Operator::Greater => ">",
1748+
Operator::LessOrEqual => "<=",
1749+
Operator::GreaterOrEqual => ">=",
1750+
Operator::Not => "NOT",
1751+
Operator::And => "AND",
1752+
Operator::Or => "OR",
1753+
Operator::Xor => "XOR",
14671754
Operator::Exponentiation => "**",
1468-
_ => unimplemented!(),
14691755
};
14701756
f.write_str(symbol)
14711757
}
@@ -1543,7 +1829,9 @@ impl Operator {
15431829

15441830
#[cfg(test)]
15451831
mod tests {
1546-
use crate::ast::{ArgumentProperty, DeclarationKind, PouType, VariableBlockType};
1832+
use crate::ast::{
1833+
ArgumentProperty, AstFactory, AstNode, DeclarationKind, Operator, PouType, VariableBlockType,
1834+
};
15471835

15481836
#[test]
15491837
fn display_pou() {
@@ -1573,6 +1861,35 @@ mod tests {
15731861
assert_eq!(VariableBlockType::Global.to_string(), "Global");
15741862
assert_eq!(VariableBlockType::InOut.to_string(), "InOut");
15751863
}
1864+
1865+
#[test]
1866+
fn test_as_string() {
1867+
use crate::literals::AstLiteral;
1868+
use plc_source::source_location::SourceLocation;
1869+
1870+
// Test integer literal
1871+
let integer_node = AstNode::new_integer(42, 1, SourceLocation::internal());
1872+
assert_eq!(integer_node.as_string(), "42");
1873+
1874+
// Test identifier
1875+
let identifier_node = AstFactory::create_identifier("myVar", SourceLocation::internal(), 2);
1876+
assert_eq!(identifier_node.as_string(), "myVar");
1877+
1878+
// Test binary expression: 3 + 5
1879+
let left = AstNode::new_integer(3, 3, SourceLocation::internal());
1880+
let right = AstNode::new_integer(5, 4, SourceLocation::internal());
1881+
let binary_expr = AstFactory::create_binary_expression(left, Operator::Plus, right, 5);
1882+
assert_eq!(binary_expr.as_string(), "3 + 5");
1883+
1884+
// Test parenthesized expression: (42)
1885+
let inner = AstNode::new_integer(42, 6, SourceLocation::internal());
1886+
let paren_expr = AstFactory::create_paren_expression(inner, SourceLocation::internal(), 7);
1887+
assert_eq!(paren_expr.as_string(), "(42)");
1888+
1889+
// Test boolean literal
1890+
let bool_node = AstNode::new_literal(AstLiteral::Bool(true), 8, SourceLocation::internal());
1891+
assert_eq!(bool_node.as_string(), "TRUE");
1892+
}
15761893
}
15771894

15781895
pub struct AstFactory {}

0 commit comments

Comments
 (0)