├── 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 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
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 | 
8 |
9 | 2. You can choose file from your computer.
10 |
11 | 
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 | 
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 | 
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
--------------------------------------------------------------------------------