# Copyright lowRISC contributors. # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 import logging as log import re from typing import List, Match, Optional, Set def expand_paras(s: str, rnames: Set[str]) -> List[str]: '''Expand a description field to HTML. This supports a sort of simple pseudo-markdown. Supported Markdown features: - Separate paragraphs on a blank line - **bold** and *italicised* text - Back-ticks for pre-formatted text We also generate links to registers when a name is prefixed with a double exclamation mark. For example, if there is a register FOO then !!FOO or !!FOO.field will generate a link to that register. Returns a list of rendered paragraphs ''' # Start by splitting into paragraphs. The regex matches a newline followed # by one or more lines that just contain whitespace. Then render each # paragraph with the _expand_paragraph worker function. paras = [_expand_paragraph(paragraph.strip(), rnames) for paragraph in re.split(r'\n(?:\s*\n)+', s)] # There will always be at least one paragraph (splitting an empty string # gives ['']) assert paras return paras def _expand_paragraph(s: str, rnames: Set[str]) -> str: '''Expand a single paragraph, as described in _get_desc_paras''' def fieldsub(match: Match[str]) -> str: base = match.group(1).partition('.')[0].lower() if base in rnames: if match.group(1)[-1] == ".": return ('' + match.group(1)[:-1] + '.') else: return ('' + match.group(1) + '') log.warn('!!' + match.group(1).partition('.')[0] + ' not found in register list.') return match.group(0) # Split out pre-formatted text. Because the call to re.split has a capture # group in the regex, we get an odd number of results. Elements with even # indices are "normal text". Those with odd indices are the captured text # between the back-ticks. code_split = re.split(r'`([^`]+)`', s) expanded_parts = [] for idx, part in enumerate(code_split): if idx & 1: # Text contained in back ticks expanded_parts.append('{}'.format(part)) continue part = re.sub(r"!!([A-Za-z0-9_.]+)", fieldsub, part) part = re.sub(r"(?s)\*\*(.+?)\*\*", r'\1', part) part = re.sub(r"\*([^*]+?)\*", r'\1', part) expanded_parts.append(part) return '

{}

'.format(''.join(expanded_parts)) def render_td(s: str, rnames: Set[str], td_class: Optional[str]) -> str: '''Expand a description field and put it in a . Returns a string. See _get_desc_paras for the format that gets expanded. ''' desc_paras = expand_paras(s, rnames) class_attr = '' if td_class is None else ' class="{}"'.format(td_class) return '{}'.format(class_attr, ''.join(desc_paras))