def test_004__n1_eq_t1__with_eq(self):
        if False:
            from prettyprinter import cpprint as pp
            import p
            print(f": test_004__n1_eq_t1__with_eq")
            print(f": n1 :")
            pp(self.n1)
            print(f": t1 :")
            pp(self.t1)
        NonTerminal_enable_structural_eq()
        with self.assertRaises(AssertionError) as context:
            assert self.n1 == self.t1  # now it fails
            self.assertTrue('Internal error, AssertionError not raised !!!')
        assert self.n1 != self.t1
        assert self.n1 == deepcopy(self.n1)

        t2 = Terminal(self.dot, 0, 'one')
        n2 = NonTerminal(self.dot, [t2])
        assert self.n1 == n2

        t2 = Terminal(self.dot, 0, 'one')
        n2 = NonTerminal(self.dot, [t2])
        assert self.n1 == n2

        bang = StrMatch('!', rule_name='bang')
        t3 = Terminal(bang, 0, 'one')
        n3 = NonTerminal(bang, [t3])
        assert self.n1 != n3
Esempio n. 2
0
    def setUp(self):
        self.dot = StrMatch('.', rule_name='self.dot')

        self.s1 = 's1 : string'
        self.s2 = 's2 : string'
        self.s3 = 's3 : string'

        # rule, position, value
        self.t1 = Terminal(self.dot, 0, 'one')
        self.t2 = Terminal(self.dot, 0, 'two')
        self.t3 = Terminal(self.dot, 0, 'three')

        assert not isinstance(self.t1, list)
        assert not isinstance(self.t2, list)
        assert not isinstance(self.t3, list)

        # rule, value : a list where the first element is a node
        # self.n1 = NonTerminal(self.dot, self.t1)   # TypeError: 'Terminal' object is not subscriptable
        self.n2 = NonTerminal(self.dot, [self.t1])
        self.n3 = NonTerminal(self.dot, self.n2)
        self.n4 = NonTerminal(self.dot, [self.n2])

        assert isinstance(self.n2, list)
        assert isinstance(self.n3, list)
        assert isinstance(self.n4, list)

        self.v0 = self.n2
        self.v1 = [self.s1, self.s2]
        self.v2 = self.t1
        self.v3s = self.s3
        self.v3t = (self.s1, self.s2)
Esempio n. 3
0
 def as_lines(self, node, children, depth=0):
     if len(children):
         # print(f": as_lines() : {node.rule_name} : collecting {len(children)} children"
         #       f": {repr(children)}")
         value = '\n'.join([c.value for c in children])
     else:
         # print(f": as_lines() : {node.rule_name} : single value '{node.value}'")
         value = node.value
     return Terminal(StrMatch('', node.rule_name), 0, value)
Esempio n. 4
0
def str_match_SA(parser, node, children):
    to_match = children[0]

    # Support for autokwd metamodel param.
    if parser.metamodel.autokwd:
        match = parser.keyword_regex.match(to_match)
        if match and match.span() == (0, len(to_match)):
            regex_match = RegExMatch(r'{}\b'.format(to_match),
                                     ignore_case=parser.metamodel.ignore_case,
                                     str_repr=to_match)
            regex_match.compile()
            return regex_match
    return StrMatch(to_match, ignore_case=parser.metamodel.ignore_case)
Esempio n. 5
0
    def visit_str_match(self, node, children):
        match_str = node.value[1:-1]

        # Scan the string literal, and sequentially match those escape
        # sequences which are syntactically valid Python. Attempt to convert
        # those, raising ``GrammarError`` for any semantically invalid ones.
        def decode_escape(match):
            try:
                return codecs.decode(match.group(0), "unicode_escape")
            except UnicodeDecodeError:
                raise GrammarError("Invalid escape sequence '%s'." %
                                   match.group(0))

        match_str = PEG_ESCAPE_SEQUENCES_RE.sub(decode_escape, match_str)

        return StrMatch(match_str, ignore_case=self.ignore_case)
Esempio n. 6
0
    def visit_str_match(self, node, children):
        try:
            to_match = children[0][1:-1]
        except IndexError:
            to_match = ''

        # Support for autokwd metamodel param.
        if self.metamodel.autokwd:
            match = self.keyword_regex.match(to_match)
            if match and match.span() == (0, len(to_match)):
                regex_match = RegExMatch(
                    r'{}\b'.format(to_match),
                    ignore_case=self.metamodel.ignore_case,
                    str_repr=to_match)
                regex_match.compile()
                return regex_match
        return StrMatch(to_match, ignore_case=self.metamodel.ignore_case)
