Skip to content

Commit

Permalink
Merge pull request #22 from engineerjoe440/enhancement/support-access…
Browse files Browse the repository at this point in the history
…-specifiers-for-function-block-declarations

Enhancement: support access specifiers for function block declarations
  • Loading branch information
klauer authored Apr 19, 2022
2 parents 5ba4d5a + b4c2bdd commit c104d35
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 39 deletions.
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"python.linting.flake8Enabled": true,
"python.linting.enabled": true
}
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ include LICENSE
include blark/_version.py
include blark/*.lark
include blark/docs/*.css
recursive-include blark/tests *.TcPOU *.st
34 changes: 11 additions & 23 deletions blark/iec.lark
Original file line number Diff line number Diff line change
Expand Up @@ -520,11 +520,18 @@ DOTTED_IDENTIFIER: IDENTIFIER ( "." IDENTIFIER )*
?function_block_type_name: standard_function_block_name
| derived_function_block_name

ABSTRACT: "ABSTRACT"i

ACCESS_SPECIFIER: "ABSTRACT"i
| "PUBLIC"i
| "PRIVATE"i
| "PROTECTED"i
| "INTERNAL"i
| "FINAL"i
access_specifier: ACCESS_SPECIFIER+
extends: "EXTENDS"i DOTTED_IDENTIFIER
implements: "IMPLEMENTS"i DOTTED_IDENTIFIER ("," DOTTED_IDENTIFIER)*

function_block_type_declaration: FUNCTION_BLOCK [ ABSTRACT ] derived_function_block_name [ extends ] [ implements ] fb_var_declaration* [ function_block_body ] END_FUNCTION_BLOCK ";"*
function_block_type_declaration: FUNCTION_BLOCK [ access_specifier ] derived_function_block_name [ extends ] [ implements ] fb_var_declaration* [ function_block_body ] END_FUNCTION_BLOCK ";"*

FUNCTION_BLOCK: "FUNCTION_BLOCK"i
| "FUNCTIONBLOCK"i
Expand All @@ -545,16 +552,6 @@ temp_var_decls: "VAR_TEMP"i var_body "END_VAR"i ";"*

?function_block_body: statement_list

// TODO: really only a couple of these
METHOD_ACCESS: "ABSTRACT"i
| "PUBLIC"i
| "PRIVATE"i
| "PROTECTED"i
| "INTERNAL"i
| "FINAL"i

method_access: METHOD_ACCESS+

var_inst_declaration: "VAR_INST"i [ variable_attributes ] var_body "END_VAR"i ";"*

?method_var_declaration: fb_var_declaration
Expand All @@ -563,22 +560,13 @@ var_inst_declaration: "VAR_INST"i [ variable_attributes ] var_body "END_VAR"i ";

?method_return_type: _located_var_spec_init

function_block_method_declaration: "METHOD"i [ method_access ] DOTTED_IDENTIFIER [ ":" method_return_type ] ";"* method_var_declaration* [ function_block_body ] "END_METHOD"i ";"*

PROPERTY_ACCESS: "ABSTRACT"i
| "PUBLIC"i
| "PRIVATE"i
| "PROTECTED"i
| "INTERNAL"i
| "FINAL"i

property_access: PROPERTY_ACCESS+
function_block_method_declaration: "METHOD"i [ access_specifier ] DOTTED_IDENTIFIER [ ":" method_return_type ] ";"* method_var_declaration* [ function_block_body ] "END_METHOD"i ";"*

?property_var_declaration: fb_var_declaration

?property_return_type: _located_var_spec_init

function_block_property_declaration: "PROPERTY"i [ property_access ] DOTTED_IDENTIFIER [ ":" property_return_type ] ";"* property_var_declaration* [ function_block_body ] "END_PROPERTY"i ";"*
function_block_property_declaration: "PROPERTY"i [ access_specifier ] DOTTED_IDENTIFIER [ ":" property_return_type ] ";"* property_var_declaration* [ function_block_body ] "END_PROPERTY"i ";"*

// B.1.5.3
?program_type_name: IDENTIFIER
Expand Down
6 changes: 6 additions & 0 deletions blark/tests/source/fb_w_public_access.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FUNCTION_BLOCK PUBLIC class_PublicFB
VAR_OUTPUT
(*Some comment.*)
Error : BOOL;
END_VAR
END_FUNCTION_BLOCK
28 changes: 26 additions & 2 deletions blark/tests/test_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import pytest

from ..parse import parse, summarize
from ..parse import parse, parse_source_code, summarize
from .conftest import get_grammar

TEST_PATH = pathlib.Path(__file__).parent
Expand All @@ -14,13 +14,20 @@
if additional_pous.exists():
pous += open(additional_pous, "rt").read().splitlines()

sources = list(str(path) for path in TEST_PATH.glob("**/*.st"))


@pytest.fixture(params=pous)
def pou_filename(request):
return request.param


def test_parsing(pou_filename):
@pytest.fixture(params=sources)
def source_filename(request):
return request.param


def test_parsing_tcpous(pou_filename):
try:
((_, result),) = list(parse(pou_filename))
except FileNotFoundError:
Expand All @@ -34,6 +41,23 @@ def test_parsing(pou_filename):
print(summarize(result))


