actually commit TP2

This commit is contained in:
Rémi Di Guardia
2022-09-19 11:57:16 +02:00
parent 7f66aeacb8
commit 4a02f248df
23 changed files with 548 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
grammar Arit;
// MIF08@Lyon1 and CAP@ENSL, arit evaluator
@header {
# header - mettre les déclarations globales
import sys
idTab = {};
class UnknownIdentifier(Exception):
pass
class DivByZero(Exception):
pass
}
prog: ID {print("prog = "+str($ID.text));} ;
COMMENT
: '//' ~[\r\n]* -> skip
;
ID : ('a'..'z'|'A'..'Z')+;
INT: '0'..'9'+;
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
+35
View File
@@ -0,0 +1,35 @@
MAINFILE = arit
PACKAGE = Arit
ifndef ANTLR4
$(error variable ANTLR4 is not set)
endif
$(PACKAGE)Listener.py $(PACKAGE)Lexer.py $(PACKAGE)Lexer.tokens $(PACKAGE)Parser.py $(PACKAGE).tokens: $(PACKAGE).g4
$(ANTLR4) $< -Dlanguage=Python3
main-deps: $(PACKAGE)Lexer.py $(PACKAGE)Parser.py
#use pytest !!
run: $(MAINFILE).py main-deps
python3 $<
TESTFILE=tests/test01.txt
print-lisp: $(MAINFILE).py main-deps
python3 $< $(TESTFILE) --lisp
print-tree: $(MAINFILE).py main-deps
python3 $< $(TESTFILE) --lisp --debug
test: test_ariteval.py main-deps
python3 -m pytest -v $<
tar: clean
dir=$$(basename "$$PWD") && cd .. && \
tar cvfz "$$dir.tgz" --exclude="*.riscv" --exclude=".git" --exclude=".pytest_cache" \
--exclude="htmlcov" --exclude="*.dot" --exclude="*.pdf" "$$dir"
clean:
rm -rf *~ $(PACKAGE)*.py $(PACKAGE)*.pyc *.tokens __pycache* .cache *.interp *.java *.class *.dot *.dot.pdf
+65
View File
@@ -0,0 +1,65 @@
#! /usr/bin/env python3
"""
Usage:
python3 arit.py <filename>
"""
# Main file for MIF08 - Lab03 - 2018, changed in 2022
from AritLexer import AritLexer
from AritParser import AritParser, UnknownIdentifier, DivByZero
from antlr4 import FileStream, CommonTokenStream, StdinStream
from antlr4.tree.Trees import Trees
from antlr4.Utils import escapeWhitespace
import argparse
def getNodeText(node, parser):
return escapeWhitespace(Trees.getNodeText(node, recog=parser), True).replace('\\', '\\\\')
def _toDot(t, g, parser):
for c in Trees.getChildren(t):
g.node(str(id(c)), getNodeText(c, parser))
g.edge(str(id(t)), str(id(c)))
_toDot(c, g, parser)
def toDot(t, parser):
from graphviz import Digraph
g = Digraph()
g.node(str(id(t)), getNodeText(t, parser))
_toDot(t, g, parser)
g.render("tree.dot", view=True)
def main(inputname, lisp, debug):
if inputname is None:
lexer = AritLexer(StdinStream())
else:
lexer = AritLexer(FileStream(inputname))
stream = CommonTokenStream(lexer)
parser = AritParser(stream)
try:
tree = parser.prog()
if lisp:
print(tree.toStringTree(tree, parser))
if debug:
toDot(tree, parser)
except UnknownIdentifier as exc: # Parser's exception
print('{} is undefined'.format(exc.args[0]))
exit(1)
except DivByZero:
print('Division by zero')
exit(1)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='AritEval lab')
parser.add_argument('filename', type=str, nargs='?', help='Source file.')
parser.add_argument('--lisp', default=False, action='store_true',
help="Print parse tree in Lisp format")
parser.add_argument('--debug', default=False, action='store_true',
help="Print parse tree graphically")
args = parser.parse_args()
main(args.filename, args.lisp, args.debug)
+28
View File
@@ -0,0 +1,28 @@
#! /usr/bin/env python3
import pytest
import glob
import sys
from test_expect_pragma import TestExpectPragmas
ALL_FILES = glob.glob('./tests/hello*.txt')
# only test programs of these shapes!
# ALL_FILES = glob.glob('./tests/test*.txt')
# + glob.glob('./tests/bad*.txt')
EVAL = 'arit.py'
class TestEVAL(TestExpectPragmas):
def evaluate(self, file):
return self.run_command(['python3', EVAL, file])
@pytest.mark.parametrize('filename', ALL_FILES)
def test_expect(self, filename):
expect = self.get_expect(filename)
eval = self.evaluate(filename)
assert expect == eval
if __name__ == '__main__':
pytest.main(sys.argv)
+95
View File
@@ -0,0 +1,95 @@
import collections
import re
import os
import subprocess
import sys
testresult = collections.namedtuple('testresult', ['exitcode', 'output'])
def cat(filename):
with open(filename, "rb") as f:
for line in f:
sys.stdout.buffer.write(line)
class TestExpectPragmas(object):
"""Base class for tests that read the expected result as annotations
in test files.
get_expect(file) will parse the file, looking EXPECT and EXITCODE
pragmas.
run_command(command) is a wrapper around subprocess.check_output()
that extracts the output and exit code.
"""
def get_expect(self, filename):
"""Parse "filename" looking for EXPECT and EXITCODE annotations.
Look for a line "EXPECTED" (possibly with whitespaces and
comments). Text after this "EXPECTED" line is the expected
output.
The file may also contain a line like "EXITCODE <n>" where <n>
is an integer, and is the expected exitcode of the command.
The result is cached to avoid re-parsing the file multiple
times.
"""
if filename not in self.__expect:
self.__expect[filename] = self._extract_expect(filename)
return self.__expect[filename]
def remove(self, file):
"""Like os.remove(), but ignore errors, e.g. don't complain if the
file doesn't exist.
"""
try:
os.remove(file)
except OSError:
pass
def run_command(self, cmd):
"""Run the command cmd (given as [command, arg1, arg2, ...]), and
return testresult(exitcode=..., output=...) containing the
exit code of the command it its standard output + standard error.
"""
try:
output = subprocess.check_output(cmd, timeout=60,
stderr=subprocess.STDOUT)
exitcode = 0
except subprocess.CalledProcessError as e:
output = e.output
exitcode = e.returncode
return testresult(exitcode=exitcode, output=output.decode())
__expect = {}
def _extract_expect(self, file):
exitcode = 0
inside_expected = False
expected_lines = []
with open(file, encoding="utf-8") as f:
for line in f.readlines():
# Ignore non-comments
if not re.match(r'\s*//', line):
continue
# Cleanup comment start and whitespaces
line = re.sub(r'\s*//\s*', '', line)
line = re.sub(r'\s*$', '', line)
if line == 'END EXPECTED':
inside_expected = False
elif line.startswith('EXITCODE'):
words = line.split(' ')
assert len(words) == 2
exitcode = int(words[1])
elif line == 'EXPECTED':
inside_expected = True
elif inside_expected:
expected_lines.append(line)
expected_lines.append('')
return testresult(exitcode=exitcode,
output=os.linesep.join(expected_lines))
+4
View File
@@ -0,0 +1,4 @@
a = 4 +b ;
// EXPECTED
// b is undefined
// EXITCODE 1
+3
View File
@@ -0,0 +1,3 @@
Hello
// EXPECTED
// prog = Hello
+10
View File
@@ -0,0 +1,10 @@
20 + 22;
a = 4;
a + 2;
a * 5;
// EXPECTED
// 20+22 = 42
// a now equals 4
// a+2 = 6
// a*5 = 20