Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modernization/fixes such as updates for Python 3 #5

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 67 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# PL/0 Language Tools

The PL/0 Language Tools serve as an example of how to construct a
compiler. The language 'PL/0' was originally introduced in the book
"Algorithms + Data Structures = Programs", by Niklaus Wirth in 1975.
compiler. The language [PL/0](https://en.wikipedia.org/wiki/PL/0) was
originally introduced in the book "Algorithms + Data Structures = Programs",
by Niklaus Wirth in 1975.

![Overview](Overview.png)

Expand All @@ -15,19 +16,42 @@ It is designed to be clear and concise at the expense of performance. It
is easy to extend and modify, e.g. adding new syntax constructs or
machine instructions.

## Installation
## Install dependencies

Install ply:
* The [ply](https://www.dabeaz.com/ply/) Python library is used for lexical analysis and parsing.

sudo easy_install ply
Optional:
* It is recommended to use the [xdot](https://github.com/jrfonseca/xdot.py#readme) program to view the abstract syntax tree. If `xdot` isn't available, the PDF viewer `evince` is used as a fallback.

Then, simply download the files `pl0_*.py` and run them.
### Fedora

dnf install -y python-ply python-xdot evince

### Debian/Ubuntu

apt update && apt install -y python-is-python3 python3-ply

### Unix-based

Package names may be similar to the ones above.

For macOS it is possible to use the [Homebrew](https://brew.sh/) installer.

### Windows

Open a Command Prompt (press Win + R, type `cmd`) and install Python.

winget install Python

Use [pip](https://pip.pypa.io/) to install the ply library.

pip install ply

## Basic Usage

Here is a full example using the interpreter:

$ ./pl0_interpreter.py < examples/fibonacci.pl1
$ python pl0_interpreter.py < examples/fibonacci.pl0
1
1
2
Expand All @@ -51,14 +75,46 @@ Here is a full example using the interpreter:
10946
-- Stack Frame --
Constants: {'K': 20}
Variables: {'count': 21, 'k': 17711, 'm': 17711, 'n': 28657}
Variables: {'m': 17711, 'n': 28657, 'k': 17711, 'count': 21}
Procedures: {}

If you want to see a abstract syntax tree of your program, use the pl0_graphviz.py command:
Example of using the compiler, assembler and virtual machine:

$ python pl0_compiler.py < examples/fibonacci.pl0 | python pl0_assembler.py | python pl0_machine.py
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
-- Machine State --
Sequence: [16, 6, 17711, 28657, 17711, 21, 10, 1, 7, 2, 10, 1, 7, 3, 10, 1, 7, 4, 10, 0, 7, 5, 6, 5, 10, 20, 31, 22, 57, 6, 4, 50, 11, 6, 3, 7, 4, 6, 2, 6, 3, 45, 7, 3, 6, 4, 7, 2, 6, 5, 10, 1, 45, 7, 5, 16, 22, 1]
Stack: []
Offset: -1

To see the result of lexical analysis and parsing:

python pl0_lexer.py < examples/fibonacci.pl0
python pl0_parser.py < examples/fibonacci.pl0

./pl0_graphviz.py < examples/fibonacci.pl1
To get a graphical view of the abstract syntax tree:

A sample graph is included in the `examples` directory.
python pl0_graphviz.py < examples/fibonacci.pl0

For more advanced usage, including documentation on individual components, please see the [online documentation](http://programming.dojo.net.nz/study/pl0-language-tools/index).

Expand Down
Binary file removed examples/fibonacci.pdf
Binary file not shown.
Empty file modified examples/fibonacci.pl0
100755 → 100644
Empty file.
2 changes: 1 addition & 1 deletion expect/fibonacci.run
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@
10946
-- Stack Frame --
Constants: {'K': 20}
Variables: {'count': 21, 'k': 17711, 'm': 17711, 'n': 28657}
Variables: {'m': 17711, 'n': 28657, 'k': 17711, 'count': 21}
Procedures: {}
36 changes: 18 additions & 18 deletions pl0_ansforth.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
#
# Copyright (c) 2012 Michal J Wallace. <http://www.michaljwallace.com/>
# Copyright (c) 2012 Charles R Childers
Expand Down Expand Up @@ -29,7 +29,7 @@
from pl0_node_visitor import StackingNodeVisitor
import sys
import pl0_parser
import StringIO
import io
import os
import types

Expand All @@ -51,7 +51,7 @@
'NE' : '<>',
}

UNKNOWN, VAR, CONST, PROCEDURE = range(4)
UNKNOWN, VAR, CONST, PROCEDURE = list(range(4))
CATEGORY = "UNKNOWN VAR CONST PROCEDURE".split()


Expand Down Expand Up @@ -84,7 +84,7 @@ def local_vars(self):
returns a list of names of variables to preserve
when calling functions recursively
"""
return [ key for (key, (kind, _)) in self.local_defs.items()
return [ key for (key, (kind, _)) in list(self.local_defs.items())
if kind == VAR ]

def lookup(self, name):
Expand All @@ -106,19 +106,19 @@ def visit( self, node ):

def accept_number(self, nid, value):
if self.negate:
print "-{0}".format(value),
print("-{0}".format(value), end=' ')
self.negate = False
else:
sys.stdout.write(" ")
print value,
print(value, end=' ')

# logically, print ("!") would come much later
# but i'm putting these in implementation order,
# and i want this up front so i can see the
# results of running the code.
def accept_print(self, nid, expr):
self.visit( expr )
print ' . ',
print(' . ', end=' ')

#-- expressions --------------------

Expand All @@ -131,7 +131,7 @@ def accept_term( self, nid, *factors_tup ):
self.visit( factors.pop( 0 ))
for operator, operand in factors:
self.visit( operand )
print ops[ operator ],
print(ops[ operator ], end=' ')

# expression = [ "+"|"-"] term { ("+"|"-") term}.
def accept_expression(self, nid, sign, *terms_tup):
Expand All @@ -154,10 +154,10 @@ def accept_expression(self, nid, sign, *terms_tup):
else:
operator, term = node
self.visit( term )
print ops[ operator ],
print(ops[ operator ], end=' ')

if negate_after:
print "-1 *",
print("-1 *", end=' ')

#-- named constants ----------------

Expand All @@ -184,7 +184,7 @@ def accept_name(self, nid, name):

def accept_variables(self, nid, *names):
for nid, name in names:
print "variable " + name + "\n",
print("variable " + name + "\n", end=' ')
self.local_defs[ name ] = (VAR, name)

def accept_set(self, nid, name, expr):
Expand All @@ -198,7 +198,7 @@ def accept_set(self, nid, name, expr):

def accept_odd(self, nid, expr):
self.visit( expr )
print "2 mod 1 =",
print("2 mod 1 =", end=' ')

def accept_condition(self, nid, lhs, rel, rhs):
self.visit( lhs )
Expand Down Expand Up @@ -228,11 +228,11 @@ def accept_while(self, nid, cond, stmt):
# <cond> [ [ <stmt> <cond> ] while ] ifTrue
#
# TODO: lower level/faster WHILE implementation
print " begin ",
print(" begin ", end=' ')
self.visit( cond )
print " while ",
print(" while ", end=' ')
self.visit( stmt )
print " repeat ",
print(" repeat ", end=' ')


#-- procedures ---------------------
Expand All @@ -245,7 +245,7 @@ def accept_program(self, nid, block):
self.visit(stmt)
sys.stdout.write(" ;\n")

print "run\n"
print("run\n")


# for recursion, we need to maintain a stack
Expand All @@ -271,14 +271,14 @@ def accept_call(self, nid, name):
#TODO: only push/pop shadowed variables
recursive = name in self.proc_path

def call(): print name,
def call(): print(name, end=' ')

keep = self.local_vars()

if recursive:
for ident in keep:
sys.stdout.write(ident + ' @ ')
print " recurse ",
print(" recurse ", end=' ')
for ident in reversed( keep ):
sys.stdout.write(ident + ' ! ')
else:
Expand Down
14 changes: 7 additions & 7 deletions pl0_assembler.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
#
# Copyright (c) 2012 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
#
Expand All @@ -23,12 +23,12 @@

import pl0_machine
import sys
import StringIO
import io
import re

label_re = re.compile('\s*(.*?):')
whitespace_re = re.compile('\s+')
comment_re = re.compile('^\s*#.*$')
label_re = re.compile(r'\s*(.*?):')
whitespace_re = re.compile(r'\s+')
comment_re = re.compile(r'^\s*#.*$')

def is_integer(string):
try:
Expand Down Expand Up @@ -59,7 +59,7 @@ def assemble(input):
for argument in command:
if is_integer(argument):
buffer.append(int(argument))
elif pl0_machine.OPCODES.has_key(argument):
elif argument in pl0_machine.OPCODES:
buffer.append(pl0_machine.OPCODES[argument])
else:
# A label
Expand All @@ -70,4 +70,4 @@ def assemble(input):

if __name__ == '__main__':
code = assemble(sys.stdin)
print `code`
print(repr(code))
Loading