This commit is contained in:
augustin64 2024-10-08 08:39:14 +02:00
parent 5af1ff8677
commit 6cc1790cea
30 changed files with 528 additions and 29 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
**/.venv
**/*.interp
**/*.tokens
**/__pycache__

View File

@ -24,6 +24,7 @@ stat: assignment SCOL
| if_stat
| while_stat
| print_stat
| for_fortran_stat
;
assignment: ID ASSIGN expr #assignStat;
@ -31,6 +32,9 @@ assignment: ID ASSIGN expr #assignStat;
if_stat: IF OPAR expr CPAR then_block=stat_block
(ELSE else_block=stat_block)? #ifStat;
for_fortran_stat: FOR ID ASSIGN expr TO expr
(BY expr)? stat_block #forStat;
stat_block: OBRACE block CBRACE
| stat
;
@ -96,6 +100,9 @@ CBRACE : '}';
TRUE : 'true';
FALSE : 'false';
IF : 'if';
FOR : 'for';
TO : 'to';
BY : 'by';
ELSE : 'else';
WHILE : 'while';
RETURN : 'return';

View File

@ -29,4 +29,6 @@ TODO: explain your choices - explain the limitations of your implementation.
# Known bugs
- On a division by zero, the interpreter prints "Division by 0" message and returns 1.
This differs from certain versions of GCC. The test is therefore skipped.
TODO: document any known bug and limitations. Did you do everything asked for? Did you implement an extension?

View File

@ -22,10 +22,20 @@ class MiniCInterpretVisitor(MiniCVisitor):
def visitVarDecl(self, ctx) -> None:
# Initialise all variables in self._memory
type_str = ctx.typee().getText()
raise NotImplementedError(f"Initialization for type {type_str}")
default_value = {
"float": 0.0,
"int": 0,
"bool": False,
"string": ""
}[type_str]
names_str = self.visit(ctx.id_l())
for name in names_str:
self._memory[name] = default_value
def visitIdList(self, ctx) -> List[str]:
raise NotImplementedError()
ids = self.visit(ctx.id_l())
return ids + [ctx.ID().getText()]
def visitIdListBase(self, ctx) -> List[str]:
return [ctx.ID().getText()]
@ -45,7 +55,7 @@ class MiniCInterpretVisitor(MiniCVisitor):
return ctx.getText() == "true"
def visitIdAtom(self, ctx) -> MINIC_VALUE:
raise NotImplementedError()
return self._memory[ctx.getText()]
def visitStringAtom(self, ctx) -> str:
return ctx.getText()[1:-1] # Remove the ""
@ -115,10 +125,20 @@ class MiniCInterpretVisitor(MiniCVisitor):
return lval * rval
elif ctx.myop.type == MiniCParser.DIV:
# TODO : interpret division
raise NotImplementedError()
if rval == 0:
raise MiniCRuntimeError("Division by 0")
if type(lval) == type(1) and type(rval) == type(1):
# Integer division
if lval * rval < 0:
return -int(abs(lval)/abs(rval))
return int(abs(lval)/abs(rval))
raise MiniCInternalError("Attempting to divide elements of different type")
elif ctx.myop.type == MiniCParser.MOD:
# TODO : interpret modulo
raise NotImplementedError()
if rval == 0:
raise MiniCRuntimeError("Division by 0")
if lval < 0:
return -(abs(lval) % abs(rval))
return lval % abs(rval)
else:
raise MiniCInternalError(
f"Unknown multiplicative operator '{ctx.myop}'")
@ -150,13 +170,42 @@ class MiniCInterpretVisitor(MiniCVisitor):
print(val)
def visitAssignStat(self, ctx) -> None:
raise NotImplementedError()
v = self.visit(ctx.expr())
self._memory[ctx.ID().getText()] = v
def visitIfStat(self, ctx) -> None:
raise NotImplementedError()
b = self.visit(ctx.expr())
blocks = ctx.stat_block()
if b != 0:
self.visit(blocks[0])
elif len(blocks) > 1:
self.visit(blocks[1])
def visitWhileStat(self, ctx) -> None:
raise NotImplementedError()
while self.visit(ctx.expr()) != 0:
self.visit(ctx.stat_block())
def visitForStat(self, ctx) -> None:
from_val = self.visit(ctx.expr(0))
to_val = self.visit(ctx.expr(1))
stride = 1
if len(ctx.expr()) > 2:
stride = self.visit(ctx.expr(2))
def cond_fun(stride):
if stride > 0:
return lambda x,y: x < y
return lambda x,y: x > y
condition = cond_fun(stride)
counter = ctx.ID().getText()
self._memory[counter] = from_val
while condition(self._memory[counter], to_val):
old_counter = self._memory[counter]
self.visit(ctx.stat_block())
self._memory[counter] = old_counter + stride # type: ignore
# TOPLEVEL
def visitProgRule(self, ctx) -> None:

