示例#1
0
def _to_diagram_element(
    element: pyparsing.ParserElement,
    diagrams=None,
    vertical: typing.Union[int, bool] = 5,
) -> typing.Tuple[railroad.DiagramItem, typing.Dict[int, NamedDiagram]]:
    """
    Recursively converts a PyParsing Element to a railroad Element
    :param vertical: Controls at what point we make a list of elements vertical. If this is an integer (the default),
    it sets the threshold of the number of items before we go vertical. If True, always go vertical, if False, never
    do so
    :returns: A tuple, where the first item is the converted version of the input element, and the second item is a
    list of extra diagrams that also need to be displayed in order to represent recursive grammars
    """
    if diagrams is None:
        diagrams = {}
    else:
        # We don't want to be modifying the parent's version of the dict, although we do use it as a foundation
        diagrams = diagrams.copy()

    # Convert the nebulous list of child elements into a single list objects for easy use
    if hasattr(element, "exprs"):
        exprs = element.exprs
    elif hasattr(element, "expr"):
        exprs = [element.expr]
    else:
        exprs = []

    name = get_name(element)

    if isinstance(element, pyparsing.Forward):
        # If we encounter a forward reference, we have to split the diagram in two and return a new diagram which
        # represents the forward reference on its own

        # Python's id() is used to provide a unique identifier for elements
        el_id = id(element)
        if el_id in diagrams:
            name = diagrams[el_id].name
        else:
            # If the Forward has no real name, we name it Group N to at least make it unique
            count = len(diagrams) + 1
            name = get_name(element, "Group {}".format(count))
            # We have to first put in a placeholder so that, if we encounter this element deeper down in the tree,
            # we won't have an infinite loop
            diagrams[el_id] = NamedDiagram(name=name, diagram=None)

            # At this point we create a new subdiagram, and add it to the dictionary of diagrams
            forward_element, forward_diagrams = _to_diagram_element(
                exprs[0], diagrams)
            diagram = railroad.Diagram(forward_element)
            diagrams.update(forward_diagrams)
            diagrams[el_id] = diagrams[el_id]._replace(diagram=diagram)
            diagram.format(20)

        # Here we just use the element's name as a placeholder for the recursive grammar which is defined separately
        ret = railroad.NonTerminal(text=name)
    else:
        # If we don't encounter a Forward, we can continue to recurse into the tree

        # Recursively convert child elements
        children = []
        for expr in exprs:
            item, subdiagrams = _to_diagram_element(expr, diagrams)
            children.append(item)
            diagrams.update(subdiagrams)

        # Here we find the most relevant Railroad element for matching pyparsing Element
        if isinstance(element, pyparsing.And):
            if _should_vertical(vertical, len(children)):
                ret = railroad.Stack(*children)
            else:
                ret = railroad.Sequence(*children)
        elif isinstance(element, (pyparsing.Or, pyparsing.MatchFirst)):
            if _should_vertical(vertical, len(children)):
                ret = railroad.HorizontalChoice(*children)
            else:
                ret = railroad.Choice(0, *children)
        elif isinstance(element, pyparsing.Optional):
            ret = railroad.Optional(children[0])
        elif isinstance(element, pyparsing.OneOrMore):
            ret = railroad.OneOrMore(children[0])
        elif isinstance(element, pyparsing.ZeroOrMore):
            ret = railroad.ZeroOrMore(children[0])
        elif isinstance(element, pyparsing.Group):
            # Generally there isn't any merit in labelling a group as a group if it doesn't have a custom name
            ret = railroad.Group(children[0], label=get_name(element, ""))
        elif len(exprs) > 1:
            ret = railroad.Sequence(children[0])
        elif len(exprs) > 0:
            ret = railroad.Group(children[0], label=name)
        else:
            ret = railroad.Terminal(name)

    return ret, diagrams
示例#2
0
 def __init__(self, *items):
     choice_item = railroad.Choice(len(items) - 1, *items)
     one_or_more_item = railroad.OneOrMore(item=choice_item)
     super().__init__(one_or_more_item, label=self.all_label)
class Parser:
    exit_cmd = rr.Terminal("exit")
 
    echo_cmd = rr.Sequence(
        "echo",
        rr.Choice(1,
            rr.NonTerminal("{path}"),
            rr.NonTerminal("{string}"),
            rr.NonTerminal("[variable_access]")
        )
    )

    pwd_cmd = rr.Terminal("pwd")

    showpath_cmd = rr.Terminal("showpath")

    addpath_cmd = rr.Sequence(
        "addpath",
        rr.NonTerminal("{path}"),
    )

    delpath_cmd = rr.Terminal("delpath")

    internal_cmd = rr.Group(
        rr.Choice(0,
            echo_cmd,
            pwd_cmd,
            showpath_cmd,
            addpath_cmd,
            delpath_cmd,
            exit_cmd,
        ),
        "internal_cmd"
    )

    ########################################################################

    external_cmd = rr.Group(
        rr.Sequence(
            rr.MultipleChoice(0, "any",
                rr.NonTerminal("{path}"),
                rr.NonTerminal("{arg}"),
                rr.NonTerminal("[variable_access]"),
            ),
            # rr.Terminal("<<EOL>>")
        ),
        "external_cmd"
    )

    ########################################################################

    variables = rr.Choice(0,
        rr.Group(
            rr.Sequence(
                rr.NonTerminal("{id}"),
                rr.Terminal("="),
                rr.Choice(1,
                    rr.Skip(),
                    rr.NonTerminal("{word}"),
                    rr.NonTerminal("{string}"),
                )
            ),
            "variable_assign"
        ),
        rr.Group(
            rr.Sequence(
                "$",
                rr.NonTerminal("{id}"),
            ),
            "variable_access"
        )
    )
    
    ########################################################################

    handlers = rr.Group(
        rr.Sequence(
            rr.Choice(0,
                rr.Sequence(">", rr.NonTerminal("{path}")),
                rr.Sequence(">>", rr.NonTerminal("{path}")),
                rr.Sequence("|", rr.NonTerminal("[CMD]")),
                rr.Sequence("&"),
            )
        ),
        "handlers_cmd"
    )

    ########################################################################

    cmd = rr.Group(
        rr.Choice(0,
            rr.NonTerminal("[internal_cmd]"),
            rr.NonTerminal("[external_cmd]"),
        ),
        "CMD"
    )

    shell = rr.Choice(1,
        rr.NonTerminal("[variables]"),
        rr.Sequence(
            cmd, 
            handlers,
        ),
    )
        
    ########################################################################

    parser_SHELL = rr.Diagram(
        rr.Start(label="Parser"),
        rr.Choice(0,
            shell,
            rr.Comment("ERROR"),
        )
    )

    parser_VAR = rr.Diagram(
        rr.Start(type="complex", label="variables"),
        variables,
        rr.End(type="complex")
    )

    parser_INT_CMD = rr.Diagram(
        rr.Start(type="complex", label="internal_cmd"),
        internal_cmd,
        rr.End(type="complex")
    )

    parser_EXT_CMD = rr.Diagram(
        rr.Start(type="complex", label="external_cmd"),
        external_cmd,
        rr.End(type="complex")
    )

    def create_diagram(self):
        generate_svg(self.parser_SHELL, "parser-SHELL")
        generate_svg(self.parser_VAR, "parser-VAR")
        generate_svg(self.parser_INT_CMD, "parser-INT-CMD")
        generate_svg(self.parser_EXT_CMD, "parser-EXT-CMD")