cap-lab22-samy/MiniC/TP03/MiniCInterpretVisitor.py

216 lines
6.6 KiB
Python

# Visitor to *interpret* MiniC files
from typing import Dict, List, cast
from MiniCVisitor import MiniCVisitor
from MiniCParser import MiniCParser
from Lib.Errors import MiniCRuntimeError, MiniCInternalError
MINIC_VALUE = int | str | bool | float | List['MINIC_VALUE']
class MiniCInterpretVisitor(MiniCVisitor):
_memory: Dict[str, MINIC_VALUE]
def __init__(self):
self._memory = dict() # store all variable ids and values.
self.has_main = False
# visitors for variable declarations
def visitVarDecl(self, ctx) -> None:
# Initialise all variables in self._memory
typee = ctx.typee().getText()
names = self.visit(ctx.id_l())
val = 42
if(typee == "int"):
val = 0
elif(typee == "string"):
val = ""
elif(typee == "bool"):
val = False
elif(typee == "float"):
val = 0.0
for name in names:
self._memory[name] = val
def visitIdList(self, ctx) -> List[str]:
l = self.visit(ctx.id_l())
l.append(ctx.ID().getText())
return l
def visitIdListBase(self, ctx) -> List[str]:
return [ctx.ID().getText()]
# visitors for atoms --> value
def visitParExpr(self, ctx) -> MINIC_VALUE:
return self.visit(ctx.expr())
def visitIntAtom(self, ctx) -> int:
return int(ctx.getText())
def visitFloatAtom(self, ctx) -> float:
return float(ctx.getText())
def visitBooleanAtom(self, ctx) -> bool:
return ctx.getText() == "true"
def visitIdAtom(self, ctx) -> MINIC_VALUE:
return self._memory[ctx.getText()]
def visitStringAtom(self, ctx) -> str:
return ctx.getText()[1:-1] # Remove the ""
# visit expressions
def visitAtomExpr(self, ctx) -> MINIC_VALUE:
return self.visit(ctx.atom())
def visitOrExpr(self, ctx) -> bool:
lval = self.visit(ctx.expr(0))
rval = self.visit(ctx.expr(1))
return lval | rval
def visitAndExpr(self, ctx) -> bool:
lval = self.visit(ctx.expr(0))
rval = self.visit(ctx.expr(1))
return lval & rval
def visitEqualityExpr(self, ctx) -> bool:
assert ctx.myop is not None
lval = self.visit(ctx.expr(0))
rval = self.visit(ctx.expr(1))
# be careful for float equality
if ctx.myop.type == MiniCParser.EQ:
return lval == rval
else:
return lval != rval
def visitRelationalExpr(self, ctx) -> bool:
assert ctx.myop is not None
lval = self.visit(ctx.expr(0))
rval = self.visit(ctx.expr(1))
if ctx.myop.type == MiniCParser.LT:
return lval < rval
elif ctx.myop.type == MiniCParser.LTEQ:
return lval <= rval
elif ctx.myop.type == MiniCParser.GT:
return lval > rval
elif ctx.myop.type == MiniCParser.GTEQ:
return lval >= rval
else:
raise MiniCInternalError(
"Unknown comparison operator '%s'" % ctx.myop
)
def visitAdditiveExpr(self, ctx) -> MINIC_VALUE:
assert ctx.myop is not None
lval = self.visit(ctx.expr(0))
rval = self.visit(ctx.expr(1))
if ctx.myop.type == MiniCParser.PLUS:
if any(isinstance(x, str) for x in (lval, rval)):
return '{}{}'.format(lval, rval)
else:
return lval + rval
elif ctx.myop.type == MiniCParser.MINUS:
return lval - rval
else:
raise MiniCInternalError(
"Unknown additive operator '%s'" % ctx.myop)
def visitMultiplicativeExpr(self, ctx) -> MINIC_VALUE:
assert ctx.myop is not None
lval = self.visit(ctx.expr(0))
rval = self.visit(ctx.expr(1))
if ctx.myop.type == MiniCParser.MULT:
return lval * rval
elif ctx.myop.type == MiniCParser.DIV:
if rval == 0:
raise MiniCRuntimeError("Division by 0")
if isinstance(lval, int):
return lval // rval
else:
return lval / rval
elif ctx.myop.type == MiniCParser.MOD:
if rval == 0:
raise MiniCRuntimeError("Division by 0")
return lval % rval
else:
raise MiniCInternalError(
"Unknown multiplicative operator '%s'" % ctx.myop)
def visitNotExpr(self, ctx) -> bool:
return not self.visit(ctx.expr())
def visitUnaryMinusExpr(self, ctx) -> MINIC_VALUE:
return -self.visit(ctx.expr())
# visit statements
def visitPrintlnintStat(self, ctx) -> None:
val = self.visit(ctx.expr())
print(val)
def visitPrintlnfloatStat(self, ctx) -> None:
val = self.visit(ctx.expr())
if isinstance(val, float):
val = "%.2f" % val
print(val)
def visitPrintlnboolStat(self, ctx) -> None:
val = self.visit(ctx.expr())
print('1' if val else '0')
def visitPrintlnstringStat(self, ctx) -> None:
val = self.visit(ctx.expr())
print(val)
def visitAssignStat(self, ctx) -> None:
name = ctx.ID().getText()
val = self.visit(ctx.expr())
self._memory[name] = val
def visitIfStat(self, ctx) -> None:
condval = self.visit(ctx.expr())
if(condval==True):
self.visit(ctx.then_block)
else:
if(ctx.else_block != None):
self.visit(ctx.else_block)
def visitWhileStat(self, ctx) -> None:
condexpr = ctx.expr()
inexpr = ctx.body
while(self.visit(condexpr)==True):
self.visit(inexpr)
def visitForStat(self, ctx):
init_stat = ctx.init_stat
cond = ctx.cond
loop_stat = ctx.loop_stat
body = ctx.stat_block()
if(init_stat != None):
self.visit(init_stat)
while(True if cond==None else self.visit(cond)):
self.visit(body)
if(loop_stat != None):
self.visit(loop_stat)
# TOPLEVEL
def visitProgRule(self, ctx) -> None:
self.visitChildren(ctx)
if not self.has_main:
# A program without a main function is compilable (hence
# it's not a typing error per se), but not executable,
# hence we consider it a runtime error.
raise MiniCRuntimeError("No main function in file")
# Visit a function: ignore if non main!
def visitFuncDef(self, ctx) -> None:
funname = ctx.ID().getText()
if funname == "main":
self.has_main = True
self.visit(ctx.vardecl_l())
self.visit(ctx.block())