View File

@ -43,7 +43,13 @@ class MiniCTypingVisitor(MiniCVisitor):
# type declaration
def visitVarDecl(self, ctx) -> None:
raise NotImplementedError()
names_str = self.visit(ctx.id_l())
type_minic = self.visit(ctx.typee())
for name in names_str:
if name in self._memorytypes:
self._raiseNonType(ctx, f"Variable {name} already declared")
self._memorytypes[name] = type_minic
def visitBasicType(self, ctx):
assert ctx.mytype is not None
@ -51,14 +57,18 @@ class MiniCTypingVisitor(MiniCVisitor):
return BaseType.Integer
elif ctx.mytype.type == MiniCParser.FLOATTYPE:
return BaseType.Float
else: # TODO: same for other types
raise NotImplementedError()
elif ctx.mytype.type == MiniCParser.STRINGTYPE:
return BaseType.String
elif ctx.mytype.type == MiniCParser.BOOLTYPE:
return BaseType.Boolean
def visitIdList(self, ctx) -> List[str]:
raise NotImplementedError()
ids = self.visit(ctx.id_l())
return ids + [ctx.ID().getText()]
def visitIdListBase(self, ctx) -> List[str]:
raise NotImplementedError()
return [ctx.ID().getText()]
# typing visitors for expressions, statements !
@ -73,14 +83,13 @@ class MiniCTypingVisitor(MiniCVisitor):
return BaseType.Float
def visitBooleanAtom(self, ctx):
raise NotImplementedError()
return BaseType.Boolean
def visitIdAtom(self, ctx):
try:
return self._memorytypes[ctx.getText()]
except KeyError:
self._raiseNonType(ctx,
"Undefined variable {}".format(ctx.getText()))
self._raiseNonType(ctx, "Undefined variable {}".format(ctx.getText()))
def visitStringAtom(self, ctx):
return BaseType.String
@ -91,30 +100,67 @@ class MiniCTypingVisitor(MiniCVisitor):
return self.visit(ctx.atom())
def visitOrExpr(self, ctx):
raise NotImplementedError()
ltype = self.visit(ctx.expr(0))
rtype = self.visit(ctx.expr(1))
self._assertSameType(ctx, "|", ltype, rtype)
if ltype == BaseType.String:
self._raise(ctx, "string not allowed", ltype)
return ltype
def visitAndExpr(self, ctx):
raise NotImplementedError()
ltype = self.visit(ctx.expr(0))
rtype = self.visit(ctx.expr(1))
self._assertSameType(ctx, "&", ltype, rtype)
if ltype == BaseType.String:
self._raise(ctx, "string not allowed", ltype)
return ltype
def visitEqualityExpr(self, ctx):
raise NotImplementedError()
ltype = self.visit(ctx.expr(0))
rtype = self.visit(ctx.expr(1))
self._assertSameType(ctx, "==", ltype, rtype)
return BaseType.Boolean
def visitRelationalExpr(self, ctx):
raise NotImplementedError()
ltype = self.visit(ctx.expr(0))
rtype = self.visit(ctx.expr(1))
self._assertSameType(ctx, "relational expr need same type", ltype, rtype)
if ltype not in [BaseType.Integer, BaseType.Float]:
self._raise(ctx, "non numeric type", ltype)
return BaseType.Boolean
def visitAdditiveExpr(self, ctx):
assert ctx.myop is not None
raise NotImplementedError()
ltype = self.visit(ctx.expr(0))
rtype = self.visit(ctx.expr(1))
self._assertSameType(ctx, "Additive", ltype, rtype)
if ltype not in [BaseType.String, BaseType.Integer, BaseType.Float]:
self._raise(ctx, "additive operands", ltype, rtype)
if ltype == BaseType.String and ctx.myop.type != MiniCParser.PLUS:
self._raise(ctx, "Minus not compatible with string", ltype)
return ltype
def visitMultiplicativeExpr(self, ctx):
assert ctx.myop is not None
raise NotImplementedError()
ltype = self.visit(ctx.expr(0))
rtype = self.visit(ctx.expr(1))
if ltype == BaseType.String or rtype == BaseType.String:
self._raise(ctx, "multiplicative operands", ltype, rtype)
self._assertSameType(ctx, "multiplicative operands", ltype, rtype)
return ltype
def visitNotExpr(self, ctx):
raise NotImplementedError()
ltype = self.visit(ctx.expr())
if ltype != BaseType.Boolean:
self._raise(ctx, "NOT: only boolean allowed", ltype)
return BaseType.Boolean
def visitUnaryMinusExpr(self, ctx):
raise NotImplementedError()
ltype = self.visit(ctx.expr())
if ltype not in [BaseType.Integer, BaseType.Float]:
self._raise(ctx, "non integer type", ltype)
return ltype
# visit statements
@ -139,10 +185,42 @@ class MiniCTypingVisitor(MiniCVisitor):
self._raise(ctx, 'println_string statement', etype)
def visitAssignStat(self, ctx):
raise NotImplementedError()
etype = self.visit(ctx.expr())
if ctx.ID().getText() not in self._memorytypes:
self._raiseNonType(ctx, f"Undefined variable {ctx.ID().getText()}")
self._assertSameType(ctx, ctx.ID().getText(), self._memorytypes[ctx.ID().getText()], etype)
def visitWhileStat(self, ctx):
raise NotImplementedError()
etype = self.visit(ctx.expr())
if etype != BaseType.Boolean:
self._raise(ctx, "non boolean as condition", etype)
self.visit(ctx.stat_block())
def visitIfStat(self, ctx):
raise NotImplementedError()
etype = self.visit(ctx.expr())
if etype != BaseType.Boolean:
self._raise(ctx, "non boolean as condition", etype)
for block in ctx.stat_block():
self.visit(block)
def visitForStat(self, ctx) -> None:
from_type = self.visit(ctx.expr(0))
to_type = self.visit(ctx.expr(1))
if from_type != BaseType.Integer:
self._raise(ctx, "non-integer from value", from_type)
if to_type != BaseType.Integer:
self._raise(ctx, "non-integer to value", to_type)
if ctx.ID().getText() not in self._memorytypes:
self._raiseNonType(ctx, f"Undefined variable {ctx.ID().getText()}")
if self._memorytypes[ctx.ID().getText()] != BaseType.Integer:
self._raise(ctx, "non-integer for loop counter", self._memorytypes[ctx.ID().getText()])
if len(ctx.expr()) > 2:
stride_type = self.visit(ctx.expr(2))
if stride_type != BaseType.Integer:
self._raise(ctx, "non-integer stride value", stride_type)
self.visit(ctx.stat_block())

