Skip to content

Commit 03fb9d6

Browse files
authored
Merge pull request #2272 from strictdoc-project/stanislaw/test_descriptions
backend/sdoc_source_code: parse SDoc nodes from source files
2 parents 579a5a0 + df8b2cb commit 03fb9d6

File tree

27 files changed

+1130
-177
lines changed

27 files changed

+1130
-177
lines changed

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ classifiers = [
4949
# @sdoc[SDOC-SRS-89]
5050
dependencies = [
5151
"textx >= 4.0.0, == 4.*",
52+
"lark >= 1.2.2",
53+
5254
"jinja2 >= 2.11.2",
5355
# Reading project config from strictdoc.toml file.
5456
"toml",

strictdoc/backend/sdoc/models/node.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ def enumerate_meta_fields(
487487

488488
# A field is considered singleline if it goes before the STATEMENT
489489
# field and vice versa.
490-
if field_index > reference_field_index:
490+
if field_index >= reference_field_index:
491491
is_single_line_field = False
492492
else:
493493
is_single_line_field = True
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from lark import Lark, ParseTree, UnexpectedToken
2+
3+
from strictdoc.backend.sdoc_source_code.constants import (
4+
REGEX_REQ,
5+
RESERVED_KEYWORDS,
6+
)
7+
8+
GRAMMAR = f"""
9+
start: (relation_marker | node_field | _NORMAL_STRING | _WS)*
10+
11+
relation_marker: "@relation" _BRACE_LEFT _WS? (relation_node_uid _SEP _WS)+ "scope=" relation_scope ("," _WS "role=" relation_role)? _WS? _BRACE_RIGHT
12+
13+
relation_node_uid: /{REGEX_REQ}/
14+
relation_scope: /file|class|function|line|range_start|range_end/
15+
relation_role: ALPHANUMERIC_WORD
16+
17+
node_field: node_name ":" _WS_INLINE node_multiline_value
18+
node_name: /(?!({RESERVED_KEYWORDS}))[A-Z_]+/
19+
node_multiline_value: (NORMAL_STRING_VALUE _NL)+
20+
NORMAL_STRING_VALUE.2: /[ ]*(?!\\s*@relation)(?![A-Z_]+:)[^\n\r]+/x
21+
22+
NORMAL_STRING: /(?!\\s*@relation)((?![A-Z_]+:)|({RESERVED_KEYWORDS})).+/
23+
_NORMAL_STRING: NORMAL_STRING
24+
25+
_BRACE_LEFT: /[\\(\\{{]/
26+
_BRACE_RIGHT: /[\\)\\}}]/
27+
28+
_SEP: ","
29+
_NL : NL
30+
_WS : WS
31+
_WS_INLINE : WS_INLINE
32+
33+
ALPHANUMERIC_WORD: /[a-zA-Z0-9_]+/
34+
NL: /\\r?\\n/
35+
36+
%import common.WS -> WS
37+
%import common.WS_INLINE -> WS_INLINE
38+
"""
39+
40+
41+
class MarkerLexer:
42+
@staticmethod
43+
def parse(source_input: str) -> ParseTree:
44+
parser: Lark = Lark(
45+
GRAMMAR, parser="lalr", cache=True, propagate_positions=True
46+
)
47+
48+
try:
49+
# FIXME: Without rstrip, there is an edge case where the parser
50+
# breaks when resolving conflicts between multiline node
51+
# fields and normal strings.
52+
# See also test: test_31_single_node_field.
53+
tree: ParseTree = parser.parse(source_input.rstrip() + "\n")
54+
except UnexpectedToken as exception_:
55+
print( # noqa: T201
56+
"error: could not parse source comment:\n" + source_input
57+
)
58+
raise exception_
59+
return tree

strictdoc/backend/sdoc_source_code/constants.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
from enum import Enum
22

3+
REGEX_REQ = r"(?!scope=)[A-Za-z][A-Za-z0-9_\/\.\\-]+"
4+
REGEX_ROLE = r"[A-Za-z][A-Za-z0-9\\-]+"
5+
RESERVED_KEYWORDS = "FIXME|NOTE|TODO|TBD|WARNING"
6+
37

48
class FunctionAttribute(Enum):
59
STATIC = "static"

strictdoc/backend/sdoc_source_code/helpers/comment_preprocessor.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,24 @@
77
def preprocess_source_code_comment(comment: str) -> str:
88
"""
99
Remove all Doxygen/Python/etc comment markers for processing.
10+
11+
FIXME: Maybe there is a more efficient way of doing this with no two
12+
re.sub() calls.
1013
"""
1114

1215
def replace_with_spaces(match: Match[str]) -> str:
1316
# Return a string of spaces with the same length as the matched text.
1417
return " " * len(match.group(0))
1518

16-
return re.sub(
19+
replacement = re.sub(
1720
rf"(^/\*\*)|^{WS}*\*/?|(^///)|(^//)|(^#+)",
1821
replace_with_spaces,
1922
comment,
2023
flags=re.MULTILINE,
2124
)
25+
return re.sub(
26+
r"^[ \t]+$",
27+
"",
28+
replacement,
29+
flags=re.MULTILINE,
30+
)

0 commit comments

Comments
 (0)