Skip to content

Commit 63e3562

Browse files
authored
Merge pull request #279 from boriel/feature/use_PLY_inline
Uses PLY 4.0
2 parents 5287030 + 32d9daf commit 63e3562

17 files changed

Lines changed: 3715 additions & 9 deletions

api/constants.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,20 @@
99
# the GNU General License
1010
# ----------------------------------------------------------------------
1111

12+
import os
13+
1214
from .decorator import classproperty
1315

1416

1517
# -------------------------------------------------
1618
# Global constants
1719
# -------------------------------------------------
1820

21+
# Path to main ZX Basic compiler executable
22+
ZXBASIC_ROOT = os.path.abspath(os.path.join(
23+
os.path.abspath(os.path.dirname(os.path.abspath(__file__))), os.path.pardir)
24+
)
25+
1926
# ----------------------------------------------------------------------
2027
# Class enums
2128
# ----------------------------------------------------------------------

api/utils.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
#!/usr/bin/env python
22
# -*- coding: utf-8 -*-
33

4+
import os
5+
import shelve
6+
7+
from . import constants
48
from . import global_
59
from . import errmsg
610

@@ -10,6 +14,9 @@
1014
__doc__ = """Utils module contains many helpers for several task, like reading files
1115
or path management"""
1216

17+
SHELVE_PATH = os.path.join(constants.ZXBASIC_ROOT, 'parsetab', 'tabs.dbm')
18+
SHELVE = shelve.open(SHELVE_PATH)
19+
1320

