Skip to content

Commit

Permalink
Implemented issue #1
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeroen van der Heijden committed Oct 24, 2016
1 parent 37f0e5a commit 8a481d7
Show file tree
Hide file tree
Showing 20 changed files with 167 additions and 4 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ __pycache__/
.project
.pydevproject
.idea
checklist.txt
checklist.txt
build/
5 changes: 5 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
2016.10.24, Version 1.1.3 (Stable)

- Added export to Python method. This can be useful even if the source is
already defined in Python language. (see issue #1)

2016.07.02, Version 1.1.2 (Stable)

- Fixed export_c() to support Ref(). An update to cleri is not needed since
Expand Down
2 changes: 1 addition & 1 deletion pyleri/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@
__author__ = 'Jeroen van der Heijden'
__maintainer__ = 'Jeroen van der Heijden'
__email__ = 'jeroen@transceptor.technology'
__version__ = '1.1.2'
__version__ = '1.1.3'
16 changes: 16 additions & 0 deletions pyleri/choice.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,22 @@ def _stop_at_first_match(self, root, tree, rule, s, node):
def _run_export_js(self, js_identation, ident, classes):
return self._export_js_elements(js_identation, ident, classes)

def _run_export_py(self, py_identation, ident, classes):
new_ident = ident + 1
value = ',\n'.join(['{ident}{elem}'.format(
ident=py_identation * new_ident,
elem=elem._export_py(
py_identation,
new_ident,
classes)) for elem in self._elements])
return 'Choice(\n{val},\n{ident}most_greedy={mg})'.format(
mg='{mg}'.format(
ident=py_identation * (ident + 1),
mg=('False', 'True')[
self._get_node_result == self._most_greedy_result]),
val=value,
ident=py_identation * new_ident)

@c_export
def _run_export_c(self, c_identation, ident, enums, gid):
new_ident = ident + 1
Expand Down
21 changes: 21 additions & 0 deletions pyleri/elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,27 @@ def _export_js_elements(self, js_identation, ident, classes):
def _run_export_js(self, js_identation, ident, classes):
return 'not_implemented'

def _export_py(self, py_identation, ident, classes):
classes.add(self.__class__.__name__.lstrip('_'))
if hasattr(self, 'name') and ident:
return self.name
return self._run_export_py(py_identation, ident or 1, classes)

def _export_py_elements(self, py_identation, ident, classes):
new_ident = ident + 1
value = ',\n'.join(['{ident}{elem}'.format(
ident=py_identation * new_ident,
elem=elem._export_py(
py_identation,
new_ident, classes)) for elem in self._elements])
return '{class_name}(\n{value}\n{ident})'.format(
class_name=self.__class__.__name__.lstrip('_'),
value=value,
ident=py_identation * ident)

def _run_export_py(self, py_identation, ident, classes):
return 'not_implemented'

@c_export
def _export_c(self, c_identation, ident, enums, gid):
if hasattr(self, 'name') and ident:
Expand Down
70 changes: 69 additions & 1 deletion pyleri/grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,25 @@ class Grammar(metaclass=_OrderedClass):
}})(
{constructors}
);
'''.lstrip()

PY_IDENTATION = ' ' * 4
PY_MODULE_NAME = 'pyleri'
PY_TEMPLATE = '''
"""
This grammar is generated using the Grammar.export_py() method and
should be used with the {py_module} python module.
Source class: {name}
Created at: {datetime}
"""
import re
{imports}
class {name}(Grammar):
{ident}
{ident}RE_KEYWORDS = re.compile('{re_keywords}')
{language}
'''.lstrip()

C_IDENTATION = ' ' * 4
Expand Down Expand Up @@ -204,7 +223,7 @@ def export_js(
js_module_name=JS_MODULE_NAME,
js_template=JS_TEMPLATE,
js_identation=JS_IDENTATION):
'''Export the grammar as a JavaScript file which can be
'''Export the grammar to a JavaScript file which can be
used with the js-lrparsing module.'''