View File

@ -0,0 +1,10 @@
#include "printlib.h"
int main() {
println_string("hello"+" "+"there");
return 0;
}
// SKIP TEST EXPECTED
// EXPECTED
// hello there

View File

@ -0,0 +1,11 @@
#include "printlib.h"
int main() {
println_string("hello" && "hi");
return 0;
}
// SKIP TEST EXPECTED
// EXITCODE 2
// EXPECTED
// In function main: Line 4 col 17: invalid type for string not allowed: string

View File

@ -0,0 +1,16 @@
#include "printlib.h"
int main() {
bool x, y;
if (x <= y) {
println_string("ok");
}
return 0;
}
// SKIP TEST EXPECTED
// EXITCODE 2
// EXPECTED
// In function main: Line 6 col 6: invalid type for non numeric type: boolean

View File

@ -0,0 +1,14 @@
#include "printlib.h"
int main() {
int x, y;
x = 5;
y = 0;
println_int(x/0);
return 0;
}
// SKIP TEST EXPECTED
// EXECCODE 1
// EXPECTED
// Division by 0

View File

@ -0,0 +1,14 @@
#include "printlib.h"
int main() {
float x, y;
x = 5.0;
y = 0.0;
x = x/y;
return 0;
}
// SKIP TEST EXPECTED
// EXECCODE 1
// EXPECTED
// Division by 0

View File

@ -0,0 +1,16 @@
#include "printlib.h"
int main() {
int x, y;
float xf, yf;
x = 5;
y = 2;
println_int((-x)/y);
println_int(x/y);
return 0;
}
// EXPECTED
// -2
// 2

View File

@ -0,0 +1,15 @@
#include "printlib.h"
int main() {
int x, y;
if (x != 0) {
y = 12;
} else {
y = -3;
}
println_int(y);
return 0;
}
// EXPECTED
// -3

View File

@ -0,0 +1,16 @@
#include "printlib.h"
int main() {
int x, y;
if (x) {
y = 5;
}
println_int(y);
return 0;
}
// SKIP TEST EXPECTED
// EXITCODE 2
// EXPECTED
// In function main: Line 6 col 2: invalid type for non boolean as condition: integer

View File

@ -0,0 +1,19 @@
#include "printlib.h"
int main() {
int x, y;
x = 5;
y = 3;
println_int(x%y);
println_int((-x)%y);
y = 0;
println_int(x%y);
return 0;
}
// SKIP TEST EXPECTED
// EXECCODE 1
// EXPECTED
// 2
// -2
// Division by 0

View File

@ -0,0 +1,11 @@
#include "printlib.h"
int main() {
println_int(!(12+2));
return 0;
}
// SKIP TEST EXPECTED
// EXITCODE 2
// EXPECTED
// In function main: Line 4 col 14: invalid type for NOT: only boolean allowed: integer

