This commit is contained in:
Emma Nardino 2024-09-23 11:04:28 +02:00
parent e675f7593d
commit ff5f2319ec
25 changed files with 578 additions and 0 deletions

11
TP02/ITE/ITE.g4 Normal file
View File

@ -0,0 +1,11 @@
grammar ITE;
prog: stmt EOF;
stmt : ifStmt | ID ;
ifStmt : 'if' ID 'then' thenstmt=stmt ('else' elsestmt=stmt)?;
ID : [a-zA-Z]+;
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines

17
TP02/ITE/Makefile Normal file
View File

@ -0,0 +1,17 @@
MAINFILE = main
PACKAGE = ITE
ifndef ANTLR4
$(error variable ANTLR4 is not set)
endif
default: $(PACKAGE)Parser.py
$(PACKAGE)Parser.py: $(PACKAGE).g4
$(ANTLR4) $^ -Dlanguage=Python3
run: $(MAINFILE).py $(PACKAGE)Parser.py
python3 $<
clean:
rm -rf *~ $(PACKAGE)*.py $(PACKAGE)*.pyc *.interp *.tokens __pycache*

21
TP02/ITE/main.py Normal file
View File

@ -0,0 +1,21 @@
from antlr4 import InputStream
from antlr4 import CommonTokenStream
# include to use the generated lexer and parser
from ITELexer import ITELexer
from ITEParser import ITEParser
import sys
def main():
lexer = ITELexer(InputStream(sys.stdin.read()))
stream = CommonTokenStream(lexer)
parser = ITEParser(stream)
parser.prog()
print("Finished")
# warns pb if py file is included in others
if __name__ == '__main__':
main()

27
TP02/ariteval/Arit.g4 Normal file
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

36
TP02/ariteval/Makefile Normal file
View File

@ -0,0 +1,36 @@
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"
@echo "Created ../$$dir.tgz"
clean:
rm -rf *~ $(PACKAGE)*.py $(PACKAGE)*.pyc *.tokens __pycache* .cache *.interp *.java *.class *.dot *.dot.pdf

66
TP02/ariteval/arit.py Normal file
View File

@ -0,0 +1,66 @@
#! /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
TP02/ariteval/test_ariteval.py Executable file
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)

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))

View File

@ -0,0 +1,4 @@
a = 4 +b ;
// EXPECTED
// b is undefined
// EXITCODE 1

View File

@ -0,0 +1,3 @@
Hello
// EXPECTED
// prog = Hello

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

1
TP02/demo_files/ex1/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/Example1.py

View File

@ -0,0 +1,9 @@
//define a lexical analyser called Example1
lexer grammar Example1;
OP : '+'| '*' | '-' | '/' ;
DIGIT : [0-9] ;
LETTER : [A-Za-z] ;
ID : LETTER (LETTER | DIGIT)* ; // match idents
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines

View File

@ -0,0 +1,17 @@
MAINFILE = main
PACKAGE = Example1
ifndef ANTLR4
$(error variable ANTLR4 is not set)
endif
default: $(PACKAGE).py
$(PACKAGE).py: $(PACKAGE).g4
$(ANTLR4) $^ -Dlanguage=Python3
run: $(MAINFILE).py $(PACKAGE)*.py
python3 $<
clean:
rm -rf *~ $(PACKAGE)*.py $(PACKAGE)*.pyc *.interp *.tokens __pycache*

View File

@ -0,0 +1,25 @@
from antlr4 import InputStream
from antlr4 import CommonTokenStream
from Example1 import Example1
import sys
def main():
# InputStream reads characters (from stdin in our case)
input_stream = InputStream(sys.stdin.read())
# The generated lexer groups characters into Tokens ...
lexer = Example1(input_stream)
# ... and the stream of Tokens is managed by the TokenStream.
stream = CommonTokenStream(lexer)
# Display the token stream
stream.fill() # needed to get stream.tokens (otherwise lazily filled-in)
for t in stream.tokens:
print(t)
print("Finished")
# warns pb if py file is included in others
if __name__ == '__main__':
main()

View File

@ -0,0 +1,17 @@
//define a tiny grammar for arith expressions with identifiers
grammar Example2;
full_expr: expr ';' EOF ;
expr: expr OP expr
| ID {print('oh an id : '+$ID.text)}
| INT
;
OP : '+'| '*' | '-' | '/' ;
INT : '0'..'9'+ ;
ID : ('a'..'z'|'A'..'Z')+ ;
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines

View File

@ -0,0 +1,17 @@
MAINFILE = main
PACKAGE = Example2
ifndef ANTLR4
$(error variable ANTLR4 is not set)
endif
default: $(PACKAGE)Parser.py
$(PACKAGE)Parser.py: $(PACKAGE).g4
$(ANTLR4) $^ -Dlanguage=Python3
run: $(MAINFILE).py $(PACKAGE)Parser.py
python3 $<
clean:
rm -rf *~ $(PACKAGE)*.py $(PACKAGE)*.pyc *.interp *.tokens __pycache*

View File