Esempio n. 7
0
def empty_string_is_accepted(name_prefix, rule_f, ch):
    """Add test that empty string is accepted
    """
    def create_method(rule):
        def the_method(self):
            nonlocal rule
            parsed = ParserPython(rule, skipws=False).parse('')

        return the_method

    rule = rule_f()
    if isinstance(rule, str):
        rule = StrMatch(ch, rule_f.__name__)

    test_name = "empty_string_is_accepted"
    method_name = f"test_{name_prefix}_rule_{rule.rule_name}_{test_name}"
    method = create_method(rule)
    setattr(Test_Common, method_name, method)
Esempio n. 8
0
def empty_string_raises_NoMatch(name_prefix, rule_f, ch):
    """Add test that empty string does not match
    """
    def create_method(rule):
        def the_method(self):
            nonlocal rule
            with self.assertRaises(NoMatch):
                parsed = ParserPython(rule, skipws=False).parse('')

        return the_method

    rule = rule_f()
    if isinstance(rule, str):
        rule = StrMatch(ch, rule_f.__name__)

    test_name = "empty_string_raises_NoMatch"
    method_name = f"test_{name_prefix}_rule_{rule.rule_name}_{test_name}"
    method = create_method(rule)
    setattr(Test_Common, method_name, method)
Esempio n. 9
0
    def visit_repeatable(self, node, children, depth=0):

        if False and str(children) in [
                "[command 'turn' [0], command 'rise' [0]]",
                "[command 'move' [0]]"
        ]:
            print(f": repeatable : {node.name} : {children}")
            pp(NonTerminal(node.rule, children))
            print('')

        n_children = len(children)
        assert n_children in [1, 2]

        term = children[0]

        if n_children == 1:
            return term

        assert children[1] == self.REPEATING

        return NonTerminal(StrMatch(':repeating:', 'repeating'), [term])
 def grammar():
     return UnorderedGroup("a", "b", "c", sep=StrMatch(",")), EOF
Esempio n. 11
0
def r_literal(rule_f, s):
    rule = rule_f()
    if isinstance(rule, str):
        return StrMatch(s, rule_name=rule_f.__name__)
    return rule
 def setUp(self):
     NonTerminal_restore_original_eq()
     self.dot = StrMatch('.', rule_name='dot')
     self.t1 = Terminal(self.dot, 0, 'one')
     self.n1 = NonTerminal(self.dot, [self.t1])
Esempio n. 13
0
def or_more_from_charset(rule_f, charset, suffix, levels, empty_ok=False):
    for ch in charset:
        s = ch + suffix
        string_test('2_class', rule_f, s, empty_ok=empty_ok)
        if levels > 1:
            or_more_from_charset(rule_f, charset, s, levels - 1, empty_ok)


#------------------------------------------------------------------------------

if tst_individual_characters:

    for name, ch in common.CHARACTER_NAME_TO_CHAR.items():
        (ch_hexes, ch_names) = character_lookup(ch)
        rule_f = lambda: StrMatch(
            ch, rule_name=f"common_character_{ch_hexes[0]}_{ch_names[0]}")
        string_test("1_individual", rule_f, ch)
        if tst_debug_first_char:
            break

if tst_whitespace_chars:

    for ch in common.WHITESPACE_CHARS:
        (ch_hexes, ch_names) = character_lookup(ch)
        rule_f = lambda: StrMatch(
            ch, rule_name=f"whitespace_character_{ch_hexes[0]}_{ch_names[0]}")
        string_test("1_individual", rule_f, ch, ch_names)
        if tst_debug_first_char:
            break

