This commit is contained in:
Nguyễn Anh Khoa 2018-12-04 02:05:06 +07:00
commit bed5a47d55
24 changed files with 6321 additions and 0 deletions

611
.gitignore vendored Normal file
View File

@ -0,0 +1,611 @@
# Created by https://www.gitignore.io/api/vim,python,eclipse,netbeans,sublimetext,visualstudio,visualstudiocode
# Edit at https://www.gitignore.io/?templates=vim,python,eclipse,netbeans,sublimetext,visualstudio,visualstudiocode
### Eclipse ###
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# CDT- autotools
.autotools
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
# Annotation Processing
.apt_generated/
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet
### Eclipse Patch ###
# Eclipse Core
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath
# Annotation Processing
.apt_generated
.sts4-cache/
### NetBeans ###
**/nbproject/private/
build/
nbbuild/
dist/
nbdist/
.nb-gradle/
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
develop-eggs/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
### Python Patch ###
.venv/
### SublimeText ###
# Cache files for Sublime Text
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache
# Workspace files are user-specific
*.sublime-workspace
# Project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using Sublime Text
# *.sublime-project
# SFTP configuration file
sftp-config.json
# Package control specific files
Package Control.last-run
Package Control.ca-list
Package Control.ca-bundle
Package Control.system-ca-bundle
Package Control.cache/
Package Control.ca-certs/
Package Control.merged-ca-bundle
Package Control.user-ca-bundle
oscrypto-ca-bundle.crt
bh_unicode_properties.cache
# Sublime-github package stores a github token in this file
# https://packagecontrol.io/packages/sublime-github
GitHub.sublime-settings
### Vim ###
# Swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Session
Session.vim
# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
### VisualStudio ###
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp_proj
*_wpftmp.csproj
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true
**/wwwroot/lib/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# End of https://www.gitignore.io/api/vim,python,eclipse,netbeans,sublimetext,visualstudio,visualstudiocode
**/.DS_Store
tests/
**/*.jar
**/*.mp
parser/.antlr4
parser/*.tokens
parser/*.interp
parser/MPLexer.py
parser/MPParser.py
parser/MPVisitor.py

36
README.md Normal file
View File

@ -0,0 +1,36 @@
# MP Compiler
From my Principle of Programming Languages assignment, I have created a compiler for the MP language. The assignment phase is divided to 4 phases, from doing Lexer, Parser, AST generation to Static Checker and Jasmin Code generation.
The assignment code structure is quite ugle, so I re-organized the code, adding some more steps to make the code look nicer and compile a \*.mp file to a jar file.
Given the mp file as follows:
```mp
// hello.mp
procedure main();
begin
putString("Hello World");
end
```
Compile and run the file by issuing these commands:
```shell
python mpc.py hello.mp
java -jar hello.jar
```
More documentation is being built.
## Notes
Because I was having serious deadlines at the end of the semester, I drop on working on ArrayCell, which will be added later.
Because the lexer and parser are given by the famous `ANTLR4` engine, there should exists a path to antlr4.jar on the environment variable `ANTLR_LIB` or else, the program will use the antlr4 file in the external folder.
Before running `mpc.py`, you must be sure that you have generate neccessary files from ANTLR4.
```shell
python genANTRL4.py
```

402
astgen/ASTGeneration.py Normal file
View File

@ -0,0 +1,402 @@
from parser.MPVisitor import MPVisitor
from parser.MPParser import MPParser
from functools import reduce
# * is not a good use case
from utils.AST import (
IntType,
FloatType,
BoolType,
StringType,
ArrayType,
# VoidType,
Program,
# Decl,
VarDecl,
FuncDecl,
# Stmt,
Assign,
If,
While,
For,
Break,
Continue,
Return,
With,
CallStmt,
# Expr,
BinaryOp,
UnaryOp,
CallExpr,
# LHS,
Id,
ArrayCell,
# Literal,
IntLiteral,
FloatLiteral,
StringLiteral,
BooleanLiteral
)
def flatten(listOflist):
return reduce(lambda x, item: x + item, listOflist, [])
class ASTGeneration(MPVisitor):
def visitProgram(self, ctx: MPParser.ProgramContext):
"""
return Program(list of Decl)
where Decl:
+ VarDecl ==> var_decl
+ FuncDecl ==> func_decl
+ ProcDecl ==> proc_decl
"""
return Program(self.visit(ctx.manydecl()))
def visitManydecl(self, ctx: MPParser.ManydeclContext):
"""
return list of decl expanded
"""
decl = self.visit(ctx.decl())
if ctx.manydecl():
return decl + self.visit(ctx.manydecl())
else:
return decl
def visitDecl(self, ctx: MPParser.DeclContext):
"""
return either
+ var_decl
+ func_decl
+ proc_decl
"""
decl = self.visit(ctx.getChild(0))
if ctx.var_decl():
return decl
return [decl]
def visitVar_decl(self, ctx: MPParser.Var_declContext):
"""
return varlist
"""
return self.visit(ctx.varlist())
def visitVarlist(self, ctx: MPParser.VarlistContext):
"""
return list of VarDecl(iden, mptype)
"""
var = self.visit(ctx.var())
if ctx.varlist():
return var + self.visit(ctx.varlist())
else:
return var
def visitVar(self, ctx: MPParser.VarContext):
"""
return list of VarDecl(iden, mptype)
"""
mptype = self.visit(ctx.mptype())
idenlist = self.visit(ctx.idenlist())
# apply VarDecl(x, mptype) to idenlist where x is item in idenlist
def compose(f, arg):
def h(x):
return f(x, arg)
return h
hoo = compose(lambda x, y: VarDecl(x, y), mptype)
return list(map(hoo, idenlist))
def visitIdenlist(self, ctx: MPParser.IdenlistContext):
"""
return list of iden
"""
ident = Id(ctx.IDENT().getText())
if ctx.idenlist():
return [ident] + self.visit(ctx.idenlist())
else:
return [ident]
def visitMptype(self, ctx: MPParser.MptypeContext):
return self.visit(ctx.getChild(0))
def visitPrimitive_type(self, ctx: MPParser.Primitive_typeContext):
if ctx.INTEGER():
return IntType()
elif ctx.BOOLEAN():
return BoolType()
elif ctx.REAL():
return FloatType()
elif ctx.STRING():
return StringType()
def visitCompound_type(self, ctx: MPParser.Compound_typeContext):
"""
return ArrayType(low, high, type)
"""
low, high = self.visit(ctx.array_value())
pri_type = self.visit(ctx.primitive_type())
return ArrayType(low, high, pri_type)
def visitArray_value(self, ctx: MPParser.Array_valueContext):
"""
return low, high
"""
low = int(ctx.NUM_INT(0).getText())
high = int(ctx.NUM_INT(1).getText())
sub = len(ctx.MINUS())
if sub == 0:
pass
elif sub == 2:
low = -low
high = -high
elif ctx.getChild(1).getText() == '-':
low = -low
else:
high = -high
return low, high
def visitFunc_decl(self, ctx: MPParser.Func_declContext):
ident = Id(ctx.IDENT().getText())
param_list = self.visit(ctx.param_list()) if ctx.param_list() else []
mptype = self.visit(ctx.mptype())
var_decl = flatten(list(map(self.visit, ctx.var_decl())))
compound_statement = self.visit(ctx.compound_statement())
return FuncDecl(
ident,
param_list,
var_decl,
compound_statement,
mptype
)
def visitParam_list(self, ctx: MPParser.Param_listContext):
"""
return list of VarDecl(iden, mptype)
"""
var = self.visit(ctx.var())
# var is a list of VarDecl
if ctx.param_list():
# concat
return var + self.visit(ctx.param_list())
else:
# plain list return
return var
return
def visitProc_decl(self, ctx: MPParser.Proc_declContext):
ident = Id(ctx.IDENT().getText())
param_list = self.visit(ctx.param_list()) if ctx.param_list() else []
var_decl = flatten(list(map(self.visit, ctx.var_decl())))
compound_statement = self.visit(ctx.compound_statement())
return FuncDecl(
ident,
param_list,
var_decl,
compound_statement
)
def visitExpression(self, ctx: MPParser.ExpressionContext):
if ctx.getChildCount() == 1:
return self.visit(ctx.expression_lv1())
if ctx.AND():
op = "andthen"
else:
op = "orelse"
return BinaryOp(
op,
self.visit(ctx.expression()),
self.visit(ctx.expression_lv1())
)
def visitExpression_lv1(self, ctx: MPParser.Expression_lv1Context):
if ctx.getChildCount() == 1:
return self.visit(ctx.expression_lv2(0))
return BinaryOp(
ctx.getChild(1).getText(),
self.visit(ctx.expression_lv2(0)),
self.visit(ctx.expression_lv2(1))
)
def visitExpression_lv2(self, ctx: MPParser.Expression_lv2Context):
if ctx.getChildCount() == 1:
return self.visit(ctx.expression_lv3())
return BinaryOp(
ctx.getChild(1).getText(),
self.visit(ctx.expression_lv2()),
self.visit(ctx.expression_lv3())
)
def visitExpression_lv3(self, ctx: MPParser.Expression_lv3Context):
if ctx.getChildCount() == 1:
return self.visit(ctx.expression_lv4())
return BinaryOp(
ctx.getChild(1).getText(),
self.visit(ctx.expression_lv3()),
self.visit(ctx.expression_lv4())
)
def visitExpression_lv4(self, ctx: MPParser.Expression_lv4Context):
if ctx.getChildCount() == 1:
return self.visit(ctx.index_expression())
return UnaryOp(
ctx.getChild(0).getText(),
self.visit(ctx.expression_lv4())
)
def visitIndex_expression(self, ctx: MPParser.Index_expressionContext):
if ctx.getChildCount() == 1:
return self.visit(ctx.factor())
return ArrayCell(
self.visit(ctx.index_expression()),
self.visit(ctx.expression())
)
def visitInvocation_expression(
self, ctx: MPParser.Invocation_expressionContext):
if ctx.call_param():
return self.visit(ctx.call_param())
return []
def visitFactor(self, ctx: MPParser.FactorContext):
if ctx.expression():
return self.visit(ctx.expression())
elif ctx.invocation_expression():
return CallExpr(Id(ctx.IDENT().getText()),
self.visit(ctx.invocation_expression()))
elif ctx.literal():
return self.visit(ctx.literal())
elif ctx.IDENT():
return Id(ctx.IDENT().getText())
elif ctx.STRING_LITERAL():
return StringLiteral(ctx.STRING_LITERAL().getText())
return
def visitStatement(self, ctx: MPParser.StatementContext):
return self.visit(ctx.getChild(0))
def visitStructured_statement(
self, ctx: MPParser.Structured_statementContext):
if ctx.compound_statement():
return self.visit(ctx.getChild(0))
else:
return [self.visit(ctx.getChild(0))]
def visitNormal_statement(self, ctx: MPParser.Normal_statementContext):
if ctx.assignment_statement():
return self.visit(ctx.getChild(0))
else:
return [self.visit(ctx.getChild(0))]
def visitAssignment_statement(
self, ctx: MPParser.Assignment_statementContext):
"""
return list of Assign(lhs, exp)
"""
expression = self.visit(ctx.expression())
assignment_lhs_list = self.visit(ctx.assignment_lhs_list())
rhs_list = assignment_lhs_list[1:] + [expression]
# def compose(arg):
# def h(x):
# return Assign(x, arg)
# return h
# hoo = list(map(lambda x: compose(x), rhs_list))
return [Assign(lhs, rhs)
for lhs, rhs in zip(assignment_lhs_list, rhs_list)][::-1]
def visitAssignment_lhs_list(
self, ctx: MPParser.Assignment_lhs_listContext):
"""
return list of lhs
"""
lhs = self.visit(ctx.lhs())
if ctx.assignment_lhs_list():
return [lhs] + self.visit(ctx.assignment_lhs_list())
else:
return [lhs]
def visitLhs(self, ctx: MPParser.LhsContext):
"""
return IDENT or index_pression
"""
if ctx.IDENT():
return Id(ctx.IDENT().getText())
else:
return self.visit(ctx.index_expression())
def visitIf_statement(self, ctx: MPParser.If_statementContext):
expression = self.visit(ctx.expression())
if ctx.ELSE():
then_statement = self.visit(ctx.statement(0))
else_statement = self.visit(ctx.statement(1))
return If(expression, then_statement, else_statement)
else:
then_statement = self.visit(ctx.statement(0))
return If(expression, then_statement)
def visitWhile_statement(self, ctx: MPParser.While_statementContext):
return While(self.visit(ctx.expression()), self.visit(ctx.statement()))
def visitFor_statement(self, ctx: MPParser.For_statementContext):
up = True if ctx.TO() else False
return For(
Id(ctx.IDENT().getText()),
self.visit(ctx.expression(0)),
self.visit(ctx.expression(1)),
up,
self.visit(ctx.statement()))
def visitBreak_statement(self, ctx: MPParser.Break_statementContext):
return Break()
def visitContinue_statement(self, ctx: MPParser.Continue_statementContext):
return Continue()
def visitReturn_statement(self, ctx: MPParser.Return_statementContext):
if ctx.expression():
return Return(self.visit(ctx.expression()))
else:
return Return()
def visitCompound_statement(self, ctx: MPParser.Compound_statementContext):
if ctx.statement():
return flatten(list(map(self.visit, ctx.statement())))
else:
return []
def visitWith_statement(self, ctx: MPParser.With_statementContext):
return With(self.visit(ctx.varlist()), self.visit(ctx.statement()))
def visitCall_statement(self, ctx: MPParser.Call_statementContext):
param = self.visit(ctx.call_param()) if ctx.call_param() else []
return CallStmt(Id(ctx.IDENT().getText()), param)
def visitCall_param(self, ctx: MPParser.Call_paramContext):
expression = self.visit(ctx.expression())
if ctx.call_param():
return [expression] + self.visit(ctx.call_param())
else:
return [expression]
def visitEmpty(self, ctx: MPParser.EmptyContext):
return
def visitLiteral(self, ctx: MPParser.LiteralContext):
if ctx.number():
return self.visit(ctx.number())
elif ctx.BOOL_LIT():
if ctx.BOOL_LIT().getText().lower() == 'true':
return BooleanLiteral(True)
else:
return BooleanLiteral(False)
elif ctx.STRING_LITERAL():
return StringLiteral(ctx.STRING_LITERAL().getText())
return
def visitNumber(self, ctx: MPParser.NumberContext):
if ctx.NUM_INT():
return IntLiteral(int(ctx.NUM_INT().getText()))
elif ctx.NUM_REAL():
return FloatLiteral(float(ctx.NUM_REAL().getText()))

0
astgen/__init__.py Normal file
View File

778
checker/StaticCheck.py Normal file
View File

@ -0,0 +1,778 @@
from utils.Visitor import BaseVisitor
from utils.Utils import Utils
from checker.StaticError import (
# Kind,
Function,
Procedure,
Variable,
Parameter,
Identifier,
# StaticError,
Undeclared,
Redeclared,
TypeMismatchInExpression,
TypeMismatchInStatement,
FunctionNotReturn,
BreakNotInLoop,
ContinueNotInLoop,
NoEntryPoint,
UnreachableStatement,
Unreachable
)
from utils.AST import (
IntType,
FloatType,
BoolType,
StringType,
ArrayType,
VoidType,
# Program,
# Decl,
# VarDecl,
FuncDecl,
# Stmt,
# Assign,
# If,
# While,
# For,
# Break,
# Continue,
# Return,
# With,
# CallStmt,
# Expr,
# BinaryOp,
# UnaryOp,
CallExpr,
# LHS,
# Id,
# ArrayCell,
# Literal,
# IntLiteral,
# FloatLiteral,
# StringLiteral,
# BooleanLiteral
)
from functools import reduce
class MType:
def __init__(self, partype, rettype):
self.partype = partype
self.rettype = rettype
def __str__(self):
return 'MType([{}],{})'.format(
','.join([str(x) for x in self.partype]),
str(self.rettype)
)
class Symbol:
def __init__(self, name, mtype, value=0):
self.name = name
self.mtype = mtype
self.value = value
def __str__(self):
return 'Symbol({},{})'.format(
self.name,
str(self.mtype)
)
# DEBUG = True
DEBUG = False
def printEnv(env, stop=False):
if not DEBUG:
return
if stop:
try:
input(','.join([str(e) for e in env]))
except EOFError:
print(','.join([str(e) for e in env]))
else:
print(','.join([str(e) for e in env]))
def printDebug(desc, **kwargs):
if not DEBUG:
return
print(desc)
if 'env' in kwargs:
if 'stop' in kwargs:
printEnv(kwargs['env'], kwargs['stop'])
else:
printEnv(kwargs['env'])
class StaticChecker(BaseVisitor, Utils):
global_envi = [
# from specification, section 7: Built-in Functions/Procedures
Symbol("getInt", MType([], IntType())),
Symbol("putInt", MType([IntType()], VoidType())),
Symbol("putIntLn", MType([IntType()], VoidType())),
Symbol("getFloat", MType([], FloatType())),
Symbol("putFloat", MType([FloatType()], VoidType())),
Symbol("putFloatLn", MType([FloatType()], VoidType())),
Symbol("putBool", MType([BoolType()], VoidType())),
Symbol("putBoolLn", MType([BoolType()], VoidType())),
Symbol("putString", MType([StringType()], VoidType())),
Symbol("putStringLn", MType([StringType()], VoidType())),
Symbol("putLn", MType([], VoidType()))
]
def __init__(self, ast):
self.ast = ast
def check(self):
return self.visit(self.ast, StaticChecker.global_envi)
def checkRedeclared(self, symbol, kind, env):
res = self.lookup(symbol.name.lower(), env, lambda e: e.name.lower())
if res is not None:
raise Redeclared(kind, symbol.name)
def mergeGlobal2Local(self, local_scope, global_scope):
for s in global_scope:
res = self.lookup(s.name, local_scope, lambda e: e.name.lower())
if res is None:
local_scope.append(s)
def checkTypeCompatibility(self, lhs, rhs, error):
# array check
if isinstance(lhs, ArrayType):
if not isinstance(rhs, ArrayType):
raise error
if lhs.lower != rhs.lower or \
lhs.upper != rhs.upper:
raise error
# self.checkTypeCompatibility(lhs.eleType, rhs.eleType, error)
if not isinstance(lhs.eleType, type(rhs.eleType)):
raise error
# float/int coersion
elif isinstance(lhs, FloatType):
if not isinstance(rhs, (IntType, FloatType)):
raise error
# else
elif not isinstance(lhs, type(rhs)):
raise error
def callBody(self, ast, env):
'''
ast: CallStmt | CallExpr
env: List[Symbol]
raise TypeMismatchInStatement | TypeMismatchInExpression
=> Type: MType
; Used by CallStmt and CallExpr
; Both have exact structure difference only on
; Raising Error and Kind Expectation
'''
callParam = [self.visit(x, env) for x in ast.param]
mtype = self.visit( # visits Id
ast.method,
{
'env': env,
'kind': Function() if isinstance(ast, CallExpr) \
else Procedure(),
}
)
rightParam = mtype.partype
# lazy init of Error
error = TypeMismatchInExpression if isinstance(ast, CallExpr) \
else TypeMismatchInStatement
if len(rightParam) != len(callParam):
raise error(ast)
# LHSs are formal parameters and RHSs are arguments
for pair in zip(rightParam, callParam):
lhs = pair[0]
rhs = pair[1]
self.checkTypeCompatibility(lhs, rhs, error(ast))
return mtype.rettype
def loopBody(self, stmts, param):
'''
stmt: List[Statement]
param: {
'env': List[Symbol],
'inloop': Bool,
'rettype': Type
}
'''
env = param['env']
rettype = param['rettype']
outFlag = False
for stmt in stmts:
if outFlag:
raise UnreachableStatement(stmt)
if self.visit(
stmt, {
'env': env, 'inloop': True, 'rettype': rettype
}
):
outFlag = True
return False
def processStatement(self, stmts, param):
returnFlag = False
for stmt in stmts:
if returnFlag:
raise UnreachableStatement(stmt)
if self.visit(stmt, param):
returnFlag = True
return returnFlag
def visitProgram(self, ast, env):
printDebug("======SCAN PROGRAM======")
global_scope = reduce(
lambda returnList, decl:
[self.visit(
decl,
{'env': returnList, 'scan': False}
)] + returnList,
ast.decl,
env[:]
)
printDebug("======GLOBAL======", env=global_scope)
if not any(map(
lambda symbol: isinstance(
symbol.mtype,
MType) and symbol.name.lower() == 'main' and isinstance(
symbol.mtype.rettype,
VoidType) and len(symbol.mtype.partype) == 0,
global_scope)):
raise NoEntryPoint()
funcs = filter(lambda x: isinstance(x, FuncDecl), ast.decl)
for func in funcs:
self.visit(func, {'env': global_scope, 'scan': True})
for symbol in global_scope:
if not isinstance(symbol.mtype, MType):
continue
if symbol.name.lower() == 'main' and \
isinstance(symbol.mtype.rettype, VoidType):
continue
if symbol.value == 0:
if symbol in env:
continue
raise Unreachable(
Procedure() if isinstance(symbol.mtype.rettype, VoidType)
else Function(),
symbol.name)
return global_scope
def visitFuncDecl(self, ast, param):
'''
ast: FuncDecl
param: {
env: List[Symbol], # Global Reference Environment
scan: Bool
}
raise Redeclared(Parameter)
raise Redeclared(Variable)
raise UnreachableStatement
raise FunctionNotReturn
=> Symbol if not scan else None
'''
env = param['env']
scan = param['scan']
if not scan:
printDebug("FUNCDECL", env=env, stop=False)
s = Symbol(
ast.name.name,
MType(
[x.varType for x in ast.param],
ast.returnType
))
kind = Procedure() if isinstance(ast.returnType, VoidType) \
else Function()
self.checkRedeclared(s, kind, env)
return s
else:
printDebug("========SCAN FUNC========")
printDebug(str(ast))
try:
# visits VarDecl -- throws Redeclared(Variable)
parameter = reduce(
lambda scope, vardecl:
[self.visit(vardecl, {'env': scope})] + scope,
ast.param,
# env[:] # copy
[]
)
except Redeclared as e:
raise Redeclared(Parameter(), e.n)
printDebug("PARAM", env=parameter)
# visits VarDecl -- throws Redeclared(Variable)
local_scope = reduce(
lambda scope, vardecl:
[self.visit(vardecl, {'env': scope})] + scope,
ast.local,
parameter # for safety reason, copy
)
printDebug("LOCAL_VAR", env=local_scope)
# self.mergeGlobal2Local(local_scope, env)
local_scope += env
printDebug("LOCAL_ENV", env=local_scope, stop=False)
# check in body
if not self.processStatement(
ast.body,
{
'env': local_scope,
'inloop': False,
'rettype': ast.returnType
}) and not isinstance(ast.returnType, VoidType):
raise FunctionNotReturn(ast.name.name)
def visitVarDecl(self, ast, param):
'''
ast: VarDecl
param: {
env: List[Symbol]
~~scan: Bool~~ # ignore
}
=> Symbol
'''
# print(param, file=sys.stderr)
env = param['env']
printDebug("VARDECL", env=env, stop=False)
s = Symbol(
ast.variable.name,
ast.varType
)
self.checkRedeclared(s, Variable(), env)
return s
def visitIntType(self, asttree, param):
return None
def visitFloatType(self, asttree, param):
return None
def visitBoolType(self, asttree, param):
return None
def visitStringType(self, asttree, param):
return None
def visitVoidType(self, asttree, param):
return None
def visitArrayType(self, asttree, param):
return None
def visitBinaryOp(self, ast, param):
'''
ast: BinaryOp
param: list[Symbol]
raise TypeMismatchInExpression
(/) --> Float/Int:Float/Int => Float
(+,-,*) --> Float:Float/Int => Float
--> Float/Int:Float => Float
--> Int:Int => Int
(div,mod) --> Int:Int => Int
(<,<=,=,>=,>,<>) --> Float:Float/Int => Bool
--> Float/Int:Float => Bool
--> Int:Int => Bool
(and,or,andthen,orelse) --> Bool:Bool => Bool
=> Type
'''
op = ast.op.lower()
# visits (Id, BinaryOp, UnaryOp, CallExpr, ArrayCell)
left_type = self.visit(ast.left, param)
right_type = self.visit(ast.right, param)
def deferType(acceptableTypes, returnType=None):
if not isinstance(left_type, acceptableTypes):
raise TypeMismatchInExpression(ast)
if not isinstance(right_type, acceptableTypes):
raise TypeMismatchInExpression(ast)
if returnType is not None:
return returnType
if isinstance(left_type, FloatType) or \
isinstance(right_type, FloatType):
return FloatType()
if isinstance(left_type, type(right_type)):
return left_type
raise TypeMismatchInExpression(ast)
if op in ('and', 'or', 'andthen', 'orelse'):
return deferType((BoolType), BoolType())
if op in ('div', 'mod'):
return deferType((IntType), IntType())
if op in ('+', '-', '*'):
return deferType((IntType, FloatType))
if op in ('/'):
return deferType((IntType, FloatType), FloatType())
if op in ('<', '<=', '=', '>=', '>', '<>'):
return deferType((IntType, FloatType), BoolType())
def visitUnaryOp(self, ast, param):
'''
ast: UnaryOp
param: List[Symbol]
raise TypeMismatchInExpression
not Bool => Bool
- Int => Int
- Float => Float
=> Type
'''
op = ast.op.lower()
expr = self.visit(ast.body, param)
if op in ('not'):
if not isinstance(expr, BoolType):
raise TypeMismatchInExpression(ast)
return BoolType()
if op in ('-'):
if not isinstance(expr, (IntType, FloatType)):
raise TypeMismatchInExpression(ast)
return expr
def visitCallExpr(self, ast, env):
'''
ast: CallExpr ~ CallStmt
env: list[Symbol]
raise Undeclared(Function)
raise TypeMismatchInExpression
wrong param size
wrong param type
Array[n..m] of X --> Array[n..m] of X
Float --> Float/Int
X --> X
=> Type
'''
return self.callBody(ast, env)
def visitId(self, ast, param):
'''
ast: Id
param: List[Symbol] or {
'env': List[Symbol]
'kind': kind expectation
}
raise Undeclared
=> Type: Symbol.mtype
'''
env = param['env'] if not isinstance(param, list) else param
kind = param['kind'] if not isinstance(param, list) else Identifier()
# type(res) == Symbol
# printDebug("ID", env=env, stop=False)
res = self.lookup(ast.name.lower(), env, lambda e: e.name.lower())
if res is None:
raise Undeclared(kind, ast.name)
if isinstance(kind, Identifier):
if isinstance(res.mtype, MType):
raise Undeclared(kind, ast.name)
return res.mtype
# param is dict
if isinstance(kind, Function) or isinstance(kind, Procedure):
# check if mtype -- aka function
if not isinstance(res.mtype, MType):
raise Undeclared(kind, ast.name)
if isinstance(kind, Function):
if isinstance(res.mtype.rettype, VoidType):
raise Undeclared(kind, ast.name)
res.value += 1
elif isinstance(kind, Procedure):
if not isinstance(res.mtype.rettype, VoidType):
raise Undeclared(kind, ast.name)
res.value += 1
return res.mtype
def visitArrayCell(self, ast, param):
'''
ast: ArrayCell
param: List[Symbol]
raise TypeMismatchInExpression
arr[idx] --> ArrayType[IntType] => ArrayType.eleType
=> Type
'''
arr = self.visit(ast.arr, param)
idx = self.visit(ast.idx, param)
if not isinstance(idx, IntType) or \
not isinstance(arr, ArrayType):
raise TypeMismatchInExpression(ast)
return arr.eleType
'''
Statements are passed with a dict() with simple params:
env: List[Symbol] # local referencing environment
inloop: Bool # in loop flag
rettype: Type # return type of function/procedure
Simple statements deal only with 'env',
Continue/Break statements use 'inloop' to check for non in loop call
Return statements use 'rettype' to check for type compatibility when return
Function/Procedure statements pass all these params,
For/While statements pass inloop as True when processing loop statements
while preserving 'rettype
If statements pass param to then/else statements
With statements ...
All other statements uses param as read only
Return of statements are the status of the function/procedure whether
it has returned or not
For example:
procedure main();
begin
if (n and mask) then
return 1;
else
return 0;
end
the corresponding AST Tree will be:
Program([
FuncDecl(Id(main),[],VoidType(),[],[
If(BinaryOp(and,Id(n),Id(mask)),[
Return(Some(IntLiteral(1)))
],[
Return(Some(IntLiteral(0)))
])
])
])
The If in AST will return True as both of the branch returns.
There is no else if statement, but this algorithm works as
expect that in if statement, all branch returns means the function is ended
The same logic can also be applied to for/while/with statements
'''
def visitAssign(self, ast, param):
'''
ast: Assign
param: {
'env': List[Symbol]
'inloop': Bool
}
raise TypeMismatchInStatement
Float := Float/Int
Int := Int
Bool := Bool
// no String, Array
=> Returned?
'''
env = param['env']
left_type = self.visit(ast.lhs, env)
right_type = self.visit(ast.exp, env)
if isinstance(left_type, (StringType, ArrayType)):
raise TypeMismatchInStatement(ast)
self.checkTypeCompatibility(
left_type, right_type, TypeMismatchInStatement(ast))
return False
def visitWith(self, ast, param):
'''
ast: With
param: {
'env': List[Symbol],
'inloop': Bool,
'rettype': Type
}
'''
env = param['env']
with_scope = reduce(
lambda with_scope, decl:
[self.visit(decl, {'env': with_scope})] + with_scope,
ast.decl,
[]
)
with_scope += env
return self.processStatement(
ast.stmt,
{
'env': with_scope,
'inloop': param['inloop'],
'rettype': param['rettype']
})
def visitIf(self, ast, param):
'''
ast: If
param: {
'env': List[Symbol]
'inloop': Bool
'rettype': Type
}
=> Returned?
'''
expr = self.visit(ast.expr, param['env'])
if not isinstance(expr, BoolType):
raise TypeMismatchInStatement(ast)
thenReturnFlag = False
for stmt in ast.thenStmt:
if thenReturnFlag:
raise UnreachableStatement(ast)
if self.visit(stmt, param):
thenReturnFlag = True
elseReturnFlag = False
for stmt in ast.elseStmt:
if elseReturnFlag:
raise UnreachableStatement(ast)
if self.visit(stmt, param):
elseReturnFlag = True
return thenReturnFlag and elseReturnFlag
def visitFor(self, ast, param):
'''
ast: For
param: {
'env': List[Symbol],
'rettype': Type
}
'''
env = param['env']
idType = self.visit(ast.id, env)
expr1 = self.visit(ast.expr1, env)
expr2 = self.visit(ast.expr2, env)
if not isinstance(idType, IntType) or \
not isinstance(expr1, IntType) or \
not isinstance(expr2, IntType):
raise TypeMismatchInStatement(ast)
return self.loopBody(ast.loop, param)
def visitContinue(self, ast, param):
'''
ast: Continue
param: {
'inloop': Bool
}
'''
if not param['inloop']:
raise ContinueNotInLoop()
return True
def visitBreak(self, ast, param):
'''
ast: Break
param: {
'inloop': Bool
}
'''
if not param['inloop']:
raise BreakNotInLoop()
return True
def visitReturn(self, ast, param):
'''
ast: Return
param: {
'env': List[Symbol]
'rettype': Type
}
'''
rettype = param['rettype']
env = param['env']
if isinstance(rettype, VoidType):
if ast.expr is not None:
raise TypeMismatchInStatement(ast)
else:
if ast.expr is None:
raise TypeMismatchInStatement(ast)
self.checkTypeCompatibility(
rettype, self.visit(ast.expr, env),
TypeMismatchInStatement(ast)
)
return True
def visitWhile(self, ast, param):
'''
ast: While
param: {
'env': List[Symbol],
'inloop': Bool,
'rettype': Type
}
'''
env = param['env']
exp = self.visit(ast.exp, env)
if not isinstance(exp, BoolType):
raise TypeMismatchInStatement(ast)
return self.loopBody(ast.sl, param)
def visitCallStmt(self, ast, param):
'''
ast: CallStmt
param: {
'env': list[Symbol]
}
raise Undeclared(Procedure)
raise TypeMismatchInStatement
wrong param size
wrong param type
Array[n..m] of X --> Array[n..m] of X
Float --> Float/Int
X --> X
=> Returned?
'''
self.callBody(ast, param['env']) # skips return
return False
def visitIntLiteral(self, asttree, param):
return IntType()
def visitFloatLiteral(self, asttree, param):
return FloatType()
def visitBooleanLiteral(self, asttree, param):
return BoolType()
def visitStringLiteral(self, asttree, param):
return StringType()

125
checker/StaticError.py Normal file
View File

@ -0,0 +1,125 @@
from abc import ABC
class Kind(ABC):
pass
class Function(Kind):
def __str__(self):
return "Function"
class Procedure(Kind):
def __str__(self):
return "Procedure"
class Parameter(Kind):
def __str__(self):
return "Parameter"
class Variable(Kind):
def __str__(self):
return "Variable"
class Identifier(Kind):
def __str__(self):
return "Identifier"
class StaticError(Exception):
pass
class Undeclared(StaticError):
"""k: Kind
n: string: name of identifier """
def __init__(self, k, n):
self.k = k
self.n = n
def __str__(self):
return "Undeclared " + str(self.k) + ": " + self.n
class Redeclared(StaticError):
"""k: Kind
n: string: name of identifier """
def __init__(self, k, n):
self.k = k
self.n = n
def __str__(self):
return "Redeclared " + str(self.k) + ": " + self.n
class TypeMismatchInExpression(StaticError):
"""exp: AST.Expr"""
def __init__(self, exp):
self.exp = exp
def __str__(self):
return "Type Mismatch In Expression: " + str(self.exp)
class TypeMismatchInStatement(StaticError):
"""stmt:AST.Stmt"""
def __init__(self, stmt):
self.stmt = stmt
def __str__(self):
return "Type Mismatch In Statement: " + str(self.stmt)
class FunctionNotReturn(StaticError):
"""m is a string that is the name of the function"""
def __init__(self, m):
self.m = m
def __str__(self):
# return "Function " + self.m + "Not Return "
return "Function " + self.m + " Not Return"
class BreakNotInLoop(StaticError):
def __str__(self):
return "Break Not In Loop"
class ContinueNotInLoop(StaticError):
def __str__(self):
return "Continue Not In Loop"
class NoEntryPoint(StaticError):
def __str__(self):
return "No entry point"
class UnreachableStatement(StaticError):
"""stmt is AST.Stmt"""
def __init__(self, stmt):
self.stmt = stmt
def __str__(self):
return "Unreachable statement: " + str(self.stmt)
class Unreachable(StaticError):
"""m is a string that is the name of the unreachable function/procedure"""
def __init__(self, k, m):
self.k = k
self.m = m
def __str__(self):
return "Unreachable " + str(self.k) + ": " + self.m

0
checker/__init__.py Normal file
View File

16
codegen/CodeGenError.py Normal file
View File

@ -0,0 +1,16 @@
class IllegalOperandException(Exception):
def __init__(self, msg):
# msg:string
self.s = msg
def __str__(self):
return "Illegal Operand: " + self.s + "\n"
class IllegalRuntimeException(Exception):
def __init__(self, msg):
# msg:string
self.s = msg
def __str__(self):
return "Illegal Runtime: " + self.s + "\n"

940
codegen/CodeGenerator.py Normal file
View File

@ -0,0 +1,940 @@
from utils.Utils import Utils
from utils.Visitor import BaseVisitor
from checker.StaticCheck import MType, Symbol
from utils.AST import (
# Type,
IntType,
FloatType,
BoolType,
StringType,
# ArrayType,
VoidType,
# Program,
# Decl,
# VarDecl,
FuncDecl,
# Stmt,
Assign,
# If,
While,
# For,
# Break,
# Continue,
# Return,
# With,
# CallStmt,
# Expr,
BinaryOp,
# UnaryOp,
# CallExpr,
# LHS,
Id,
# ArrayCell,
# Literal,
IntLiteral,
# FloatLiteral,
# StringLiteral,
# BooleanLiteral,
ArrayPointerType,
ClassType
)
from codegen.Emitter import Emitter
from codegen.Frame import Frame
from abc import ABC # , abstractmethod
# from functools import reduce
class CodeGenerator(Utils):
def __init__(self):
self.libName = "io"
def init(self):
return [
# from specification, section 7: Built-in Functions/Procedures
Symbol(
"getInt",
MType([], IntType()),
CName(self.libName)
),
Symbol(
"putInt",
MType([IntType()], VoidType()),
CName(self.libName)
),
Symbol(
"putIntLn",
MType([IntType()], VoidType()),
CName(self.libName)
),
Symbol(
"getFloat",
MType([], FloatType()),
CName(self.libName)
),
Symbol(
"putFloat",
MType([FloatType()], VoidType()),
CName(self.libName)
),
Symbol(
"putFloatLn",
MType([FloatType()], VoidType()),
CName(self.libName)
),
Symbol(
"putBool",
MType([BoolType()], VoidType()),
CName(self.libName)
),
Symbol(
"putBoolLn",
MType([BoolType()], VoidType()),
CName(self.libName)
),
Symbol(
"putString",
MType([StringType()], VoidType()),
CName(self.libName)
),
Symbol(
"putStringLn",
MType([StringType()], VoidType()),
CName(self.libName)
),
Symbol(
"putLn",
MType([], VoidType()),
CName(self.libName)
)
]
def gen(self, ast, dir_, name):
# ast: AST
# dir_: String
gl = self.init()
gc = CodeGenVisitor(ast, gl, dir_, name)
gc.visit(ast, None) # visits Program
class SubBody():
def __init__(self, frame, sym):
# frame: Frame
# sym: List[Symbol]
self.frame = frame
self.sym = sym
self.gen = False
self.isFor = False
def __str__(self):
return "SubBody({},{})".format(
str(self.frame),
','.join([str(x) for x in self.sym])
)
class Access():
def __init__(self, frame, sym, **kwargs):
# frame: Frame
# sym: List[Symbol]
# isLeft: Boolean
# isFirst: Boolean
self.frame = frame
self.sym = sym
self.isLeft = kwargs.get('isLeft', False)
self.isFirst = kwargs.get('isFirst', False)
class Val(ABC):
pass
class Index(Val):
def __init__(self, value):
# value: Int
self.value = value
class CName(Val):
def __init__(self, value):
# value: String
self.value = value
class CodeGenVisitor(BaseVisitor, Utils):
def __init__(self, astTree, env, dir_, name):
'''
astTree: AST
env: List[Symbol]
dir_: File
'''
self.astTree = astTree
self.env = env[:] # safety reason
self.className = name
self.path = dir_
self.emit = Emitter(
self.path + "/" + self.className + ".j")
# import sys
# print(astTree, file=sys.stderr)
def genMETHOD(self, consdecl, o, frame):
'''
consdecl: FuncDecl
o: SubBody,
frame: Frame
Submethod to generate code for body,
most of this is given by teacher himself
with slight modifications,
add local variable code generation
'''
isInit = consdecl.returnType is None
isMain = consdecl.name.name == "main" and \
len(consdecl.param) == 0 and \
isinstance(consdecl.returnType, VoidType)
if isInit:
returnType = VoidType()
methodName = '<init>'
else:
returnType = consdecl.returnType
methodName = consdecl.name.name
if isMain:
intype = [ArrayPointerType(StringType())]
else:
intype = [v.varType for v in consdecl.param]
mtype = MType(intype, returnType)
self.emit.printout(
self.emit.emitMETHOD(
methodName,
mtype,
not isInit,
frame
))
frame.enterScope(True)
glenv = o
# Generate code for parameter declarations
if isInit:
self.emit.printout(
self.emit.emitVAR(
frame.getNewIndex(),
"this",
ClassType(self.className),
frame.getStartLabel(),
frame.getEndLabel(),
frame
))
if isMain:
self.emit.printout(
self.emit.emitVAR(
frame.getNewIndex(),
"args",
ArrayPointerType(StringType()),
frame.getStartLabel(),
frame.getEndLabel(),
frame
))
# set up local variables
if glenv is None:
glenv = []
local = []
for p in consdecl.param:
s = self.visit(
p,
SubBody(frame, local + glenv)
)
local = [s] + local
for l in consdecl.local:
s = self.visit(
l,
SubBody(frame, local + glenv)
)
local = [s] + local
glenv = local + glenv
body = consdecl.body
self.emit.printout(
self.emit.emitLABEL(
frame.getStartLabel(),
frame
))
# Generate code for statements
if isInit:
self.emit.printout(
self.emit.emitREADVAR(
"this",
ClassType(self.className),
0,
frame
))
self.emit.printout(
self.emit.emitINVOKESPECIAL(frame)
)
# visit statement
list(map(lambda x:
self.visit(
x,
SubBody(frame, glenv)),
body
))
self.emit.printout(
self.emit.emitLABEL(
frame.getEndLabel(),
frame
))
'''
prevents:
LabelX:
LabelY:
end
'''
self.emit.printout(
self.emit.emitRETURN(
VoidType(),
frame
))
self.emit.printout(
self.emit.emitENDMETHOD(frame)
)
frame.exitScope()
def callBody(self, ast, ctxt):
'''
ast: CallExpr | CallStmt
ctxt: SubBody if CallStmt
Access if CallExpr
=> name, type if CallExpr
CallExpr and CallStmt has similar routine,
Except that CallExpr returns (name, type)
of the function being called
'''
frame = ctxt.frame
sym = ctxt.sym
symbol, ctype = self.visit( # visits Id
ast.method,
Access(frame, sym, isFirst=True)
)
cname = symbol.value
params = ctype.partype
in_ = ("", list())
for i, arg in enumerate(ast.param):
str1, typ1 = self.visit(
arg,
Access(frame, sym, isFirst=True)
)
if isinstance(typ1, Symbol):
typ1 = typ1.mtype.rettype
if not isinstance(typ1, type(params[i])):
# foo(float) ==> foo(int)
# others: Checker catches
str1 += self.emit.emitI2F(frame)
in_ = (in_[0] + str1, in_[1] + [typ1])
# funcname = cname.value + '/' + ast.method.name
funcname = cname.value + '/' + symbol.name
code = ''
code += in_[0]
code += self.emit.emitINVOKESTATIC(
funcname,
ctype,
frame
)
return code, symbol
def visitProgram(self, ast, c):
'''
ast: Program
c: Unknown
=> c
'''
self.emit.printout(
self.emit.emitPROLOG(
self.className,
"java.lang.Object"
))
e = SubBody(None, self.env)
# generate global declare list
for x in ast.decl:
# returns Symbol on no Gen
self.env += [self.visit(x, e)]
e.gen = True
for x in ast.decl:
# recall that e = SubBody(None, self.env)
# and e.sym = self.env as reference
self.visit(x, e)
# generate default constructor
self.genMETHOD(
FuncDecl(
Id("<init>"),
list(),
list(),
list(),
None
),
c,
Frame("<init>", VoidType)
)
self.emit.emitEPILOG()
return c
def visitFuncDecl(self, ast, subbody):
'''
ast: FuncDecl
subbody: SubBody
=> None if generate code
=> else Symbol
'''
if subbody.gen:
# on gen
frame = Frame(ast.name, ast.returnType)
self.genMETHOD(
ast,
subbody.sym,
frame
)
else:
# on first scan to get global Symbol
params = [v.varType for v in ast.param]
return Symbol(
ast.name.name,
MType(params, ast.returnType),
CName(self.className)
)
def visitVarDecl(self, ast, subbody):
'''
ast: VarDecl
subbody: SubBody
=> Symbol(,,CName) for global VarDecl
=> Symbol(,,Index) for local VarDecl
'''
if subbody.gen:
return
if subbody.frame is None:
# generate code for global var decl
# returns Symbol with CName
code = self.emit.emitATTRIBUTE(
ast.variable.name,
ast.varType,
False,
''
)
self.emit.printout(code)
return Symbol(
ast.variable.name,
ast.varType,
CName(self.className)
)
else:
# generate code for local var decl
# returns Symbol with Index
frame = subbody.frame
idx = frame.getNewIndex()
code = self.emit.emitVAR(
idx,
ast.variable.name,
ast.varType,
frame.getStartLabel(),
frame.getEndLabel(),
frame
)
self.emit.printout(code)
return Symbol(
ast.variable.name,
ast.varType,
Index(idx)
)
def visitBinaryOp(self, ast, param):
'''
ast: BinaryOp
param: Access
=> code, type
Rules:
(/) --> Float/Int:Float/Int => Float
(+,-,*) --> Float:Float/Int => Float
--> Float/Int:Float => Float
--> Int:Int => Int
(div,mod) --> Int:Int => Int
(<,<=,=,>=,>,<>) --> Float:Float/Int => Bool
--> Float/Int:Float => Bool
--> Int:Int => Bool
(and,or,andthen,orelse) --> Bool:Bool => Bool
'''
frame = param.frame
sym = param.sym
lhs, ltype = self.visit(
ast.left,
Access(frame, sym)
)
rhs, rtype = self.visit(
ast.right,
Access(frame, sym)
)
if isinstance(ltype, MType):
ltype = ltype.rettype
if isinstance(rtype, MType):
rtype = rtype.rettype
if (isinstance(ltype, type(rtype))):
# because idiv returns int not float
# while MP / returns float on all cases
if ast.op == '/' and\
isinstance(ltype, IntType):
lhs += self.emit.emitI2F(frame)
rhs += self.emit.emitI2F(frame)
intype = FloatType()
rettype = FloatType()
else:
intype = ltype
rettype = ltype
else:
# only Float:Int or Int:Float
# I2F on Int
if isinstance(ltype, IntType):
lhs += self.emit.emitI2F(frame)
if isinstance(rtype, IntType):
rhs += self.emit.emitI2F(frame)
intype = FloatType()
rettype = FloatType()
code = ''
code += lhs
code += rhs
# self.emit.printout(lhs) # push left
# self.emit.printout(rhs) # push right
# generate code for each operator
if ast.op in ('+', '-'):
code += self.emit.emitADDOP(
ast.op,
intype,
frame
)
elif ast.op in ('*', '/'):
code += self.emit.emitMULOP(
ast.op,
intype,
frame
)
elif ast.op in ('<', '<=', '=', '>=', '>', '<>'):
code += self.emit.emitREOP(
ast.op,
intype,
frame
)
rettype = BoolType()
elif ast.op.lower() == 'div':
code += self.emit.emitDIV(frame)
elif ast.op.lower() == 'mod':
code += self.emit.emitMOD(frame)
elif ast.op.lower() == 'and':
code += self.emit.emitANDOP(frame)
elif ast.op.lower() == 'or':
code += self.emit.emitOROP(frame)
elif ast.op.lower() == 'andthen':
code = ''
falseLabel = frame.getNewLabel()
endLabel = frame.getNewLabel()
code += lhs
code += self.emit.emitIFEQ(falseLabel, frame)
code += rhs
code += self.emit.emitIFEQ(falseLabel, frame)
code += self.emit.emitPUSHICONST(1, frame)
code += self.emit.emitGOTO(endLabel, frame)
code += self.emit.emitLABEL(falseLabel, frame)
code += self.emit.emitPUSHICONST(0, frame)
code += self.emit.emitLABEL(endLabel, frame)
rettype = BoolType()
elif ast.op.lower() == 'orelse':
code = ''
trueLabel = frame.getNewLabel()
endLabel = frame.getNewLabel()
code += lhs
# if lhs != 0 True
code += self.emit.emitIFNE(trueLabel, frame)
code += rhs
# if rhs != 0 True
code += self.emit.emitIFNE(trueLabel, frame)
code += self.emit.emitPUSHICONST(0, frame)
code += self.emit.emitGOTO(endLabel, frame)
code += self.emit.emitLABEL(trueLabel, frame)
code += self.emit.emitPUSHICONST(1, frame)
code += self.emit.emitLABEL(endLabel, frame)
rettype = BoolType()
return code, rettype
def visitUnaryOp(self, ast, param):
frame = param.frame
sym = param.sym
bodycode, bodytype = self.visit(
ast.body,
Access(frame, sym)
)
code = ''
code += bodycode
if ast.op.lower() == 'not':
code += self.emit.emitNOT(bodytype, frame)
elif ast.op.lower() == '-':
code += self.emit.emitNEGOP(bodytype, frame)
return code, bodytype
def visitCallExpr(self, ast, o):
'''
ast: CallExpr
o: Access
=> (name, type) of function being called
'''
return self.callBody(ast, o)
def visitId(self, ast, param):
'''
ast: Id
param: Access
'''
res = self.lookup(
ast.name,
param.sym,
lambda env: env.name
)
# not likely, checker catches
if res is None:
return None, None
# function call
if isinstance(res.mtype, MType):
return res, res.mtype
frame = param.frame
# global variable
# res: Symbol(,,CName)
if isinstance(res.value, CName):
varname = res.value.value + '/' + res.name
if param.isLeft:
code = self.emit.emitPUTSTATIC(
varname,
res.mtype,
frame
)
else:
code = self.emit.emitGETSTATIC(
varname,
res.mtype,
frame
)
return code, res.mtype
# Local variable
if param.isLeft:
code = self.emit.emitWRITEVAR(
res.name,
res.mtype,
res.value.value,
frame
)
else:
code = self.emit.emitREADVAR(
res.name,
res.mtype,
res.value.value,
frame
)
return code, res.mtype
def visitArrayCell(self, ast, param):
'''
ast: ArrayCell,
param: Access
'''
frame = param.frame
sym = param.sym
arrcode, arrtype = self.visit(
ast.arr,
Access(frame, sym)
)
idxcode, idxtype = self.visit(
ast.idx,
Access(frame, sym)
)
return None
def visitAssign(self, ast, param):
'''
ast: Assign
param: SubBody
'''
frame = param.frame
sym = param.sym
expcode, exptype = self.visit(
ast.exp,
Access(frame, sym)
)
lhscode, lhstype = self.visit(
ast.lhs,
Access(frame, sym, isLeft=True)
)
if isinstance(exptype, Symbol):
exptype = exptype.mtype.rettype
if not isinstance(exptype, type(lhstype)):
# float = int
# others => checker catches
expcode += self.emit.emitI2F(param.frame)
code = ''
code += expcode + lhscode
self.emit.printout(code)
def visitWith(self, ast, param):
'''
ast: With
param: SubBody
'''
frame = param.frame
sym = param.sym
frame.enterScope(False)
startLabel = self.emit.emitLABEL(frame.getStartLabel(), frame)
endLabel = self.emit.emitLABEL(frame.getEndLabel(), frame)
local = sym[:]
for var in ast.decl:
local = [self.visit(var, SubBody(frame, local))] + local
self.emit.printout(startLabel)
for stmt in ast.stmt:
self.visit(stmt, SubBody(frame, local))
self.emit.printout(endLabel)
frame.exitScope()
def visitIf(self, ast, param):
'''
ast: If
param: SubBody
'''
frame = param.frame
sym = param.sym
haveElse = True if len(ast.elseStmt) > 0 \
else False
code = ''
expcode, exptype = self.visit(
ast.expr, Access(frame, sym)
)
code += expcode
elseLabel = frame.getNewLabel()
if haveElse:
endLabel = frame.getNewLabel()
code += self.emit.emitIFEQ(elseLabel, frame)
self.emit.printout(code)
code = ''
# printout then code
for stmt in ast.thenStmt:
self.visit(stmt, param)
if haveElse:
code += self.emit.emitGOTO(endLabel, frame)
code += self.emit.emitLABEL(elseLabel, frame)
self.emit.printout(code)
code = ''
if not haveElse:
return
# printout else code
for stmt in ast.elseStmt:
self.visit(stmt, param)
code += self.emit.emitLABEL(endLabel, frame)
self.emit.printout(code)
def visitFor(self, ast, param):
conditionOp = '<=' if ast.up else '>='
continueOp = '+' if ast.up else '-'
loopCondition = BinaryOp(conditionOp, ast.id, ast.expr2)
continueInstruction = Assign(
ast.id, BinaryOp(
continueOp, ast.id, IntLiteral(1)))
loop = ast.loop[:] + [continueInstruction]
param.isFor = True
self.visit(Assign(ast.id, ast.expr1), param)
self.visit(While(loopCondition, loop), param)
def visitContinue(self, ast, param):
frame = param.frame
continueLabel = frame.getContinueLabel()
code = self.emit.emitGOTO(continueLabel, frame)
self.emit.printout(code)
def visitBreak(self, ast, param):
frame = param.frame
breakLabel = frame.getBreakLabel()
code = self.emit.emitGOTO(breakLabel, frame)
self.emit.printout("\t; Break\n")
self.emit.printout(code)
def visitReturn(self, ast, param):
frame = param.frame
sym = param.sym
rettype = frame.returnType
if ast.expr:
expcode, exptype = self.visit(
ast.expr,
Access(frame, sym)
)
else:
expcode, exptype = '', VoidType()
code = ''
code += expcode
if not isinstance(exptype, type(rettype)):
# return int in float function
# others -> Checker catches
code += self.emit.emitI2F(frame)
code += self.emit.emitRETURN(rettype, frame)
self.emit.printout(code)
def visitWhile(self, ast, param):
'''
ast: While
param: SubBody
'''
frame = param.frame
sym = param.sym
isFor = param.isFor
param.isFor = False
code = ''
frame.enterLoop()
breakLabel = frame.getBreakLabel()
continueLabel = frame.getContinueLabel()
expcode, exptype = self.visit(
ast.exp,
Access(frame, sym)
)
if isFor:
continueInstruction = ast.sl[-1]
ast.sl = ast.sl[:-1]
code += expcode
code += self.emit.emitIFEQ(breakLabel, frame)
bodyLabel = frame.getNewLabel()
code += '; body\n'
code += self.emit.emitLABEL(bodyLabel, frame)
self.emit.printout(code)
code = ''
for stmt in ast.sl:
self.visit(stmt, param)
code += '; continue\n'
code += self.emit.emitLABEL(continueLabel, frame)
if isFor:
self.emit.printout(code)
code = ''
self.visit(continueInstruction, param)
code += expcode
code += self.emit.emitIFNE(bodyLabel, frame)
else:
code += expcode
code += self.emit.emitIFNE(bodyLabel, frame)
code += '; break\n'
code += self.emit.emitLABEL(breakLabel, frame)
self.emit.printout(code)
frame.exitLoop()
def visitCallStmt(self, ast, o):
'''
ast: CallStmt
o: SubBody
'''
code, symbol = self.callBody(ast, o)
self.emit.printout(code)
def visitIntLiteral(self, ast, param):
'''
ast: IntLiteral
param: Access
=> code, type
'''
frame = param.frame
return self.emit.emitPUSHICONST(
ast.value,
frame
), IntType()
def visitFloatLiteral(self, ast, param):
'''
ast: FloatLiteral
param: Access
=> code, type
'''
frame = param.frame
return self.emit.emitPUSHFCONST(
str(ast.value),
frame
), FloatType()
def visitBooleanLiteral(self, ast, param):
frame = param.frame
return self.emit.emitPUSHICONST(
str(ast.value).lower(),
frame
), BoolType()
def visitStringLiteral(self, ast, param):
frame = param.frame
return self.emit.emitPUSHCONST(
# emitLDC doesn't add "
'"{}"'.format(ast.value),
StringType(),
frame
), StringType()

784
codegen/Emitter.py Normal file
View File

@ -0,0 +1,784 @@
from codegen.CodeGenError import (
# Utils,
# IllegalEscape,
IllegalOperandException,
# IllegalRuntimeException
)
# from Utils import *
from checker.StaticCheck import MType # , Symbol
# from StaticError import (
# # Kind,
# Function,
# Procedure,
# Variable,
# Parameter,
# Identifier,
# # StaticError,
# Undeclared,
# Redeclared,
# TypeMismatchInExpression,
# TypeMismatchInStatement,
# FunctionNotReturn,
# BreakNotInLoop,
# ContinueNotInLoop,
# NoEntryPoint,
# UnreachableStatement,
# Unreachable
# )
from utils.AST import (
IntType,
FloatType,
BoolType,
StringType,
# ArrayType,
VoidType,
# Program,
# Decl,
# VarDecl,
# FuncDecl,
# Stmt,
# Assign,
# If,
# While,
# For,
# Break,
# Continue,
# Return,
# With,
# CallStmt,
# Expr,
# BinaryOp,
# UnaryOp,
# CallExpr,
# LHS,
# Id,
# ArrayCell,
# Literal,
IntLiteral,
# FloatLiteral,
# StringLiteral,
# BooleanLiteral,
ArrayPointerType,
ClassType
)
# from codegen.CodeGenerator import ArrayPointerType, ClassType
from codegen.MachineCode import JasminCode
class Emitter():
def __init__(self, filename):
self.filename = filename
self.buff = list()
self.jvm = JasminCode()
def getJVMType(self, inType):
'''
https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.3.2
'''
typeIn = type(inType)
if typeIn is IntType:
return "I"
elif typeIn is FloatType:
return "F"
elif typeIn is BoolType:
return "Z"
elif typeIn is StringType:
return "Ljava/lang/String;"
elif typeIn is VoidType:
return "V"
elif typeIn is ArrayPointerType:
return "[" + self.getJVMType(inType.eleType)
elif typeIn is MType:
return "({}){}".format(
"".join(list(map(lambda x: self.getJVMType(x),
inType.partype))),
self.getJVMType(inType.rettype)
)
# return "(" + "".join(list(map(lambda x: self.getJVMType(x),
# inType.partype))) + ")" + self.getJVMType(inType.rettype)
elif typeIn is ClassType:
return "L" + inType.cname + ";"
def getFullType(inType):
typeIn = type(inType)
if typeIn is IntType:
return "int"
elif typeIn is StringType:
return "java/lang/String"
elif typeIn is VoidType:
return "void"
def emitPUSHICONST(self, in_, frame):
# in: Int or Sring
# frame: Frame
frame.push()
if isinstance(in_, int):
i = in_
if i >= -1 and i <= 5:
return self.jvm.emitICONST(i)
elif i >= -128 and i <= 127:
return self.jvm.emitBIPUSH(i)
elif i >= -32768 and i <= 32767:
return self.jvm.emitSIPUSH(i)
elif isinstance(in_, str):
if in_ == "true":
return self.emitPUSHICONST(1, frame)
elif in_ == "false":
return self.emitPUSHICONST(0, frame)
else:
return self.emitPUSHICONST(int(in_), frame)
def emitPUSHFCONST(self, in_, frame):
# in_: String
# frame: Frame
f = float(in_)
frame.push()
rst = "{0:.4f}".format(f)
if rst == "0.0" or rst == "1.0" or rst == "2.0":
return self.jvm.emitFCONST(rst)
else:
return self.jvm.emitLDC(in_)
'''
* generate code to push a constant onto the operand stack.
* @param in the lexeme of the constant
* @param typ the type of the constant
'''
def emitPUSHCONST(self, in_, typ, frame):
# in_: String
# typ: Type
# frame: Frame
if isinstance(typ, IntType):
return self.emitPUSHICONST(in_, frame)
elif isinstance(typ, BoolType):
return self.emitPUSHICONST(in_, frame)
elif isinstance(typ, StringType):
frame.push()
return self.jvm.emitLDC(in_)
else:
raise IllegalOperandException(in_)
###############################################
def emitALOAD(self, in_, frame):
# in_: Type
# frame: Frame
# ..., arrayref, index, value -> ...
frame.pop()
if isinstance(in_, IntType):
return self.jvm.emitIALOAD()
elif isinstance(in_, ArrayPointerType) or\
isinstance(in_, ClassType) or\
isinstance(in_, StringType):
return self.jvm.emitAALOAD()
else:
raise IllegalOperandException(str(in_))
def emitASTORE(self, in_, frame):
# in_: Type
# frame: Frame
# ..., arrayref, index, value -> ...
frame.pop()
frame.pop()
frame.pop()
if isinstance(in_, IntType):
return self.jvm.emitIASTORE()
elif isinstance(in_, ArrayPointerType) or\
isinstance(in_, ClassType) or\
isinstance(in_, StringType):
return self.jvm.emitAASTORE()
else:
raise IllegalOperandException(str(in_))
''' generate the var directive for a local variable.
* @param in the index of the local variable.
* @param varName the name of the local variable.
* @param inType the type of the local variable.
* @param fromLabel the starting label of the scope
* where the variable is active.
* @param toLabel the ending label of the scope
* where the variable is active.
'''
def emitVAR(self, in_, varName, inType, fromLabel, toLabel, frame):
# in_: Int
# varName: String
# inType: Type
# fromLabel: Int
# toLabel: Int
# frame: Frame
return self.jvm.emitVAR(
in_,
varName,
self.getJVMType(inType),
fromLabel,
toLabel)
def emitREADVAR(self, name, inType, index, frame):
# name: String
# inType: Type
# index: Int
# frame: Frame
# ... -> ..., value
frame.push()
if isinstance(inType, (IntType, BoolType)):
return self.jvm.emitILOAD(index)
elif isinstance(inType, FloatType):
return self.jvm.emitFLOAD(index)
elif isinstance(inType, ArrayPointerType) or\
isinstance(inType, ClassType) or\
isinstance(inType, StringType):
return self.jvm.emitALOAD(index)
else:
raise IllegalOperandException(name)
''' generate the second instruction for array cell access
*
'''
def emitREADVAR2(self, name, typ, frame):
# name: String
# typ: Type
# frame: Frame
# ... -> ..., value
# frame.push()
raise IllegalOperandException(name)
'''
* generate code to pop a value on top of the operand stack
* and store it to a block-scoped variable.
* @param name the symbol entry of the variable.
'''
def emitWRITEVAR(self, name, inType, index, frame):
# name: String
# inType: Type
# index: Int
# frame: Frame
# ..., value -> ...
frame.pop()
if isinstance(inType, (IntType, BoolType)):
return self.jvm.emitISTORE(index)
elif isinstance(inType, FloatType):
return self.jvm.emitFSTORE(index)
elif isinstance(inType, ArrayPointerType) or\
isinstance(inType, ClassType) or\
isinstance(inType, StringType):
return self.jvm.emitASTORE(index)
else:
raise IllegalOperandException(name)
''' generate the second instruction for array cell access
*
'''
def emitWRITEVAR2(self, name, typ, frame):
# name: String
# typ: Type
# frame: Frame
# ..., value -> ...
# frame.push()
raise IllegalOperandException(name)
''' generate the field (static) directive for a class mutable or
* immutable attribute.
* @param lexeme the name of the attribute.
* @param in the type of the attribute.
* @param isFinal true in case of constant; false otherwise
'''
def emitATTRIBUTE(self, lexeme, in_, isFinal, value):
# lexeme: String
# in_: Type
# isFinal: Boolean
# value: String
return self.jvm.emitSTATICFIELD(lexeme, self.getJVMType(in_), False)
def emitGETSTATIC(self, lexeme, in_, frame):
# lexeme: String
# in_: Type
# frame: Frame
frame.push()
return self.jvm.emitGETSTATIC(lexeme, self.getJVMType(in_))
def emitPUTSTATIC(self, lexeme, in_, frame):
# lexeme: String
# in_: Type
# frame: Frame
frame.pop()
return self.jvm.emitPUTSTATIC(lexeme, self.getJVMType(in_))
def emitGETFIELD(self, lexeme, in_, frame):
# lexeme: String
# in_: Type
# frame: Frame
return self.jvm.emitGETFIELD(lexeme, self.getJVMType(in_))
def emitPUTFIELD(self, lexeme, in_, frame):
# lexeme: String
# in_: Type
# frame: Frame
frame.pop()
frame.pop()
return self.jvm.emitPUTFIELD(lexeme, self.getJVMType(in_))
''' generate code to invoke a static method
* @param lexeme the qualified name of the method
* (i.e., class-name/method-name)
* @param in the type descriptor of the method.
'''
def emitINVOKESTATIC(self, lexeme, in_, frame):
# lexeme: String
# in_: Type
# frame: Frame
typ = in_
list(map(lambda x: frame.pop(), typ.partype))
if not isinstance(typ.rettype, VoidType):
frame.push()
return self.jvm.emitINVOKESTATIC(lexeme, self.getJVMType(in_))
''' generate code to invoke a special method
* @param lexeme the qualified name of the method
* (i.e., class-name/method-name)
* @param in the type descriptor of the method.
'''
def emitINVOKESPECIAL(self, frame, lexeme=None, in_=None):
# lexeme: String
# in_: Type
# frame: Frame
if lexeme is not None and in_ is not None:
typ = in_
list(map(lambda x: frame.pop(), typ.partype))
frame.pop()
if not isinstance(typ.rettype, VoidType):
frame.push()
return self.jvm.emitINVOKESPECIAL(lexeme, self.getJVMType(in_))
elif lexeme is None and in_ is None:
frame.pop()
return self.jvm.emitINVOKESPECIAL()
''' generate code to invoke a virtual method
* @param lexeme the qualified name of the method
* (i.e., class-name/method-name)
* @param in the type descriptor of the method.
'''
def emitINVOKEVIRTUAL(self, lexeme, in_, frame):
# lexeme: String
# in_: Type
# frame: Frame
typ = in_
list(map(lambda x: frame.pop(), typ.partype))
frame.pop()
if not isinstance(typ, VoidType):
frame.push()
return self.jvm.emitINVOKEVIRTUAL(lexeme, self.getJVMType(in_))
'''
* generate ineg, fneg.
* @param in the type of the operands.
'''
def emitNEGOP(self, in_, frame):
# in_: Type
# frame: Frame
# ..., value -> ..., result
if isinstance(in_, IntType):
return self.jvm.emitINEG()
else:
return self.jvm.emitFNEG()
def emitNOT(self, in_, frame):
# in_: Type
# frame: Frame
label1 = frame.getNewLabel()
label2 = frame.getNewLabel()
result = list()
result.append(self.emitIFTRUE(label1, frame))
result.append(self.emitPUSHCONST("true", in_, frame))
result.append(self.emitGOTO(label2, frame))
result.append(self.emitLABEL(label1, frame))
result.append(self.emitPUSHCONST("false", in_, frame))
result.append(self.emitLABEL(label2, frame))
return ''.join(result)
'''
* generate iadd, isub, fadd or fsub.
* @param lexeme the lexeme of the operator.
* @param in the type of the operands.
'''
def emitADDOP(self, lexeme, in_, frame):
# lexeme: String
# in_: Type
# frame: Frame
# ..., value1, value2 -> ..., result
frame.pop()
if lexeme == "+":
if isinstance(in_, IntType):
return self.jvm.emitIADD()
else:
return self.jvm.emitFADD()
else:
if isinstance(in_, IntType):
return self.jvm.emitISUB()
else:
return self.jvm.emitFSUB()
'''
* generate imul, idiv, fmul or fdiv.
* @param lexeme the lexeme of the operator.
* @param in the type of the operands.
'''
def emitMULOP(self, lexeme, in_, frame):
# lexeme: String
# in_: Type
# frame: Frame
# ..., value1, value2 -> ..., result
frame.pop()
if lexeme == "*":
if isinstance(in_, IntType):
return self.jvm.emitIMUL()
else:
return self.jvm.emitFMUL()
else:
if isinstance(in_, IntType):
return self.jvm.emitIDIV()
else:
return self.jvm.emitFDIV()
def emitDIV(self, frame):
# frame: Frame
frame.pop()
return self.jvm.emitIDIV()
def emitMOD(self, frame):
# frame: Frame
frame.pop()
return self.jvm.emitIREM()
'''
* generate iand
'''
def emitANDOP(self, frame):
# frame: Frame
frame.pop()
return self.jvm.emitIAND()
'''
* generate ior
'''
def emitOROP(self, frame):
# frame: Frame
frame.pop()
return self.jvm.emitIOR()
def emitREOP(self, op, in_, frame):
# op: String
# in_: Type
# frame: Frame
# ..., value1, value2 -> ..., result
result = list()
labelF = frame.getNewLabel()
labelO = frame.getNewLabel()
frame.pop()
frame.pop()
if isinstance(in_, FloatType):
frame.push()
result.append(self.jvm.emitFCMPL())
result.append(self.emitPUSHICONST(0, frame))
frame.pop()
if op == ">":
result.append(self.jvm.emitIFICMPLE(labelF))
elif op == ">=":
result.append(self.jvm.emitIFICMPLT(labelF))
elif op == "<":
result.append(self.jvm.emitIFICMPGE(labelF))
elif op == "<=":
result.append(self.jvm.emitIFICMPGT(labelF))
elif op == "<>":
result.append(self.jvm.emitIFICMPEQ(labelF))
elif op == "=":
result.append(self.jvm.emitIFICMPNE(labelF))
result.append(self.emitPUSHCONST("1", IntType(), frame))
frame.pop()
result.append(self.emitGOTO(labelO, frame))
result.append(self.emitLABEL(labelF, frame))
result.append(self.emitPUSHCONST("0", IntType(), frame))
result.append(self.emitLABEL(labelO, frame))
return ''.join(result)
def emitRELOP(self, op, in_, trueLabel, falseLabel, frame):
# op: String
# in_: Type
# trueLabel: Int
# falseLabel: Int
# frame: Frame
# ..., value1, value2 -> ..., result
result = list()
frame.pop()
frame.pop()
if op == ">":
result.append(self.jvm.emitIFICMPLE(falseLabel))
result.append(self.emitGOTO(trueLabel))
elif op == ">=":
result.append(self.jvm.emitIFICMPLT(falseLabel))
elif op == "<":
result.append(self.jvm.emitIFICMPGE(falseLabel))
elif op == "<=":
result.append(self.jvm.emitIFICMPGT(falseLabel))
elif op == "!=":
result.append(self.jvm.emitIFICMPEQ(falseLabel))
elif op == "==":
result.append(self.jvm.emitIFICMPNE(falseLabel))
result.append(self.jvm.emitGOTO(trueLabel))
return ''.join(result)
''' generate the method directive for a function.
* @param lexeme the qualified name of the method
* (i.e., class-name/method-name).
* @param in the type descriptor of the method.
* @param isStatic <code>true</code> if the method is static;
* <code>false</code> otherwise.
'''
def emitMETHOD(self, lexeme, in_, isStatic, frame):
# lexeme: String
# in_: Type
# isStatic: Boolean
# frame: Frame
return self.jvm.emitMETHOD(lexeme, self.getJVMType(in_), isStatic)
''' generate the end directive for a function.
'''
def emitENDMETHOD(self, frame):
# frame: Frame
buffer = list()
buffer.append(self.jvm.emitLIMITSTACK(frame.getMaxOpStackSize()))
buffer.append(self.jvm.emitLIMITLOCAL(frame.getMaxIndex()))
buffer.append(self.jvm.emitENDMETHOD())
return ''.join(buffer)
def getConst(self, ast):
# ast: Literal
if isinstance(ast, IntLiteral):
return (str(ast.value), IntType())
''' generate code to initialize a local array variable.<p>
* @param index the index of the local variable.
* @param in the type of the local array variable.
'''
''' generate code to initialize local array variables.
* @param in the list of symbol entries corresponding to
* local array variable.
'''
def emitIFEQ(self, label, frame):
frame.pop()
return self.jvm.emitIFEQ(label)
def emitIFNE(self, label, frame):
frame.pop()
return self.jvm.emitIFNE(label)
''' generate code to jump to label if the value on top of
* operand stack is true.<p>
* ifgt label
* @param label the label where the execution continues if
* the value on top of stack is true.
'''
def emitIFTRUE(self, label, frame):
# label: Int
# frame: Frame
frame.pop()
return self.jvm.emitIFGT(label)
'''
* generate code to jump to label if the value on top of
* operand stack is false.<p>
* ifle label
* @param label the label where the execution continues if
* the value on top of stack is false.
'''
def emitIFFALSE(self, label, frame):
# label: Int
# frame: Frame
frame.pop()
return self.jvm.emitIFLE(label)
def emitIFICMPGT(self, label, frame):
# label: Int
# frame: Frame
frame.pop()
return self.jvm.emitIFICMPGT(label)
def emitIFICMPLT(self, label, frame):
# label: Int
# frame: Frame
frame.pop()
return self.jvm.emitIFICMPLT(label)
''' generate code to duplicate the value on the top of the operand stack.<p>
* Stack:<p>
* Before: ...,value1<p>
* After: ...,value1,value1<p>
'''
def emitDUP(self, frame):
# frame: Frame
frame.push()
return self.jvm.emitDUP()
def emitPOP(self, frame):
# frame: Frame
frame.pop()
return self.jvm.emitPOP()
''' generate code to exchange an integer on top of
stack to a floating-point number.
'''
def emitI2F(self, frame):
# frame: Frame
return self.jvm.emitI2F()
''' generate code to return.
* <ul>
* <li>ireturn if the type is IntegerType or BooleanType
* <li>freturn if the type is RealType
* <li>return if the type is null
* </ul>
* @param in the type of the returned expression.
'''
def emitRETURN(self, in_, frame):
# in_: Type
# frame: Frame
if isinstance(in_, (IntType, BoolType)):
frame.pop()
return self.jvm.emitIRETURN()
elif isinstance(in_, FloatType):
frame.pop()
return self.jvm.emitFRETURN()
elif isinstance(in_, VoidType):
return self.jvm.emitRETURN()
''' generate code that represents a label
* @param label the label
* @return code Label<label>:
'''
def emitLABEL(self, label, frame):
# label: Int
# frame: Frame
return self.jvm.emitLABEL(label)
''' generate code to jump to a label
* @param label the label
* @return code goto Label<label>
'''
def emitGOTO(self, label, frame):
# label: Int
# frame: Frame
return self.jvm.emitGOTO(label)
''' generate some starting directives for a class.<p>
* .source MPC.CLASSNAME.java<p>
* .class public MPC.CLASSNAME<p>
* .super java/lang/Object<p>
'''
def emitPROLOG(self, name, parent):
# name: String
# parent: String
result = list()
result.append(self.jvm.emitSOURCE(name + ".java"))
result.append(self.jvm.emitCLASS("public " + name))
result.append(
self.jvm.emitSUPER(
"java/land/Object" if parent == "" else parent))
return ''.join(result)
def emitLIMITSTACK(self, num):
# num: Int
return self.jvm.emitLIMITSTACK(num)
def emitLIMITLOCAL(self, num):
# num: Int
return self.jvm.emitLIMITLOCAL(num)
def emitEPILOG(self):
file = open(self.filename, "w")
file.write(''.join(self.buff))
file.close()
''' print out the code to screen
* @param in the code to be printed out
'''
def printout(self, in_):
# in_: String
self.buff.append(in_)
def clearBuff(self):
self.buff.clear()

206
codegen/Frame.py Normal file
View File

@ -0,0 +1,206 @@
# from Utils import (
# # IllegalEscape,
# # IllegalOperandException,
# # IllegalRuntimeException
# )
from codegen.CodeGenError import (
# IllegalOperandException,
IllegalRuntimeException
)
class Frame():
def __init__(self, name, returnType):
# name: String
# returnType: Type
self.name = name
self.returnType = returnType
self.currentLabel = 0
self.currOpStackSize = 0
self.maxOpStackSize = 0
self.currIndex = 0
self.maxIndex = 0
self.startLabel = list()
self.endLabel = list()
self.indexLocal = list()
self.conLabel = list()
self.brkLabel = list()
def getCurrIndex(self):
return self.currIndex
def setCurrIndex(self, index):
# index: Int
self.currIndex = index
'''
* return a new label in the method.
* @return an integer representing the label.
'''
def getNewLabel(self):
tmp = self.currentLabel
self.currentLabel = self.currentLabel + 1
return tmp
'''
* simulate an instruction that pushes a value onto operand stack.
'''
def push(self):
self.currOpStackSize = self.currOpStackSize + 1
if self.maxOpStackSize < self.currOpStackSize:
self.maxOpStackSize = self.currOpStackSize
'''
* simulate an instruction that pops a value out of operand stack.
'''
def pop(self):
self.currOpStackSize = self.currOpStackSize - 1
if self.currOpStackSize < 0:
raise IllegalRuntimeException("Pop empty stack")
def getStackSize(self):
return self.currOpStackSize
'''
* return the maximum size of the operand stack that
* the method needs to use.
* @return an integer that represent the maximum stack size
'''
def getMaxOpStackSize(self):
return self.maxOpStackSize
'''
* check if the operand stack is empty or not.
* @throws IllegalRuntimeException if the operand stack is not empty.
'''
def checkOpStack(self):
if self.currOpStackSize != 0:
raise IllegalRuntimeException("Stack not empty")
'''
* invoked when parsing into a new scope inside a method.<p>
* This method will create 2 new labels that represent the starting
* and ending points of the scope.<p>
* Then, these labels are pushed onto corresponding stacks.<p>
* These labels can be retrieved by getStartLabel() and getEndLabel().<p>
* In addition, this method also saves the current index of local variable
'''
def enterScope(self, isProc):
# isProc: Boolean
start = self.getNewLabel()
end = self.getNewLabel()
self.startLabel.append(start)
self.endLabel.append(end)
self.indexLocal.append(self.currIndex)
if isProc:
self.maxOpStackSize = 0
self.maxIndex = 0
'''
* invoked when parsing out of a scope in a method.<p>
* This method will pop the starting and ending labels of this scope
* and restore the current index
'''
def exitScope(self):
if not self.startLabel or not self.endLabel or not self.indexLocal:
raise IllegalRuntimeException("Error when exit scope")
self.startLabel.pop()
self.endLabel.pop()
self.currIndex = self.indexLocal.pop()
'''
* return the starting label of the current scope.
* @return an integer representing the starting label
'''
def getStartLabel(self):
if not self.startLabel:
raise IllegalRuntimeException("None start label")
return self.startLabel[-1]
'''
* return the ending label of the current scope.
* @return an integer representing the ending label
'''
def getEndLabel(self):
if not self.endLabel:
raise IllegalRuntimeException("None end label")
return self.endLabel[-1]
'''
* return a new index for a local variable declared in a scope.
* @return an integer that represents the index of the local variable
'''
def getNewIndex(self):
tmp = self.currIndex
self.currIndex = self.currIndex + 1
if self.currIndex > self.maxIndex:
self.maxIndex = self.currIndex
return tmp
'''
* return the maximum index used in generating code for the current method
* @return an integer representing the maximum index
'''
def getMaxIndex(self):
return self.maxIndex
'''
* invoked when parsing into a loop statement.<p>
* This method creates 2 new labels that represent the starting and
* ending label of the loop.<p>
* These labels are pushed onto corresponding stacks and are
* retrieved by getBeginLoopLabel() and getEndLoopLabel().
'''
def enterLoop(self):
con = self.getNewLabel()
brk = self.getNewLabel()
self.conLabel.append(con)
self.brkLabel.append(brk)
'''
* invoked when parsing out of a loop statement.
* This method will take 2 labels representing the starting and
* ending labels of the current loop out of its stacks.
'''
def exitLoop(self):
if not self.conLabel or not self.brkLabel:
raise IllegalRuntimeException("Error when exit loop")
self.conLabel.pop()
self.brkLabel.pop()
'''
* return the label of the innest enclosing loop to which continue
* statement would jump
* @return an integer representing the continue label
'''
def getContinueLabel(self):
if not self.conLabel:
raise IllegalRuntimeException("None continue label")
return self.conLabel[-1]
'''
* return the label of the innest enclosing loop to which break
* statement would jump
* @return an integer representing the break label
'''
def getBreakLabel(self):
if not self.brkLabel:
raise IllegalRuntimeException("None break label")
return self.brkLabel[-1]

776
codegen/MachineCode.py Normal file
View File

@ -0,0 +1,776 @@
'''
* @author Dr.Nguyen Hua Phung
* @version 1.0
* 28/6/2006
* This class provides facilities for method generation
*
'''
from abc import ABC, abstractmethod # , ABCMeta
from codegen.CodeGenError import (
IllegalOperandException
)
class MachineCode(ABC):
@abstractmethod
def emitPUSHNULL(self):
pass
@abstractmethod
def emitICONST(self, i):
# i: Int
pass
@abstractmethod
def emitBIPUSH(self, i):
# i: Int
pass
@abstractmethod
def emitSIPUSH(self, i):
# i: Int
pass
@abstractmethod
def emitLDC(self, in_):
# in_: String
pass
@abstractmethod
def emitFCONST(self, i):
# i: String
pass
@abstractmethod
def emitILOAD(self, in_):
# in_: Int
pass
@abstractmethod
def emitFLOAD(self, in_):
# in_: Int
pass
@abstractmethod
def emitISTORE(self, in_):
# in_: Int
pass
@abstractmethod
def emitFSTORE(self, in_):
# in_: Int
pass
@abstractmethod
def emitALOAD(self, in_):
# in_: Int
pass
@abstractmethod
def emitASTORE(self, in_):
# in_: Int
pass
@abstractmethod
def emitIASTORE(self):
pass
@abstractmethod
def emitFASTORE(self):
pass
@abstractmethod
def emitBASTORE(self):
pass
@abstractmethod
def emitAASTORE(self):
pass
@abstractmethod
def emitIALOAD(self):
pass
@abstractmethod
def emitFALOAD(self):
pass
@abstractmethod
def emitBALOAD(self):
pass
@abstractmethod
def emitAALOAD(self):
pass
@abstractmethod
def emitGETSTATIC(self, lexeme, typ):
# lexeme: String
# typ: String
pass
@abstractmethod
def emitPUTSTATIC(self, lexeme, typ):
# lexeme: String
# typ: String
pass
@abstractmethod
def emitGETFIELD(self, lexeme, typ):
# lexeme: String
# typ: String
pass
@abstractmethod
def emitPUTFIELD(self, lexeme, typ):
# lexeme: String
# typ: String
pass
@abstractmethod
def emitIADD(self):
pass
@abstractmethod
def emitFADD(self):
pass
@abstractmethod
def emitISUB(self):
pass
@abstractmethod
def emitFSUB(self):
pass
@abstractmethod
def emitIMUL(self):
pass
@abstractmethod
def emitFMUL(self):
pass
@abstractmethod
def emitIDIV(self):
pass
@abstractmethod
def emitFDIV(self):
pass
@abstractmethod
def emitIAND(self):
pass
@abstractmethod
def emitIOR(self):
pass
@abstractmethod
def emitIREM(self):
pass
@abstractmethod
def emitIFACMPEQ(self, label):
# label: Int
pass
@abstractmethod
def emitIFACMPNE(self, label):
# label: Int
pass
@abstractmethod
def emitIFICMPEQ(self, label):
# label: Int
pass
@abstractmethod
def emitIFICMPNE(self, label):
# label: Int
pass
@abstractmethod
def emitIFICMPLT(self, label):
# label: Int
pass
@abstractmethod
def emitIFICMPLE(self, label):
# label: Int
pass
@abstractmethod
def emitIFICMPGT(self, label):
# label: Int
pass
@abstractmethod
def emitIFICMPGE(self, label):
# label: Int
pass
@abstractmethod
def emitIFEQ(self, label):
# label: Int
pass
@abstractmethod
def emitIFNE(self, label):
# label: Int
pass
@abstractmethod
def emitIFLT(self, label):
# label: Int
pass
@abstractmethod
def emitIFLE(self, label):
# label: Int
pass
@abstractmethod
def emitIFGT(self, label):
# label: Int
pass
@abstractmethod
def emitIFGE(self, label):
# label: Int
pass
@abstractmethod
def emitLABEL(self, label):
# label: Int
pass
@abstractmethod
def emitGOTO(self, label):
# label: Int
pass
@abstractmethod
def emitINEG(self):
pass
@abstractmethod
def emitFNEG(self):
pass
@abstractmethod
def emitDUP(self):
pass
@abstractmethod
def emitDUPX2(self):
pass
@abstractmethod
def emitPOP(self):
pass
@abstractmethod
def emitI2F(self):
pass
@abstractmethod
def emitNEW(self, lexeme):
# lexeme: String
pass
@abstractmethod
def emitNEWARRAY(self, lexeme):
# lexeme: String
pass
@abstractmethod
def emitANEWARRAY(self, lexeme):
# lexeme: String
pass
@abstractmethod
def emitMULTIANEWARRAY(self, typ, dimensions):
# typ: String
# dimensions: Int
pass
@abstractmethod
def emitINVOKESTATIC(self, lexeme, typ):
# lexeme: String
# typ: String
pass
@abstractmethod
def emitINVOKESPECIAL(self, lexeme=None, typ=None):
# lexeme: String
# typ: String
pass
@abstractmethod
def emitINVOKEVIRTUAL(self, lexeme, typ):
# lexeme: String
# typ: String
pass
@abstractmethod
def emitI(self):
pass
@abstractmethod
def emitF(self):
pass
@abstractmethod
def emit(self):
pass
@abstractmethod
def emitLIMITSTACK(self, in_):
# in_: String
pass
@abstractmethod
def emitFCMPL(self):
pass
@abstractmethod
def emitLIMITLOCAL(self, in_):
# in_: String
pass
@abstractmethod
def emitVAR(self, in_, varName, inType, fromLabel, toLabel):
# in_: Int
# varName: String
# inType: String
# fromLabel: Int
# toLabel: Int
pass
@abstractmethod
def emitMETHOD(self, lexeme, typ, isStatic):
# lexeme: String
# typ: String
# isStaic: Boolean
pass
@abstractmethod
def emitENDMETHOD(self):
pass
@abstractmethod
def emitSOURCE(self, lexeme):
# lexeme: String
pass
@abstractmethod
def emitCLASS(self, lexeme):
# lexeme: String
pass
@abstractmethod
def emitSUPER(self, lexeme):
# lexeme: String
pass
@abstractmethod
def emitSTATICFIELD(self, lexeme, typ, isFinal):
# lexeme: String
# typ: String
# isFinal: Boolean
pass
@abstractmethod
def emitINSTANCEFIELD(self, lexeme, typ):
# lexeme: String
# typ: String
pass
@abstractmethod
def emitRETURN(self):
pass
@abstractmethod
def emitIRETURN(self):
pass
@abstractmethod
def emitFRETURN(self):
pass
@abstractmethod
def emitARETURN(self):
pass
class JasminCode(MachineCode):
END = "\n"
INDENT = "\t"
def emitPUSHNULL(self):
return JasminCode.INDENT + "aconst_null" + JasminCode.END
def emitICONST(self, i):
# i: Int
if i == -1:
return JasminCode.INDENT + "iconst_ml" + JasminCode.END
elif i >= 0 or i <= 5:
return JasminCode.INDENT + "iconst_" + str(i) + JasminCode.END
else:
raise IllegalOperandException(str(i))
def emitBIPUSH(self, i):
# i: Int
if (i >= -128 and i < -1) or (i > 5 and i <= 127):
return JasminCode.INDENT + "bipush " + str(i) + JasminCode.END
else:
raise IllegalOperandException(str(i))
def emitSIPUSH(self, i):
# i: Int
if (i >= -32768 and i < -128) or (i > 127 and i <= 32767):
return JasminCode.INDENT + "sipush " + str(i) + JasminCode.END
else:
raise IllegalOperandException(str(i))
def emitLDC(self, in_):
# in_: String
return JasminCode.INDENT + "ldc " + in_ + JasminCode.END
def emitFCONST(self, i):
# i: String
if i == "0.0":
return JasminCode.INDENT + "fconst_0" + JasminCode.END
elif i == "1.0":
return JasminCode.INDENT + "fconst_1" + JasminCode.END
elif i == "2.0":
return JasminCode.INDENT + "fconst_2" + JasminCode.END
else:
raise IllegalOperandException(i)
def emitILOAD(self, in_):
# in_: Int
if in_ >= 0 and in_ <= 3:
return JasminCode.INDENT + "iload_" + str(in_) + JasminCode.END
else:
return JasminCode.INDENT + "iload " + str(in_) + JasminCode.END
def emitFLOAD(self, in_):
# in_: Int
if in_ >= 0 and in_ <= 3:
return JasminCode.INDENT + "fload_" + str(in_) + JasminCode.END
else:
return JasminCode.INDENT + "fload " + str(in_) + JasminCode.END
def emitISTORE(self, in_):
# in_: Int
if in_ >= 0 and in_ <= 3:
return JasminCode.INDENT + "istore_" + str(in_) + JasminCode.END
else:
return JasminCode.INDENT + "istore " + str(in_) + JasminCode.END
def emitFSTORE(self, in_):
# in_: Int
if in_ >= 0 and in_ <= 3:
return JasminCode.INDENT + "fstore_" + str(in_) + JasminCode.END
else:
return JasminCode.INDENT + "fstore " + str(in_) + JasminCode.END
def emitALOAD(self, in_):
# in_: Int
if in_ >= 0 and in_ <= 3:
return JasminCode.INDENT + "aload_" + str(in_) + JasminCode.END
else:
return JasminCode.INDENT + "aload " + str(in_) + JasminCode.END
def emitASTORE(self, in_):
# in_: Int
if in_ >= 0 and in_ <= 3:
return JasminCode.INDENT + "astore_" + str(in_) + JasminCode.END
else:
return JasminCode.INDENT + "astore " + str(in_) + JasminCode.END
def emitIASTORE(self):
return JasminCode.INDENT + "iastore" + JasminCode.END
def emitFASTORE(self):
return JasminCode.INDENT + "fastore" + JasminCode.END
def emitBASTORE(self):
return JasminCode.INDENT + "bastore" + JasminCode.END
def emitAASTORE(self):
return JasminCode.INDENT + "aastore" + JasminCode.END
def emitIALOAD(self):
return JasminCode.INDENT + "iaload" + JasminCode.END
def emitFALOAD(self):
return JasminCode.INDENT + "faload" + JasminCode.END
def emitBALOAD(self):
return JasminCode.INDENT + "baload" + JasminCode.END
def emitAALOAD(self):
return JasminCode.INDENT + "aaload" + JasminCode.END
def emitGETSTATIC(self, lexeme, typ):
# lexeme: String
# typ: String
return JasminCode.INDENT + "getstatic " + lexeme + " " + typ + JasminCode.END
def emitPUTSTATIC(self, lexeme, typ):
# lexeme: String
# typ: String
return JasminCode.INDENT + "putstatic " + lexeme + " " + typ + JasminCode.END
def emitGETFIELD(self, lexeme, typ):
# lexeme: String
# typ: String
return JasminCode.INDENT + "getfield " + lexeme + " " + typ + JasminCode.END
def emitPUTFIELD(self, lexeme, typ):
# lexeme: String
# typ: String
return JasminCode.INDENT + "putfield " + lexeme + " " + typ + JasminCode.END
def emitIADD(self):
return JasminCode.INDENT + "iadd" + JasminCode.END
def emitFADD(self):
return JasminCode.INDENT + "fadd" + JasminCode.END
def emitISUB(self):
return JasminCode.INDENT + "isub" + JasminCode.END
def emitFSUB(self):
return JasminCode.INDENT + "fsub" + JasminCode.END
def emitIMUL(self):
return JasminCode.INDENT + "imul" + JasminCode.END
def emitFMUL(self):
return JasminCode.INDENT + "fmul" + JasminCode.END
def emitIDIV(self):
return JasminCode.INDENT + "idiv" + JasminCode.END
def emitFDIV(self):
return JasminCode.INDENT + "fdiv" + JasminCode.END
def emitIAND(self):
return JasminCode.INDENT + "iand" + JasminCode.END
def emitIOR(self):
return JasminCode.INDENT + "ior" + JasminCode.END
def emitIREM(self):
return JasminCode.INDENT + "irem" + JasminCode.END
def emitIFACMPEQ(self, label):
# label: Int
return JasminCode.INDENT + "if_acmpeq Label" + \
str(label) + JasminCode.END
def emitIFACMPNE(self, label):
# label: Int
return JasminCode.INDENT + "if_acmpne Label" + \
str(label) + JasminCode.END
def emitIFICMPEQ(self, label):
# label: Int
return JasminCode.INDENT + "if_icmpeq Label" + \
str(label) + JasminCode.END
def emitIFICMPNE(self, label):
# label: Int
return JasminCode.INDENT + "if_icmpne Label" + \
str(label) + JasminCode.END
def emitIFICMPLT(self, label):
# label: Int
return JasminCode.INDENT + "if_icmplt Label" + \
str(label) + JasminCode.END
def emitIFICMPLE(self, label):
# label: Int
return JasminCode.INDENT + "if_icmple Label" + \
str(label) + JasminCode.END
def emitIFICMPGT(self, label):
# label: Int
return JasminCode.INDENT + "if_icmpgt Label" + \
str(label) + JasminCode.END
def emitIFICMPGE(self, label):
# label: Int
return JasminCode.INDENT + "if_icmpge Label" + \
str(label) + JasminCode.END
def emitIFEQ(self, label):
# label: Int
return JasminCode.INDENT + "ifeq Label" + str(label) + JasminCode.END
def emitIFNE(self, label):
# label: Int
return JasminCode.INDENT + "ifne Label" + str(label) + JasminCode.END
def emitIFLT(self, label):
# label: Int
return JasminCode.INDENT + "iflt Label" + str(label) + JasminCode.END
def emitIFLE(self, label):
# label: Int
return JasminCode.INDENT + "ifle Label" + str(label) + JasminCode.END
def emitIFGT(self, label):
# label: Int
return JasminCode.INDENT + "ifgt Label" + str(label) + JasminCode.END
def emitIFGE(self, label):
# label: Int
return JasminCode.INDENT + "ifge Label" + str(label) + JasminCode.END
def emitLABEL(self, label):
# label: Int
return "Label" + str(label) + ":" + JasminCode.END
def emitGOTO(self, label):
# label: Int
return JasminCode.INDENT + "goto Label" + str(label) + JasminCode.END
def emitINEG(self):
return JasminCode.INDENT + "ineg" + JasminCode.END
def emitFNEG(self):
return JasminCode.INDENT + "fneg" + JasminCode.END
def emitDUP(self):
return JasminCode.INDENT + "dup" + JasminCode.END
def emitDUPX2(self):
return JasminCode.INDENT + "dup_x2" + JasminCode.END
def emitPOP(self):
return JasminCode.INDENT + "pop" + JasminCode.END
def emitI2F(self):
return JasminCode.INDENT + "i2f" + JasminCode.END
def emitNEW(self, lexeme):
# lexeme: String
return JasminCode.INDENT + "new " + lexeme + JasminCode.END
def emitNEWARRAY(self, lexeme):
# lexeme: String
return JasminCode.INDENT + "newarray " + lexeme + JasminCode.END
def emitANEWARRAY(self, lexeme):
# lexeme: String
return JasminCode.INDENT + "anewarray " + lexeme + JasminCode.END
def emitMULTIANEWARRAY(self, typ, dimensions):
# typ: String
# dimensions: Int
return JasminCode.INDENT + "multianewarray " + \
typ + " " + dimensions + JasminCode.END
def emitINVOKESTATIC(self, lexeme, typ):
# lexeme: String
# typ: String
return JasminCode.INDENT + "invokestatic " + lexeme + typ + JasminCode.END
def emitINVOKESPECIAL(self, lexeme=None, typ=None):
# lexeme: String
# typ: String
if lexeme is None and typ is None:
return JasminCode.INDENT + "invokespecial java/lang/Object/<init>()V" + \
JasminCode.END
elif lexeme is not None and typ is not None:
return JasminCode.INDENT + "invokespecial " + lexeme + typ + JasminCode.END
def emitINVOKEVIRTUAL(self, lexeme, typ):
# lexeme: String
# typ: String
return JasminCode.INDENT + "invokevirtual " + lexeme + typ + JasminCode.END
def emitI(self):
return JasminCode.INDENT + "i" + JasminCode.END
def emitF(self):
return JasminCode.INDENT + "f" + JasminCode.END
def emit(self):
return JasminCode.INDENT + "" + JasminCode.END
def emitLIMITSTACK(self, in_):
# in_: Int
return ".limit stack " + str(in_) + JasminCode.END
def emitFCMPL(self):
return JasminCode.INDENT + "fcmpl" + JasminCode.END
def emitLIMITLOCAL(self, in_):
# in_: Int
return ".limit locals " + str(in_) + JasminCode.END
def emitVAR(self, in_, varName, inType, fromLabel, toLabel):
# in_: Int
# varName: String
# inType: String
# fromLabel: Int
# toLabel: Int
return ".var " + str(in_) + " is " + varName + " " + inType + " from Label" + \
str(fromLabel) + " to Label" + str(toLabel) + JasminCode.END
def emitMETHOD(self, lexeme, typ, isStatic):
# lexeme: String
# typ: String
# isStaic: Boolean
if isStatic:
return JasminCode.END + ".method public static " + lexeme + typ + JasminCode.END
else:
return JasminCode.END + ".method public " + lexeme + typ + JasminCode.END
def emitENDMETHOD(self):
return ".end method" + JasminCode.END
def emitSOURCE(self, lexeme):
# lexeme: String
return ".source " + lexeme + JasminCode.END
def emitCLASS(self, lexeme):
# lexeme: String
return ".class " + lexeme + JasminCode.END
def emitSUPER(self, lexeme):
# lexeme: String
return ".super " + lexeme + JasminCode.END
def emitSTATICFIELD(self, lexeme, typ, isFinal):
# lexeme: String
# typ: String
# isFinal: Boolean
if isFinal:
return ".field static final " + lexeme + " " + typ + JasminCode.END
else:
return ".field static " + lexeme + " " + typ + JasminCode.END
def emitINSTANCEFIELD(self, lexeme, typ):
# lexeme: String
# typ: String
return ".field " + lexeme + " " + typ + JasminCode.END
def emitRETURN(self):
return JasminCode.INDENT + "return" + JasminCode.END
def emitIRETURN(self):
return JasminCode.INDENT + "ireturn" + JasminCode.END
def emitFRETURN(self):
return JasminCode.INDENT + "freturn" + JasminCode.END
def emitARETURN(self):
return JasminCode.INDENT + "areturn" + JasminCode.END

0
codegen/__init__.py Normal file
View File

37
genANTLR4.py Normal file
View File

@ -0,0 +1,37 @@
import os
import subprocess
ANTLR_JAR = os.environ.get('ANTLR_LIB')
JASMIN_JAR = './external/jasmin.jar'
if ANTLR_JAR is None:
# fall back, not recommended
ANTLR_JAR = './external/antrl4.jar'
def generate():
files_from_antlr4 = [
'MP.interp',
'MPLexer.interp',
'MPLexer.py',
'MPLexer.tokens',
'MPParser.py',
'MP.tokens',
'MPVisitor.py'
]
if all(list(map(os.path.isfile, files_from_antlr4))):
return
gen_antlr_class_cmd = [
"java",
"-jar",
ANTLR_JAR,
"-no-listener",
"-visitor",
"parser/MP.g4"
]
subprocess.run(gen_antlr_class_cmd)
if __name__ == '__main__':
generate()

BIN
libs/io.class Normal file

Binary file not shown.

156
libs/io.java Normal file
View File

@ -0,0 +1,156 @@
import java.io.*;
import java.io.IOException;
//import bkool.codegeneration.IllegalRuntimeException;
public class io {
//private static final DataInputStream input = new DataInputStream(System.in);
public static BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
public static Writer writer = new BufferedWriter(new OutputStreamWriter(System.out));
//public io(String name) throws IOException {
//}
/** reads and returns an integer value from the standard input
* @return int an integer number read from standard input
*/
public static int getInt() {
String tmp = "";
try {
tmp = input.readLine();
return Integer.parseInt(tmp);
} catch (IOException e) {
e.printStackTrace();
} catch (NumberFormatException e) {
e.printStackTrace();
}
return 0;
}
/** print out the value of the integer i to the standard output
* @param i the value is printed out
*/
public static void putInt(int i) {
System.out.print(i+"");
}
/** same as putInt except that it also prints a newline
* @param i the value is printed out
*/
public static void putIntLn(int i) {
System.out.println(i+"");
}
/** return a floating-point value read from the standard input
* @return float the floating-point value
*/
public static float getFloat() {
String tmp ="";
try {
tmp = input.readLine();
return Float.parseFloat(tmp);
}
catch (IOException e) {
e.printStackTrace();;
}
catch (NumberFormatException e) {
e.printStackTrace();;
}
return 0.0F;
}
/** print out the value of the float f to the standard output
* @param f the floating-point value is printed out
*/
public static void putFloat(float f) {
System.out.print(f+"");
}
/** same as putFloat except that it also prints a newline
* @param f the floating-point value is printed out
*/
public static void putFloatLn(float f) {
System.out.println(f+"");
}
/** reads and returns a boolean value from the standard input
* @return int a boolean value read from standard input
*/
public static boolean getBool() {
String tmp = "";
try {
tmp = input.readLine();
if (tmp.equalsIgnoreCase("true"))
return true;
else //if (tmp.equalsIgnoreCase("false"))
return false;
// else throw new IllegalRuntimeException(tmp);
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
/** print out the value of the boolean b to the standard output
* @param b the boolean value is printed out
*/
public static void putBool(boolean b) {
System.out.print(b+"");
}
/** same as putBoolLn except that it also prints a new line
* @param b the boolean value is printed out
*/
public static void putBoolLn(boolean b) {
System.out.println(b+"");
}
/** reads and returns a boolean value from the standard input
* @return int a boolean value read from standard input
*/
/*public static String Str() {
String tmp = "";
try {
tmp = input.readLine();
return tmp;
} catch (IOException e) {
e.printStackTrace();
}
return tmp;
}*/
/** prints the value of the string to the standard output
* @param a the string is printed out
*/
public static void putString(String a ) {
System.out.print(a);
}
/** same as putString except that it also prints a new line
* @param a the string is printed out
*/
public static void putStringLn(String a) {
System.out.println(a);
}
/** print out an empty line
**/
public static void putLn() {
System.out.println();
}
public static void close() {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

112
mpc.py Normal file
View File

@ -0,0 +1,112 @@
import sys
import os
import subprocess
from antlr4 import FileStream, CommonTokenStream # Token
from antlr4.error.ErrorListener import ConsoleErrorListener # ErrorListener
try:
from parser.MPLexer import MPLexer as Lexer
from parser.MPParser import MPParser as Parser
from astgen.ASTGeneration import ASTGeneration
from checker.StaticCheck import StaticChecker
from codegen.CodeGenerator import CodeGenerator
except ModuleNotFoundError:
print('Generate ANTLR4 first')
print('python genANTLR4.py')
exit(1)
ANTLR_JAR = os.environ.get('ANTLR_LIB')
JASMIN_JAR = './external/jasmin.jar'
if ANTLR_JAR is None:
# fall back, not recommended
ANTLR_JAR = './external/antrl4.jar'
class SyntaxException(Exception):
def __init__(self, msg):
self.message = msg
class NewErrorListener(ConsoleErrorListener):
INSTANCE = None
def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e):
raise SyntaxException(
"Error on line " +
str(line) +
" col " +
str(column) +
": " +
offendingSymbol.text)
def compile(inputfile):
lexer = Lexer(FileStream(inputfile))
tokens = CommonTokenStream(lexer)
try:
# listener = TestParser.createErrorListener()
listener = NewErrorListener().INSTANCE
parser = Parser(tokens)
parser.removeErrorListeners()
parser.addErrorListener(listener)
tree = parser.program()
except SyntaxException as f:
msg = f.message.split(':')[0].split(' ')
line = int(msg[3])
col = int(msg[5])
error_line = open(inputfile).read()
error_line = error_line.split('\n')[line - 1]
print(error_line)
print('~' * (col) + '^')
print(f.message)
raise f
asttree = ASTGeneration().visit(tree)
checker = StaticChecker(asttree)
checker.check()
path = os.path.dirname(inputfile)
filename = os.path.basename(inputfile).split('.')[0]
codeGen = CodeGenerator()
codeGen.gen(asttree, path, filename)
subprocess.call(
# "java -jar " + JASMIN_JAR + " " + path + "/MPClass.j",
"java -jar {} {}/{}.j -d {}".format(JASMIN_JAR, path, filename, path),
shell=True,
stderr=subprocess.STDOUT
)
with open('{}/io.class'.format(path), 'wb') as iofile:
iofile.write(open('libs/io.class', 'rb').read())
subprocess.call(
# 'jar cvfm {}/{}.jar {}/manifest.mf {} {}.class'.format(
'jar cvfe {0}.jar {0} io.class {0}.class'.format(
filename
),
cwd=path,
shell=True,
stderr=subprocess.STDOUT
)
os.remove('{}/{}.j'.format(path, filename))
os.remove('{}/{}.class'.format(path, filename))
os.remove('{}/io.class'.format(path))
if __name__ == "__main__":
argv = sys.argv
if len(argv) != 2:
exit(1)
try:
print("Compiling {}".format(os.path.relpath(argv[1])))
compile(argv[1])
except BaseException as e:
print(e)
exit(1)
print("Compiled successfully")

588
parser/MP.g4 Normal file
View File

@ -0,0 +1,588 @@
// 1611617
grammar MP;
@lexer::header {
from parser.lexererr import *
}
options{
language=Python3;
}
program
: manydecl EOF
;
manydecl
: decl manydecl
| decl
;
decl
: var_decl
| func_decl
| proc_decl
;
var_decl
: VAR varlist
;
varlist
: var SEMI varlist
| var SEMI
;
var
: idenlist COLON mptype
;
idenlist
: IDENT COMMA idenlist
| IDENT
;
mptype
: primitive_type
| compound_type
;
primitive_type
: INTEGER
| REAL
| BOOLEAN
| STRING
;
compound_type
: ARRAY array_value OF primitive_type
;
array_value
: LB MINUS? NUM_INT DOUBLE_DOT MINUS? NUM_INT RB
;
func_decl
: FUNCTION IDENT LR param_list? RR COLON mptype SEMI var_decl* compound_statement
;
param_list
: var SEMI param_list
| var
;
proc_decl
: PROCEDURE IDENT LR param_list? RR SEMI var_decl* compound_statement
;
expression
: expression (AND THEN | OR ELSE) expression_lv1
| expression_lv1
;
expression_lv1
: expression_lv2 (EQUAL | NOT_EQUAL | LT | LE | GE | GT) expression_lv2
| expression_lv2
;
expression_lv2
: expression_lv2 (PLUS | MINUS | OR) expression_lv3
| expression_lv3
;
expression_lv3
: expression_lv3 (STAR | SLASH | DIV | MOD | AND) expression_lv4
| expression_lv4
;
expression_lv4
: (NOT | MINUS) expression_lv4
| index_expression
;
index_expression
: index_expression LB expression RB
| factor
;
invocation_expression
: LR call_param? RR
;
factor
: LR expression RR
| literal
| IDENT
| IDENT invocation_expression
;
statement
: normal_statement SEMI
| structured_statement
;
structured_statement
: if_statement
| for_statement
| compound_statement
| with_statement
| while_statement
;
normal_statement
: assignment_statement
| break_statement
| continue_statement
| return_statement
| call_statement
;
assignment_statement
: assignment_lhs_list expression
;
assignment_lhs_list
: lhs ASSIGN assignment_lhs_list
| lhs ASSIGN
;
lhs
: index_expression
| IDENT
;
if_statement
: IF expression THEN statement (ELSE statement)?
;
while_statement
: WHILE expression DO statement
;
for_statement
: FOR IDENT ASSIGN expression (TO | DOWNTO) expression DO statement
;
break_statement
: BREAK
;
continue_statement
: CONTINUE
;
return_statement
: RETURN expression?
;
compound_statement
: BEGIN statement* END
;
with_statement
: WITH varlist DO statement
;
call_statement
: IDENT LR call_param? RR
;
call_param
: expression COMMA call_param
| expression
;
empty
:
;
fragment A
: ('A' | 'a')
;
fragment B
: ('B' | 'b')
;
fragment C
: ('C' | 'c')
;
fragment D
: ('D' | 'd')
;
fragment E
: ('E' | 'e')
;
fragment F
: ('F' | 'f')
;
fragment G
: ('G' | 'g')
;
fragment H
: ('H' | 'h')
;
fragment I
: ('I' | 'i')
;
fragment J
: ('J' | 'j')
;
fragment K
: ('K' | 'k')
;
fragment L
: ('L' | 'l')
;
fragment M
: ('M' | 'm')
;
fragment N
: ('N' | 'n')
;
fragment O
: ('O' | 'o')
;
fragment P
: ('P' | 'p')
;
fragment Q
: ('Q' | 'q')
;
fragment R
: ('R' | 'r')
;
fragment S
: ('S' | 's')
;
fragment T
: ('T' | 't')
;
fragment U
: ('U' | 'u')
;
fragment V
: ('V' | 'v')
;
fragment W
: ('W' | 'w')
;
fragment X
: ('X' | 'x')
;
fragment Y
: ('Y' | 'y')
;
fragment Z
: ('Z' | 'z')
;
literal
: number
| BOOL_LIT
| STRING_LITERAL
;
STRING_LITERAL
: UNCLOSE_STRING '"'
{self.text = self.text[1:-1]}
;
UNCLOSE_STRING
: '"' ('\\' [btrnf\\'"] | ~[\b\t\r\n\f\\'"])*
{raise UncloseString(self.text[1:])}
;
ILLEGAL_ESCAPE
: UNCLOSE_STRING '\\' ~[btnfr"'\\]
{raise IllegalEscape(self.text[1:])}
;
fragment ESCAPE_STRING
: '\\b'
| '\\f'
| '\\r'
| '\\n'
| '\\t'
| '\\\''
| '\\"'
| '\\\\'
;
number
: NUM_INT
| NUM_REAL
;
NUM_INT
: [0-9]+
;
NUM_REAL
: [0-9]+ '.' [0-9]* EXPONENT?
| '.' [0-9]+ EXPONENT?
| [0-9]+ EXPONENT
;
BOOL_LIT
: TRUE
| FALSE
;
fragment EXPONENT
: ('e' | 'E') '-'? ('0' .. '9')+
;
LCURLY
: '{'
;
RCURLY
: '}'
;
LR
: '('
;
RR
: ')'
;
LB
: '['
;
RB
: ']'
;
SEMI
: ';'
;
DOUBLE_DOT
: '..'
;
COMMA
: ','
;
COLON
: ':'
;
BREAK
: B R E A K
;
CONTINUE
: C O N T I N U E
;
FOR
: F O R
;
TO
: T O
;
DOWNTO
: D O W N T O
;
DO
: D O
;
IF
: I F
;
THEN
: T H E N
;
ELSE
: E L S E
;
RETURN
: R E T U R N
;
WHILE
: W H I L E
;
BEGIN
: B E G I N
;
END
: E N D
;
FUNCTION
: F U N C T I O N
;
PROCEDURE
: P R O C E D U R E
;
VAR
: V A R
;
TRUE
: T R U E
;
FALSE
: F A L S E
;
ARRAY
: A R R A Y
;
OF
: O F
;
REAL
: R E A L
;
BOOLEAN
: B O O L E A N
;
INTEGER
: I N T E G E R
;
STRING
: S T R I N G
;
NOT
: N O T
;
AND
: A N D
;
OR
: O R
;
DIV
: D I V
;
MOD
: M O D
;
WITH
: W I T H
;
PLUS
: '+'
;
MINUS
: '-'
;
STAR
: '*'
;
SLASH
: '/'
;
EQUAL
: '='
;
NOT_EQUAL
: '<>'
;
LT
: '<'
;
LE
: '<='
;
GE
: '>='
;
GT
: '>'
;
ASSIGN
: ':='
;
IDENT
: [a-zA-Z_] [a-zA-Z0-9_]*
;
COMMENT_3
: '//' .*? '\r'* '\n'+ -> skip
;
WS
: [ \t\r\n] -> skip
; // skip spaces, tabs
COMMENT_1
: '(*' .*? '*)' -> skip
;
COMMENT_2
: '{' .*? '}' -> skip
;
ERROR_CHAR
: .
{raise ErrorToken(self.text)}
;

0
parser/__init__.py Normal file
View File

14
parser/lexererr.py Normal file
View File

@ -0,0 +1,14 @@
class ErrorToken(Exception):
def __init__(self, s):
self.message = "Error Token " + s
class UncloseString(Exception):
def __init__(self, s):
self.message = "Unclosed String: " + s
class IllegalEscape(Exception):
def __init__(self, s):
self.message = "Illegal Escape In String: " + s

534
utils/AST.py Normal file
View File

@ -0,0 +1,534 @@
from abc import ABC, abstractmethod, ABCMeta
# from Visitor import Visitor
# cheated = True
cheated = False
class AST(ABC):
def __eq__(self, other):
return self.__dict__ == other.__dict__
@abstractmethod
def accept(self, v, param):
return v.visit(self, param)
class Type(AST):
__metaclass__ = ABCMeta
pass
class IntType(Type):
global cheated
def __str__(self):
if not cheated:
return "IntType"
else:
return "IntType()"
def accept(self, v, param):
return v.visitIntType(self, param)
class FloatType(Type):
global cheated
def __str__(self):
if not cheated:
return "FloatType"
else:
return "FloatType()"
def accept(self, v, param):
return v.visitFloatType(self, param)
class BoolType(Type):
global cheated
def __str__(self):
if not cheated:
return "BoolType"
else:
return "BoolType()"
def accept(self, v, param):
return v.visitBoolType(self, param)
class StringType(Type):
global cheated
def __str__(self):
if not cheated:
return "StringType"
else:
return "StringType()"
def accept(self, v, param):
return v.visitStringType(self, param)
class ArrayType(Type):
global cheated
# lower:int
# upper:int
# eleType:Type
def __init__(self, lower, upper, eleType):
self.lower = lower
self.upper = upper
self.eleType = eleType
def __str__(self):
return "ArrayType(" + str(self.lower) + "," + \
str(self.upper) + "," + str(self.eleType) + ")"
def accept(self, v, param):
return v.visitArrayType(self, param)
class VoidType(Type):
global cheated
def __str__(self):
return "VoidType()"
def accept(self, v, param):
return v.visitVoidType(self, param)
class Program(AST):
global cheated
# decl:list(Decl)
def __init__(self, decl):
self.decl = decl
def __str__(self):
return "Program([" + ','.join(str(i) for i in self.decl) + "])"
def accept(self, v, param):
return v.visitProgram(self, param)
class Decl(AST):
__metaclass__ = ABCMeta
pass
class VarDecl(Decl):
global cheated
# variable:Id
# varType: Type
def __init__(self, variable, varType):
self.variable = variable
self.varType = varType
def __str__(self):
return "VarDecl(" + str(self.variable) + "," + str(self.varType) + ")"
def accept(self, v, param):
return v.visitVarDecl(self, param)
class FuncDecl(Decl):
global cheated
# name: Id
# param: list(VarDecl)
# returnType: Type => VoidType for Procedure
# local:list(VarDecl)
# body: list(Stmt)
def __init__(self, name, param, local, body, returnType=VoidType()):
self.name = name
self.param = param
self.returnType = returnType
self.local = local
self.body = body
def __str__(self):
if not cheated:
return "FuncDecl(" + str(self.name) + ",[" + \
','.join(str(i) for i in self.param) + "]," + \
str(self.returnType) + ",[" + \
','.join(str(i) for i in self.local) + "],[" + \
','.join(str(i) for i in self.body) + "])"
else:
return "FuncDecl(" + str(self.name) + ",[" + \
','.join(str(i) for i in self.param) + "],[" + \
','.join(str(i) for i in self.local) + "],[" + \
','.join(str(i) for i in self.body) + "]," + \
str(self.returnType) + ")"
def accept(self, v, param):
return v.visitFuncDecl(self, param)
class Stmt(AST):
__metaclass__ = ABCMeta
pass
class Assign(Stmt):
global cheated
# lhs:Expr
# exp:Expr
def __init__(self, lhs, exp):
self.lhs = lhs
self.exp = exp
def __str__(self):
if not cheated:
return "AssignStmt(" + str(self.lhs) + "," + str(self.exp) + ")"
else:
return "Assign(" + str(self.lhs) + "," + str(self.exp) + ")"
def accept(self, v, param):
return v.visitAssign(self, param)
class If(Stmt):
global cheated
# expr:Expr
# thenStmt:list(Stmt)
# elseStmt:list(Stmt)
def __init__(self, expr, thenStmt, elseStmt=[]):
self.expr = expr
self.thenStmt = thenStmt
self.elseStmt = elseStmt
def __str__(self):
return "If(" + str(self.expr) + ",[" + \
','.join(str(i) for i in self.thenStmt) + "],[" + \
','.join(str(i) for i in self.elseStmt) + "])"
def accept(self, v, param):
return v.visitIf(self, param)
class While(Stmt):
global cheated
# sl:list(Stmt)
# exp: Expr
def __init__(self, exp, sl):
self.sl = sl
self.exp = exp
def __str__(self):
return "While(" + str(self.exp) + \
",[" + ','.join(str(i) for i in self.sl) + "])"
def accept(self, v, param):
return v.visitWhile(self, param)
class For(Stmt):
global cheated
# id:Id
# expr1,expr2:Expr
# loop:list(Stmt)
# up:Boolean #True => increase; False => decrease
def __init__(self, id, expr1, expr2, up, loop):
self.id = id
self.expr1 = expr1
self.expr2 = expr2
self.up = up
self.loop = loop
def __str__(self):
if not cheated:
return "For(" + str(self.id) + ","\
+ str(self.expr1) + "," \
+ str(self.expr2) + ","\
+ str(self.up) + ',[' \
+ ','.join(str(i) for i in self.loop) + "])"
else:
return "For(" + str(self.id) + ","\
+ str(self.expr1) + "," \
+ str(self.expr2) + ","\
+ str(self.up) + ',[' \
+ ','.join(str(i) for i in self.loop) + "])"
def accept(self, v, param):
return v.visitFor(self, param)
class Break(Stmt):
global cheated
def __str__(self):
if not cheated:
return "Break"
else:
return "Break()"
def accept(self, v, param):
return v.visitBreak(self, param)
class Continue(Stmt):
global cheated
def __str__(self):
if not cheated:
return "Continue"
else:
return "Continue()"
def accept(self, v, param):
return v.visitContinue(self, param)
class Return(Stmt):
global cheated
# expr:Expr
def __init__(self, expr=None):
self.expr = expr
def __str__(self):
if not cheated:
return "Return(" + ("None" if (self.expr is None)
else "Some(" + str(self.expr) + ")") + ")"
else:
return "Return()" if self.expr is None \
else "Return(" + str(self.expr) + ")"
def accept(self, v, param):
return v.visitReturn(self, param)
class With(Stmt):
global cheated
# decl:list(VarDecl)
# stmt:list(Stmt)
def __init__(self, decl, stmt):
self.decl = decl
self.stmt = stmt
def __str__(self):
return "With([" + ','.join(str(i) for i in self.decl) + \
"],[" + ','.join(str(i) for i in self.stmt) + "])"
def accept(self, v, param):
return v.visitWith(self, param)
class CallStmt(Stmt):
global cheated
# method:Id
# param:list(Expr)
def __init__(self, method, param):
self.method = method
self.param = param
def __str__(self):
return "CallStmt(" + str(self.method) + \
",[" + ','.join(str(i) for i in self.param) + "])"
def accept(self, v, param):
return v.visitCallStmt(self, param)
class Expr(AST):
__metaclass__ = ABCMeta
pass
class BinaryOp(Expr):
global cheated
# op:string: AND THEN => andthen; OR ELSE => orelse; other => keep it
# left:Expr
# right:Expr
def __init__(self, op, left, right):
self.op = op
self.left = left
self.right = right
def __str__(self):
if not cheated:
return "BinaryOp(" + self.op + "," + str(self.left) + \
"," + str(self.right) + ")"
else:
return "BinaryOp(\"" + self.op + "\"," + str(self.left) + \
"," + str(self.right) + ")"
def accept(self, v, param):
return v.visitBinaryOp(self, param)
class UnaryOp(Expr):
global cheated
# op:string
# body:Expr
def __init__(self, op, body):
self.op = op
self.body = body
def __str__(self):
if not cheated:
return "UnaryOp(" + self.op + "," + str(self.body) + ")"
else:
return "UnaryOp(\"" + self.op + "\"," + str(self.body) + ")"
def accept(self, v, param):
return v.visitUnaryOp(self, param)
class CallExpr(Expr):
global cheated
# method:Id
# param:list(Expr)
def __init__(self, method, param):
self.method = method
self.param = param
def __str__(self):
return "CallExpr(" + str(self.method) + \
",[" + ','.join(str(i) for i in self.param) + "])"
def accept(self, v, param):
return v.visitCallExpr(self, param)
class LHS(Expr):
global cheated
__metaclass__ = ABCMeta
pass
class Id(LHS):
global cheated
# name:string
def __init__(self, name):
self.name = name
def __str__(self):
if not cheated:
return "Id(" + self.name + ")"
else:
return "Id(\"" + self.name + "\")"
def accept(self, v, param):
return v.visitId(self, param)
class ArrayCell(LHS):
global cheated
# arr:Expr
# idx:Expr
def __init__(self, arr, idx):
self.arr = arr
self.idx = idx
def __str__(self):
return "ArrayCell(" + str(self.arr) + "," + str(self.idx) + ")"
def accept(self, v, param):
return v.visitArrayCell(self, param)
class Literal(Expr):
global cheated
__metaclass__ = ABCMeta
pass
class IntLiteral(Literal):
global cheated
# value:int
def __init__(self, value):
self.value = value
def __str__(self):
return "IntLiteral(" + str(self.value) + ")"
def accept(self, v, param):
return v.visitIntLiteral(self, param)
class FloatLiteral(Literal):
global cheated
# value:float
def __init__(self, value):
self.value = value
def __str__(self):
return "FloatLiteral(" + str(self.value) + ")"
def accept(self, v, param):
return v.visitFloatLiteral(self, param)
class StringLiteral(Literal):
global cheated
# value:string
def __init__(self, value):
self.value = value
def __str__(self):
if not cheated:
return "StringLiteral(" + self.value + ")"
else:
return "StringLiteral(\"" + self.value + "\")"
def accept(self, v, param):
return v.visitStringLiteral(self, param)
class BooleanLiteral(Literal):
global cheated
# value:boolean
def __init__(self, value):
self.value = value
def __str__(self):
return "BooleanLiteral(" + str(self.value) + ")"
def accept(self, v, param):
return v.visitBooleanLiteral(self, param)
class ArrayPointerType(Type):
def __init__(self, ctype):
# cname: String
self.eleType = ctype
def __str__(self):
return "ArrayPointerType({0})".format(
str(self.eleType)
)
def accept(self, v, param):
return None
class ClassType(Type):
def __init__(self, cname):
self.cname = cname
def __str__(self):
return "Class({0})".format(str(self.cname))
def accept(self, v, param):
return None

7
utils/Utils.py Normal file
View File

@ -0,0 +1,7 @@
class Utils:
def lookup(self, name, lst, func):
for x in lst:
if name == func(x):
return x
return None

199
utils/Visitor.py Normal file
View File

@ -0,0 +1,199 @@
from abc import ABC, abstractmethod # , ABCMeta
class Visitor(ABC):
def visit(self, ast, param):
return ast.accept(self, param)
@abstractmethod
def visitProgram(self, asttree, param):
pass
@abstractmethod
def visitVarDecl(self, asttree, param):
pass
@abstractmethod
def visitFuncDecl(self, asttree, param):
pass
@abstractmethod
def visitIntType(self, asttree, param):
pass
@abstractmethod
def visitFloatType(self, asttree, param):
pass
@abstractmethod
def visitBoolType(self, asttree, param):
pass
@abstractmethod
def visitStringType(self, asttree, param):
pass
@abstractmethod
def visitVoidType(self, asttree, param):
pass
@abstractmethod
def visitArrayType(self, asttree, param):
pass
@abstractmethod
def visitBinaryOp(self, asttree, param):
pass
@abstractmethod
def visitUnaryOp(self, asttree, param):
pass
@abstractmethod
def visitCallExpr(self, asttree, param):
pass
@abstractmethod
def visitId(self, asttree, param):
pass
@abstractmethod
def visitArrayCell(self, asttree, param):
pass
@abstractmethod
def visitAssign(self, asttree, param):
pass
@abstractmethod
def visitWith(self, asttree, param):
pass
@abstractmethod
def visitIf(self, asttree, param):
pass
@abstractmethod
def visitFor(self, asttree, param):
pass
@abstractmethod
def visitContinue(self, asttree, param):
pass
@abstractmethod
def visitBreak(self, asttree, param):
pass
@abstractmethod
def visitReturn(self, asttree, param):
pass
@abstractmethod
def visitWhile(self, asttree, param):
pass
@abstractmethod
def visitCallStmt(self, asttree, param):
pass
@abstractmethod
def visitIntLiteral(self, asttree, param):
pass
@abstractmethod
def visitFloatLiteral(self, asttree, param):
pass
@abstractmethod
def visitBooleanLiteral(self, asttree, param):
pass
@abstractmethod
def visitStringLiteral(self, asttree, param):
pass
class BaseVisitor(Visitor):
def visitProgram(self, asttree, param):
return None
def visitVarDecl(self, asttree, param):
return None
def visitFuncDecl(self, asttree, param):
return None
def visitIntType(self, asttree, param):
return None
def visitFloatType(self, asttree, param):
return None
def visitBoolType(self, asttree, param):
return None
def visitStringType(self, asttree, param):
return None
def visitVoidType(self, asttree, param):
return None
def visitArrayType(self, asttree, param):
return None
def visitBinaryOp(self, asttree, param):
return None
def visitUnaryOp(self, asttree, param):
return None
def visitCallExpr(self, asttree, param):
return None
def visitId(self, asttree, param):
return None
def visitArrayCell(self, asttree, param):
return None
def visitAssign(self, asttree, param):
return None
def visitWith(self, asttree, param):
return None
def visitIf(self, asttree, param):
return None
def visitFor(self, asttree, param):
return None
def visitContinue(self, asttree, param):
return None
def visitBreak(self, asttree, param):
return None
def visitReturn(self, asttree, param):
return None
def visitWhile(self, asttree, param):
return None
def visitCallStmt(self, asttree, param):
return None
def visitIntLiteral(self, asttree, param):
return None
def visitFloatLiteral(self, asttree, param):
return None
def visitBooleanLiteral(self, asttree, param):
return None
def visitStringLiteral(self, asttree, param):
return None

0
utils/__init__.py Normal file
View File