11# autodoc.py: A basic VHDL parser and documentation extractor
22# Copyright (C) 2021 CESNET z.s.p.o.
33# Author(s): Jindrich Dite <xditej01@stud.fit.vutbr.cz>
4+ # Jakub Cabal <cabal@cesnet.cz>
45#
56# SPDX-License-Identifier: BSD-3-Clause
67
1011from typing import Optional , List
1112from enum import Enum , auto
1213
13- import logging
14-
15- LOG = logging .getLogger ('sphinxvhdl-autodoc' )
14+ from sphinx .util import logging
15+ logger = logging .getLogger (__name__ )
1616
1717entities = {}
1818portsignals = defaultdict (dict )
2828functions = {}
2929
3030# Function for parsing line comments
31- def parse_inline_doc_or_raise ( line : str , current_doc : List [ str ] ):
31+ def parse_inline_doc_or_print_error ( current_doc , filename , line , lineno ):
3232 if '-- ' in line :
3333 if len (current_doc ) > 0 :
34- raise ValueError (
35- 'Documented entity has both a pre- and inline documentation; only one is allowed. Offending line:\n ' +
36- line )
34+ logger .warning (f"SPHINX-VHDL: Documented entity has both a pre- and inline documentation; only one is allowed!\n Offending line: { line } " , location = f"{ filename } :{ lineno } " )
3735 else :
3836 current_doc .append (line .split ('-- ' , 1 )[1 ])
3937
4038
41- def parse_inline_doc_or_print_error (current_doc , filename , line , lineno ):
42- try :
43- parse_inline_doc_or_raise (line , current_doc )
44- except ValueError as ex :
45- LOG .warning (f'Error parsing file { filename } at line { lineno } :' )
46- LOG .warning (ex .args )
47-
48-
4939class ParseState (Enum ):
5040 ENTITY_DECL = auto ()
5141 ARCH_DECL = auto ()
@@ -79,24 +69,25 @@ def init(path: str) -> None:
7969 for line in source_code :
8070 lineno += 1
8171 line = line .strip ()
72+ line_lowercase = line .lower ()
8273 # Group parsing logic
8374 if state == ParseState .PORT and group_state == ParseState .GENERIC :
8475 current_group = ""
8576
8677 # Line comments logic
87- if line .startswith ('-- ' ):
78+ if line_lowercase .startswith ('-- ' ):
8879 # Logic for sampling names of groups of ports and generics
89- if (state == ParseState .PORT or state == ParseState .GENERIC ) and '====' in line :
80+ if (state == ParseState .PORT or state == ParseState .GENERIC ) and '====' in line_lowercase :
9081 group_state = state
9182 state = ParseState .GROUPS
9283 current_group = ""
9384 current_doc = []
94- elif state == ParseState .GROUPS and current_group != '' and '====' not in line :
85+ elif state == ParseState .GROUPS and current_group != '' and '====' not in line_lowercase :
9586 current_doc .append (line [3 :])
96- elif state == ParseState .GROUPS and '====' not in line :
87+ elif state == ParseState .GROUPS and '====' not in line_lowercase :
9788 current_group = current_entity + " " + line [3 :].strip ()
9889 current_doc = []
99- elif state == ParseState .GROUPS and '====' in line :
90+ elif state == ParseState .GROUPS and '====' in line_lowercase :
10091 group_definition = current_doc
10192 groups_desc [current_group ] = group_definition
10293 state = group_state
@@ -105,12 +96,12 @@ def init(path: str) -> None:
10596 current_doc .append (line [3 :])
10697
10798 # If line start with keyword architecture then save name of architecture
108- elif line . lower () .startswith ('architecture' ):
99+ elif line_lowercase .startswith ('architecture' ):
109100 state = ParseState .ARCH_DECL
110101 current_constant = line .split ()[3 ]
111102
112103 # If line contains keyword constant and state is not generice then start to collecting constants
113- elif state == ParseState .ARCH_DECL and 'constant' in line :
104+ elif state == ParseState .ARCH_DECL and 'constant' in line_lowercase :
114105 parse_inline_doc_or_print_error (current_doc , filename , line , lineno )
115106 definition = line .split ('--' )[0 ].split (';' )[0 ]
116107 if ':=' not in definition :
@@ -120,30 +111,30 @@ def init(path: str) -> None:
120111 current_doc = []
121112
122113 # If there is -- without gap, then ignore
123- elif line == '--' :
114+ elif line_lowercase == '--' :
124115 current_doc .append ('' )
125116
126117 # If there is word entity then try parse, save entity name and add description of entity to associative array
127118 # ID of ass. array is name of entity. At the end clear current description and change state to entity declaration
128- elif line . lower (). startswith ('entity ' ) and ' is' in line :
119+ elif line_lowercase . startswith ('entity ' ) and ' is' in line_lowercase :
129120 parse_inline_doc_or_print_error (current_doc , filename , line , lineno )
130121 current_entity = line .split ()[1 ]
131122 entities [current_entity .lower ()] = current_doc
132123 current_doc = []
133124 state = ParseState .ENTITY_DECL
134125
135126 # Check if there is any port declaration
136- elif state == ParseState .ENTITY_DECL and line . lower () .startswith ('port' ):
127+ elif state == ParseState .ENTITY_DECL and line_lowercase .startswith ('port' ):
137128 state = ParseState .PORT
138129 current_doc = []
139130
140131 # Check if there is any generic declaration
141- elif state == ParseState .ENTITY_DECL and line . lower () .startswith ('generic' ):
132+ elif state == ParseState .ENTITY_DECL and line_lowercase .startswith ('generic' ):
142133 state = ParseState .GENERIC
143134 current_doc = []
144135
145136 # If there is line which contains ":" then it's one of ports, parse it and save his definition
146- elif state == ParseState .PORT and ':' in line :
137+ elif state == ParseState .PORT and ':' in line_lowercase :
147138 parse_inline_doc_or_print_error (current_doc , filename , line , lineno )
148139 definition = line .split ('--' )[0 ].split (';' )[0 ].split (':=' )[0 ].strip ()
149140 if definition .lower ().startswith ('signal' ):
@@ -157,7 +148,7 @@ def init(path: str) -> None:
157148 current_doc = []
158149
159150 # If there is line which contains ":" then it's one of generic, parse it and save his definition
160- elif state == ParseState .GENERIC and ':' in line :
151+ elif state == ParseState .GENERIC and ':' in line_lowercase :
161152 parse_inline_doc_or_print_error (current_doc , filename , line , lineno )
162153 definition = line .split ('--' )[0 ].split (';' )[0 ].strip ()
163154 if ':=' not in definition :
@@ -173,27 +164,27 @@ def init(path: str) -> None:
173164 current_doc = []
174165
175166 # End of the entity was found
176- elif state == ParseState .ENTITY_DECL and line . lower () .startswith ('end' ):
167+ elif state == ParseState .ENTITY_DECL and line_lowercase .startswith ('end' ):
177168 state = None
178169 group_state = None
179170 current_doc = []
180171
181172 # If there is magic word package then parse package and save his definition
182- elif (state is None or state is ParseState .PACKAGE ) and line . lower () .startswith ('package' ):
173+ elif (state is None or state is ParseState .PACKAGE ) and line_lowercase .startswith ('package' ):
183174 parse_inline_doc_or_print_error (current_doc , filename , line , lineno )
184175 state = ParseState .PACKAGE
185176 current_package = ('' if current_package == '' else (current_package + '.' )) + line .split ()[1 ]
186177 packages [current_package .lower ()] = current_doc
187178 current_doc = []
188179
189180 # Signalization of end of the package
190- elif state is ParseState .PACKAGE and line . lower () .startswith ('end package' ):
181+ elif state is ParseState .PACKAGE and line_lowercase .startswith ('end package' ):
191182 current_package = '.' .join (current_package .split ('.' )[:- 1 ])
192183 state = None if current_package == '' else ParseState .PACKAGE
193184 current_doc = []
194185
195186 # Package contains type, parse it
196- elif (state is None or state is ParseState .PACKAGE ) and line . lower () .startswith ('type' ):
187+ elif (state is None or state is ParseState .PACKAGE ) and line_lowercase .startswith ('type' ):
197188 if ' record' in line .split ('--' )[0 ].lower ().split (maxsplit = 2 )[- 1 ]:
198189 parse_inline_doc_or_print_error (current_doc , filename , line , lineno )
199190 records [line .split ()[1 ]] = current_doc
@@ -212,7 +203,7 @@ def init(path: str) -> None:
212203 current_doc = []
213204
214205 # Signalization of the end of record
215- elif state is ParseState .RECORD and line . lower () .startswith ('end record' ):
206+ elif state is ParseState .RECORD and line_lowercase .startswith ('end record' ):
216207 if current_package != '' :
217208 state = ParseState .PACKAGE
218209 else :
@@ -228,16 +219,16 @@ def init(path: str) -> None:
228219
229220 # Enumarate parsing
230221 elif state is ParseState .ENUM :
231- if not line .startswith (')' ):
222+ if not line_lowercase .startswith (')' ):
232223 parse_inline_doc_or_print_error (current_doc , filename , line , lineno )
233224 enumvals [current_type_name ][line .split (',' )[0 ]] = current_doc
234225 current_doc = []
235226
236227 # Function parsing
237- elif line . lower () .startswith ('function' ) and line .split ('--' )[0 ].strip ().endswith (';' ):
228+ elif line_lowercase .startswith ('function' ) and line .split ('--' )[0 ].strip ().endswith (';' ):
238229 parse_inline_doc_or_print_error (current_doc , filename , line , lineno )
239230 return_type = '' if 'return' not in line else (line .split ('return' )[1 ].strip ()[:- 1 ] + '.' )
240- functions [return_type + line . lower () .split ()[1 ]] = current_doc
231+ functions [return_type + line_lowercase .split ()[1 ]] = current_doc
241232 current_doc = []
242233
243234 # Ignore others
0 commit comments