#------------------------------------------------------------------------------
Esempio n. 14
0
class DocOptSimplifyVisitor_Pass2(object):

    classes = \
        ( ' divulge_list '
          ' divulge_single '
          ' divulge_terminal '
          ' text '
          ' empty '
        ) . split()

    UNUSED_unwrap = \
        ( ' other_sections other '
          ' required '  # explicit no differnet than explicit
          # ' expression '  	# NO, determinant for sides of implicit choice
          ' term argument '
          ' usage_line '          # NOT: usage or usage_pattern
          ' option short long long_with_eq_all_caps long_with_eq_angle '
          ' option_line option_list option_single '
          ' operand_line ' # operand_section '
          # ' short_no_arg short_stacked '	-- necessary for semantics
          # ' long_no_arg long_with_eq_arg '	-- necessary for semantics
        )

    # A 'list' being a NonTerminal with one or more children
    _divulge_list = \
        ( ' other_sections '
          ' required '
          ' term '
          # NOT why working on option.list # ' list '
          # !o # ' option_list '
          ' option_list_comma '
          ' option_list_bar '
          ' option_list_space '
        )

    # A 'single' being a NonTerminal with only one child
    _divulge_single = \
        ( ' other '
          ' usage_line '          # NOT usage or usage_pattern
          ' argument '
          # !o # ' option '
          ' short '
          ' long '
          ' option_line option_single '
          # ' long_with_eq_arg '
        )

    # A 'terminal' being, obviously, a Terminal node, contents in node.value
    _divulge_terminal = \
        ( ' short_no_arg_ '
          ' '
          # TERMINALS: short_no_arg short_stacked long_with_eq_all_caps long_with_eq_angle
        )

    _text = \
        ( ' intro '
          ' operand_help '
          ' option_help '
          ' short_no_arg '
          ' long_no_arg '
          ' operand_no_space '
          # ' operand_intro operand_help  '
          # ' option_intro option_help '
        )

    _empty = \
        ( ' intro_line '
          ' usage_entry '
          ' operand_all_caps operand_angle '
          ' EQ LPAREN RPAREN LBRACKET RBRACKET OR COMMA EOF blankline newline '
        )

    # Used to make BAR directly searchable within the choice list
    BAR = Terminal(StrMatch('|', rule_name='BAR'), 0, '|')

    # '--long= <argument>' => '--long' '=' '<argument>'
    # FIXME: revamp grammar to explicitly handle all whitespace, see issues.txt
    EQ = Terminal(StrMatch('=', rule_name='repeated'), 0, '=')

    #--------------------------------------------------------------------------

    def __init__(self, *args, **kwargs):

        dprint.debug = False

        dprint(": pass 2 : init : ENTER")

        super().__init__(*args, **kwargs)

        # 'unwrap' -- did way too much, break into judiciously defined
        #             groupings that can be handled easily and explicitly.

        for _class in self.classes:
            dprint(f"  : handler '{_class}'")
            method = getattr(self, _class)
            for rule_name in getattr(self, f"_{_class}").split():
                alias = f"visit_{rule_name}"
                dprint(f"     - rule '{rule_name}'")
                setattr(self, alias, method)

        dprint(": pass 2 : init : LEAVE")

    #--------------------------------------------------------------------------

    def visit(self, node, depth=0, path=[]):

        i = ' ' * 3 * depth

        dprint('')
        dprint(f"{i} : visit : {node.name} -- START")

        if not isinstance(node,
                          (NonTerminal, Terminal, SemanticActionResults)):
            dprint(f"{i}   ** Invalid type '{str(type(node))}'")
            dprint(f"{i}   => {_res(node)}")
            dprint(f"{i} : visit : {node.name} -- DONE")
            return node

        #----------------------------------------------------------------------

        dprint('')
        dprint(f"{i}   Process Children -- START")
        dprint(f"{i}     # essentially, thus :")
        dprint(f"{i}     children = []")
        dprint(f"{i}     for child in node :")
        dprint(f"{i}         response = visit(child)")
        dprint(f"{i}         if response is not None :")
        dprint(
            f"{i}            children.append(response)  # generally reformed")
        dprint('')

        children = []
        if isinstance(node, (NonTerminal, SemanticActionResults)):
            # these object types are lists
            for child in node:  # NonTerminal IS the list
                #print(f"{i}   Process Children -- START")
                if hasattr(child, 'name'):
                    dprint(f"{i}   - '{child.name}'")
                else:
                    if hasattr(child, '__name__'):
                        dprint(f"{i}   - '{child.__name__}'")
                    else:
                        dprint(
                            f"{i}   - id = {id(child)} : {str(type(child))}")
                response = self.visit(child,
                                      depth=1 + depth,
                                      path=path + [node.name])
                dprint(f"{i}   - '{child.name}'")
                dprint(f"{i}     : response   = {_res(response)}")
                if response is not None:
                    value = unwrap(response)
                    dprint(f"{i}     : unwrapped  = {_res(value)}")
                    children.append(value)

        dprint('')
        dprint(f"[ children : final ]\n{_res(children)}")
        dprint(f"{i}   Process Children -- Done\n")
        dprint('')

        #----------------------------------------------------------------------

        # In extreme circumstances, rule_name may be list.  Note, that
        # such probably means unwrapping has gone too far and your node
        # is merely an empty list.
        rule_name = str(node.rule_name)
        # print(f": visit : {rule_name}")
        method = f"visit_{rule_name}"
        if hasattr(self, method):
            dprint(f"\n*** VISIT_{node.name} -- START")
            out = getattr(self, method)(node, children)
            dprint(f"   => {_res(out)}\n")
            dprint("*** VISIT_{node.name} -- DONE\n")
            dprint('')
            return out

        if isinstance(node, Terminal):
            dprint(
                f"{i}   Terminal without a visit method.  Return unchanged.")
            dprint(f"{i}   => {_res(node)}")
            dprint(f"{i} : visit : {node.name} -- DONE")
            dprint('')
            return node

        if len(children) > 0:

            if type(children) is list and len(children) == 1:
                if type(children[0]) is list:
                    dprint(
                        f": visit : {node.name} : list w/ single child, divulge"
                    )
                    children = children[0]

            if isinstance(children, (list, NonTerminal)):
                which = None
                if isinstance(children[0], ParseTreeNode):
                    dprint(
                        f": visit : {node.name} : list w/ children => NonTerminal"
                    )
                    out = NonTerminal(node.rule, children)
                    verb = 'is'
                    #
                    # *NO* : it strips rule info which we need.
                    #  was :
                    #     out = NonTerminal(node.rule, wrap(None))
                    #     del out[0]
                    #     out.extend(children)
                    #
                else:
                    out = NonTerminal(node.rule, wrap(children))
                    verb = 'is not'
                dprint('')
                dprint(f"{i}   : list or NonTerminal and [0] {verb} a node")
                dprint(f"{i}   => {_res(out)}")
                dprint(f"{i} : visit : {node.name} -- DONE")
                dprint('')
                return out

            internal_error(
                context, node, "Has children but neither a list nor "
                "ParseTreeNode.  Nothing left to try.  ")

            raise ValueError(f"Visiting {node.name}, nothing left to try.")

        # - node can't be a terminal node (as they bailed earlier).
        # - node can't be the result of a visit_* method (bailed earlier).
        #
        # - Academically, we should crash.  Let's continue in Battle Mode
        #   and wrap it in a Terminal -- complaining first.

        with redirect_stdout(sys.stderr):
            print(
                f"INTERNAL ERROR : Unhandled configuration for children of a NonTerminal"
            )
            print('')
            print(f"  path :  ", end='')
            _path = path + [node.name]
            prefix = ''
            for idx in range(len(_path)):
                i = ' ' * 3 * idx
                print(f"{prefix}{i}{_path[idx]}")
                prefix = ' ' * 10
            print('')
            print(f"  node = {node.name} : depth {depth}")
            seq = isinstance(children, Sequence)
            seq_text = ': is a sequence' if seq else ''
            print(f"  children type     = {str(type(children))} {seq_text}")
            if seq:
                print(f"  children[0] type  = {str(type(children[0]))}")
            print(": children =")
            pp(children)
            print(f"Please report this scenario to the maintainer.")
            out = Terminal(node.rule, 0, wrap(children))
            dprint(f": visit : {node.name} => {_res(out)}")
            dprint('')
            return out

    #--------------------------------------------------------------------------

    def empty(self, node, children):
        return None

    #--------------------------------------------------------------------------

    # A 'terminal' being, obviously, a Terminal node, contents in node.value
    def divulge_terminal(self, node, children):
        """ Dispense with an unneeded Terminal enclosing a value.  Sanity
	checks may result in a automatci divulge upgrade (i.e. if it is not
	actually a Terminal node (but not quietly).
	"""
        context = 'divulge_terminal'

        dprint(f": {context} : {node.name} : value = {node.value}")

        if False:
            # Academic Research Mode - every step must be perfect
            assert isinstance(node, Terminal), \
                internal_error(context, node, "is not a Terminal node")
            assert len(children) == 0, \
                internal_error(context, node, "Is Terminal with children ?")
        else:
            # Battle Mode -- keep going at all cost !  With some complaints ...
            upgrade = False
            if not isinstance(node, Terminal):
                internal_error(context, node, "Is not a Terminal node.")
                upgrade = True
            else:
                n = len(children)
                if n > 0:
                    internal_error(context, node,
                                   f"How does a Terminal have {n} children ?")
                    upgrade = True
            if upgrade:
                return self.divulge_single(node, children)

        dprint(f": {context} : '{node.name}' => {_res(node.value)}")
        # Intentially breaks the Parse Tree structure so that the parent or
        # other ancestor may trivially gather it's components.  Said gatherer
        # must have a visitor method of course.  text() is use to gather
        # text fragments into

        return node.value

    #--------------------------------------------------------------------------

    def divulge_NonTerminal(self, context: str, node: ParseTreeNode,
                            children: list, only_child: bool):
        """Sanity checks for a NonTerminal being 'divulged'.  Automatically
	upgrades or downgrades the divulge action as necessary (with complaints).
	"""

        # Battle Mode -- keep going at all costs -- with noisy complaints for improvement ...

        if not isinstance(node, NonTerminal):
            internal_error(
                context, node,
                "Is not a NonTerminal node.  Perhaps it is a list ?")
            if not isinstance(node, list):
                internal_error(context, node, "Also not list.")
                if isinstance(node, Terminal):
                    internal_error(
                        context, node,
                        "It is a Terminal.  Downgrading automatically.")
                    return self.divulge_terminal(node, children)

        if only_child and len(children) > 1:
            internal_error(context, node,
                           f"Too many children.  Upgrading automatically.")
            return self.divulge_list(node, children)

        if len(children) <= 0:
            internal_error(
                context, node, f"How does a NonTerminal have ZERO children ?  "
                "Let's try handling it like a Terminal.")
            w = wrap(node.value)
            dprint(f": {context} : '{node.name}' => {_res(w)}")
            return w

        return None

    #--------------------------------------------------------------------------

    # A 'single' being a NonTerminal with only one child, a ParseTreeNode
    def divulge_single(self, node, children):
        """ Dispense with an unneeded NonTerminal enclosing a single node."""
        context = 'divulge_single'

        out = self.divulge_NonTerminal(context,
                                       node,
                                       children,
                                       only_child=True)
        if out is not None:
            return out

        single = children[0]
        if isinstance(single, ParseTreeNode):
            internal_error(
                context, node, f"And it's single child is not ParseTreeNode.  "
                "We'll simply let that slide.")
        w = wrap(single)
        dprint(f": divulge single '{node.name}' => {_res(w)}")
        return w

    #--------------------------------------------------------------------------

    def divulge_list(self, node, children):
        """ Dispense with an unneeded NonTerminal enclosing a 'list' of one
        or more nodes.  Though, if it can only ever have one child,
        divulge_single() would be more appropriate.
	"""
        context = 'divulge_list'

        out = self.divulge_NonTerminal(context,
                                       node,
                                       children,
                                       only_child=False)
        if out is not None:
            return out

        if isinstance(children[0], ParseTreeNode):
            internal_error(
                context, node, f"And it's first child is not ParseTreeNode.  "
                "We'll simply let that slide.")

        w = wrap(children)
        dprint(f": {context} : '{node.name}' => {_res(w)}")
        return w

    #--------------------------------------------------------------------------

    # operand name is all that is relevant (i.e. FILE or <src>)
    def visit_operand(self, node, children):
        return Terminal(node.rule, 0, node.value)

    #--------------------------------------------------------------------------

    # make BAR directly searchable within the choice list
    def visit_BAR(self, node, children):
        return self.BAR

    def visit_choice(self, node, children):

        # *** Need to maintain valid Parse Tree, up one level ?
        # ***   >>> return sentinel indicating that visit should unwrap as
        # ***       children of choice's parent.

        # Eliminate fake choice, now must look into expressions
        #
        # XXX Test case 12 : 'Usage: copy move\ncopy ( move )'
        # XXX      output : error/name/lost-choice-and-expressions
        # XXX
        # XXX Error manifests without this enabled.
        #
        # This isn't the cause, it simply magnifies the error.
        #
        # Error is caused by the puzzling loss of the 'usage_pattern' enclosure
        # >>> unwrap() was doing too much unwrapping
        #
        if True:
            if (len(children) == 1 and isinstance(children[0], NonTerminal)
                    and children[0].rule_name == 'expression'
                    and self.BAR not in children[0]):
                return Unwrap(children[0])

        # Elimnate unnecessary expression wrapper when it has a single child
        #
        # *** If must be FALSE, document why and which test case
        #
        # >>> 'expression' context needful to discriminate between choice factors
        #
        if True:
            # DO NOT ENABLE THIS for now
            for idx in range(len(children)):
                child = children[idx]
                if (isinstance(child, NonTerminal)
                        and child.rule_name == 'expression'
                        and len(child) == 1):
                    children[idx] = child[0]

        # Unchain cascading choices -- only necessary if recursive
        #   i.e. choice = expression ( BAR choice )
        #
        # NO LONGER NEEDED:  choice = expression ( BAR expression )*
        #
        if False and (isinstance(children, list) and len(children) == 3
                      and isinstance(children[-1], list) and children[-1][0]):
            additional = children[-1]
            del children[-1]
            children.extend(additional)

        return NonTerminal(node.rule, children)

    #--------------------------------------------------------------------------

    #example #EXAMPLE
    def visit_Non_Terminal(self, node, children):
        return NonTerminal(node.rule, children)

    #--------------------------------------------------------------------------

    def visit_options_intro(self, node, children):
        # print(f": visit_options_intro : {node.name}")
        # pp(children)
        return Terminal(node.rule, 0, '\n'.join(children))

    def visit_operand_intro(self, node, children):
        # print(f": visit_operand_intro : {node.name}")
        # pp(children)
        return Terminal(node.rule, 0, '\n'.join(children))

    def _visit_operand_help(self, node, children):
        while isinstance(children[-1], list):
            tmp = children[-1]
            children = children[:-1]
            children.extend(tmp)
        return Terminal(node.rule, 0, '\n'.join(children))

    def _visit_option_help(self, node, children):
        print(f": visit_option_help : {node.name}")
        pp(children)
        while isinstance(children[-1], list):
            additional = children[-1]
            children = children[:-1]
            children.extend(additional)
        if isinstance(children[-1], Terminal):
            children[-1] = children[-1].value
        return Terminal(node.rule, 0, '\n'.join(children))

    #--------------------------------------------------------------------------

    def text(self, node, children):
        # print(f": text : {node.rule_name} : len = {len(children)}")
        # example: option_help = words ( newline !option_single option_help )*
        if isinstance(children[-1], Terminal):
            children[-1] = children[-1].value
        else:
            # example: intro = newline* !usage_entry line+ newline
            while isinstance(children[-1], list):
                additional = children[-1]
                children = children[:-1]
                children.extend(additional)
        return Terminal(node.rule, 0, '\n'.join(children))

    #--------------------------------------------------------------------------

    def visit_description(self, node, children):
        return Terminal(node.rule, 0, '\n'.join(children))

    def visit_line(self, node, children):
        assert len(children) == 1
        return children[0]

    def visit_words(self, node, children):
        return ' '.join(children)

    def visit_word(self, node, children):
        return node.value

    #--------------------------------------------------------------------------

    visit_trailing = text

    def visit_trailing_line(self, node, children):
        assert len(children) == 1
        return children[0]

    def visit_trailing_strings(self, node, children):
        return ' '.join(children)

    def visit_string_no_whitespace(self, node, children):
        return node.value
