Coverage for trlc/trlc_markdown_parser.py: 82%
43 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-11-06 14:44 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-11-06 14:44 +0000
1#!/usr/bin/env python3
2#
3# TRLC - Treat Requirements Like Code
4# Copyright (C) 2026 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
5#
6# This file is part of the TRLC Python Reference Implementation.
7#
8# TRLC is free software: you can redistribute it and/or modify it
9# under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# TRLC is distributed in the hope that it will be useful, but WITHOUT
14# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
16# License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with TRLC. If not, see <https://www.gnu.org/licenses/>.
21from trlc import ast
22from trlc.errors import TRLC_Error
23from trlc.parser import Parser
26class TrlcMarkdownParser(Parser):
27 """Parser for .trlc.md files.
29 Reuses TRLC Parser logic for record/object/value semantics and only
30 overrides markdown-specific preamble entry points.
31 """
33 # The H1 heading takes the role of the 'package' keyword, so the
34 # inherited preamble parsing applies unchanged to markdown files.
35 PACKAGE_KEYWORD = "#"
37 def __init__(
38 self,
39 mh,
40 stab,
41 file_name,
42 lint_mode,
43 error_recovery,
44 primary_file=True,
45 lexer=None,
46 ):
47 super().__init__(
48 mh=mh,
49 stab=stab,
50 file_name=file_name,
51 lint_mode=lint_mode,
52 error_recovery=error_recovery,
53 primary_file=primary_file,
54 lexer=lexer,
55 )
56 if lexer is not None and hasattr(lexer, "KEYWORDS"): 56 ↛ exitline 56 didn't return from function '__init__' because the condition on line 56 was always true
57 self.language_keywords = lexer.KEYWORDS
59 def parse_preamble(self, kind):
60 # markdown files are routed through TRLC flow in Source_Manager
61 assert kind == "trlc"
62 super().parse_preamble(kind)
64 def parse_trlc_entry(self):
65 self.cu.add_item(self.parse_record_object_declaration())
67 def parse_trlc_file(self):
68 assert self.cu.package is not None
70 ok = True
71 while self.peek("IDENTIFIER"):
72 try:
73 self.parse_trlc_entry()
74 except TRLC_Error as err:
75 if not self.error_recovery or err.kind == "lex error": 75 ↛ 76line 75 didn't jump to line 76 because the condition on line 75 was never true
76 raise
78 ok = False
80 # Mirror TRLC recovery style: scan until likely new entry.
81 self.skip_until_newline()
82 while not self.peek_eof():
83 if not self.peek("IDENTIFIER"):
84 pass
85 elif self.stab.contains(self.nt.value): 85 ↛ 86line 85 didn't jump to line 86 because the condition on line 85 was never true
86 n_sym = self.stab.lookup_assuming(self.mh, self.nt.value)
87 if isinstance(n_sym, ast.Package):
88 break
89 elif self.cu.package.symbols.contains(self.nt.value):
90 n_sym = self.cu.package.symbols.lookup_assuming(
91 self.mh, self.nt.value
92 )
93 if isinstance(n_sym, ast.Record_Type): 93 ↛ 95line 93 didn't jump to line 95 because the condition on line 93 was always true
94 break
95 self.advance()
96 self.skip_until_newline()
98 self.match_eof()
100 for tok in self.lexer.tokens:
101 if tok.kind == "COMMENT": 101 ↛ 102line 101 didn't jump to line 102 because the condition on line 101 was never true
102 self.cu.package.set_ast_link(tok)
104 return ok