Skip to content

Commit a21d04b

Browse files
authored
Merge pull request #2202 from strictdoc-project/stanislaw/merge_nodes_2
backend/sdoc: document grammar validations for new [[NODE]] syntax
2 parents 6305985 + 1be6944 commit a21d04b

File tree

66 files changed

+159
-23
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+159
-23
lines changed

strictdoc/backend/sdoc/error_handling.py

Lines changed: 72 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# mypy: disable-error-code="attr-defined,no-untyped-call,no-untyped-def,union-attr"
1+
# mypy: disable-error-code="attr-defined,union-attr"
22
from typing import Optional, Union
33

44
from textx import TextXSyntaxError
@@ -30,7 +30,7 @@
3030
)
3131

3232

33-
def get_textx_syntax_error_message(exception: TextXSyntaxError):
33+
def get_textx_syntax_error_message(exception: TextXSyntaxError) -> str:
3434
return f"SDoc markup error: {exception.context}."
3535

3636

@@ -43,7 +43,7 @@ def __init__(
4343
line: Optional[int] = None,
4444
col: Optional[int] = None,
4545
filename: Optional[str] = None,
46-
):
46+
) -> None:
4747
super().__init__(title, hint, line, col, filename)
4848
self.title = title
4949
self.hint = hint
@@ -53,7 +53,9 @@ def __init__(
5353
self.file_path = filename
5454

5555
@staticmethod
56-
def unknown_node_type(node: SDocNode, path_to_sdoc_file: str):
56+
def unknown_node_type(
57+
node: SDocNode, path_to_sdoc_file: str
58+
) -> "StrictDocSemanticError":
5759
return StrictDocSemanticError(
5860
title=f"Invalid node type: {node.node_type}.",
5961
hint=None,
@@ -63,14 +65,63 @@ def unknown_node_type(node: SDocNode, path_to_sdoc_file: str):
6365
filename=path_to_sdoc_file,
6466
)
6567

68+
@staticmethod
69+
def composite_node_and_non_composite_element_mismatch(
70+
node: SDocNode, path_to_sdoc_file: str
71+
) -> "StrictDocSemanticError":
72+
return StrictDocSemanticError(
73+
title=f"The composite node's grammar element is declared as non-composite: [[{node.node_type}]].",
74+
hint=(
75+
"The composite node grammar element declaration must "
76+
"have a 'PROPERTIES/IS_COMPOSITE: True' declaration."
77+
),
78+
example="""\
79+
[GRAMMAR]
80+
ELEMENTS:
81+
- TAG: NODE
82+
PROPERTIES:
83+
IS_COMPOSITE: True
84+
FIELDS:
85+
...
86+
""",
87+
line=node.ng_line_start,
88+
col=node.ng_col_start,
89+
filename=path_to_sdoc_file,
90+
)
91+
92+
@staticmethod
93+
def non_composite_node_and_composite_element_composite(
94+
node: SDocNode, path_to_sdoc_file: str
95+
) -> "StrictDocSemanticError":
96+
return StrictDocSemanticError(
97+
title=f"The non-composite node's grammar element is declared as composite: [[{node.node_type}]].",
98+
hint=(
99+
"The composite node grammar element declaration must "
100+
"have a 'PROPERTIES/IS_COMPOSITE: False' declaration or the "
101+
"PROPERTIES/IS_COMPOSITE can be simply omitted."
102+
),
103+
example="""\
104+
[GRAMMAR]
105+
ELEMENTS:
106+
- TAG: NODE
107+
PROPERTIES:
108+
IS_COMPOSITE: False
109+
FIELDS:
110+
...
111+
""",
112+
line=node.ng_line_start,
113+
col=node.ng_col_start,
114+
filename=path_to_sdoc_file,
115+
)
116+
66117
@staticmethod
67118
def unregistered_field(
68119
*,
69120
field_name: str,
70121
requirement: SDocNode,
71122
document_grammar: DocumentGrammar,
72123
path_to_sdoc_file: str,
73-
):
124+
) -> "StrictDocSemanticError":
74125
grammar_dump = document_grammar.dump_fields(requirement.node_type)
75126
return StrictDocSemanticError(
76127
title=f"Invalid requirement field: {field_name}",
@@ -90,7 +141,7 @@ def missing_required_field(
90141
grammar_field: GrammarElementField,
91142
document_grammar: DocumentGrammar,
92143
path_to_sdoc_file: str,
93-
):
144+
) -> "StrictDocSemanticError":
94145
grammar_fields = document_grammar.dump_fields(node.node_type)
95146
return StrictDocSemanticError(
96147
title=(
@@ -113,7 +164,7 @@ def unexpected_field_outside_grammar(
113164
requirement_field: SDocNodeField,
114165
document_grammar: DocumentGrammar,
115166
path_to_sdoc_file: str,
116-
):
167+
) -> "StrictDocSemanticError":
117168
grammar_fields = document_grammar.dump_fields(node.node_type)
118169
return StrictDocSemanticError(
119170
title=(
@@ -136,7 +187,7 @@ def wrong_field_order(
136187
document_grammar: DocumentGrammar,
137188
problematic_field: SDocNodeField,
138189
path_to_sdoc_file: str,
139-
):
190+
) -> "StrictDocSemanticError":
140191
assert isinstance(problematic_field, SDocNodeField), (
141192
f"{problematic_field}"
142193
)
@@ -161,7 +212,7 @@ def invalid_choice_field(
161212
document_grammar: DocumentGrammar,
162213
requirement_field: SDocNodeField,
163214
path_to_sdoc_file: str,
164-
):
215+
) -> "StrictDocSemanticError":
165216
return StrictDocSemanticError(
166217
title=(
167218
f"Requirement field has an invalid SingleChoice value: "
@@ -187,7 +238,7 @@ def invalid_multiple_choice_field(
187238
document_grammar: DocumentGrammar,
188239
requirement_field: SDocNodeField,
189240
path_to_sdoc_file: str,
190-
):
241+
) -> "StrictDocSemanticError":
191242
return StrictDocSemanticError(
192243
title=(
193244
f"Requirement field has an invalid MultipleChoice value: "
@@ -211,8 +262,8 @@ def invalid_multiple_choice_field(
211262
def not_comma_separated_choices(
212263
node: SDocNode,
213264
requirement_field: SDocNodeField,
214-
path_to_sdoc_file,
215-
):
265+
path_to_sdoc_file: str,
266+
) -> "StrictDocSemanticError":
216267
return StrictDocSemanticError(
217268
title=(
218269
f"Requirement field of type MultipleChoice is invalid: "
@@ -230,7 +281,7 @@ def not_comma_separated_tag_field(
230281
node: SDocNode,
231282
requirement_field: SDocNodeField,
232283
path_to_sdoc_file: str,
233-
):
284+
) -> "StrictDocSemanticError":
234285
return StrictDocSemanticError(
235286
title=(
236287
f"Requirement field of type Tag is invalid: "
@@ -248,7 +299,7 @@ def invalid_reference_type_item(
248299
node: SDocNode,
249300
reference_item: Reference,
250301
path_to_sdoc_file: str,
251-
):
302+
) -> "StrictDocSemanticError":
252303
role_and_type = (
253304
f"{reference_item.ref_type} / {reference_item.role}"
254305
if reference_item.role is not None
@@ -277,7 +328,7 @@ def invalid_marker_role(
277328
ForwardRangeMarker,
278329
],
279330
path_to_src_file: str,
280-
):
331+
) -> "StrictDocSemanticError":
281332
role_and_type = marker.role if marker.role is not None else "Any"
282333
return StrictDocSemanticError(
283334
title=(f"File marker role is not registered: {role_and_type}."),
@@ -294,7 +345,7 @@ def grammar_missing_reserved_statement(
294345
path_to_sdoc_file: str,
295346
line: int,
296347
column: int,
297-
):
348+
) -> "StrictDocSemanticError":
298349
return StrictDocSemanticError(
299350
title=(
300351
f"Grammar element '{grammar_element.tag}' is missing a reserved"
@@ -318,7 +369,7 @@ def grammar_reserved_statement_must_be_required(
318369
path_to_sdoc_file: str,
319370
line: int,
320371
column: int,
321-
):
372+
) -> "StrictDocSemanticError":
322373
return StrictDocSemanticError(
323374
title=(
324375
f"Grammar element '{grammar_element.tag}'s {field_title} field "
@@ -339,7 +390,7 @@ def grammar_reserved_statement_must_be_required(
339390
def grammar_element_has_no_mid_field(
340391
grammar_element: GrammarElement,
341392
path_to_sdoc_file: str,
342-
):
393+
) -> "StrictDocSemanticError":
343394
return StrictDocSemanticError(
344395
title=(
345396
f"Grammar element '{grammar_element.tag}' is missing the MID field "
@@ -360,7 +411,7 @@ def view_references_nonexisting_grammar_element(
360411
document_view: DocumentView,
361412
view_element: ViewElement,
362413
object_type: str,
363-
):
414+
) -> "StrictDocSemanticError":
364415
return StrictDocSemanticError(
365416
title=(
366417
f"View element '{view_element.view_id}' references a non-existing"
@@ -383,7 +434,7 @@ def view_references_nonexisting_field(
383434
view_element: ViewElement,
384435
object_type: str,
385436
field_name: str,
386-
):
437+
) -> "StrictDocSemanticError":
387438
return StrictDocSemanticError(
388439
title=(
389440
f"View element '{view_element.view_id}' references a non-existing"
@@ -404,7 +455,7 @@ def default_view_doesnt_exist(
404455
document: SDocDocument,
405456
document_config: DocumentConfig,
406457
default_view: str,
407-
):
458+
) -> "StrictDocSemanticError":
408459
filename = document.meta.input_doc_full_path
409460

410461
return StrictDocSemanticError(

strictdoc/backend/sdoc/processor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def process_composite_requirement(
201201
) and self.parse_context.document_config.auto_levels:
202202
composite_requirement.ng_resolved_custom_level = "None"
203203

204-
def process_requirement(self, requirement: SDocNode):
204+
def process_requirement(self, requirement: SDocNode) -> None:
205205
self.parse_context.document_has_requirements = True
206206

207207
if self.parse_context.document_config.auto_levels:

strictdoc/backend/sdoc/validations/sdoc_validator.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# mypy: disable-error-code="arg-type,no-untyped-call,no-untyped-def,union-attr"
1+
# mypy: disable-error-code="arg-type,union-attr"
22
import re
33
from typing import Iterator, Optional, Set
44

@@ -177,6 +177,18 @@ def validate_node(
177177
grammar_element: GrammarElement = document_grammar.elements_by_type[
178178
requirement.node_type
179179
]
180+
181+
if requirement.is_composite:
182+
if not grammar_element.property_is_composite:
183+
raise StrictDocSemanticError.composite_node_and_non_composite_element_mismatch(
184+
requirement, path_to_sdoc_file
185+
)
186+
else:
187+
if grammar_element.property_is_composite:
188+
raise StrictDocSemanticError.non_composite_node_and_composite_element_composite(
189+
requirement, path_to_sdoc_file
190+
)
191+
180192
registered_fields: Set[str] = set(grammar_element.get_field_titles())
181193

182194
for field_name in requirement.ordered_fields_lookup:

0 commit comments

Comments
 (0)