Esempio n. 15
0
        contents=list(value),
    )


# @register_pretty(Unwrap)
def _pretty_Unwrap(value, ctx):
    return pretty_call(
        ctx,
        Unwrap,
        value=value.value,
    )


import p

#------------------------------------------------------------------------------

x = Terminal(StrMatch('.', 'x'), 0, 'value:x')

y = Terminal(StrMatch('.', 'y'), 0, 'value:y')

v = NonTerminal(StrMatch('.', 'x_y'), [x, y])

t = NonTerminal(StrMatch('.', 'v_v'), [v, v])

pp(v)

pp(t, indent=2)

#------------------------------------------------------------------------------
Esempio n. 16
0
class DocOptSimplifyVisitor_Pass1(object):

    rule_groups = ' empty as_value as_lines '.split()

    _empty = (' _ EOF blankline COMMA LBRACKET RBRACKET LPAREN RPAREN EOF '
              ' newline ')

    _as_value = (' string_no_whitespace word words line '
                 ' short_no_arg_ '
                 ' short_stacked ')

    _as_lines = (' description ' ' ')

    #--------------------------------------------------------------------------

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for group in self.rule_groups:
            if not hasattr(self, group):
                raise ValueError(
                    f"INTERNAL ERROR, rule group '{group}' method "
                    "missing.  Please contact the maintainer.")
            if group not in kwargs:
                rules = getattr(self, f"_{group}").split()
            else:
                rules = kwargs[group]
                rules = rules.split() if isinstance(rules, str) else rules
                if not isinstance(rules, list):
                    raise ValueError(f"Invalid type for rule group argument "
                                     f"'{group}'. Expected string or list, "
                                     f"found {str(type(rules))}")
            for rule_name in rules:
                setattr(self, f"visit_{rule_name}", getattr(self, group))

    #--------------------------------------------------------------------------

    def visit(self, node, depth=0):

        i = ' ' * 3 * depth
        dprint(f"{i} [ p2 : node = {node}")

        if not hasattr(node, 'rule_name'):
            dprint(f"{i}   - not a known container : {str(type(node))}")
            dprint(f"{i}   => itself")
            dprint(f"{i} ]")
            if isinstance(node, list) and len(node) == 1:
                return node[0]
            return node

        #----------------------------------------------------------------------

        children = []
        if isinstance(node, (NonTerminal, SemanticActionResults)):
            dprint(
                f"{i}   - visiting children of '{node.name}' : len = {len(node)}"
            )
            # each of these object types is a list
            for child in node:
                response = self.visit(child, 1 + depth)
                if response:
                    if not isinstance(response, NonTerminal):
                        children.append(response)
                        continue
                    dprint(f": repeatable : {node.name} : response =")
                    # pp(response)
                    if (response.rule_name == 'repeating' and len(children) > 0
                            and response[0] == children[-1]):
                        dprint(f": repeatable : {node.name} : children =")
                        # pp(children)
                        children[-1] = response
                    else:
                        children.append(response)

        dprint(f"{i}   - visited children = {children}")

        #----------------------------------------------------------------------

        # rule name specific visitor ?

        rule_name = str(node.rule_name)
        method = f"visit_{rule_name}"
        dprint(f"{i}   - {method} ?")
        if hasattr(self, method):
            dprint(f"{i}   - method found, applying to {node.name}")
            out = getattr(self, method)(node, children, 1 + depth)
            dprint(f"{i}   => {_res(out,i)}")
            dprint(f"{i} ]")
            return out
        else:
            dprint(f"{i}   - no such method")

        #----------------------------------------------------------------------

        if len(children) <= 0:
            out = Terminal(node.rule, 0, node.value)
            dprint(f"{i}   => {_res(out,i)}")
            dprint(f"{i} ]")
            return out

        # if len(children) == 1:
        #     children = children[0]  # can break the Parse Tree

        out = NonTerminal(node.rule, children)
        if False:
            try:
                out = NonTerminal(node.rule, children)
            except:
                # automatically unwrap
                children[0] = Unwrap(children[0])
                out = NonTerminal(node.rule, children)
                out[0] = out[0].value
                # FIXME: this breaks the parse tree
        dprint(f"{i}   => {_res(out,i)}")
        dprint(f"{i} ]")
        return out

    #------------------------------------------------------------------------------

    def empty(self, node, children, depth=0):
        return None

    #--------------------------------------------------------------------------

    # Gather composition elements into a terminal
    #   (i.e. a line from a sequence of words)
    def as_value(self, node, children, depth=0):
        if len(children):
            # print(f"\n: as_value() : {node.rule_name} : collecting {len(children)} children"
            #       f": {repr(children)}")
            value = ' '.join([c.value for c in children])
        else:
            # print(f"\n: as_value() : {node.rule_name} : single value '{node.value}'")
            value = node.value
        return Terminal(StrMatch('', node.rule_name), 0, value)

    #--------------------------------------------------------------------------

    # Gather composition elements into a terminal with line breaks
    def as_lines(self, node, children, depth=0):
        if len(children):
            # print(f": as_lines() : {node.rule_name} : collecting {len(children)} children"
            #       f": {repr(children)}")
            value = '\n'.join([c.value for c in children])
        else:
            # print(f": as_lines() : {node.rule_name} : single value '{node.value}'")
            value = node.value
        return Terminal(StrMatch('', node.rule_name), 0, value)

    #--------------------------------------------------------------------------

    REPEATING = Terminal(StrMatch('...', rule_name='repeating'), 0, '...')

    # make REPEATED directly searchable within expressions
    def visit_repeated(self, node, children, depth=0):
        return self.REPEATED

    #--------------------------------------------------------------------------

    # Repeatable is in Pass1 because in Pass2, the assertions fail due to unwrapping.
    #
    # Collapse and encapsulate repeated terms
    # i.e. expression ( <other> <foo> <foo> '...' )expression ( <other> <foo> <foo> '...' )
    #   => expression ( <other> repeated(<foo>) )
    #
    # Or improve the grammar:  repeatable = term repeating?
    #
    def visit_repeatable(self, node, children, depth=0):

        if False and str(children) in [
                "[command 'turn' [0], command 'rise' [0]]",
                "[command 'move' [0]]"
        ]:
            print(f": repeatable : {node.name} : {children}")
            pp(NonTerminal(node.rule, children))
            print('')

        n_children = len(children)
        assert n_children in [1, 2]

        term = children[0]

        if n_children == 1:
            return term

        assert children[1] == self.REPEATING

        return NonTerminal(StrMatch(':repeating:', 'repeating'), [term])
