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

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/>. 

20 

21from trlc import ast 

22from trlc.errors import TRLC_Error 

23from trlc.parser import Parser 

24 

25 

26class TrlcMarkdownParser(Parser): 

27 """Parser for .trlc.md files. 

28 

29 Reuses TRLC Parser logic for record/object/value semantics and only 

30 overrides markdown-specific preamble entry points. 

31 """ 

32 

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 = "#" 

36 

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 

58 

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) 

63 

64 def parse_trlc_entry(self): 

65 self.cu.add_item(self.parse_record_object_declaration()) 

66 

67 def parse_trlc_file(self): 

68 assert self.cu.package is not None 

69 

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 

77 

78 ok = False 

79 

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() 

97 

98 self.match_eof() 

99 

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) 

103 

104 return ok