cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rstFlatTable.py (13330B)


      1#!/usr/bin/env python3
      2# -*- coding: utf-8; mode: python -*-
      3# pylint: disable=C0330, R0903, R0912
      4
      5u"""
      6    flat-table
      7    ~~~~~~~~~~
      8
      9    Implementation of the ``flat-table`` reST-directive.
     10
     11    :copyright:  Copyright (C) 2016  Markus Heiser
     12    :license:    GPL Version 2, June 1991 see linux/COPYING for details.
     13
     14    The ``flat-table`` (:py:class:`FlatTable`) is a double-stage list similar to
     15    the ``list-table`` with some additional features:
     16
     17    * *column-span*: with the role ``cspan`` a cell can be extended through
     18      additional columns
     19
     20    * *row-span*: with the role ``rspan`` a cell can be extended through
     21      additional rows
     22
     23    * *auto span* rightmost cell of a table row over the missing cells on the
     24      right side of that table-row.  With Option ``:fill-cells:`` this behavior
     25      can be changed from *auto span* to *auto fill*, which automatically inserts
     26      (empty) cells instead of spanning the last cell.
     27
     28    Options:
     29
     30    * header-rows:   [int] count of header rows
     31    * stub-columns:  [int] count of stub columns
     32    * widths:        [[int] [int] ... ] widths of columns
     33    * fill-cells:    instead of autospann missing cells, insert missing cells
     34
     35    roles:
     36
     37    * cspan: [int] additionale columns (*morecols*)
     38    * rspan: [int] additionale rows (*morerows*)
     39"""
     40
     41# ==============================================================================
     42# imports
     43# ==============================================================================
     44
     45from docutils import nodes
     46from docutils.parsers.rst import directives, roles
     47from docutils.parsers.rst.directives.tables import Table
     48from docutils.utils import SystemMessagePropagation
     49
     50# ==============================================================================
     51# common globals
     52# ==============================================================================
     53
     54__version__  = '1.0'
     55
     56# ==============================================================================
     57def setup(app):
     58# ==============================================================================
     59
     60    app.add_directive("flat-table", FlatTable)
     61    roles.register_local_role('cspan', c_span)
     62    roles.register_local_role('rspan', r_span)
     63
     64    return dict(
     65        version = __version__,
     66        parallel_read_safe = True,
     67        parallel_write_safe = True
     68    )
     69
     70# ==============================================================================
     71def c_span(name, rawtext, text, lineno, inliner, options=None, content=None):
     72# ==============================================================================
     73    # pylint: disable=W0613
     74
     75    options  = options if options is not None else {}
     76    content  = content if content is not None else []
     77    nodelist = [colSpan(span=int(text))]
     78    msglist  = []
     79    return nodelist, msglist
     80
     81# ==============================================================================
     82def r_span(name, rawtext, text, lineno, inliner, options=None, content=None):
     83# ==============================================================================
     84    # pylint: disable=W0613
     85
     86    options  = options if options is not None else {}
     87    content  = content if content is not None else []
     88    nodelist = [rowSpan(span=int(text))]
     89    msglist  = []
     90    return nodelist, msglist
     91
     92
     93# ==============================================================================
     94class rowSpan(nodes.General, nodes.Element): pass # pylint: disable=C0103,C0321
     95class colSpan(nodes.General, nodes.Element): pass # pylint: disable=C0103,C0321
     96# ==============================================================================
     97
     98# ==============================================================================
     99class FlatTable(Table):
    100# ==============================================================================
    101
    102    u"""FlatTable (``flat-table``) directive"""
    103
    104    option_spec = {
    105        'name': directives.unchanged
    106        , 'class': directives.class_option
    107        , 'header-rows': directives.nonnegative_int
    108        , 'stub-columns': directives.nonnegative_int
    109        , 'widths': directives.positive_int_list
    110        , 'fill-cells' : directives.flag }
    111
    112    def run(self):
    113
    114        if not self.content:
    115            error = self.state_machine.reporter.error(
    116                'The "%s" directive is empty; content required.' % self.name,
    117                nodes.literal_block(self.block_text, self.block_text),
    118                line=self.lineno)
    119            return [error]
    120
    121        title, messages = self.make_title()
    122        node = nodes.Element()          # anonymous container for parsing
    123        self.state.nested_parse(self.content, self.content_offset, node)
    124
    125        tableBuilder = ListTableBuilder(self)
    126        tableBuilder.parseFlatTableNode(node)
    127        tableNode = tableBuilder.buildTableNode()
    128        # SDK.CONSOLE()  # print --> tableNode.asdom().toprettyxml()
    129        if title:
    130            tableNode.insert(0, title)
    131        return [tableNode] + messages
    132
    133
    134# ==============================================================================
    135class ListTableBuilder(object):
    136# ==============================================================================
    137
    138    u"""Builds a table from a double-stage list"""
    139
    140    def __init__(self, directive):
    141        self.directive = directive
    142        self.rows      = []
    143        self.max_cols  = 0
    144
    145    def buildTableNode(self):
    146
    147        colwidths    = self.directive.get_column_widths(self.max_cols)
    148        if isinstance(colwidths, tuple):
    149            # Since docutils 0.13, get_column_widths returns a (widths,
    150            # colwidths) tuple, where widths is a string (i.e. 'auto').
    151            # See https://sourceforge.net/p/docutils/patches/120/.
    152            colwidths = colwidths[1]
    153        stub_columns = self.directive.options.get('stub-columns', 0)
    154        header_rows  = self.directive.options.get('header-rows', 0)
    155
    156        table = nodes.table()
    157        tgroup = nodes.tgroup(cols=len(colwidths))
    158        table += tgroup
    159
    160
    161        for colwidth in colwidths:
    162            colspec = nodes.colspec(colwidth=colwidth)
    163            # FIXME: It seems, that the stub method only works well in the
    164            # absence of rowspan (observed by the html builder, the docutils-xml
    165            # build seems OK).  This is not extraordinary, because there exists
    166            # no table directive (except *this* flat-table) which allows to
    167            # define coexistent of rowspan and stubs (there was no use-case
    168            # before flat-table). This should be reviewed (later).
    169            if stub_columns:
    170                colspec.attributes['stub'] = 1
    171                stub_columns -= 1
    172            tgroup += colspec
    173        stub_columns = self.directive.options.get('stub-columns', 0)
    174
    175        if header_rows:
    176            thead = nodes.thead()
    177            tgroup += thead
    178            for row in self.rows[:header_rows]:
    179                thead += self.buildTableRowNode(row)
    180
    181        tbody = nodes.tbody()
    182        tgroup += tbody
    183
    184        for row in self.rows[header_rows:]:
    185            tbody += self.buildTableRowNode(row)
    186        return table
    187
    188    def buildTableRowNode(self, row_data, classes=None):
    189        classes = [] if classes is None else classes
    190        row = nodes.row()
    191        for cell in row_data:
    192            if cell is None:
    193                continue
    194            cspan, rspan, cellElements = cell
    195
    196            attributes = {"classes" : classes}
    197            if rspan:
    198                attributes['morerows'] = rspan
    199            if cspan:
    200                attributes['morecols'] = cspan
    201            entry = nodes.entry(**attributes)
    202            entry.extend(cellElements)
    203            row += entry
    204        return row
    205
    206    def raiseError(self, msg):
    207        error =  self.directive.state_machine.reporter.error(
    208            msg
    209            , nodes.literal_block(self.directive.block_text
    210                                  , self.directive.block_text)
    211            , line = self.directive.lineno )
    212        raise SystemMessagePropagation(error)
    213
    214    def parseFlatTableNode(self, node):
    215        u"""parses the node from a :py:class:`FlatTable` directive's body"""
    216
    217        if len(node) != 1 or not isinstance(node[0], nodes.bullet_list):
    218            self.raiseError(
    219                'Error parsing content block for the "%s" directive: '
    220                'exactly one bullet list expected.' % self.directive.name )
    221
    222        for rowNum, rowItem in enumerate(node[0]):
    223            row = self.parseRowItem(rowItem, rowNum)
    224            self.rows.append(row)
    225        self.roundOffTableDefinition()
    226
    227    def roundOffTableDefinition(self):
    228        u"""Round off the table definition.
    229
    230        This method rounds off the table definition in :py:member:`rows`.
    231
    232        * This method inserts the needed ``None`` values for the missing cells
    233        arising from spanning cells over rows and/or columns.
    234
    235        * recount the :py:member:`max_cols`
    236
    237        * Autospan or fill (option ``fill-cells``) missing cells on the right
    238          side of the table-row
    239        """
    240
    241        y = 0
    242        while y < len(self.rows):
    243            x = 0
    244
    245            while x < len(self.rows[y]):
    246                cell = self.rows[y][x]
    247                if cell is None:
    248                    x += 1
    249                    continue
    250                cspan, rspan = cell[:2]
    251                # handle colspan in current row
    252                for c in range(cspan):
    253                    try:
    254                        self.rows[y].insert(x+c+1, None)
    255                    except: # pylint: disable=W0702
    256                        # the user sets ambiguous rowspans
    257                        pass # SDK.CONSOLE()
    258                # handle colspan in spanned rows
    259                for r in range(rspan):
    260                    for c in range(cspan + 1):
    261                        try:
    262                            self.rows[y+r+1].insert(x+c, None)
    263                        except: # pylint: disable=W0702
    264                            # the user sets ambiguous rowspans
    265                            pass # SDK.CONSOLE()
    266                x += 1
    267            y += 1
    268
    269        # Insert the missing cells on the right side. For this, first
    270        # re-calculate the max columns.
    271
    272        for row in self.rows:
    273            if self.max_cols < len(row):
    274                self.max_cols = len(row)
    275
    276        # fill with empty cells or cellspan?
    277
    278        fill_cells = False
    279        if 'fill-cells' in self.directive.options:
    280            fill_cells = True
    281
    282        for row in self.rows:
    283            x =  self.max_cols - len(row)
    284            if x and not fill_cells:
    285                if row[-1] is None:
    286                    row.append( ( x - 1, 0, []) )
    287                else:
    288                    cspan, rspan, content = row[-1]
    289                    row[-1] = (cspan + x, rspan, content)
    290            elif x and fill_cells:
    291                for i in range(x):
    292                    row.append( (0, 0, nodes.comment()) )
    293
    294    def pprint(self):
    295        # for debugging
    296        retVal = "[   "
    297        for row in self.rows:
    298            retVal += "[ "
    299            for col in row:
    300                if col is None:
    301                    retVal += ('%r' % col)
    302                    retVal += "\n    , "
    303                else:
    304                    content = col[2][0].astext()
    305                    if len (content) > 30:
    306                        content = content[:30] + "..."
    307                    retVal += ('(cspan=%s, rspan=%s, %r)'
    308                               % (col[0], col[1], content))
    309                    retVal += "]\n    , "
    310            retVal = retVal[:-2]
    311            retVal += "]\n  , "
    312        retVal = retVal[:-2]
    313        return retVal + "]"
    314
    315    def parseRowItem(self, rowItem, rowNum):
    316        row = []
    317        childNo = 0
    318        error   = False
    319        cell    = None
    320        target  = None
    321
    322        for child in rowItem:
    323            if (isinstance(child , nodes.comment)
    324                or isinstance(child, nodes.system_message)):
    325                pass
    326            elif isinstance(child , nodes.target):
    327                target = child
    328            elif isinstance(child, nodes.bullet_list):
    329                childNo += 1
    330                cell = child
    331            else:
    332                error = True
    333                break
    334
    335        if childNo != 1 or error:
    336            self.raiseError(
    337                'Error parsing content block for the "%s" directive: '
    338                'two-level bullet list expected, but row %s does not '
    339                'contain a second-level bullet list.'
    340                % (self.directive.name, rowNum + 1))
    341
    342        for cellItem in cell:
    343            cspan, rspan, cellElements = self.parseCellItem(cellItem)
    344            if target is not None:
    345                cellElements.insert(0, target)
    346            row.append( (cspan, rspan, cellElements) )
    347        return row
    348
    349    def parseCellItem(self, cellItem):
    350        # search and remove cspan, rspan colspec from the first element in
    351        # this listItem (field).
    352        cspan = rspan = 0
    353        if not len(cellItem):
    354            return cspan, rspan, []
    355        for elem in cellItem[0]:
    356            if isinstance(elem, colSpan):
    357                cspan = elem.get("span")
    358                elem.parent.remove(elem)
    359                continue
    360            if isinstance(elem, rowSpan):
    361                rspan = elem.get("span")
    362                elem.parent.remove(elem)
    363                continue
    364        return cspan, rspan, cellItem[:]