Esempio n. 17
0
def string_test(name_prefix, rule_f, s, ch_names=None, empty_ok=False):
    """Generate test methods whether <rule_f> matches <s> in these scenarios :
         - by itself, <s> is the entire text string to be parsed.
         - at start of the parsed text, followed by a phrase
         - in the middle between twp phrases
         - at the end of the parsed text, preceeded by a phrase
         - scattered a various points about a paragraph, at least once
           two <s>'s directly next to each other.
         - Test whether or not the specified rule matches an empty string :
           If empty_ok is False (DEFAULT), a NoMatch exception is expected.
           If empty_ok is True, a successful match is expected.

         Each scenario generates an individual test.  The test names
         include the characters being tested against.

         In each scenario, the 'text' is ensured to not include any
         characters present in <s>.

    """
    def create_method(name, rule_f, rule, s, grammar, text, expect):
        grammar_obj = grammar

        def the_method(self):
            nonlocal name, rule_f, rule, s, grammar_obj, text, expect

            def grammar():
                grammar_obj.rule_name = 'grammar'
                return grammar_obj

            self.verify_grammar(grammar, text, expect, skipws=False)

        return the_method

    #--------------------------------------------------------------------------

    assert len(s) > 0, "Zero length 's' invalid for string_test()"

    for ch in s:
        assert ch not in INUSE_CHARACTERS, \
            ( f"argument s '{s}', contains '{ch}' character whuch is a "
              f"member of INUSE_CHARACTERS, test code must be reconfigured "
              f"in order to handle this character." )

    if not tst_debug_first_scenario:
        if empty_ok:
            empty_string_is_accepted(name_prefix, rule_f, ch)
        else:
            empty_string_raises_NoMatch(name_prefix, rule_f, ch)

    rule = rule_f()
    if isinstance(rule, str):
        # print(f"- rule '{rule_f.__name__}' : create StrMatch() rule for "
        #       f"literal '{ch}' : {ord(ch)} : {hex(ord(ch))}")
        rule = StrMatch(ch, rule_f.__name__)
        # or eval("{rule_f.__name__}_m")

    (ch_hexes, ch_names) = character_lookup(s, ch_names)

    if len(ch_hexes) == 1:
        ch_hexes = ch_hexes[0]
        ch_names = ch_names[0]

    for test_name, grammar, text, expect in scenarios(rule, ch):
        method_name = f"test_{name_prefix}_rule_{rule.rule_name}__AGAINST_{ch_hexes}_{ch_names}_{test_name}"
        # print(f"Adding {method_name}")
        method = create_method(method_name, rule_f, rule, ch, grammar, text,
                               expect)
        setattr(Test_Common, method_name, method)
        if tst_debug_first_scenario:
            break