Skip to content

Commit 9b93640

Browse files
committed
Add part 9
1 parent c0abbaa commit 9b93640

85 files changed

Lines changed: 3504 additions & 89 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/python-app.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,4 @@ jobs:
3636
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
3737
- name: Test with pytest
3838
run: |
39-
pytest base/InterpreterTest.py
39+
pytest test/InterpreterTest.py

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ obj
88
.vscode
99
.pytest_cache
1010
TestResults
11+
dist
12+
build

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ repos:
1818
rev: v2.2.2
1919
hooks:
2020
- id: codespell
21+
exclude: main.spec
2122

2223
- repo: https://github.com/pycqa/isort
2324
rev: 5.10.1

README.md

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,96 @@
1-
# Pascal interpreter
1+
# Pascal Interpreter
2+
23
A simple pascal interpreter based on [Let's Build a Simple Interpreter](https://github.com/rspivak/lsbasi) series by [Ruslan Spivak](https://github.com/rspivak/) with usage of [EBNF grammar tester](https://mdkrajnak.github.io/ebnftest/) by [mdkrajnak](https://github.com/mdkrajnak/), [TatSu](https://github.com/neogeny/TatSu) and [railroad diagrams](https://github.com/tabatkins/railroad-diagrams).
4+
5+
![img.png](src/img.png)
6+
7+
## Grammar
8+
9+
```ebnf
10+
<program> ::= <compound_statement> <DOT>
11+
12+
<compound_statement> ::= <BEGIN> <statement_list> <END>
13+
14+
<statement_list> ::= <statement> { <SEMI> <statement_list> }*
15+
16+
<statement> ::= <compound_statement>
17+
| <assignment_statement>
18+
| <empty>
19+
20+
<assignment_statement> ::= <variable> <ASSIGN> <expression>
21+
22+
<empty> ::= ''
23+
24+
<factor> ::= <PLUS> <factor>
25+
| <MINUS> <factor>
26+
| <INTEGER>
27+
| <LPAREN> <expression> <RPAREN>
28+
| <variable>
29+
30+
<expression> ::= <term> { (<PLUS> | <MINUS>) <term> }*
31+
32+
<term> ::= <factor> { (<MUL> | <DIV>) <factor> }*
33+
34+
<variable> ::= <ID>
35+
36+
<ID> ::= [a-zA-Z_][a-zA-Z0-9_]*
37+
38+
<INTEGER> ::= <digit>+
39+
40+
<digit> ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
41+
42+
<BEGIN> ::= 'BEGIN'
43+
<END> ::= 'END'
44+
<DOT> ::= '.'
45+
<SEMI> ::= ';'
46+
<ASSIGN> ::= ':='
47+
<PLUS> ::= '+'
48+
<MINUS> ::= '-'
49+
<MUL> ::= '*'
50+
<DIV> ::= 'DIV'
51+
<LPAREN> ::= '('
52+
<RPAREN> ::= ')'
53+
54+
```
55+
56+
## Diagram
57+
58+
### Program
59+
60+
![](src/diagram1.svg)
61+
62+
### Compound Statement
63+
64+
![](src/diagram2.svg)
65+
66+
### Statement List
67+
68+
![](src/diagram3.svg)
69+
70+
### Statement
71+
72+
![](src/diagram4.svg)
73+
74+
### Assignment Statement
75+
76+
![](src/diagram5.svg)
77+
78+
### Variable
79+
80+
![](src/diagram6.svg)
81+
82+
### Empty Statement
83+
84+
![](src/diagram7.svg)
85+
86+
### Factor
87+
88+
![](src/diagram8.svg)
89+
90+
### Expression
91+
92+
![](src/diagram9.svg)
93+
94+
### Term
95+
96+
![](src/diagram10.svg)

base/AST.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
class AST(object):
2+
pass
3+
4+
5+
class BinOp(AST):
6+
def __init__(self, left, op, right):
7+
self.left = left
8+
self.token = self.op = op
9+
self.right = right
10+
11+
12+
class Num(AST):
13+
def __init__(self, token):
14+
self.token = token
15+
self.value = token.value
16+
17+
18+
class UnaryOp(AST):
19+
def __init__(self, op, expr):
20+
self.token = self.op = op
21+
self.expr = expr
22+
23+
24+
class Compound(AST):
25+
"""Represents a 'BEGIN ... END' block"""
26+
27+
def __init__(self):
28+
self.children = []
29+
30+
31+
class Assign(AST):
32+
def __init__(self, left, op, right):
33+
self.left = left
34+
self.token = self.op = op
35+
self.right = right
36+
37+
38+
class Var(AST):
39+
"""The Var node is constructed out of ID token."""
40+
41+
def __init__(self, token):
42+
self.token = token
43+
self.value = token.value
44+
45+
46+
class NoOp(AST):
47+
pass

base/Interpreter.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from .Node import NodeVisitor
2+
from .Parser import Parser
3+
from .Token import RESERVED_KEYWORDS, Token
4+
5+
6+
class Interpreter(NodeVisitor):
7+
8+
GLOBAL_SCOPE = {}
9+
10+
def __init__(self, text):
11+
self.parser = Parser(text)
12+
Interpreter.GLOBAL_SCOPE = {}
13+
14+
def visit_binop(self, node):
15+
if node.op.type == Token.PLUS:
16+
return self.visit(node.left) + self.visit(node.right)
17+
elif node.op.type == Token.MINUS:
18+
return self.visit(node.left) - self.visit(node.right)
19+
elif node.op.type == Token.MUL:
20+
return self.visit(node.left) * self.visit(node.right)
21+
elif node.op.type == RESERVED_KEYWORDS['DIV'].type:
22+
return self.visit(node.left) // self.visit(node.right)
23+
else:
24+
raise Exception('Unknown binary operator')
25+
26+
def visit_num(self, node):
27+
return node.value
28+
29+
def visit_unaryop(self, node):
30+
op = node.op.type
31+
if op == Token.PLUS:
32+
return +self.visit(node.expr)
33+
elif op == Token.MINUS:
34+
return -self.visit(node.expr)
35+
else:
36+
raise Exception('Unknown unary operator')
37+
38+
def visit_compound(self, node):
39+
for child in node.children:
40+
self.visit(child)
41+
42+
def visit_assign(self, node):
43+
var_name = node.left.value
44+
self.GLOBAL_SCOPE[var_name] = self.visit(node.right)
45+
46+
def visit_var(self, node):
47+
var_name = node.value
48+
val = self.GLOBAL_SCOPE.get(var_name)
49+
if val is None:
50+
raise Exception(f'NameError: name \'{var_name}\' is not defined')
51+
else:
52+
return val
53+
54+
def visit_noop(self, node):
55+
pass
56+
57+
def interpret(self):
58+
tree = self.parser.parse()
59+
if tree is None:
60+
return ''
61+
62+
return self.visit(tree)

base/InterpreterTest.py

Lines changed: 0 additions & 85 deletions
This file was deleted.

0 commit comments

Comments
 (0)