Skip to content

Commit 39be10a

Browse files
author
Oleksii Okhrymenko
committed
Initial 1.0 release
1 parent 811c84b commit 39be10a

File tree

9 files changed

+689
-3
lines changed

9 files changed

+689
-3
lines changed

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2016 SimplePEG
3+
Copyright (c) 2016 Oleksii Okhrymenko (aka aiboy)
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

-2
This file was deleted.

README.rst

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Python version of SimplePEG
2+
--------
3+
4+
To use, simply do::
5+
6+
>>> import SPEG from simplepeg
7+
>>> parser = s.SPEG()
8+
>>> # will throw Exception if grammar is invalid
9+
>>> parser.parse_grammar('GRAMMAR test b -> "a";')
10+
>>> # will throw Exception if text have invalid grammar
11+
>>> ast = parser.parse_text('a')
12+
>>> print ast.to_json()
13+
14+
or::
15+
16+
>>> import SPEG from simplepeg
17+
>>> parser = s.SPEG()
18+
>>> ast = parser.parse('GRAMMAR test b -> "a";', 'a')
19+
>>> print ast.to_json()

setup.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from setuptools import setup
2+
3+
def readme():
4+
with open('README.rst') as f:
5+
return f.read()
6+
7+
setup(name='simplepeg',
8+
version='1.0.0',
9+
description='Python version of SimplePEG',
10+
long_description=readme(),
11+
classifiers=[
12+
'Development Status :: 5 - Production/Stable',
13+
'License :: OSI Approved :: MIT License',
14+
'Programming Language :: Python :: 2.7',
15+
'Topic :: Text Processing :: Linguistic',
16+
],
17+
url='https://github.com/SimplePEG/Python',
18+
author='Oleksii Okhrymenko',
19+
author_email='[email protected]',
20+
keywords='peg parser grammar',
21+
license='MIT',
22+
packages=['simplepeg'],
23+
include_package_data=True,
24+
zip_safe=False)

simplepeg/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .speg import SPEG

simplepeg/rd_parser.py