View File

@ -0,0 +1,11 @@
#include "printlib.h"
int main() {
println_string("hello" || "hi");
return 0;
}
// SKIP TEST EXPECTED
// EXITCODE 2
// EXPECTED
// In function main: Line 4 col 17: invalid type for string not allowed: string

View File

@ -0,0 +1,14 @@
#include "printlib.h"
int main() {
int x, y;
println_bool(true);
println_bool(y+2);
return 0;
}
// SKIP TEST EXPECTED
// EXITCODE 2
// EXPECTED
// In function main: Line 7 col 2: invalid type for println_bool statement: integer

View File

@ -0,0 +1,14 @@
#include "printlib.h"
int main() {
int x, y;
println_float(15.0);
println_float(y+2);
return 0;
}
// SKIP TEST EXPECTED
// EXITCODE 2
// EXPECTED
// In function main: Line 7 col 2: invalid type for println_float statement: integer

View File

@ -0,0 +1,14 @@
#include "printlib.h"
int main() {
int x, y;
println_string("hello !");
println_string(y-2);
return 0;
}
// SKIP TEST EXPECTED
// EXITCODE 2
// EXPECTED
// In function main: Line 7 col 2: invalid type for println_string statement: integer

View File

@ -0,0 +1,11 @@
#include "printlib.h"
int main() {
println_string("hello" - "llo");
return 0;
}
// SKIP TEST EXPECTED
// EXITCODE 2
// EXPECTED
// In function main: Line 4 col 17: invalid type for Minus not compatible with string: string

View File

@ -0,0 +1,11 @@
#include "printlib.h"
int main() {
println_string(-"hello");
return 0;
}
// SKIP TEST EXPECTED
// EXITCODE 2
// EXPECTED
// In function main: Line 4 col 17: invalid type for non integer type: string

View File

@ -0,0 +1,13 @@
#include "printlib.h"
int main() {
int x;
while (x < 12) {
x = x+2;
}
println_int(x);
return 0;
}
// EXPECTED
// 12

View File

@ -0,0 +1,17 @@
#include "printlib.h"
int main() {
int x, y;
while (x) {
y = 5;
x = x+1;
}
println_int(y);
return 0;
}
// SKIP TEST EXPECTED
// EXITCODE 2
// EXPECTED
// In function main: Line 6 col 2: invalid type for non boolean as condition: integer

View File

@ -1 +0,0 @@
Add your own tests for the C-like 'for' loop in this directory (tests for the interpreter).

View File

@ -0,0 +1,32 @@
#include "printlib.h"
int main() {
int x, y;
int zero, trois;
trois = 3;
// simple loop
for x=zero to trois {
y = y+3;
}
println_int(y);
println_int(x);
// inversed loop
for x=5 to 1 by -2 {
println_int(x);
}
// empty loop
for x=5 to 0 {
println_int(-1);
}
return 0;
}
// SKIP TEST EXPECTED
// EXPECTED
// 9
// 3
// 5
// 3

View File

@ -0,0 +1,17 @@
#include "printlib.h"
int main() {
int x, y;
float fl;
fl = 12.0;
for x=fl to 3 {
y = y+3;
}
return 0;
}
// SKIP TEST EXPECTED
// EXITCODE 2
// EXPECTED
// In function main: Line 8 col 2: invalid type for non-integer from value: float

View File

@ -0,0 +1,17 @@
#include "printlib.h"
int main() {
int x, y;
float fl;
fl = 12.0;
for fl=0 to 3 {
y = y+3;
}
return 0;
}
// SKIP TEST EXPECTED
// EXITCODE 2
// EXPECTED
// In function main: Line 8 col 2: invalid type for non-integer for loop counter: float

View File

@ -0,0 +1,15 @@
#include "printlib.h"
int main() {
int x, y;
for x=0 to 3 by 1.0 {
y = y+3;
}
return 0;
}
// SKIP TEST EXPECTED
// EXITCODE 2
// EXPECTED
// In function main: Line 6 col 2: invalid type for non-integer stride value: float

View File

@ -0,0 +1,17 @@
#include "printlib.h"
int main() {
int x, y;
float fl;
fl = 12.0;
for x=0 to fl {
y = y+3;
}
return 0;
}
// SKIP TEST EXPECTED
// EXITCODE 2
// EXPECTED
// In function main: Line 8 col 2: invalid type for non-integer to value: float

View File

@ -0,0 +1,15 @@
#include "printlib.h"
int main() {
int x, y;
for undef=0 to 3 {
y = y+3;
}
return 0;
}
// SKIP TEST EXPECTED
// EXITCODE 2
// EXPECTED
// In function main: Line 6 col 2: Undefined variable undef