language = []
Expand Down Expand Up @@ -249,7 +268,56 @@ def export_js(
'window',
js_module_name, n]) for n in classes])))

def export_py(
self,
py_module_name=PY_MODULE_NAME,
py_template=PY_TEMPLATE,
py_identation=PY_IDENTATION):
'''Export the grammar to a python file which can be
used with the pyleri module. This can be useful when python code
if used to auto-create a grammar and an export of the final result is
required.'''

language = []
classes = {'Grammar'}
ident = 0

for name in self._order:
elem = getattr(self, name, None)
if not isinstance(elem, Element):
continue
if not hasattr(elem, '_export_py'):
continue
language.append('{ident}{name} = {value}'.format(
ident=py_identation,
name=name,
value=elem._export_py(py_identation, ident, classes)))

for name, ref in self._refs.items():
language.append('{ident}{name} = {value}'
.format(
ident=py_identation,
name=name,
value=ref._element._export_py(
py_identation,
ident,
classes)))

return py_template.format(
name=self.__class__.__name__,
ident=py_identation,
py_module=py_module_name,
datetime=time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()),
language='\n'.join(language),
re_keywords=self.RE_KEYWORDS.pattern.replace('\\', '\\\\'),
imports='\n'.join(
map(lambda s: s,
[' '.join(['from', py_module_name, 'import', n])
for n in classes if n != 'Rule'])))

def export_c(self, target=C_TARGET, c_identation=C_IDENTATION):
'''Export the grammar to a c (source and header) file which can be
used with the cleri module.'''
language = []
ident = 0
enums = set()
Expand Down
5 changes: 5 additions & 0 deletions pyleri/keyword.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ def _run_export_js(self, js_identation, ident, classes):
self._keyword,
', true' if self._ign_case else '')

def _run_export_py(self, py_identation, ident, classes):
return 'Keyword(\'{}\'{})'.format(
self._keyword,
', True' if self._ign_case else '')

@c_export
def _run_export_c(self, c_identation, ident, enums, gid):
return 'cleri_keyword({}, "{}", {})'.format(
Expand Down
8 changes: 8 additions & 0 deletions pyleri/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ def _run_export_js(self, js_identation, ident, classes):
self._max or 'undefined',
'true' if self._opt else 'false')

def _run_export_py(self, py_identation, ident, classes):
return 'List({}, {}, {}, {}, {})'.format(
self._element._export_py(py_identation, ident, classes),
self._delimiter._export_py(py_identation, ident, classes),
self._min,
self._max or 'None',
'True' if self._opt else 'False')

