Skip to content
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
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ PLC Doc's documentation!
src/directives
src/examples
src/modules
src/limitations


##################
Expand Down
49 changes: 49 additions & 0 deletions docs/src/limitations.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
######################
Limitations and Issues
######################

For an up-to-date insight on issues, see https://github.com/DEMCON/plcdoc .
Please help maintaining this project by adding any new issues there.

Grammar
=======

Grammar refers to the parsing of PLC code.
This is done through a language specification, custom made in TextX.
And so there is no guarantee that all possible PLC code can be parsed without error.
Nonetheless, much of the most occurring code will parse with no problems.

Expressions
-----------

With expressions we mean all the literally typed constructions, from a simple ``5`` or ``'Hello World!'`` to a more complex ``CONCAT(INT_TO_STRING(5 + 3 * 8), '...')``.

Currently, expressions are not parsed recursively as they would be for a real interpreter.
Instead, when an expression is expected the whole item is simply matched as a string.
So in the following...

.. code-block::

my_int : INT := 1 + 1;
my_string : STRING := CONCAT("+", MY_CONST);

...the initial values are registered as just ``"1 + 1"`` and ``"CONCAT("+", MY_CONST)"``.
This means e.g. variables are never recognized in variable initialization and won't be linked.
Aside from this, the expression should be printed normally in your generated docs item.

String Escape
-------------

Because `expressions <#Expressions>`_ are typically matched until the next ``;``, this breaks when a literal semicolon appears in a string.
This is avoided specifically for singular string expressions, but not for any expression:

.. code-block::

str1 : STRING := 'Hi;'; // This will parse
str2 : STRING := CONCAT('Hi', ';'); // This will cause a parsing error

Workaround
^^^^^^^^^^

For now you might have to use ``$3B`` as a `string constant <https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/2529327243.html&id=>`_ as a replacement for the literal semicolon.
Or introduce (const) variables to break up those expressions.
15 changes: 15 additions & 0 deletions src/plcdoc/st_declaration.tx
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,21 @@ SemiColon:
Anything that is considered a value: a literal, a variable, or e.g. a sum
*/
Expression:
ExpressionString | ExpressionAnything
;

/*
Because a string expression could use a syntax character, we need to make an effort match string content, to
escape the content.

We use a literal string match, instead of TextX's `STRING`, because we need to keep the quotes so we can later
still distinguish a literal string type (over e.g. a variable name).
*/
ExpressionString:
/'.*'/
;

ExpressionAnything:
/[^;]*/
// Match anything, including parentheses, up to (but excluding) the semicolon
;
Expand Down
4 changes: 4 additions & 0 deletions tests/plc_code/FB_Variables.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ myfloat_no_ws:REAL;
mystring_size5 : STRING(35) := 'Unknown';
mystring_size6 : STRING[Module.SIZE] := 'Unknown';

mystring_escape : STRING := ':;"';

mystring_concat : STRING := CONCAT('abc', 'xyz');

myint : INT := SomeConstant;
myint2 : INT := E_Error.NoError;

Expand Down
4 changes: 3 additions & 1 deletion tests/test_st_grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ def test_grammar_variables(meta_model):
("mystring_size4", "STRING[SIZE]", None, None, None, None),
("mystring_size5", "STRING(35)", "'Unknown'", None, None, None),
("mystring_size6", "STRING[Module.SIZE]", "'Unknown'", None, None, None),
("mystring_escape", "STRING", "':;\"'", None, None, None),
("mystring_concat", "STRING", "CONCAT('abc','xyz')", None, None, None),
("myint", "INT", "SomeConstant", None, None, None),
("myint2", "INT", "E_Error.NoError", None, None, None),
("mylist", "BOOL", None, None, "0..4", None),
Expand Down Expand Up @@ -151,7 +153,7 @@ def test_grammar_variables(meta_model):
("inline1", "INT", None, None, None, None),
]

assert len(variables) == 45
assert len(variables) == 47

for i, expected in enumerate(expected_list):
assert_variable(variables[i], expected)
Expand Down