summaryrefslogblamecommitdiffstats
path: root/rtemsspec/glossary.py
blob: 50b6df82e93a9b03e8d07e8d9735f82c5a1dac5b (plain) (tree)
1
2
3
4


                                                                        
                                                        























                                                                             
                                              
 
                                                 
                                                                            



                         

                                

                         

 










                                                                              
                                                                    
                                                         
                                               


                                                  
                                                
                                           

 
                                                                        
                                                           
                             
                                       

                                
                               
                                                                             
                                                                 

                                                            
                                        
                         

 

                                        
                           

 
                                                            
                                                      


                                                             
                                                     



                                                  
                                            

                                                  

 
                                  
 


                                                            







                                                                              

                                                 
 

                                                             
                                              
                                                                      

 
                                                                             
                                                           
              
                                                                        
                                          

 

                                                                  
                                
                                            
                                                            
                                           
                                                                
                                                        

 
                                                                              






                                                                           
                                        



                                                       
 
                                                                          
                                                                

                                               
                                                                              
# SPDX-License-Identifier: BSD-2-Clause
""" This module provides functions for glossary of terms generation. """

# Copyright (C) 2019, 2020 embedded brains GmbH & Co. KG
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

import glob
import re
from typing import Any, Dict, List, NamedTuple

from rtemsspec.sphinxcontent import SphinxContent
from rtemsspec.items import Item, ItemCache, ItemGetValueContext, ItemMapper

ItemMap = Dict[str, Item]


class _Glossary(NamedTuple):
    """ A glossary of terms. """
    uid_to_item: ItemMap
    term_to_item: ItemMap


def augment_glossary_terms(item: Item, path: List[str]) -> None:
    """
    Augments the glossary term items of the cache with a glossary path prefix.
    """
    for child in item.children("requirement-refinement"):
        augment_glossary_terms(child, path + [child["name"]])
    for child in item.children("glossary-member"):
        term = " - ".join(path + [child["term"]])
        child["_term"] = term


def _gather_glossary_terms(item: Item, glossary: _Glossary) -> None:
    for child in item.children("requirement-refinement"):
        _gather_glossary_terms(child, glossary)
    for child in item.children("glossary-member"):
        glossary.uid_to_item[child.uid] = child
        term = child["_term"]
        assert term not in glossary.term_to_item
        glossary.term_to_item[term] = child


def _generate_glossary_content(terms: ItemMap, header: str, target: str,
                               mapper: ItemMapper) -> None:
    content = SphinxContent()
    content.add_header(header, level=1)
    content.add(".. glossary::")
    with content.indent():
        content.add(":sorted:")
        for item in sorted(terms.values(), key=lambda x: x["_term"].lower()):
            content.register_license_and_copyrights_of_item(item)
            text = mapper.substitute(item["text"], item)
            content.add_definition_item(item["_term"], text)
    content.add_licence_and_copyrights()
    content.write(target)


_TERM = re.compile(r":term:`([^`]+)`")
_TERM_2 = re.compile(r"^[^<]+<([^>]+)>")
_SPACE = re.compile(r"\s+")


def _find_glossary_terms(path: str, document_terms: ItemMap,
                         glossary: _Glossary) -> None:
    for src in glob.glob(path + "/**/*.rst", recursive=True):
        if src.endswith("glossary.rst"):
            continue
        with open(src, "r", encoding="utf-8") as out:
            for term in _TERM.findall(out.read()):
                match = _TERM_2.search(term)
                if match:
                    term = match.group(1)
                term = _SPACE.sub(" ", term)
                item = glossary.term_to_item[term]
                document_terms[item.uid] = item


class _GlossaryMapper(ItemMapper):

    def __init__(self, item: Item, document_terms: ItemMap):
        super().__init__(item)
        self._document_terms = document_terms
        self.add_get_value("glossary/term:/term", self._add_to_terms)
        self.add_get_value("glossary/term:/plural", self._add_to_terms)

    def _add_to_terms(self, ctx: ItemGetValueContext) -> Any:
        if ctx.item.uid not in self._document_terms:
            self._document_terms[ctx.item.uid] = ctx.item
            _GlossaryMapper(ctx.item,
                            self._document_terms).substitute(ctx.item["text"])
        # The value of this substitute is unused.
        return ""


def _resolve_glossary_terms(document_terms: ItemMap) -> None:
    for term in list(document_terms.values()):
        _GlossaryMapper(term, document_terms).substitute(term["text"])


def _generate_project_glossary(glossary: _Glossary, header: str, target: str,
                               mapper: ItemMapper) -> None:
    if target:
        _generate_glossary_content(glossary.uid_to_item, header, target,
                                   mapper)


def _generate_document_glossary(config: dict, glossary: _Glossary,
                                mapper: ItemMapper) -> None:
    document_terms: ItemMap = {}
    for path in config["rest-source-paths"]:
        _find_glossary_terms(path, document_terms, glossary)
    _resolve_glossary_terms(document_terms)
    _generate_glossary_content(document_terms, config["header"],
                               config["target"], mapper)


def generate(config: dict, item_cache: ItemCache, mapper: ItemMapper) -> None:
    """
    Generates glossaries of terms according to the configuration.

    :param config: A dictionary with configuration entries.
    :param item_cache: The specification item cache containing the glossary
                       groups and terms.
    """
    project_glossary = _Glossary({}, {})
    for uid in config["project-groups"]:
        group = item_cache[uid]
        assert group.type == "glossary/group"
        _gather_glossary_terms(group, project_glossary)

    _generate_project_glossary(project_glossary, config["project-header"],
                               config["project-target"], mapper)

    for document_config in config["documents"]:
        _generate_document_glossary(document_config, project_glossary, mapper)