@@ -16,6 +16,7 @@ use crate::{
16
16
literals:: { AstLiteral , StringValue } ,
17
17
pre_processor,
18
18
provider:: IdProvider ,
19
+ visitor:: { AstVisitor , Walker } ,
19
20
} ;
20
21
21
22
use plc_source:: source_location:: * ;
@@ -1318,6 +1319,10 @@ impl AstNode {
1318
1319
matches ! ( node. get_stmt_peeled( ) , AstStatement :: Super ( Some ( _) ) )
1319
1320
}
1320
1321
1322
+ pub fn is_super_or_super_deref ( & self ) -> bool {
1323
+ self . is_super ( ) || self . is_super_deref ( )
1324
+ }
1325
+
1321
1326
pub fn has_super_metadata ( & self ) -> bool {
1322
1327
self . get_metadata ( )
1323
1328
. or_else ( || self . get_identifier ( ) . and_then ( |it| it. get_metadata ( ) ) )
@@ -1424,6 +1429,30 @@ impl AstNode {
1424
1429
AstNode { metadata : Some ( metadata) , ..self }
1425
1430
}
1426
1431
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
+
1427
1456
pub fn get_deref_expr ( & self ) -> Option < & ReferenceExpr > {
1428
1457
match & self . stmt {
1429
1458
AstStatement :: ReferenceExpr ( expr) => match expr {
@@ -1433,6 +1462,255 @@ impl AstNode {
1433
1462
_ => None ,
1434
1463
}
1435
1464
}
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
+ }
1436
1714
}
1437
1715
1438
1716
#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
@@ -1463,9 +1741,17 @@ impl Display for Operator {
1463
1741
Operator :: Multiplication => "*" ,
1464
1742
Operator :: Division => "/" ,
1465
1743
Operator :: Equal => "=" ,
1744
+ Operator :: NotEqual => "<>" ,
1466
1745
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" ,
1467
1754
Operator :: Exponentiation => "**" ,
1468
- _ => unimplemented ! ( ) ,
1469
1755
} ;
1470
1756
f. write_str ( symbol)
1471
1757
}
@@ -1543,7 +1829,9 @@ impl Operator {
1543
1829
1544
1830
#[ cfg( test) ]
1545
1831
mod tests {
1546
- use crate :: ast:: { ArgumentProperty , DeclarationKind , PouType , VariableBlockType } ;
1832
+ use crate :: ast:: {
1833
+ ArgumentProperty , AstFactory , AstNode , DeclarationKind , Operator , PouType , VariableBlockType ,
1834
+ } ;
1547
1835
1548
1836
#[ test]
1549
1837
fn display_pou ( ) {
@@ -1573,6 +1861,35 @@ mod tests {
1573
1861
assert_eq ! ( VariableBlockType :: Global . to_string( ) , "Global" ) ;
1574
1862
assert_eq ! ( VariableBlockType :: InOut . to_string( ) , "InOut" ) ;
1575
1863
}
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
+ }
1576
1893
}
1577
1894
1578
1895
pub struct AstFactory { }
0 commit comments