├── X0-Compiler.exe ├── images ├── GIF1.gif ├── GIF2.gif ├── GIF3.gif └── GIF4.gif ├── .idea ├── dictionaries │ └── GooCoder.xml ├── encodings.xml ├── misc.xml ├── vcs.xml ├── modules.xml ├── X0-Compiler-GUI.iml └── inspectionProfiles │ └── Project_Default.xml ├── README.md ├── stackdisp.py ├── stackobj.py ├── editor.py ├── GUI.py └── Interpret.py /X0-Compiler.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serend1p1ty/X0-Compiler-GUI/HEAD/X0-Compiler.exe -------------------------------------------------------------------------------- /images/GIF1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serend1p1ty/X0-Compiler-GUI/HEAD/images/GIF1.gif -------------------------------------------------------------------------------- /images/GIF2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serend1p1ty/X0-Compiler-GUI/HEAD/images/GIF2.gif -------------------------------------------------------------------------------- /images/GIF3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serend1p1ty/X0-Compiler-GUI/HEAD/images/GIF3.gif -------------------------------------------------------------------------------- /images/GIF4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serend1p1ty/X0-Compiler-GUI/HEAD/images/GIF4.gif -------------------------------------------------------------------------------- /.idea/dictionaries/GooCoder.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/X0-Compiler-GUI.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # X0-Compiler-GUI 2 | 3 | This is the GUI of [X0-Compiler](https://github.com/GooCoder/X0-Compiler). It has these following features. 4 | 5 | 1. Graceful code editor 6 | 7 | ![](https://github.com/GooCoder/X0-Compiler-GUI/blob/master/images/GIF1.gif) 8 | 9 | 2. You can choose file from your computer. 10 | 11 | ![](https://github.com/GooCoder/X0-Compiler-GUI/blob/master/images/GIF2.gif) 12 | 13 | 3. If you want to **run** a program: 14 | 15 | - Step1: Compile the program and then you can see intermediate code. 16 | 17 | - Step2: Run the program. 18 | 19 | ![](https://github.com/GooCoder/X0-Compiler-GUI/blob/master/images/GIF3.gif) 20 | 21 | 4. If you want to **debug** a program: 22 | 23 | - Step1: Compile the program. 24 | 25 | - Step2: Debug the program and you can see dynamic changes in data stack. 26 | 27 | ![](https://github.com/GooCoder/X0-Compiler-GUI/blob/master/images/GIF4.gif) 28 | 29 | 30 | ## Installation 31 | 32 | 1. Install `PyQt5`using `pip install pyqt5`. 33 | 34 | 2. Clone the repository to your computer and open it with **Pycharm**. 35 | 36 | ## Help 37 | 38 | If you have some problems can't be solved, please contact the author by email: **zjli1997@qq.com**. 39 | -------------------------------------------------------------------------------- /stackdisp.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QWidget, QStyleOption, QStyle 2 | from PyQt5.QtGui import QPainter, QColor, QFont 3 | from PyQt5.QtCore import Qt, QRect 4 | import Interpret as Ipt 5 | 6 | class StackDisplayer(QWidget): 7 | def __init__(self, parent=None): 8 | super(QWidget, self).__init__(parent) 9 | self.parent = parent 10 | 11 | def paintEvent(self, QPaintEvent): 12 | opt = QStyleOption() 13 | opt.initFrom(self) 14 | # painter = QPainter(self) 15 | self.style().drawPrimitive(QStyle.PE_Widget, opt, QPainter(self), self) 16 | 17 | if self.parent.isDebug == 0: 18 | return 19 | 20 | colorTable = [0xFF0000, 0xFF7F00, 0xFFFF00, 0x0000FF, 21 | 0x8B00FF, 0x00FF00, 0x00FFFF] 22 | 23 | rec = self.contentsRect() 24 | height = rec.height() * 5 / 16 25 | width = rec.width() / 25 26 | topx = 0 27 | topy = rec.height() * 11 / 32 28 | painter = QPainter(self) 29 | 30 | font = QFont() 31 | font.setFamily("consolas") 32 | painter.setFont(font) 33 | painter.setPen(QColor(163, 163, 163)) 34 | 35 | # Draw instruction text 36 | fctCodeString = ["lit", "opr", "lod", "sto", "cal", "ini", "jmp", "jpc", "add", "sub", "tad"] 37 | inst = Ipt.code[Ipt.p] 38 | if inst.fct == Ipt.FctCode.lit and inst.opr2 != 0: 39 | painter.drawText(QRect(0, 20, 300, 25), Qt.AlignVCenter, 40 | "Next instruction:%s %s %s" % (fctCodeString[inst.fct], 41 | inst.opr1, round(inst.opr2, 2))) 42 | else: 43 | painter.drawText(QRect(0, 20, 300, 25), Qt.AlignVCenter, 44 | "Next instruction:%s %s %s" % (fctCodeString[inst.fct], 45 | inst.opr1, int(inst.opr2))) 46 | 47 | 48 | typeString = ["", "int", "dbl", "chr", "bol"] 49 | for i in range(1, Ipt.t + 1): 50 | # Draw name and type 51 | painter.drawText(QRect(topx, topy + height, width, 25), 52 | Qt.AlignHCenter | Qt.AlignVCenter, "%s" % typeString[Ipt.s[i].dataType]) 53 | painter.drawText(QRect(topx, topy - 25, width, 25), Qt.AlignHCenter | Qt.AlignVCenter, Ipt.stype[i]) 54 | 55 | 56 | # Draw rectangle 57 | if Ipt.stype[i] == "null": 58 | color =QColor(colorTable[0]) 59 | elif Ipt.stype[i] == "DL": 60 | color =QColor(colorTable[1]) 61 | elif Ipt.stype[i] == "RA": 62 | color =QColor(colorTable[2]) 63 | else: 64 | color = QColor(colorTable[Ipt.s[i].dataType + 2]) 65 | painter.fillRect(topx, topy, width, height, color) 66 | painter.setPen(color.darker()) 67 | painter.drawLine(topx, topy, topx, topy + height) 68 | painter.setPen(QColor(163, 163, 163)) 69 | painter.drawLine(topx, topy, topx + width, topy) 70 | painter.drawLine(topx + width, topy, topx + width, topy + height) 71 | painter.drawLine(topx, topy + height, topx + width, topy + height) 72 | 73 | 74 | # Draw specific data 75 | if Ipt.s[i].dataType == Ipt.DataType.Double: 76 | painter.drawText(QRect(topx, topy + height / 2 - 12.5, width, 25), Qt.AlignHCenter | Qt.AlignVCenter, 77 | "%s" % round(Ipt.s[i].dblData, 2)) 78 | else: 79 | painter.drawText(QRect(topx, topy + height / 2 - 12.5, width, 25), Qt.AlignHCenter | Qt.AlignVCenter, 80 | "%s" % Ipt.s[i].intData) 81 | topx = topx + width 82 | 83 | 84 | -------------------------------------------------------------------------------- /stackobj.py: -------------------------------------------------------------------------------- 1 | class DataType(object): 2 | Nul = 0 3 | Int = 1 4 | Double = 2 5 | Char = 3 6 | Bool = 4 7 | 8 | 9 | # object stored in data stack 10 | class StackObject: 11 | def __init__(self): 12 | self.dataType = DataType.Nul 13 | self.intData = 0 14 | self.dblData = 0.0 15 | 16 | @staticmethod 17 | def result_type(dt1, dt2): 18 | if dt1 == DataType.Double or dt2 == DataType.Double: 19 | return DataType.Double 20 | elif dt1 == DataType.Char or dt2 == DataType.Char: 21 | return DataType.Char 22 | else: 23 | return DataType.Int 24 | 25 | def assign(self, other): 26 | if self.dataType != DataType.Double: 27 | if other.dataType != DataType.Double: 28 | self.intData = other.intData 29 | else: 30 | self.intData = other.dblData 31 | else: 32 | if other.dataType != DataType.Double: 33 | self.dblData = other.intData 34 | else: 35 | self.dblData = other.dblData 36 | 37 | def __add__(self, other): 38 | res = StackObject() 39 | if self.dataType != DataType.Double and other.dataType != DataType.Double: 40 | res.intData = self.intData + other.intData 41 | else: 42 | res.dblData = (self.intData if(self.dataType != DataType.Double) else self.dblData) \ 43 | + (other.intData if(other.dataType != DataType.Double) else other.dblData) 44 | res.dataType = StackObject.result_type(self.dataType, other.dataType) 45 | return res 46 | 47 | def __sub__(self, other): 48 | res = StackObject() 49 | if self.dataType != DataType.Double and other.dataType != DataType.Double: 50 | res.intData = self.intData - other.intData 51 | else: 52 | res.dblData = (self.intData if (self.dataType != DataType.Double) else self.dblData) \ 53 | - (other.intData if (other.dataType != DataType.Double) else other.dblData) 54 | res.dataType = StackObject.result_type(self.dataType, other.dataType) 55 | return res 56 | 57 | def __mul__(self, other): 58 | res = StackObject() 59 | if self.dataType != DataType.Double and other.dataType != DataType.Double: 60 | res.intData = self.intData * other.intData 61 | else: 62 | res.dblData = (self.intData if (self.dataType != DataType.Double) else self.dblData) \ 63 | * (other.intData if (other.dataType != DataType.Double) else other.dblData) 64 | res.dataType = StackObject.result_type(self.dataType, other.dataType) 65 | return res 66 | 67 | def __truediv__(self, other): 68 | res = StackObject() 69 | if self.dataType != DataType.Double and other.dataType != DataType.Double: 70 | res.intData = self.intData // other.intData 71 | else: 72 | res.dblData = (self.intData if (self.dataType != DataType.Double) else self.dblData) \ 73 | / (other.intData if (other.dataType != DataType.Double) else other.dblData) 74 | res.dataType = StackObject.result_type(self.dataType, other.dataType) 75 | return res 76 | 77 | def __neg__(self): 78 | self.intData = -self.intData 79 | self.dblData = -self.dblData 80 | return self 81 | 82 | def __mod__(self, other): 83 | if self.dataType == DataType.Double or other.dataType == DataType.Double: 84 | print("the operand of mod operation must be integer!") 85 | exit(1) 86 | res = StackObject() 87 | res.intData = self.intData % other.intData 88 | res.dataType = DataType.Int 89 | return res 90 | 91 | def __eq__(self, other): 92 | res = StackObject() 93 | res.intData = (self.intData if (self.dataType != DataType.Double) else self.dblData) \ 94 | == (other.intData if (other.dataType != DataType.Double) else other.dblData) 95 | res.dataType = DataType.Bool 96 | return res 97 | 98 | def __ne__(self, other): 99 | res = StackObject() 100 | res.intData = (self.intData if (self.dataType != DataType.Double) else self.dblData) \ 101 | != (other.intData if (other.dataType != DataType.Double) else other.dblData) 102 | res.dataType = DataType.Bool 103 | return res 104 | 105 | def __gt__(self, other): 106 | res = StackObject() 107 | res.intData = (self.intData if (self.dataType != DataType.Double) else self.dblData) \ 108 | > (other.intData if (other.dataType != DataType.Double) else other.dblData) 109 | res.dataType = DataType.Bool 110 | return res 111 | 112 | def __ge__(self, other): 113 | res = StackObject() 114 | res.intData = (self.intData if (self.dataType != DataType.Double) else self.dblData) \ 115 | >= (other.intData if (other.dataType != DataType.Double) else other.dblData) 116 | res.dataType = DataType.Bool 117 | return res 118 | 119 | def __lt__(self, other): 120 | res = StackObject() 121 | res.intData = (self.intData if (self.dataType != DataType.Double) else self.dblData) \ 122 | < (other.intData if (other.dataType != DataType.Double) else other.dblData) 123 | res.dataType = DataType.Bool 124 | return res 125 | 126 | def __le__(self, other): 127 | res = StackObject() 128 | res.intData = (self.intData if (self.dataType != DataType.Double) else self.dblData) \ 129 | <= (other.intData if (other.dataType != DataType.Double) else other.dblData) 130 | res.dataType = DataType.Bool 131 | return res 132 | 133 | def __xor__(self, other): 134 | if self.dataType == DataType.Double or other.dataType == DataType.Double: 135 | print("the operand of xor operation must be integer!") 136 | exit(1) 137 | res = StackObject() 138 | res.intData = self.intData ^ other.intData 139 | res.dataType = DataType.Bool 140 | return res 141 | -------------------------------------------------------------------------------- /editor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from PyQt5.QtCore import * 5 | from PyQt5.QtWidgets import * 6 | from PyQt5.QtGui import * 7 | 8 | 9 | class X0Highlighter(QSyntaxHighlighter): 10 | def __init__(self, parent=None): 11 | super().__init__(parent) 12 | self.highlightingRules = [] 13 | 14 | # the first kind of keyword 15 | keywordFormat = QTextCharFormat() 16 | keywordFormat.setForeground(QColor(0x00C5CD)) 17 | keywordFormat.setFontWeight(QFont.Bold) 18 | keywords = ["bool", "char", "double", "int", "const", "void"] 19 | keywordPatterns = [("\\b" + keyword + "\\b") for keyword in keywords] 20 | self.highlightingRules += [(QRegExp(pattern), keywordFormat) 21 | for pattern in keywordPatterns] 22 | 23 | # the second kind of keyword 24 | keywordFormat = QTextCharFormat() 25 | keywordFormat.setForeground(QColor(0x8968CD)) 26 | keywordFormat.setFontWeight(QFont.Bold) 27 | keywords = ["break", "case", "continue", "default", "do", "else", 28 | "exit", "for", "if", "repeat", "return", "switch", 29 | "until", "while"] 30 | keywordPatterns = [("\\b" + keyword + "\\b") for keyword in keywords] 31 | self.highlightingRules += [(QRegExp(pattern), keywordFormat) 32 | for pattern in keywordPatterns] 33 | 34 | # the third kind of keyword 35 | keywordFormat = QTextCharFormat() 36 | keywordFormat.setForeground(QColor(0x8B7355)) 37 | keywordFormat.setFontWeight(QFont.Bold) 38 | keywords = ["write", "read"] 39 | keywordPatterns = [("\\b" + keyword + "\\b") for keyword in keywords] 40 | self.highlightingRules += [(QRegExp(pattern), keywordFormat) 41 | for pattern in keywordPatterns] 42 | 43 | # the fourth kind of keyword 44 | keywordFormat = QTextCharFormat() 45 | keywordFormat.setForeground(QColor(0xCD4F39)) 46 | keywordFormat.setFontWeight(QFont.Bold) 47 | keywords = ["true", "false"] 48 | keywordPatterns = [("\\b" + keyword + "\\b") for keyword in keywords] 49 | self.highlightingRules += [(QRegExp(pattern), keywordFormat) 50 | for pattern in keywordPatterns] 51 | 52 | 53 | 54 | def highlightBlock(self, text): 55 | for pattern, Format in self.highlightingRules: 56 | expression = QRegExp(pattern) 57 | index = expression.indexIn(text) 58 | while index >= 0: 59 | length = expression.matchedLength() 60 | self.setFormat(index, length, Format) 61 | index = expression.indexIn(text, index + length) 62 | self.setCurrentBlockState(0) 63 | 64 | 65 | class QLineNumberArea(QWidget): 66 | def __init__(self, parent): 67 | super().__init__(parent) 68 | self.codeEditor = parent 69 | 70 | def sizeHint(self): 71 | return QSize(self.editor.lineNumberAreaWidth(), 0) 72 | 73 | def paintEvent(self, event): 74 | self.codeEditor.lineNumberAreaPaintEvent(event) 75 | 76 | 77 | class QCodeEditor(QPlainTextEdit): 78 | def __init__(self, parent=None): 79 | super().__init__(parent) 80 | self.lineNumberArea = QLineNumberArea(self) 81 | self.blockCountChanged.connect(self.updateLineNumberAreaWidth) 82 | self.updateRequest.connect(self.updateLineNumberArea) 83 | self.cursorPositionChanged.connect(self.highlightCurrentLine) 84 | self.updateLineNumberAreaWidth() 85 | self.highlight = X0Highlighter(self.document()) 86 | 87 | def lineNumberAreaWidth(self): 88 | digits = 1 89 | max_value = max(1, self.blockCount()) 90 | while max_value >= 10: 91 | max_value /= 10 92 | digits += 1 93 | space = 3 + self.fontMetrics().width('9') * digits 94 | return space 95 | 96 | def updateLineNumberAreaWidth(self): 97 | self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0) 98 | 99 | def updateLineNumberArea(self, rect, dy): 100 | if dy: 101 | self.lineNumberArea.scroll(0, dy) 102 | else: 103 | self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(), rect.height()) 104 | if rect.contains(self.viewport().rect()): 105 | self.updateLineNumberAreaWidth() 106 | 107 | def resizeEvent(self, event): 108 | super().resizeEvent(event) 109 | cr = self.contentsRect() 110 | self.lineNumberArea.setGeometry(QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height())) 111 | 112 | def highlightCurrentLine(self): 113 | extra_selections = [] 114 | if not self.isReadOnly(): 115 | selection = QTextEdit.ExtraSelection() 116 | line_color = QColor(50, 50, 50).lighter(160) 117 | selection.format.setBackground(line_color) 118 | selection.format.setProperty(QTextFormat.FullWidthSelection, True) 119 | selection.cursor = self.textCursor() 120 | selection.cursor.clearSelection() 121 | extra_selections.append(selection) 122 | self.setExtraSelections(extra_selections) 123 | 124 | def lineNumberAreaPaintEvent(self, event): 125 | painter = QPainter(self.lineNumberArea) 126 | painter.fillRect(event.rect(), QColor(50, 50, 50)) 127 | block = self.firstVisibleBlock() 128 | block_number = block.blockNumber() 129 | top = self.blockBoundingGeometry(block).translated(self.contentOffset()).top() 130 | bottom = top + self.blockBoundingRect(block).height() 131 | 132 | # Just to make sure I use the right font 133 | height = self.fontMetrics().height() 134 | while block.isValid() and (top <= event.rect().bottom()): 135 | if block.isVisible() and (bottom >= event.rect().top()): 136 | number = str(block_number + 1) 137 | 138 | # Let line number for the selected line to be highlight 139 | if block_number == self.textCursor().blockNumber(): 140 | painter.setPen(QColor(163, 163, 163)) 141 | else: 142 | painter.setPen(QColor(100, 100, 100)) 143 | 144 | painter.drawText(0, top, self.lineNumberArea.width(), height, Qt.AlignRight, number) 145 | 146 | block = block.next() 147 | top = bottom 148 | bottom = top + self.blockBoundingRect(block).height() 149 | block_number += 1 150 | 151 | 152 | if __name__ == '__main__': 153 | import sys 154 | from PyQt5.QtWidgets import QApplication 155 | app = QApplication(sys.argv) 156 | editor = QCodeEditor() 157 | editor.show() 158 | sys.exit(app.exec_()) -------------------------------------------------------------------------------- /GUI.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from editor import QCodeEditor 5 | from PyQt5.QtWidgets import (QMainWindow, QDesktopWidget, QAction, QTabWidget, 6 | QVBoxLayout, QWidget, QFileDialog) 7 | import Interpret as Ipt 8 | from stackdisp import StackDisplayer 9 | import subprocess 10 | 11 | class MainWindow(QMainWindow): 12 | def __init__(self): 13 | super().__init__() 14 | 15 | # Create menu bar 16 | compileAct = QAction('&Compile', self) 17 | compileAct.setShortcut('Ctrl+C') 18 | compileAct.setStatusTip('Compile the program') 19 | compileAct.triggered.connect(self.compile) 20 | runAct = QAction('&Run', self) 21 | runAct.setShortcut('Ctrl+R') 22 | runAct.setStatusTip('Run the program') 23 | runAct.triggered.connect(self.run) 24 | debugAct = QAction('&Debug', self) 25 | debugAct.setShortcut('Ctrl+D') 26 | debugAct.setStatusTip('Debug the program') 27 | debugAct.triggered.connect(self.debug) 28 | fileAct = QAction('&Open file', self) 29 | fileAct.setShortcut('Ctrl+F') 30 | fileAct.setStatusTip('Choose file') 31 | fileAct.triggered.connect(self.chooseFile) 32 | 33 | menubar = self.menuBar() 34 | fileMenu = menubar.addMenu('&File') 35 | fileMenu.addAction(fileAct) 36 | runMenu = menubar.addMenu('&Run') 37 | runMenu.addAction(compileAct) 38 | runMenu.addAction(runAct) 39 | runMenu.addAction(debugAct) 40 | 41 | # Set style 42 | self.setStyleSheet("QMainWindow{\ 43 | background:rgb(43, 43, 43);\ 44 | }\ 45 | QMenuBar{\ 46 | background:rgb(43, 43, 43);\ 47 | color:rgb(163, 163, 163);\ 48 | }\ 49 | QMenuBar::item:selected{\ 50 | background-color:rgb(75,110,175);\ 51 | }\ 52 | QMenu{\ 53 | background-color:rgb(43, 43, 43);\ 54 | }\ 55 | QMenu::item{\ 56 | color:rgb(163, 163, 163);\ 57 | background-color:rgb(50, 50, 50);\ 58 | }\ 59 | QMenu::item:selected{\ 60 | background-color:rgb(75,110,175);\ 61 | }\ 62 | QStatusBar{\ 63 | color:rgb(163, 163, 163);\ 64 | }") 65 | 66 | self.GUI = GUI() 67 | self.setCentralWidget(self.GUI) 68 | self.resize(1200, 800) 69 | self.center() 70 | self.statusBar() 71 | self.setWindowTitle('X0-Compiler') 72 | 73 | # centers the window on the screen 74 | def center(self): 75 | screen = QDesktopWidget().screenGeometry() 76 | size = self.geometry() 77 | self.move((screen.width() - size.width()) / 2, 78 | (screen.height() - size.height()) / 2) 79 | 80 | def compile(self): 81 | self.GUI.output.setPlainText('Compiling with X0-Compiler\n') 82 | self.GUI.isDebug = 0 83 | 84 | # write the content of code editor into input.txt 85 | import os 86 | if not os.path.exists('./data'): 87 | os.makedirs('./data') 88 | with open("./data/input.txt", "w") as file: 89 | file.write(self.GUI.codeEdit.toPlainText()) 90 | 91 | # invoke X0-Compiler.exe to produce intermediate code and load it. 92 | process = subprocess.Popen("X0-Compiler.exe", stdout=subprocess.PIPE) 93 | process.wait() 94 | Ipt.loadData() 95 | 96 | # display intermediate code 97 | fctCodeString = ["lit", "opr", "lod", "sto", "cal", "ini", "jmp", "jpc", "add", "sub", "tad"] 98 | s = "" 99 | for i in range(Ipt.codeNum): 100 | temp = Ipt.code[i] 101 | if temp.fct == 0 and temp.opr2 != 0: 102 | s = s + '%s %s %s\n' % (fctCodeString[temp.fct], temp.opr1, round(temp.opr2, 2)) 103 | else: 104 | s = s + '%s %s %s\n' % (fctCodeString[temp.fct], temp.opr1, int(temp.opr2)) 105 | self.GUI.INTCode.setPlainText(s.strip()) 106 | 107 | self.GUI.output.appendPlainText('Compiled successfully!\n') 108 | 109 | def run(self): 110 | self.GUI.output.appendPlainText('Output of your program:') 111 | Ipt.interpretAllStep(self.GUI.output) 112 | 113 | def debug(self): 114 | if self.GUI.isDebug == 0: 115 | self.GUI.isDebug = 1 116 | self.GUI.stackDisplayer.update() 117 | self.GUI.output.appendPlainText('Output of your program:') 118 | else: 119 | Ipt.interpretOneStep(self.GUI.output) 120 | self.GUI.stackDisplayer.update() 121 | 122 | def chooseFile(self): 123 | filePath, _ = QFileDialog.getOpenFileName(None, "Choose file", 124 | "C:/", 125 | "All Files (*);;Text Files (*.txt)") 126 | if filePath == "": 127 | return 128 | s = "" 129 | with open(filePath, "r") as file: 130 | lines = file.readlines() 131 | for line in lines: 132 | s = s + line 133 | self.GUI.codeEdit.setPlainText(s) 134 | 135 | class GUI(QWidget): 136 | def __init__(self): 137 | super().__init__() 138 | self.isDebug = 0 139 | 140 | # Add code editor 141 | self.codeEdit = QCodeEditor(self) 142 | self.codeEdit.setStyleSheet("color:rgb(163, 163, 163);\ 143 | background:rgb(43, 43, 43);\ 144 | font-family:Consolas;\ 145 | font-size:10pt") 146 | tabWidth = 4 * self.codeEdit.fontMetrics().width(' ') 147 | self.codeEdit.setTabStopWidth(tabWidth) 148 | 149 | # Add output displayer 150 | self.output = QCodeEditor(self) 151 | self.output.setStyleSheet("color:rgb(163, 163, 163);\ 152 | background:rgb(43, 43, 43);\ 153 | font-family:Consolas;\ 154 | font-size:10pt;") 155 | self.output.setReadOnly(1) 156 | 157 | # Add intermediate code displayer 158 | self.INTCode = QCodeEditor(self) 159 | self.INTCode.setStyleSheet("color:rgb(163, 163, 163);\ 160 | background:rgb(43, 43, 43);\ 161 | font-family:Consolas;\ 162 | font-size:10pt;") 163 | self.INTCode.setReadOnly(1) 164 | 165 | # Add data stack displayer 166 | self.stackDisplayer = StackDisplayer(self) 167 | self.stackDisplayer.setStyleSheet("background:rgb(43, 43, 43);") 168 | 169 | 170 | self.tabWidget = QTabWidget() 171 | self.tabWidget.addTab(self.output, "Output") 172 | self.tabWidget.addTab(self.INTCode, "Intermediate code") 173 | self.tabWidget.addTab(self.stackDisplayer, "Data stack") 174 | self.tabWidget.setStyleSheet("QTabWidget::pane{\ 175 | background-color:rgb(43, 43, 43);\ 176 | }\ 177 | QTabBar::tab{\ 178 | min-width:100px;\ 179 | min-height:25px;\ 180 | font:15px consolas;\ 181 | }\ 182 | QTabBar::tab:!selected{\ 183 | color:rgb(163, 163, 163);\ 184 | background:rgb(43, 43, 43)\ 185 | }\ 186 | QTabBar::tab:selected{\ 187 | color:rgb(163, 163, 163);\ 188 | background:rgb(70, 70, 70)\ 189 | }") 190 | 191 | vbox = QVBoxLayout() 192 | vbox.addWidget(self.codeEdit) 193 | vbox.addWidget(self.tabWidget) 194 | self.setLayout(vbox) 195 | 196 | 197 | if __name__ == '__main__': 198 | import sys 199 | from PyQt5.QtWidgets import QApplication 200 | app = QApplication(sys.argv) 201 | mw = MainWindow() 202 | mw.show() 203 | sys.exit(app.exec_()) 204 | -------------------------------------------------------------------------------- /Interpret.py: -------------------------------------------------------------------------------- 1 | from ctypes import * 2 | from stackobj import * 3 | from copy import deepcopy 4 | 5 | 6 | MAX_LEN_IDENT = 20 7 | MAX_DIMENSION = 10 8 | MAX_SIZE_TABLE = 100 9 | MAX_NUM_FUNCTION = 100 10 | MAX_NUM_CODE = 1000 11 | MAX_SIZE_STACK = 10000 12 | 13 | 14 | class ObjectKind(object): 15 | intVar = 0 16 | constIntVar = 1 17 | intArray = 2 18 | doubleVar = 3 19 | constDoubleVar = 4 20 | doubleArray = 5 21 | charVar = 6 22 | constCharVar = 7 23 | charArray = 8 24 | boolVar = 9 25 | constBoolVar = 10 26 | boolArray = 11 27 | 28 | 29 | class RetType(object): 30 | retVoid = 0 31 | retInt = 1 32 | retDouble = 2 33 | retChar = 3 34 | retBool = 4 35 | 36 | 37 | class FctCode(object): 38 | lit = 0 39 | opr = 1 40 | lod = 2 41 | sto = 3 42 | cal = 4 43 | ini = 5 44 | jmp = 6 45 | jpc = 7 46 | add = 8 47 | sub = 9 48 | tad = 10 49 | 50 | 51 | class Instruction(Structure): 52 | _fields_ = [('fct', c_int), 53 | ('opr1', c_int), 54 | ('opr2', c_double)] 55 | 56 | 57 | class TableObject(Structure): 58 | _fields_ = [('name', c_char * (MAX_LEN_IDENT + 1)), 59 | ('kind', c_int), 60 | ('offset', c_int), 61 | ('size', c_int * MAX_DIMENSION), 62 | ('d', c_int), 63 | ('value', c_double)] 64 | 65 | 66 | class FunctionInfo(Structure): 67 | _fields_ = [('name', c_char * (MAX_LEN_IDENT + 1)), 68 | ('symTable', TableObject * MAX_SIZE_TABLE), 69 | ('tableSize', c_int), 70 | ('paraNum', c_int), 71 | ('startINTCode', c_int), 72 | ('retType', c_int)] 73 | 74 | 75 | p = 0 76 | b = 1 77 | t = 0 78 | fctInfo = (FunctionInfo * MAX_NUM_FUNCTION)() 79 | fctNum = 0 80 | code = (Instruction * MAX_NUM_CODE)() 81 | codeNum = 0 82 | s = [StackObject() for i in range(MAX_SIZE_STACK)] 83 | stype = [str() for i in range(MAX_SIZE_STACK)] 84 | 85 | 86 | def FindPosition(offset, pos): 87 | for i in range(fctInfo[pos].tableSize): 88 | if fctInfo[pos].symTable[i].offset == offset: 89 | return i 90 | return -1 91 | 92 | def init(): 93 | global p, b, t, fctNum, codeNum, s 94 | p = 0 95 | b = 1 96 | t = 0 97 | fctNum = 0 98 | codeNum = 0 99 | s = [StackObject() for i in range(MAX_SIZE_STACK)] 100 | 101 | def loadData(): 102 | global fctNum, codeNum 103 | init() 104 | 105 | with open('./data/fctInfo.bin', 'rb') as file: 106 | x = FunctionInfo() 107 | while file.readinto(x) == sizeof(x): 108 | fctInfo[fctNum] = x 109 | fctNum = fctNum + 1 110 | with open('./data/code.bin', 'rb') as file: 111 | x = Instruction() 112 | while file.readinto(x) == sizeof(x): 113 | code[codeNum] = x 114 | codeNum = codeNum + 1 115 | 116 | 117 | def ConvertToInt(num): 118 | if num > 0: 119 | temp = 0.5 120 | else: 121 | temp = -0.5 122 | return int(num + temp) 123 | 124 | 125 | def interpretOneStep(output): 126 | global p, b, t 127 | 128 | s[1].dataType = DataType.Int 129 | s[1].intData = 0 130 | stype[1] = "null" 131 | s[2].dataType = DataType.Int 132 | s[2].intData = 1 133 | stype[2] = "DL" 134 | s[3].dataType = DataType.Int 135 | s[3].intData = 0 136 | stype[3] = "RA" 137 | 138 | inst = code[p] 139 | p = p + 1 140 | fct = inst.fct 141 | if fct == FctCode.lit: 142 | t = t + 1 143 | if inst.opr2 == 0: 144 | s[t].dataType = DataType.Int 145 | s[t].intData = inst.opr1 146 | else: 147 | s[t].dataType = DataType.Double 148 | s[t].dblData = inst.opr2 149 | stype[t] = "null" 150 | elif fct == FctCode.opr: 151 | if inst.opr1 == 0: 152 | fctIndex = ConvertToInt(inst.opr2) 153 | paraNum = fctInfo[fctIndex].paraNum 154 | retType = fctInfo[fctIndex].retType 155 | intTmp = t 156 | t = b - 1 157 | p = s[t + 3].intData 158 | b = s[t + 2].intData 159 | t = t - paraNum 160 | if retType != RetType.retVoid: 161 | t = t + 1 162 | s[t] = deepcopy(s[intTmp]) 163 | elif inst.opr1 == 1: 164 | s[t] = -s[t] 165 | elif inst.opr1 == 2: 166 | t = t - 1 167 | s[t] = s[t] + s[t + 1] 168 | elif inst.opr1 == 3: 169 | t = t - 1 170 | s[t] = s[t] - s[t + 1] 171 | elif inst.opr1 == 4: 172 | t = t - 1 173 | s[t] = s[t] * s[t + 1] 174 | elif inst.opr1 == 5: 175 | t = t - 1 176 | s[t] = s[t] / s[t + 1] 177 | elif inst.opr1 == 6: 178 | t = t - 1 179 | s[t] = s[t] % s[t + 1] 180 | elif inst.opr1 == 7: 181 | t = t - 1 182 | if s[t + 1].dataType == DataType.Double: 183 | print("the parameter of exit function must be integer!") 184 | exit(1) 185 | exit(s[t + 1].intData) 186 | elif inst.opr1 == 8: 187 | t = t - 1 188 | s[t] = (s[t] == s[t + 1]) 189 | elif inst.opr1 == 9: 190 | t = t - 1 191 | s[t] = (s[t] != s[t + 1]) 192 | elif inst.opr1 == 10: 193 | t = t - 1 194 | s[t] = (s[t] < s[t + 1]) 195 | elif inst.opr1 == 11: 196 | t = t - 1 197 | s[t] = (s[t] >= s[t + 1]) 198 | elif inst.opr1 == 12: 199 | t = t - 1 200 | s[t] = (s[t] > s[t + 1]) 201 | elif inst.opr1 == 13: 202 | t = t - 1 203 | s[t] = (s[t] <= s[t + 1]) 204 | elif inst.opr1 == 14: 205 | t = t + 1 206 | stype[t] = "null" 207 | s[t].dataType = ConvertToInt(inst.opr2) 208 | if s[t].dataType == DataType.Int or s[t].dataType == DataType.Bool: 209 | s[t].intData = int(input()) 210 | elif s[t].dataType == DataType.Char: 211 | s[t].intData = input() 212 | else: 213 | s[t].dblData = float(input()) 214 | elif inst.opr1 == 15: 215 | if s[t].dataType == DataType.Int or s[t].dataType == DataType.Bool: 216 | output.appendPlainText("%s" % s[t].intData) 217 | # print(s[t].intData) 218 | elif s[t].dataType == DataType.Double: 219 | output.appendPlainText("%s" % round(s[t].dblData, 2)) 220 | # print(round(s[t].dblData, 2)) 221 | elif s[t].dataType == DataType.Char: 222 | output.appendPlainText("%s" % chr(s[t].intData)) 223 | # print(chr(s[t].intData)) 224 | t = t - 1 225 | elif inst.opr1 == 16: 226 | t = t - 1 227 | s[t].dataType = DataType.Bool 228 | s[t].intData = s[t].intData and s[t + 1].intData 229 | elif inst.opr1 == 17: 230 | t = t - 1 231 | s[t].dataType = DataType.Bool 232 | s[t].intData = s[t].intData or s[t + 1].intData 233 | elif inst.opr1 == 18: 234 | s[t].dataType = DataType.Bool 235 | s[t].intData = not s[t].intData 236 | elif inst.opr1 == 19: 237 | t = t - 1 238 | s[t] = s[t] ^ s[t + 1] 239 | elif inst.opr1 == 20: 240 | if s[t].dataType == DataType.Double: 241 | print("the operand of odd must be integer!") 242 | exit(1) 243 | s[t].intData = s[t].intData % 2 244 | s[t].dataType = DataType.Bool 245 | elif inst.opr1 == 21: 246 | s[t] = (s[t - 1] == s[t]) 247 | else: 248 | print("illegal operand of 'opr' instruction") 249 | exit(1) 250 | elif fct == FctCode.lod: 251 | fctIndex = ConvertToInt(inst.opr2) 252 | pos = FindPosition(inst.opr1, fctIndex) 253 | d = fctInfo[fctIndex].symTable[pos].d 254 | offset = 0 255 | for i in range(d): 256 | if s[t + 1 - d + i].dataType == DataType.Double: 257 | print("the subscript of array must be integer!") 258 | exit(1) 259 | offset = offset * fctInfo[fctIndex].symTable[pos].size[i] + s[t + 1 - d + i].intData 260 | s[t + 1 - d] = deepcopy(s[b + inst.opr1 + offset]) 261 | t = t + 1 - d 262 | stype[t] = "null" 263 | elif fct == FctCode.sto: 264 | fctIndex = ConvertToInt(inst.opr2) 265 | pos = FindPosition(inst.opr1, fctIndex) 266 | d = fctInfo[fctIndex].symTable[pos].d 267 | offset = 0 268 | for i in range(d): 269 | if s[t - d + i].dataType == DataType.Double: 270 | print("the subscript of array must be integer!") 271 | exit(1) 272 | offset = offset * fctInfo[fctIndex].symTable[pos].size[i] + s[t - d + i].intData 273 | s[b + inst.opr1 + offset].assign(s[t]) 274 | s[t - d] = deepcopy(s[t]) 275 | t = t - d 276 | elif fct == FctCode.add: 277 | fctIndex = ConvertToInt(inst.opr2) 278 | pos = FindPosition(inst.opr1, fctIndex) 279 | d = fctInfo[fctIndex].symTable[pos].d 280 | offset = 0 281 | for i in range(d): 282 | if s[t + 1 - d + i].dataType == DataType.Double: 283 | print("the subscript of array must be integer!") 284 | exit(1) 285 | offset = offset * fctInfo[fctIndex].symTable[pos].size[i] + s[t + 1 - d + i].intData 286 | s[b + inst.opr1 + offset].intData += 1 287 | elif fct == FctCode.sub: 288 | fctIndex = ConvertToInt(inst.opr2) 289 | pos = FindPosition(inst.opr1, fctIndex) 290 | d = fctInfo[fctIndex].symTable[pos].d 291 | offset = 0 292 | for i in range(d): 293 | if s[t + 1 - d + i].dataType == DataType.Double: 294 | print("the subscript of array must be integer!") 295 | exit(1) 296 | offset = offset * fctInfo[fctIndex].symTable[pos].size[i] + s[t + 1 - d + i].intData 297 | s[b + inst.opr1 + offset].intData -= 1 298 | elif fct == FctCode.tad: 299 | if s[t].dataType == DataType.Double: 300 | print("top element isn't integer, 'tad' instruction can't work!") 301 | exit(1) 302 | s[t].intData += inst.opr1 303 | elif fct == FctCode.cal: 304 | s[t + 1].dataType = DataType.Int 305 | s[t + 1].intData = 0 306 | stype[t + 1] = "null" 307 | s[t + 2].dataType = DataType.Int 308 | s[t + 2].intData = b 309 | stype[t + 2] = "DL" 310 | s[t + 3].dataType = DataType.Int 311 | s[t + 3].intData = p 312 | stype[t + 3] = "RA" 313 | b = t + 1 314 | p = inst.opr1 315 | elif fct == FctCode.ini: 316 | t = b + 2 317 | paraNum = fctInfo[inst.opr1].paraNum 318 | for i in range(fctInfo[inst.opr1].tableSize): 319 | to = fctInfo[inst.opr1].symTable[i] 320 | totalSize = 1 321 | for j in range(to.d): 322 | totalSize *= to.size[j] 323 | for j in range(1, totalSize + 1): 324 | # remove redundent 'b' and two single quotes. 325 | name = str(to.name)[2:] 326 | name = name[:len(name) - 1] 327 | if totalSize > 1: 328 | stype[t + 1] = name + '[' + str(j - 1) + ']' 329 | else: 330 | stype[t + 1] = name 331 | 332 | if to.kind == ObjectKind.intArray or to.kind == ObjectKind.intVar: 333 | t = t + 1 334 | s[t].dataType = DataType.Int 335 | elif to.kind == ObjectKind.constIntVar: 336 | t = t + 1 337 | s[t].dataType = DataType.Int 338 | s[t].intData = ConvertToInt(to.value) 339 | elif to.kind == ObjectKind.doubleArray or to.kind == ObjectKind.doubleVar: 340 | t = t + 1 341 | s[t].dataType = DataType.Double 342 | elif to.kind == ObjectKind.constDoubleVar: 343 | t = t + 1 344 | s[t].dataType = DataType.Double 345 | s[t].dblData = to.value 346 | elif to.kind == ObjectKind.charArray or to.kind == ObjectKind.charVar: 347 | t = t + 1 348 | s[t].dataType = DataType.Char 349 | elif to.kind == ObjectKind.constCharVar: 350 | t = t + 1 351 | s[t].dataType = DataType.Char 352 | s[t].intData = ConvertToInt(to.value) 353 | elif to.kind == ObjectKind.boolArray or to.kind == ObjectKind.boolVar: 354 | t = t + 1 355 | s[t].dataType = DataType.Bool 356 | elif to.kind == ObjectKind.constBoolVar: 357 | t = t + 1 358 | s[t].dataType = DataType.Bool 359 | s[t].intData = ConvertToInt(to.value) 360 | for i in range(paraNum): 361 | s[b + 3 + i].assign(s[b - paraNum + i]) 362 | elif fct == FctCode.jmp: 363 | p = inst.opr1 364 | elif fct == FctCode.jpc: 365 | if s[t].intData == 0: 366 | p = inst.opr1 367 | t = t - 1 368 | else: 369 | print("illegal function code!") 370 | exit(1) 371 | 372 | 373 | def interpretAllStep(output): 374 | # initStack() 375 | while 1: 376 | interpretOneStep(output) 377 | if p == 0: 378 | break --------------------------------------------------------------------------------