1421
def read_txt_file(fname):
1522
"""Reads a txt file, regardless of its encoding
@@ -97,3 +104,17 @@ def parse_int(str_num):
97104
return int(str_num, base)
98105
except ValueError:
99106
return None
107+
108+
109+
def load_object(key):
110+
return SHELVE[key] if key in SHELVE else None
111+
112+
113+
def save_object(key, obj):
114+
SHELVE[key] = obj
115+
SHELVE.sync()
116+
return obj
117+
118+
119+
def get_or_create(key, fn):
120+
return load_object(key) or save_object(key, fn())

asmlex.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ def find_column(self, token):
429429
# --------------------- PREPROCESSOR FUNCTIONS -------------------
430430

431431
# Needed for states
432-
tmp = lex.lex(object=Lexer(), lextab='parsetab.zxbasmlextab')
432+
tmp = lex.lex(object=Lexer())
433433

434434
if __name__ == '__main__':
435435
tmp.input(open(sys.argv[1]).read())

asmparse.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1541,4 +1541,4 @@ def main(argv):
15411541
generate_binary(OPTIONS.outputFileName.value, OPTIONS.output_file_type)
15421542

15431543

1544-
parser = yacc.yacc(method='LALR', tabmodule='parsetab.zxbasmtab', debug=OPTIONS.Debug.value > 2)
1544+
parser = api.utils.get_or_create('asmparse', lambda: yacc.yacc(debug=OPTIONS.Debug.value > 2))

parsetab/tabs.dbm.bak

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'zxbppparse', (0, 68810)
2+
'zxbparser', (69120, 707013)
3+
'asmparse', (776192, 250068)

parsetab/tabs.dbm.dat

1000 KB
Binary file not shown.

parsetab/tabs.dbm.dir

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'zxbppparse', (0, 68810)
2+
'zxbparser', (69120, 707013)
3+
'asmparse', (776192, 250068)

ply/README.md

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
# PLY (Python Lex-Yacc)
2+
3+
Copyright (C) 2001-2020
4+
David M. Beazley (Dabeaz LLC)
5+
All rights reserved.
6+
7+
Redistribution and use in source and binary forms, with or without
8+
modification, are permitted provided that the following conditions are
9+
met:
10+
11+
* Redistributions of source code must retain the above copyright notice,
12+
this list of conditions and the following disclaimer.
13+
* Redistributions in binary form must reproduce the above copyright notice,
14+
this list of conditions and the following disclaimer in the documentation
15+
and/or other materials provided with the distribution.
16+
* Neither the name of the David Beazley or Dabeaz LLC may be used to
17+
endorse or promote products derived from this software without
18+
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+
Introduction
33+
============
34+
35+
PLY is a 100% Python implementation of the common parsing tools lex
36+
and yacc. Here are a few highlights:
37+
38+
- PLY is very closely modeled after traditional lex/yacc.
39+
If you know how to use these tools in C, you will find PLY
40+
to be similar.
41+
42+
- PLY provides *very* extensive error reporting and diagnostic
43+
information to assist in parser construction. The original
44+
implementation was developed for instructional purposes. As
45+
a result, the system tries to identify the most common types
46+
of errors made by novice users.
47+
48+
- PLY provides full support for empty productions, error recovery,
49+
precedence specifiers, and moderately ambiguous grammars.
50+
51+
- Parsing is based on LR-parsing which is fast, memory efficient,
52+
better suited to large grammars, and which has a number of nice
53+
properties when dealing with syntax errors and other parsing problems.
54+
Currently, PLY builds its parsing tables using the LALR(1)
55+
algorithm used in yacc.
56+
57+
- PLY uses Python introspection features to build lexers and parsers.
58+
This greatly simplifies the task of parser construction since it reduces
59+
the number of files and eliminates the need to run a separate lex/yacc
60+
tool before running your program.
61+
62+
- PLY can be used to build parsers for "real" programming languages.
63+
Although it is not ultra-fast due to its Python implementation,
64+
PLY can be used to parse grammars consisting of several hundred
65+
rules (as might be found for a language like C). The lexer and LR
66+
parser are also reasonably efficient when parsing typically
67+
sized programs. People have used PLY to build parsers for
68+
C, C++, ADA, and other real programming languages.
69+
70+
How to Use
71+
==========
72+
73+
PLY consists of two files : lex.py and yacc.py. These are contained
74+
within the `ply` directory which may also be used as a Python package.
75+
To use PLY, simply copy the `ply` directory to your project and import
76+
lex and yacc from the associated `ply` package. For example:
77+
78+
```python
79+
from .ply import lex
80+
from .ply import yacc
81+
```
82+
83+
Alternatively, you can copy just the files lex.py and yacc.py
84+
individually and use them as modules however you see fit. For example:
85+
86+
```python
87+
import lex
88+
import yacc
89+
```
90+
91+
If you wish, you can use the install.py script to install PLY into
92+
virtual environment.
93+
94+
PLY has no third-party dependencies.
95+
96+
The docs/ directory contains complete documentation on how to use
97+
the system. Documentation available at https://ply.readthedocs.io
98+
99+
The example directory contains several different examples including a
100+
PLY specification for ANSI C as given in K&R 2nd Ed.
101+
102+
A simple example is found at the end of this document
103+
104+
Requirements
105+
============
106+
PLY requires the use of Python 3.6 or greater. However, you should
107+
use the latest Python release if possible. It should work on just
108+
about any platform.
109+
110+
Note: PLY does not support execution under `python -OO`. It can be
111+
made to work in that mode, but you'll need to change the programming
112+
interface with a decorator. See the documentation for details.
113+
114+
Resources
115+
=========
116+
117+
Official Documentation is available at:
118+
119+
* https://ply.readthedocs.io
120+
121+
More information about PLY can be obtained on the PLY webpage at:
122+
123+
* http://www.dabeaz.com/ply
124+
125+
For a detailed overview of parsing theory, consult the excellent
126+
book "Compilers : Principles, Techniques, and Tools" by Aho, Sethi, and
127+
Ullman. The topics found in "Lex & Yacc" by Levine, Mason, and Brown
128+
may also be useful.
129+
130+
The GitHub page for PLY can be found at:
131+
132+
* https://github.com/dabeaz/ply
133+
134+
Acknowledgments
135+
===============
136+
A special thanks is in order for all of the students in CS326 who
137+
suffered through about 25 different versions of these tools :-).
138+
139+
The CHANGES file acknowledges those who have contributed patches.
140+
141+
Elias Ioup did the first implementation of LALR(1) parsing in PLY-1.x.
142+
Andrew Waters and Markus Schoepflin were instrumental in reporting bugs
143+
and testing a revised LALR(1) implementation for PLY-2.0.
144+
145+
Example
146+
=======
147+
148+
Here is a simple example showing a PLY implementation of a calculator
149+
with variables.
150+
151+
```python
152+
# -----------------------------------------------------------------------------
153+
# calc.py
154+
#
155+
# A simple calculator with variables.
156+
# -----------------------------------------------------------------------------
157+
158+
tokens = (
159+
'NAME','NUMBER',
160+
'PLUS','MINUS','TIMES','DIVIDE','EQUALS',
161+
'LPAREN','RPAREN',
162+
)
163+
164+
# Tokens
165+
166+
t_PLUS = r'\+'
167+
t_MINUS = r'-'
168+
t_TIMES = r'\*'
169+
t_DIVIDE = r'/'
170+
t_EQUALS = r'='
171+
t_LPAREN = r'\('
172+
t_RPAREN = r'\)'
173+
t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
174+
175+
def t_NUMBER(t):
176+
r'\d+'
177+
t.value = int(t.value)
178+
return t
179+
180+
# Ignored characters
181+
t_ignore = " \t"
182+
183+
def t_newline(t):
184+
r'\n+'
185+
t.lexer.lineno += t.value.count("\n")
186+
187+
def t_error(t):
188+
print(f"Illegal character {t.value[0]!r}")
189+
t.lexer.skip(1)
190+
191+
# Build the lexer
192+
import ply.lex as lex
193+
lex.lex()
194+
195+
# Precedence rules for the arithmetic operators
196+
precedence = (
197+
('left','PLUS','MINUS'),
198+
('left','TIMES','DIVIDE'),
199+
('right','UMINUS'),
200+
)
201+
202+
# dictionary of names (for storing variables)
203+
names = { }
204+
205+
def p_statement_assign(p):
206+
'statement : NAME EQUALS expression'
207+
names[p[1]] = p[3]
208+
209+
def p_statement_expr(p):
210+
'statement : expression'
211+
print(p[1])
212+
213+
def p_expression_binop(p):
214+
'''expression : expression PLUS expression
215+
| expression MINUS expression
216+
| expression TIMES expression
217+
| expression DIVIDE expression'''
218+
if p[2] == '+' : p[0] = p[1] + p[3]
219+
elif p[2] == '-': p[0] = p[1] - p[3]
220+
elif p[2] == '*': p[0] = p[1] * p[3]
221+
elif p[2] == '/': p[0] = p[1] / p[3]
222+
223+
def p_expression_uminus(p):
224+
'expression : MINUS expression %prec UMINUS'
225+
p[0] = -p[2]
226+
227+
def p_expression_group(p):
228+
'expression : LPAREN expression RPAREN'
229+
p[0] = p[2]
230+
231+
def p_expression_number(p):
232+
'expression : NUMBER'
233+
p[0] = p[1]
234+
235+
def p_expression_name(p):
236+
'expression : NAME'
237+
try:
238+
p[0] = names[p[1]]
239+
except LookupError:
240+
print(f"Undefined name {p[1]!r}")
241+
p[0] = 0
242+
243+
def p_error(p):
244+
print(f"Syntax error at {p.value!r}")
245+
246+
import ply.yacc as yacc
247+
yacc.yacc()
248+
249+
while True:
250+
try:
251+
s = input('calc > ')
252+
except EOFError:
253+
break
254+
yacc.parse(s)
255+
```
256+
257+
Bug Reports and Patches
258+
=======================
259+
My goal with PLY is to simply have a decent lex/yacc implementation
260+
for Python. As a general rule, I don't spend huge amounts of time
261+
working on it unless I receive very specific bug reports and/or
262+
patches to fix problems. At this time, PLY is mature software and new
263+
features are no longer being added. If you think you have found a
264+
bug, please visit the PLY Github page at https://github.com/dabeaz/ply
265+
to report an issue.
266+
267+
Take a Class!
268+
=============
269+
270+
If you'd like to learn more about compiler principles and have a go at
271+
implementing a compiler, come take a course.
272+
https://www.dabeaz.com/compiler.html.
273+
274+
-- Dave
275+
276+
277+
278+
279+
280+
281+
282+
283+

ply/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# PLY package
2+
# Author: David Beazley (dave@dabeaz.com)
3+
# https://dabeaz.com/ply/index.html
4+
5+
__version__ = '4.0'
6+
__all__ = ['lex','yacc']

0 commit comments

Comments
 (0)