@c_export
def _run_export_c(self, c_identation, ident, enums, gid):
return 'cleri_list({}, {}, {}, {}, {}, {})'.format(
Expand Down
4 changes: 4 additions & 0 deletions pyleri/optional.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ def _run_export_js(self, js_identation, ident, classes):
return 'Optional({})'.format(
self._element._export_js(js_identation, ident, classes))

def _run_export_py(self, py_identation, ident, classes):
return 'Optional({})'.format(
self._element._export_py(py_identation, ident, classes))

@c_export
def _run_export_c(self, c_identation, ident, enums, gid):
return 'cleri_optional({}, {})'.format(
Expand Down
3 changes: 3 additions & 0 deletions pyleri/prio.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ def _get_node_result(self, root, tree, rule, s, node):
def _run_export_js(self, js_identation, ident, classes):
return self._export_js_elements(js_identation, ident, classes)

def _run_export_py(self, py_identation, ident, classes):
return self._export_py_elements(py_identation, ident, classes)

def _run_export_c(self, c_identation, ident, enums):
return self._export_c_elements(c_identation, ident, enums)

Expand Down
3 changes: 3 additions & 0 deletions pyleri/ref.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ def element(self, element):

def _run_export_js(self, js_identation, ident, classes):
return 'Ref({})'.format(self._element.__class__.__name__)

def _run_export_py(self, py_identation, ident, classes):
return 'Ref()'
4 changes: 4 additions & 0 deletions pyleri/regex.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ def _run_export_js(self, js_identation, ident, classes):
return 'Regex(\'{}\')'.format(
self._compiled.pattern.replace('\\', '\\\\').replace('\'', '\\\''))

def _run_export_py(self, py_identation, ident, classes):
return 'Regex(\'{}\')'.format(
self._compiled.pattern.replace('\\', '\\\\').replace('\'', '\\\''))

@c_export
def _run_export_c(self, c_identation, ident, enums, gid):
return 'cleri_regex({}, "{}")'.format(
Expand Down
6 changes: 6 additions & 0 deletions pyleri/repeat.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ def _run_export_js(self, js_identation, ident, classes):
self._min,
self._max or 'undefined')

def _run_export_py(self, py_identation, ident, classes):
return 'Repeat({}, {}, {})'.format(
self._element._export_py(py_identation, ident, classes),
self._min,
self._max or 'None')

@c_export
def _run_export_c(self, c_identation, ident, enums, gid):
return 'cleri_repeat({}, {}, {}, {})'.format(
Expand Down
3 changes: 3 additions & 0 deletions pyleri/rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ def _get_node_result(self, root, tree, rule, _s, node):
def _run_export_js(self, js_identation, ident, classes):
return self._element._export_js(js_identation, ident, classes)

def _run_export_py(self, py_identation, ident, classes):
return self._element._export_py(py_identation, ident, classes)

def _run_export_c(self, c_identation, ident, enums):
name = getattr(self, 'name', None)
if name is not None:
Expand Down
3 changes: 3 additions & 0 deletions pyleri/sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,8 @@ def _get_node_result(self, root, tree, rule, _s, node):
def _run_export_js(self, js_identation, ident, classes):
return self._export_js_elements(js_identation, ident, classes)

def _run_export_py(self, py_identation, ident, classes):
return self._export_py_elements(py_identation, ident, classes)

def _run_export_c(self, c_identation, ident, enums):
return self._export_c_elements(c_identation, ident, enums)
4 changes: 4 additions & 0 deletions pyleri/this.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ def _export_js(self, js_identation, ident, classes):
classes.add('THIS')
return 'THIS'

def _export_py(self, py_identation, ident, classes):
classes.add('THIS')
return 'THIS'

def _export_c(self, c_identation, ident, enums):
return 'CLERI_THIS'

Expand Down
3 changes: 3 additions & 0 deletions pyleri/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ def _get_node_result(self, root, tree, rule, s, node):
def _run_export_js(self, js_identation, ident, classes):
return 'Token(\'{}\')'.format(self._token)

def _run_export_py(self, py_identation, ident, classes):
return 'Token(\'{}\')'.format(self._token)

@c_export
def _run_export_c(self, js_identation, ident, enums, gid):
return 'cleri_token({}, "{}")'.format(
Expand Down
3 changes: 3 additions & 0 deletions pyleri/tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ def _get_node_result(self, root, tree, rule, s, node):
def _run_export_js(self, js_identation, ident, classes):
return 'Tokens(\'{}\')'

def _run_export_py(self, py_identation, ident, classes):
return 'Tokens(\'{}\')'

@c_export
def _run_export_c(self, c_identation, ident, enums, gid):
return 'cleri_tokens({}, "{}")'.format(
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from distutils.core import setup

VERSION = '1.1.2'
VERSION = '1.1.3'

setup(
name='pyleri',
Expand Down
3 changes: 3 additions & 0 deletions test/test_pyleri.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import unittest
import gc
import sys
sys.path.append('..')

from pyleri import (
Keyword,
Sequence,
Expand Down

0 comments on commit 8a481d7

Please sign in to comment.