@ -0,0 +1,22 @@
from antlr4 import InputStream
from antlr4 import CommonTokenStream
# include to use the generated lexer and parser
from Example2Lexer import Example2Lexer
from Example2Parser import Example2Parser
import sys
def main():
input_stream = InputStream(sys.stdin.read())
lexer = Example2Lexer(input_stream)
stream = CommonTokenStream(lexer)
parser = Example2Parser(stream)
parser.full_expr() # We want to recognize full_expr in grammar Example2
print("Finished")
# warns pb if py file is included in others
if __name__ == '__main__':
main()

View File

@ -0,0 +1,18 @@
//define a tiny grammar with attributes for arith expressions with identifiers
grammar Example3;
full_expr: e0=expr ';' EOF {print($e0.text + " has " + str($e0.count) + " operators!")} ;
expr returns [int count]: // expr has an integer attribute called count
| e0=expr OP e1=expr {$count = $e0.count + $e1.count + 1} // name sub-parts and access their attributes
| ID {$count = 0}
| INT {$count = 0}
;
OP : '+'| '*' | '-' | '/' ;
INT : '0'..'9'+ ;
ID : ('a'..'z'|'A'..'Z')+ ;
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines

View File

@ -0,0 +1,17 @@
MAINFILE = main
PACKAGE = Example3
ifndef ANTLR4
$(error variable ANTLR4 is not set)
endif
default: $(PACKAGE)Parser.py
$(PACKAGE)Parser.py: $(PACKAGE).g4
$(ANTLR4) $^ -Dlanguage=Python3
run: $(MAINFILE).py $(PACKAGE)Parser.py
python3 $<
clean:
rm -rf *~ $(PACKAGE)*.py $(PACKAGE)*.pyc *.interp *.tokens __pycache*

View File

@ -0,0 +1,22 @@
from antlr4 import InputStream
from antlr4 import CommonTokenStream
# include to use the generated lexer and parser
from Example3Lexer import Example3Lexer
from Example3Parser import Example3Parser
import sys
def main():
input_stream = InputStream(sys.stdin.read())
lexer = Example3Lexer(input_stream)
stream = CommonTokenStream(lexer)
parser = Example3Parser(stream)
parser.full_expr()
print("Finished")
# warns pb if py file is included in others
if __name__ == '__main__':
main()

26
TP02/python/objects.py Normal file
View File

@ -0,0 +1,26 @@
# Class without base class
class Base():
# Constructor (called by "Base()" when creating an object)
def __init__(self):
self.a = "set in Base's constructor"
def f(self):
return self.a
def g(self):
return "returned from Base's g()"
class Derived(Base):
# Function with the same name override the base class function.
def f(self):
return "returned from Derived's f()"
b = Base() # Create an object of Base
print(b.f()) # Method call, as usual OO languages
print(b.g())
d = Derived()
print(d.f())
print(d.g())

View File

@ -0,0 +1,53 @@
from typing import List
# int | float means ``either an int or a float''.
NUMBER = int | float # or Union[int, float] with Union imported from typing
def add_numbers(a: NUMBER, b: NUMBER) -> NUMBER:
return a + b
# Both int and floats can be passed to the function
print(add_numbers(1, 4.3))
def divide_numbers(a: NUMBER, b: NUMBER) -> float:
return a / b
print(divide_numbers(1, 2))
# Declare the type of a list whose elements are numbers.
LIST_OF_NUMBERS = List[NUMBER]
def increment(a: LIST_OF_NUMBERS) -> LIST_OF_NUMBERS:
return [x + 1 for x in a]
print(increment([1, 2, 3]))
# Skip the end if you are late.
# The type DEEP_LIST_OF_NUMBERS is a special case since it references itself.
# The identifier DEEP_LIST_OF_NUMBERS cannot be used before the end of its
# initialization, but the circular dependency can be broken using the string
# 'DEEP_LIST_OF_NUMBERS' instead.
DEEP_LIST_OF_NUMBERS = NUMBER | List['DEEP_LIST_OF_NUMBERS']
def deep_increment(d: DEEP_LIST_OF_NUMBERS) -> DEEP_LIST_OF_NUMBERS:
if isinstance(d, list):
# Note the unusual typing rule applied by Pyright here: because we are
# in the 'isinstance(d, list)' branch, it knows that d is a list,
# and accepts to iterate over it.
return [deep_increment(e) for e in d]
else:
# ... and here, in the 'else' branch Pyright knows that d is
# not a list,
# and can deduce that it is a NUMBER.
return d + 1
print(deep_increment([1, [2, 3]]))

16
TP02/python/typecheck.py Normal file
View File

@ -0,0 +1,16 @@
# Typing annotations for variables:
# name: type
int_variable: int
float_variable: float
int_variable = 4.2 # Static typing error, but no runtime error
float_variable = 42.0 # OK
float_variable = int_variable # OK
# Typing annotations for functions (-> means "returns")
def int_to_string(i: int) -> str:
return str(i)
print(int_to_string('Hello')) # Static typing error, but no runtime error
print(int_to_string(42) / 5) # Both static and runtime error

BIN
TP02/tp2.pdf Normal file

Binary file not shown.