def test_parsing_source(source_filename):
"""Test plain source 61131 files."""
try:
with open(source_filename, "r", encoding="utf-8") as src:
content = src.read()
result = parse_source_code(content)
except FileNotFoundError:
pytest.skip(f"Missing file: {source_filename}")
else:
print("transformed:")
print(result)
print("summary:")
if isinstance(result, Exception):
raise result
print(summarize(result))


def pytest_html_results_table_row(report, cells):
# pytest results using pytest-html; show only failures for now:
if report.passed:
Expand Down
30 changes: 30 additions & 0 deletions blark/tests/test_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,36 @@ def test_global_roundtrip(rule_name, value):
END_FUNCTION_BLOCK
"""
)),
param("function_block_type_declaration", tf.multiline_code_block(
"""
FUNCTION_BLOCK PRIVATE fbName EXTENDS OtherFbName
END_FUNCTION_BLOCK
"""
)),
param("function_block_type_declaration", tf.multiline_code_block(
"""
FUNCTION_BLOCK PUBLIC fbName EXTENDS OtherFbName
END_FUNCTION_BLOCK
"""
)),
param("function_block_type_declaration", tf.multiline_code_block(
"""
FUNCTION_BLOCK INTERNAL fbName EXTENDS OtherFbName
END_FUNCTION_BLOCK
"""
)),
param("function_block_type_declaration", tf.multiline_code_block(
"""
FUNCTION_BLOCK PROTECTED fbName EXTENDS OtherFbName
END_FUNCTION_BLOCK
"""
)),
param("function_block_type_declaration", tf.multiline_code_block(
"""
FUNCTION_BLOCK FINAL fbName EXTENDS OtherFbName
END_FUNCTION_BLOCK
"""
)),
param("function_block_type_declaration", tf.multiline_code_block(
"""
FUNCTION_BLOCK fbName
Expand Down
27 changes: 13 additions & 14 deletions blark/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,9 @@ class VariableAttributes(_FlagHelper, enum.Flag):


@_rule_handler(
"method_access",
"property_access",
"access_specifier",
)
class MethodAccess(_FlagHelper, enum.Flag):
class AccessSpecifier(_FlagHelper, enum.Flag):
public = 0b0000_0001
private = 0b0000_0010
abstract = 0b0000_0100
Expand Down Expand Up @@ -1788,7 +1787,7 @@ def __str__(self) -> str:
@_rule_handler("function_block_type_declaration", comments=True)
class FunctionBlock:
name: lark.Token
abstract: bool
access: Optional[AccessSpecifier]
extends: Optional[Extends]
implements: Optional[Implements]
declarations: List[VariableDeclarationBlock]
Expand All @@ -1798,7 +1797,7 @@ class FunctionBlock:
@staticmethod
def from_lark(
fb_token: lark.Token,
abstract: Optional[lark.Token],
access: Optional[AccessSpecifier],
derived_name: lark.Token,
extends: Optional[Extends],
implements: Optional[Implements],
Expand All @@ -1807,16 +1806,16 @@ def from_lark(
*declarations, body, _ = args
return FunctionBlock(
name=derived_name,
abstract=abstract is not None,
access=access,
extends=extends,
implements=implements,
declarations=list(declarations),
body=body,
)

def __str__(self) -> str:
abstract = "ABSTRACT " if self.abstract else ""
header = f"FUNCTION_BLOCK {abstract}{self.name}"
access_and_name = join_if(self.access, " ", self.name)
header = f"FUNCTION_BLOCK {access_and_name}"
header = join_if(header, " ", self.implements)
header = join_if(header, " ", self.extends)
return "\n".join(
Expand Down Expand Up @@ -1913,7 +1912,7 @@ def __str__(self) -> str:
@dataclass
@_rule_handler("function_block_method_declaration", comments=True)
class Method:
access: Optional[MethodAccess]
access: Optional[AccessSpecifier]
name: lark.Token
return_type: Optional[LocatedVariableSpecInit]
declarations: List[VariableDeclarationBlock]
Expand All @@ -1922,7 +1921,7 @@ class Method:

@staticmethod
def from_lark(
access: Optional[MethodAccess],
access: Optional[AccessSpecifier],
name: lark.Token,
return_type: Optional[LocatedVariableSpecInit],
*args
Expand Down Expand Up @@ -1954,7 +1953,7 @@ def __str__(self) -> str:
@dataclass
@_rule_handler("function_block_property_declaration", comments=True)
class Property:
access: Optional[MethodAccess]
access: Optional[AccessSpecifier]
name: lark.Token
return_type: Optional[LocatedVariableSpecInit]
declarations: List[VariableDeclarationBlock]
Expand All @@ -1963,7 +1962,7 @@ class Property:

@staticmethod
def from_lark(
access: Optional[MethodAccess],
access: Optional[AccessSpecifier],
name: lark.Token,
return_type: Optional[LocatedVariableSpecInit],
*args
Expand Down Expand Up @@ -3035,8 +3034,8 @@ def merge_comments(source: Any, comments: List[lark.Token]):
# Optional apischema deserializers

@apischema.deserializer
def _method_access_deserializer(access: int) -> MethodAccess:
return MethodAccess(access)
def _method_access_deserializer(access: int) -> AccessSpecifier:
return AccessSpecifier(access)

@apischema.deserializer
def _var_attrs_deserializer(attrs: int) -> VariableAttributes:
Expand Down

0 comments on commit c104d35

Please sign in to comment.