init
This commit is contained in:
commit
bed5a47d55
611
.gitignore
vendored
Normal file
611
.gitignore
vendored
Normal 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
36
README.md
Normal 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
402
astgen/ASTGeneration.py
Normal 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
0
astgen/__init__.py
Normal file
778
checker/StaticCheck.py
Normal file
778
checker/StaticCheck.py
Normal 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)
|
||||
|
||||
# LHS’s are formal parameters and RHS’s 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
125
checker/StaticError.py
Normal 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
0
checker/__init__.py
Normal file
16
codegen/CodeGenError.py
Normal file
16
codegen/CodeGenError.py
Normal 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
940
codegen/CodeGenerator.py
Normal 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
784
codegen/Emitter.py
Normal 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
206
codegen/Frame.py
Normal 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
776
codegen/MachineCode.py
Normal 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
0
codegen/__init__.py
Normal file
37
genANTLR4.py
Normal file
37
genANTLR4.py
Normal 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
BIN
libs/io.class
Normal file
Binary file not shown.
156
libs/io.java
Normal file
156
libs/io.java
Normal 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
112
mpc.py
Normal 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
588
parser/MP.g4
Normal 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
0
parser/__init__.py
Normal file
14
parser/lexererr.py
Normal file
14
parser/lexererr.py
Normal 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
534
utils/AST.py
Normal 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
7
utils/Utils.py
Normal 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
199
utils/Visitor.py
Normal 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
0
utils/__init__.py
Normal file
Loading…
Reference in New Issue
Block a user