+313
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
"""Recursince decend parser"""
2+
# pylint: disable=too-few-public-methods
3+
4+
import json
5+
import re
6+
7+
class State(object):
8+
"""Current parser state"""
9+
text = ""
10+
position = 0
11+
rules = []
12+
lastExpectations = []
13+
def __init__(self, **kwargs):
14+
self.__dict__.update(kwargs)
15+
16+
def to_json(self):
17+
"""returns json string"""
18+
return json.dumps(self, default=lambda o: o.__dict__, sort_keys=False, indent=2)
19+
20+
class Node(object):
21+
"""Node of AST"""
22+
match = ""
23+
children = None
24+
action = None
25+
def __init__(self, **kwargs):
26+
self.__dict__.update(kwargs)
27+
28+
def to_json(self):
29+
"""returns json string"""
30+
return json.dumps(self, default=lambda o: o.__dict__, sort_keys=False, indent=2)
31+
32+
33+
class Expectation(object):
34+
"""Expectation object"""
35+
def __init__(self, **kwargs):
36+
self.__dict__.update(kwargs)
37+
38+
def to_json(self):
39+
"""returns json string"""
40+
return json.dumps(self, default=lambda o: o.__dict__, sort_keys=False, indent=2)
41+
42+
def getLastError(state):
43+
if len(state.lastExpectations) < 1:
44+
return False
45+
lines = state.text.split('\n')
46+
last_exp_position = max([exp.position for exp in state.lastExpectations])
47+
last_position = 0
48+
line_of_error = ''
49+
error_line_number = None
50+
position_of_error = 0
51+
i = 0
52+
while i < len(lines):
53+
line_lenght = len(lines[i]) + 1
54+
if last_exp_position >= last_position and last_exp_position < last_position + line_lenght:
55+
line_of_error = lines[i]
56+
position_of_error = last_exp_position - last_position
57+
error_line_number = i + 1
58+
break
59+
last_position += line_lenght
60+
i += 1
61+
str_error_ln = str(error_line_number)
62+
error_ln_length = len(str_error_ln)
63+
unexpected_char = 'EOF'
64+
if last_exp_position < len(state.text):
65+
unexpected_char = state.text[last_exp_position]
66+
unexpected = 'Unexpected "' + unexpected_char + '"'
67+
expected_rules = [exp.rule for exp in state.lastExpectations]
68+
expected = ' expected (' + ' or '.join(expected_rules) + ')'
69+
pointer = ('-'*(position_of_error + 2 + error_ln_length)) + '^'
70+
extra = line_of_error + '\n' + pointer
71+
return unexpected + expected + '\n' + str_error_ln + ': ' + extra
72+
73+
def string(rule):
74+
def _(state):
75+
state.lastExpectations = []
76+
if state.text[state.position:state.position+len(rule)] == rule:
77+
start_position = state.position
78+
state.position += len(rule)
79+
return Node(
80+
type='string',
81+
match=rule,
82+
start_position=start_position,
83+
end_position=state.position
84+
)
85+
else:
86+
state.lastExpectations = [Expectation(
87+
type='string',
88+
rule=rule,
89+
position=state.position
90+
)]
91+
return False
92+
return _
93+
94+
def regex_char(rule):
95+
def _(state):
96+
state.lastExpectations = []
97+
match = re.match(rule, state.text[state.position:])
98+
if match and match.start() == 0:
99+
start_position = state.position
100+
state.position += match.end()
101+
return Node(
102+
type='regex_char',
103+
match=match.group(0),
104+
start_position=start_position,
105+
end_position=state.position
106+
)
107+
else:
108+
state.lastExpectations = [Expectation(
109+
type='regex_char',
110+
rule=rule,
111+
position=state.position
112+
)]
113+
return False
114+
return _
115+
116+
def sequence(parsers):
117+
def _(state):
118+
asts = []
119+
start_position = state.position
120+
i = 0
121+
while i < len(parsers):
122+
ast = parsers[i](state)
123+
if ast:
124+
asts.append(ast)
125+
else:
126+
return False
127+
i += 1
128+
match = ''.join([(ast.match if ast.match is not None else '') for ast in asts])
129+
return Node(
130+
type='sequence',
131+
match=match,
132+
children=asts,
133+
start_position=start_position,
134+
end_position=state.position
135+
)
136+
return _
137+
138+
def ordered_choice(parsers):
139+
def _(state):
140+
expectations = []
141+
initial_text = state.text
142+
initial_position = state.position
143+
i = 0
144+
while i < len(parsers):
145+
ast = parsers[i](state)
146+
if ast:
147+
return Node(
148+
type='ordered_choice',
149+
match=ast.match,
150+
children=[ast],
151+
start_position=initial_position,
152+
end_position=state.position,
153+
)
154+
else:
155+
state.text = initial_text
156+
state.position = initial_position
157+
expectations = expectations + state.lastExpectations
158+
i += 1
159+
state.lastExpectations = expectations
160+
return False
161+
return _
162+
163+
def zero_or_more(parser):
164+
def _(state):
165+
asts = []
166+
start_position = state.position
167+
ast = True
168+
while ast:
169+
state_position = state.position
170+
ast = parser(state)
171+
if ast:
172+
asts.append(ast)
173+
else:
174+
state.position = state_position
175+
state.lastExpectations = []
176+
match = ''.join([(ast.match if ast.match is not None else '') for ast in asts])
177+
return Node(
178+
type='zero_or_more',
179+
match=match,
180+
children=asts,
181+
start_position=start_position,
182+
end_position=state.position
183+
)
184+
return _
185+
186+
def one_or_more(parser):
187+
def _(state):
188+
asts = []
189+
start_position = state.position
190+
ast = True
191+
while ast:
192+
state_position = state.position
193+
ast = parser(state)
194+
if ast:
195+
asts.append(ast)
196+
else:
197+
state.position = state_position
198+
if len(asts) > 0:
199+
state.lastExpectations = []
200+
match = ''.join([(ast.match if ast.match is not None else '') for ast in asts])
201+
return Node(
202+
type='one_or_more',
203+
match=match,
204+
children=asts,
205+
start_position=start_position,
206+
end_position=state.position
207+
)
208+
else:
209+
return False
210+
return _
211+
212+
def optional(parser):
213+
def _(state):
214+
start_position = state.position
215+
match = None
216+
children = None
217+
ast = parser(state)
218+
if ast:
219+
match = ast.match
220+
children = [ast]
221+
return Node(
222+
type='optional',
223+
match=match,
224+
children=children,
225+
start_position=start_position,
226+
end_position=state.position
227+
)
228+
return _
229+
230+
def and_predicate(parser):
231+
def _(state):
232+
current_text = state.text
233+
current_position = state.position
234+
ast = parser(state)
235+
if ast:
236+
state.text = current_text
237+
state.position = current_position
238+
return Node(
239+
type='and_predicate',
240+
match=None,
241+
children=[ast],
242+
start_position=state.position,
243+
end_position=state.position
244+
)
245+
else:
246+
return False
247+
return _
248+
249+
def not_predicate(parser):
250+
def _(state):
251+
current_text = state.text
252+
current_position = state.position
253+
ast = parser(state)
254+
if ast:
255+
state.text = current_text
256+
state.position = current_position
257+
state.lastExpectations = [Expectation(
258+
type='not_predicate',
259+
children=[ast],
260+
position=state.position
261+
)]
262+
return False
263+
else:
264+
state.lastExpectations = []
265+
return Node(
266+
type='not_predicate',
267+
match=None,
268+
children=[],
269+
start_position=state.position,
270+
end_position=state.position
271+
)
272+
return _
273+
274+
def end_of_file():
275+
def _(state):
276+
if len(state.text) == state.position:
277+
return Node(
278+
type='end_of_file',
279+
match=None,
280+
children=[],
281+
start_position=state.position,
282+
end_position=state.position
283+
)
284+
else:
285+
state.lastExpectations = [Expectation(
286+
type='end_of_file',
287+
rule='EOF',
288+
position=state.position
289+
)]
290+
return False
291+
return _
292+
293+
def rec(func):
294+
"""Allows you to do recurrcive currying"""
295+
def _(*args, **kwargs):
296+
return func()(*args, **kwargs)
297+
return _
298+
299+
300+
def action(name, func):
301+
def _(*args, **kwargs):
302+
ast = func(*args, **kwargs)
303+
if ast:
304+
ast.action = name
305+
return ast
306+
return _
307+
308+
def call_rule_by_name(name):
309+
def _(state):
310+
rule = next((x for x in state.rules if x.name == name), None)
311+
ast = rule.parser(state)
312+
return ast
313+
return _

0 commit comments

Comments
 (0)