1# -*- coding: utf-8 -*-
2#######################################################################
3# Name: cleanpeg.py
4# Purpose: This module is a variation of the original peg.py.
5#   The syntax is slightly changed to be more readable and familiar to
6#   python users. It is based on the Yash's suggestion - issue 11
7# Author: Igor R. Dejanovic <igor DOT dejanovic AT gmail DOT com>
8# Copyright: (c) 2014-2017 Igor R. Dejanovic <igor DOT dejanovic AT gmail DOT com>
9# License: MIT License
10#######################################################################
11
12from __future__ import print_function, unicode_literals
13
14from arpeggio import Optional, ZeroOrMore, Not, OneOrMore, EOF, ParserPython, \
15    visit_parse_tree
16from arpeggio import RegExMatch as _
17from .peg import PEGVisitor
18from .peg import ParserPEG as ParserPEGOrig
19
20__all__ = ['ParserPEG']
21
22# Lexical invariants
23ASSIGNMENT = "="
24ORDERED_CHOICE = "/"
25ZERO_OR_MORE = "*"
26ONE_OR_MORE = "+"
27OPTIONAL = "?"
28UNORDERED_GROUP = "#"
29AND = "&"
30NOT = "!"
31OPEN = "("
32CLOSE = ")"
33
34# PEG syntax rules
35def peggrammar():       return OneOrMore(rule), EOF
36def rule():             return rule_name, ASSIGNMENT, ordered_choice
37def ordered_choice():   return sequence, ZeroOrMore(ORDERED_CHOICE, sequence)
38def sequence():         return OneOrMore(prefix)
39def prefix():           return Optional([AND, NOT]), sufix
40def sufix():            return expression, Optional([OPTIONAL,
41                                                     ZERO_OR_MORE,
42                                                     ONE_OR_MORE,
43                                                     UNORDERED_GROUP])
44def expression():       return [regex, rule_crossref,
45                                (OPEN, ordered_choice, CLOSE),
46                                str_match], Not(ASSIGNMENT)
47
48# PEG Lexical rules
49def regex():            return [("r'", _(r'''[^'\\]*(?:\\.[^'\\]*)*'''), "'"),
50                                ('r"', _(r'''[^"\\]*(?:\\.[^"\\]*)*'''), '"')]
51def rule_name():        return _(r"[a-zA-Z_]([a-zA-Z_]|[0-9])*")
52def rule_crossref():    return rule_name
53def str_match():        return _(r'''(?s)('[^'\\]*(?:\\.[^'\\]*)*')|'''
54                                 r'''("[^"\\]*(?:\\.[^"\\]*)*")''')
55def comment():          return "//", _(".*\n")
56
57
58class ParserPEG(ParserPEGOrig):
59
60    def _from_peg(self, language_def):
61        parser = ParserPython(peggrammar, comment, reduce_tree=False,
62                              debug=self.debug)
63        parser.root_rule_name = self.root_rule_name
64        parse_tree = parser.parse(language_def)
65
66        return visit_parse_tree(parse_tree, PEGVisitor(self.root_rule_name,
67                                                       self.comment_rule_name,
68                                                       self.ignore_case,
69                                                       debug=self.debug))
70