From 86b3cacd19ace1fdc532553e77549576c9974acd Mon Sep 17 00:00:00 2001 From: OdysseasKr Date: Thu, 14 Nov 2024 11:49:02 +0100 Subject: [PATCH 1/5] Add minus to prefix operators --- rapidchecker/parser/grammar.py | 4 ++-- rapidchecker/parser/operators.py | 1 + tests/test_grammar.py | 7 ++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/rapidchecker/parser/grammar.py b/rapidchecker/parser/grammar.py index a7ff6e3..bf42845 100644 --- a/rapidchecker/parser/grammar.py +++ b/rapidchecker/parser/grammar.py @@ -9,7 +9,7 @@ register_indent_checks, ) from .literals import RAPIDLITERAL -from .operators import infix_op +from .operators import infix_op, prefix_op pp.ParserElement.enable_packrat() @@ -39,7 +39,7 @@ | variable | RAPIDLITERAL | "(" + expression + ")" - | T.NOT + expression + | prefix_op + expression | array ) expression <<= (term + infix_op + expression) | term diff --git a/rapidchecker/parser/operators.py b/rapidchecker/parser/operators.py index 51c5cb8..24ab194 100644 --- a/rapidchecker/parser/operators.py +++ b/rapidchecker/parser/operators.py @@ -6,3 +6,4 @@ boolean_op.set_name("bool_op") infix_op = pp.oneOf("+ - * / = <= < >= > <>") | boolean_op | T.DIV | T.MOD +prefix_op = "-" | T.NOT diff --git a/tests/test_grammar.py b/tests/test_grammar.py index 69e0a5c..f38b934 100644 --- a/tests/test_grammar.py +++ b/tests/test_grammar.py @@ -44,7 +44,10 @@ def test_eval() -> None: assert result == ["%", "procCall%"] -@pytest.mark.parametrize("valid_expression", ["a + 1", "a{100}", "b.c.d{*} AND TRUE"]) +@pytest.mark.parametrize( + "valid_expression", + ["a + 1", "a{100}", "b.c.d{*} AND TRUE", "-a", "-10", "-funCall(a,b)"], +) def test_expression(valid_expression: str) -> None: assert expression.parseString(valid_expression, parseAll=True).as_list() @@ -111,6 +114,7 @@ def test_array(input_str: str) -> None: '"string"', "(1+1)", "NOT a AND B", + "-(a AND B)", "[1,2,3,4]", ], ) @@ -237,6 +241,7 @@ def test_for_stmt(input_str: str) -> None: "callProc arg1, arg2, \\switch;", "callProc arg1, arg2, \\opt:=(1+1), \\switch;", "callProc arg1, name:=arg2 \\opt:=(1+1) \\switch;", + "MoveL RelTool(target, 0, 0, -Abs(z)), v1000, fine, tool, \\WObj:=wobj0;", ], ) def test_proc_call_stmt(input_str: str) -> None: From 2c0305f0a8949508f0a6a73a4f3216a6f6cf3006 Mon Sep 17 00:00:00 2001 From: OdysseasKr Date: Thu, 14 Nov 2024 11:51:02 +0100 Subject: [PATCH 2/5] Allow INOUT to optional args --- rapidchecker/parser/identifiers.py | 4 ++-- tests/test_identifiers.py | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/rapidchecker/parser/identifiers.py b/rapidchecker/parser/identifiers.py index 93ee53e..8f741f4 100644 --- a/rapidchecker/parser/identifiers.py +++ b/rapidchecker/parser/identifiers.py @@ -15,8 +15,8 @@ parameter = ( - pp.Optional(T.INOUT | T.PERS) - + pp.Optional("\\") + pp.Optional("\\") + + pp.Optional(T.INOUT | T.PERS) + datatype + identifier + elem_index diff --git a/tests/test_identifiers.py b/tests/test_identifiers.py index ba879f5..3eef98b 100644 --- a/tests/test_identifiers.py +++ b/tests/test_identifiers.py @@ -48,7 +48,12 @@ def test_invalid_variable(invalid_variable: str) -> None: @pytest.mark.parametrize( "valid_parameter", - ["INOUT string name{100}", "string name", "\\robtarget target"], + [ + "INOUT string name{100}", + "\\INOUT string name{100}", + "string name", + "\\robtarget target", + ], ) def test_parameter(valid_parameter: str) -> None: assert parameter.parseString(valid_parameter, parseAll=True) From 3beedd9bdb3fe1946c5cfb2e033651256431e7f9 Mon Sep 17 00:00:00 2001 From: OdysseasKr Date: Thu, 14 Nov 2024 11:54:25 +0100 Subject: [PATCH 3/5] Bump version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6d2d666..9523d41 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "rapidchecker" -version = "0.1.0" +version = "0.1.1" description = " Grammar and format checker for ABB RAPID code. " readme = "README.md" requires-python = ">=3.10" From 15f1e49f8ff342609d2e061c2b5de961351c98eb Mon Sep 17 00:00:00 2001 From: OdysseasKr Date: Thu, 14 Nov 2024 13:55:07 +0100 Subject: [PATCH 4/5] Add extra test case --- tests/test_grammar.py | 11 ++++++++++- uv.lock | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/test_grammar.py b/tests/test_grammar.py index f38b934..fa3d165 100644 --- a/tests/test_grammar.py +++ b/tests/test_grammar.py @@ -46,7 +46,15 @@ def test_eval() -> None: @pytest.mark.parametrize( "valid_expression", - ["a + 1", "a{100}", "b.c.d{*} AND TRUE", "-a", "-10", "-funCall(a,b)"], + [ + "a + 1", + "a{100}", + "b.c.d{*} AND TRUE", + "-a", + "-10", + "-funCall(a,b)", + "a = b AND b <> z", + ], ) def test_expression(valid_expression: str) -> None: assert expression.parseString(valid_expression, parseAll=True).as_list() @@ -168,6 +176,7 @@ def test_var_def_section(input_str: str) -> None: "IF a THEN\n callProc;\nELSE\n callAnotherProc;\nENDIF", "IF a THEN\n callProc;\nELSEIF new_condition AND B THEN\n callNewProc;\nELSE\n callAnotherProc;\nENDIF", "IF a THEN\n callProc;\n callProc2;\nELSEIF new_condition AND B THEN\n callNewProc;\n callProc2;\nELSE\n callAnotherProc;\nENDIF", + "IF a = b AND b <> 1 THEN\n var1 := 0;\nENDIF", ], ) def test_valid_if_stmt(input_str: str) -> None: diff --git a/uv.lock b/uv.lock index 5c35c0e..78514fc 100644 --- a/uv.lock +++ b/uv.lock @@ -286,7 +286,7 @@ wheels = [ [[package]] name = "rapidchecker" -version = "0.1.0" +version = "0.1.1" source = { virtual = "." } dependencies = [ { name = "click" }, From f0a6b68466c3790e836368f8d56dce9131ed9430 Mon Sep 17 00:00:00 2001 From: OdysseasKr Date: Thu, 14 Nov 2024 14:04:10 +0100 Subject: [PATCH 5/5] Add scope keyword for variable definitions --- rapidchecker/parser/grammar.py | 11 ++++++----- rapidchecker/parser/tokens.py | 5 +++++ tests/test_grammar.py | 1 + 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/rapidchecker/parser/grammar.py b/rapidchecker/parser/grammar.py index bf42845..159a73d 100644 --- a/rapidchecker/parser/grammar.py +++ b/rapidchecker/parser/grammar.py @@ -60,12 +60,13 @@ # Variable definition var_def = ( - (T.PERS | T.VAR | T.CONST) + pp.Optional(T.LOCAL | T.TASK) + + (T.PERS | T.VAR | T.CONST) + datatype - + identifier - + pp.Optional("{" + expression + "}") - + pp.Optional(":=" + expression) - + ";" + - identifier + - pp.Optional("{" + expression + "}") + - pp.Optional(":=" + expression) + - ";" ) var_def_section = pp.ZeroOrMore(var_def) diff --git a/rapidchecker/parser/tokens.py b/rapidchecker/parser/tokens.py index b9b3fd2..63db6e0 100644 --- a/rapidchecker/parser/tokens.py +++ b/rapidchecker/parser/tokens.py @@ -56,6 +56,8 @@ def __init__(self, *args, **kwargs) -> None: DEFAULT = RapidKeyword("DEFAULT") ERROR = RapidKeyword("ERROR") INOUT = RapidKeyword("INOUT") +TASK = RapidKeyword("TASK") +LOCAL = RapidKeyword("LOCAL") RESERVED_WORD = ( MODULE @@ -98,6 +100,9 @@ def __init__(self, *args, **kwargs) -> None: | CASE | DEFAULT | ERROR + | INOUT + | TASK + | LOCAL ) MODULE_OPTIONS = oneOf("SYSMODULE NOSTEPIN VIEWONLY READONLY") diff --git a/tests/test_grammar.py b/tests/test_grammar.py index fa3d165..5199d41 100644 --- a/tests/test_grammar.py +++ b/tests/test_grammar.py @@ -154,6 +154,7 @@ def test_record_def(input_str: str) -> None: "VAR robtarget targets{1000};", "VAR robtarget targets{var1 + var2};", "CONST num number := 1 + 1;", + "LOCAL CONST num number := 1 + 1;", ], ) def test_var_def(input_str: str) -> None: