Skip to content

Commit 53a3dec

Browse files
committed
Refactor the parser code
1 parent 45fdc6a commit 53a3dec

File tree

8 files changed

+9504
-57
lines changed

8 files changed

+9504
-57
lines changed

lib/rule_engine/ast.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
import re
3939

4040
from . import errors
41-
from ._utils import parse_datetime, parse_float, parse_timedelta
41+
from .parser.utilities import parse_datetime, parse_float, parse_timedelta
4242
from .suggestions import suggest_symbol
4343
from .types import *
4444

lib/rule_engine/builtins.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@
3838
import math
3939
import random
4040

41-
from ._utils import parse_datetime, parse_float, parse_timedelta
4241
from . import ast
4342
from . import errors
4443
from . import types
44+
from .parser.utilities import parse_datetime, parse_float, parse_timedelta
4545

4646
import dateutil.tz
4747

lib/rule_engine/parser.py renamed to lib/rule_engine/parser/__init__.py

Lines changed: 5 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
33
#
4-
# rule_engine/parser.py
4+
# rule_engine/parser/__init__.py
55
#
66
# Redistribution and use in source and binary forms, with or without
77
# modification, are permitted provided that the following conditions are
@@ -32,15 +32,12 @@
3232

3333
import ast as pyast
3434
import collections
35-
import threading
3635
import types as pytypes
3736

38-
from . import ast
39-
from . import errors
40-
from ._utils import timedelta_regex
41-
42-
import ply.lex as lex
43-
import ply.yacc as yacc
37+
from .. import ast
38+
from .. import errors
39+
from .base import ParserBase
40+
from .utilities import timedelta_regex
4441

4542
literal_eval = pyast.literal_eval
4643

@@ -58,52 +55,6 @@ def build(self):
5855
constructor = getattr(self.cls, self.method)
5956
return constructor(*self.args, **self.kwargs)
6057

61-
class ParserBase(object):
62-
"""
63-
A base class for parser objects to inherit from. This does not provide any
64-
grammar related definitions.
65-
"""
66-
precedence = ()
67-
"""The precedence for operators."""
68-
tokens = ()
69-
reserved_words = {}
70-
"""
71-
A mapping of literal words which are reserved to their corresponding grammar
72-
names.
73-
"""
74-
__mutex = threading.Lock()
75-
def __init__(self, debug=False):
76-
"""
77-
:param bool debug: Whether or not to enable debugging features when
78-
using the ply API.
79-
"""
80-
self.debug = debug
81-
self.context = None
82-
# Build the lexer and parser
83-
self._lexer = lex.lex(module=self, debug=self.debug)
84-
self._parser = yacc.yacc(module=self, debug=self.debug, write_tables=self.debug)
85-
86-
def parse(self, text, context, **kwargs):
87-
"""
88-
Parse the specified text in an abstract syntax tree of nodes that can later be evaluated. This is done in two
89-
phases. First, the syntax is parsed and a tree of deferred / uninitialized AST nodes are constructed. Next each
90-
node is built recursively using it's respective :py:meth:`rule_engine.ast.ASTNodeBase.build`.
91-
92-
:param str text: The grammar text to parse into an AST.
93-
:param context: A context for specifying parsing and evaluation options.
94-
:type context: :py:class:`~rule_engine.engine.Context`
95-
:return: The parsed AST statement.
96-
:rtype: :py:class:`~rule_engine.ast.Statement`
97-
"""
98-
kwargs['lexer'] = kwargs.pop('lexer', self._lexer)
99-
with self.__mutex:
100-
self.context = context
101-
# phase 1: parse the string into a tree of deferred nodes
102-
result = self._parser.parse(text, **kwargs)
103-
self.context = None
104-
# phase 2: initialize each AST node recursively, providing them with an opportunity to define assignments
105-
return result.build()
106-
10758
class Parser(ParserBase):
10859
"""
10960
The parser class for the rule grammar. This class contains many ply specific

lib/rule_engine/parser/base.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# rule_engine/parser/base.py
5+
#
6+
# Redistribution and use in source and binary forms, with or without
7+
# modification, are permitted provided that the following conditions are
8+
# met:
9+
#
10+
# * Redistributions of source code must retain the above copyright
11+
# notice, this list of conditions and the following disclaimer.
12+
# * Redistributions in binary form must reproduce the above
13+
# copyright notice, this list of conditions and the following disclaimer
14+
# in the documentation and/or other materials provided with the
15+
# distribution.
16+
# * Neither the name of the project nor the names of its
17+
# contributors may be used to endorse or promote products derived from
18+
# this software without specific prior written permission.
19+
#
20+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
#
32+
33+
import threading
34+
35+
import ply.lex as lex
36+
import ply.yacc as yacc
37+
38+
class ParserBase(object):
39+
"""
40+
A base class for parser objects to inherit from. This does not provide any
41+
grammar related definitions.
42+
"""
43+
precedence = ()
44+
"""The precedence for operators."""
45+
tokens = ()
46+
reserved_words = {}
47+
"""
48+
A mapping of literal words which are reserved to their corresponding grammar
49+
names.
50+
"""
51+
__mutex = threading.Lock()
52+
def __init__(self, debug=False):
53+
"""
54+
:param bool debug: Whether or not to enable debugging features when
55+
using the ply API.
56+
"""
57+
self.debug = debug
58+
self.context = None
59+
# Build the lexer and parser
60+
self._lexer = lex.lex(module=self, debug=self.debug)
61+
self._parser = yacc.yacc(module=self, debug=self.debug, write_tables=self.debug)
62+
63+
def parse(self, text, context, **kwargs):
64+
"""
65+
Parse the specified text in an abstract syntax tree of nodes that can later be evaluated. This is done in two
66+
phases. First, the syntax is parsed and a tree of deferred / uninitialized AST nodes are constructed. Next each
67+
node is built recursively using it's respective :py:meth:`rule_engine.ast.ASTNodeBase.build`.
68+
69+
:param str text: The grammar text to parse into an AST.
70+
:param context: A context for specifying parsing and evaluation options.
71+
:type context: :py:class:`~rule_engine.engine.Context`
72+
:return: The parsed AST statement.
73+
:rtype: :py:class:`~rule_engine.ast.Statement`
74+
"""
75+
kwargs['lexer'] = kwargs.pop('lexer', self._lexer)
76+
with self.__mutex:
77+
self.context = context
78+
# phase 1: parse the string into a tree of deferred nodes
79+
result = self._parser.parse(text, **kwargs)
80+
self.context = None
81+
# phase 2: initialize each AST node recursively, providing them with an opportunity to define assignments
82+
return result.build()

0 commit comments

Comments
 (0)