qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

hxtool.py (7850B)


      1 # coding=utf-8
      2 #
      3 # QEMU hxtool .hx file parsing extension
      4 #
      5 # Copyright (c) 2020 Linaro
      6 #
      7 # This work is licensed under the terms of the GNU GPLv2 or later.
      8 # See the COPYING file in the top-level directory.
      9 """hxtool is a Sphinx extension that implements the hxtool-doc directive"""
     10 
     11 # The purpose of this extension is to read fragments of rST
     12 # from .hx files, and insert them all into the current document.
     13 # The rST fragments are delimited by SRST/ERST lines.
     14 # The conf.py file must set the hxtool_srctree config value to
     15 # the root of the QEMU source tree.
     16 # Each hxtool-doc:: directive takes one argument which is the
     17 # path of the .hx file to process, relative to the source tree.
     18 
     19 import os
     20 import re
     21 from enum import Enum
     22 
     23 from docutils import nodes
     24 from docutils.statemachine import ViewList
     25 from docutils.parsers.rst import directives, Directive
     26 from sphinx.errors import ExtensionError
     27 from sphinx.util.nodes import nested_parse_with_titles
     28 import sphinx
     29 
     30 # Sphinx up to 1.6 uses AutodocReporter; 1.7 and later
     31 # use switch_source_input. Check borrowed from kerneldoc.py.
     32 Use_SSI = sphinx.__version__[:3] >= '1.7'
     33 if Use_SSI:
     34     from sphinx.util.docutils import switch_source_input
     35 else:
     36     from sphinx.ext.autodoc import AutodocReporter
     37 
     38 __version__ = '1.0'
     39 
     40 # We parse hx files with a state machine which may be in one of two
     41 # states: reading the C code fragment, or inside a rST fragment.
     42 class HxState(Enum):
     43     CTEXT = 1
     44     RST = 2
     45 
     46 def serror(file, lnum, errtext):
     47     """Raise an exception giving a user-friendly syntax error message"""
     48     raise ExtensionError('%s line %d: syntax error: %s' % (file, lnum, errtext))
     49 
     50 def parse_directive(line):
     51     """Return first word of line, if any"""
     52     return re.split('\W', line)[0]
     53 
     54 def parse_defheading(file, lnum, line):
     55     """Handle a DEFHEADING directive"""
     56     # The input should be "DEFHEADING(some string)", though note that
     57     # the 'some string' could be the empty string. If the string is
     58     # empty we ignore the directive -- these are used only to add
     59     # blank lines in the plain-text content of the --help output.
     60     #
     61     # Return the heading text. We strip out any trailing ':' for
     62     # consistency with other headings in the rST documentation.
     63     match = re.match(r'DEFHEADING\((.*?):?\)', line)
     64     if match is None:
     65         serror(file, lnum, "Invalid DEFHEADING line")
     66     return match.group(1)
     67 
     68 def parse_archheading(file, lnum, line):
     69     """Handle an ARCHHEADING directive"""
     70     # The input should be "ARCHHEADING(some string, other arg)",
     71     # though note that the 'some string' could be the empty string.
     72     # As with DEFHEADING, empty string ARCHHEADINGs will be ignored.
     73     #
     74     # Return the heading text. We strip out any trailing ':' for
     75     # consistency with other headings in the rST documentation.
     76     match = re.match(r'ARCHHEADING\((.*?):?,.*\)', line)
     77     if match is None:
     78         serror(file, lnum, "Invalid ARCHHEADING line")
     79     return match.group(1)
     80 
     81 class HxtoolDocDirective(Directive):
     82     """Extract rST fragments from the specified .hx file"""
     83     required_argument = 1
     84     optional_arguments = 1
     85     option_spec = {
     86         'hxfile': directives.unchanged_required
     87     }
     88     has_content = False
     89 
     90     def run(self):
     91         env = self.state.document.settings.env
     92         hxfile = env.config.hxtool_srctree + '/' + self.arguments[0]
     93 
     94         # Tell sphinx of the dependency
     95         env.note_dependency(os.path.abspath(hxfile))
     96 
     97         state = HxState.CTEXT
     98         # We build up lines of rST in this ViewList, which we will
     99         # later put into a 'section' node.
    100         rstlist = ViewList()
    101         current_node = None
    102         node_list = []
    103 
    104         with open(hxfile) as f:
    105             lines = (l.rstrip() for l in f)
    106             for lnum, line in enumerate(lines, 1):
    107                 directive = parse_directive(line)
    108 
    109                 if directive == 'HXCOMM':
    110                     pass
    111                 elif directive == 'SRST':
    112                     if state == HxState.RST:
    113                         serror(hxfile, lnum, 'expected ERST, found SRST')
    114                     else:
    115                         state = HxState.RST
    116                 elif directive == 'ERST':
    117                     if state == HxState.CTEXT:
    118                         serror(hxfile, lnum, 'expected SRST, found ERST')
    119                     else:
    120                         state = HxState.CTEXT
    121                 elif directive == 'DEFHEADING' or directive == 'ARCHHEADING':
    122                     if directive == 'DEFHEADING':
    123                         heading = parse_defheading(hxfile, lnum, line)
    124                     else:
    125                         heading = parse_archheading(hxfile, lnum, line)
    126                     if heading == "":
    127                         continue
    128                     # Put the accumulated rST into the previous node,
    129                     # and then start a fresh section with this heading.
    130                     if len(rstlist) > 0:
    131                         if current_node is None:
    132                             # We had some rST fragments before the first
    133                             # DEFHEADING. We don't have a section to put
    134                             # these in, so rather than magicing up a section,
    135                             # make it a syntax error.
    136                             serror(hxfile, lnum,
    137                                    'first DEFHEADING must precede all rST text')
    138                         self.do_parse(rstlist, current_node)
    139                         rstlist = ViewList()
    140                     if current_node is not None:
    141                         node_list.append(current_node)
    142                     section_id = 'hxtool-%d' % env.new_serialno('hxtool')
    143                     current_node = nodes.section(ids=[section_id])
    144                     current_node += nodes.title(heading, heading)
    145                 else:
    146                     # Not a directive: put in output if we are in rST fragment
    147                     if state == HxState.RST:
    148                         # Sphinx counts its lines from 0
    149                         rstlist.append(line, hxfile, lnum - 1)
    150 
    151         if current_node is None:
    152             # We don't have multiple sections, so just parse the rst
    153             # fragments into a dummy node so we can return the children.
    154             current_node = nodes.section()
    155             self.do_parse(rstlist, current_node)
    156             return current_node.children
    157         else:
    158             # Put the remaining accumulated rST into the last section, and
    159             # return all the sections.
    160             if len(rstlist) > 0:
    161                 self.do_parse(rstlist, current_node)
    162             node_list.append(current_node)
    163             return node_list
    164 
    165     # This is from kerneldoc.py -- it works around an API change in
    166     # Sphinx between 1.6 and 1.7. Unlike kerneldoc.py, we use
    167     # sphinx.util.nodes.nested_parse_with_titles() rather than the
    168     # plain self.state.nested_parse(), and so we can drop the saving
    169     # of title_styles and section_level that kerneldoc.py does,
    170     # because nested_parse_with_titles() does that for us.
    171     def do_parse(self, result, node):
    172         if Use_SSI:
    173             with switch_source_input(self.state, result):
    174                 nested_parse_with_titles(self.state, result, node)
    175         else:
    176             save = self.state.memo.reporter
    177             self.state.memo.reporter = AutodocReporter(result, self.state.memo.reporter)
    178             try:
    179                 nested_parse_with_titles(self.state, result, node)
    180             finally:
    181                 self.state.memo.reporter = save
    182 
    183 def setup(app):
    184     """ Register hxtool-doc directive with Sphinx"""
    185     app.add_config_value('hxtool_srctree', None, 'env')
    186     app.add_directive('hxtool-doc', HxtoolDocDirective)
    187 
    188     return dict(
    189         version = __version__,
    190         parallel_read_safe = True,
    191         parallel_write_safe = True
    192     )