pgvm 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pgvm-0.1.0 → pgvm-0.2.0}/PKG-INFO +1 -1
- {pgvm-0.1.0 → pgvm-0.2.0}/pyproject.toml +1 -1
- pgvm-0.2.0/src/pgvm/editable_graph.py +361 -0
- pgvm-0.2.0/src/pgvm/error.py +28 -0
- {pgvm-0.1.0 → pgvm-0.2.0}/src/pgvm.egg-info/PKG-INFO +1 -1
- {pgvm-0.1.0 → pgvm-0.2.0}/src/pgvm.egg-info/SOURCES.txt +2 -1
- pgvm-0.2.0/tests/test_graph_execution.py +110 -0
- pgvm-0.1.0/src/pgvm/editable_graph.py +0 -303
- pgvm-0.1.0/tests/test_main.py +0 -124
- {pgvm-0.1.0 → pgvm-0.2.0}/LICENSE.txt +0 -0
- {pgvm-0.1.0 → pgvm-0.2.0}/README.md +0 -0
- {pgvm-0.1.0 → pgvm-0.2.0}/setup.cfg +0 -0
- {pgvm-0.1.0 → pgvm-0.2.0}/src/pgvm/__init__.py +0 -0
- {pgvm-0.1.0 → pgvm-0.2.0}/src/pgvm.egg-info/dependency_links.txt +0 -0
- {pgvm-0.1.0 → pgvm-0.2.0}/src/pgvm.egg-info/requires.txt +0 -0
- {pgvm-0.1.0 → pgvm-0.2.0}/src/pgvm.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
|
|
2
|
+
import graphviz
|
|
3
|
+
|
|
4
|
+
from .error import PGVMRuntimeError, PGVMInvalidPath, PGVMSyntaxError
|
|
5
|
+
|
|
6
|
+
class EditableGraph():
|
|
7
|
+
"""
|
|
8
|
+
@Summ: 編集可能なgraph構造を与えるclass.
|
|
9
|
+
|
|
10
|
+
@Desc:
|
|
11
|
+
- node番号は非負整数である。負の値はerror用。
|
|
12
|
+
"""
|
|
13
|
+
PATH_DELIMITER="/"
|
|
14
|
+
|
|
15
|
+
def __init__(self,graph,rootNode:int,program:list=None):
|
|
16
|
+
"""
|
|
17
|
+
@Summ: constructor.
|
|
18
|
+
|
|
19
|
+
@InsVars:
|
|
20
|
+
data:
|
|
21
|
+
@Summ: {node番号(int):{edgeLabel(str):node番号(int)}}。
|
|
22
|
+
@Type: Dict
|
|
23
|
+
rootNode:
|
|
24
|
+
@Summ: root nodeのnode番号。
|
|
25
|
+
@Type: Int
|
|
26
|
+
maxNodeIdx:
|
|
27
|
+
@Summ: node番号の最大値を記録する。
|
|
28
|
+
@Type: Int
|
|
29
|
+
program:
|
|
30
|
+
@Summ: programのdataを入れる。
|
|
31
|
+
@Type: Str型二次元配列。
|
|
32
|
+
programCounter:
|
|
33
|
+
@Summ: 次に実行するprogramの行番号を記録する。
|
|
34
|
+
@Desc: 例外messageを出すために必要。
|
|
35
|
+
@Type: Int
|
|
36
|
+
programLength:
|
|
37
|
+
@Summ: 次に実行するprogramの行数を記録する。
|
|
38
|
+
@Type: Int
|
|
39
|
+
"""
|
|
40
|
+
self.graph=graph
|
|
41
|
+
self.rootNode=rootNode
|
|
42
|
+
self.maxNodeIdx=None
|
|
43
|
+
self.program=program
|
|
44
|
+
self.programCounter=0
|
|
45
|
+
if(program is None):
|
|
46
|
+
self.programLength=0
|
|
47
|
+
else:
|
|
48
|
+
self.programLength=len(program)
|
|
49
|
+
|
|
50
|
+
def loadProgramCSV(self,csvFile):
|
|
51
|
+
"""
|
|
52
|
+
@Summ: programが書かれたCSV fileをloadする関数。
|
|
53
|
+
|
|
54
|
+
@Args:
|
|
55
|
+
csvFile:
|
|
56
|
+
@Summ: programが書かれたcsv file名。
|
|
57
|
+
@SemType: str型の二次元配列。
|
|
58
|
+
"""
|
|
59
|
+
with open(csvFile,mode="r",encoding="utf-8") as f:
|
|
60
|
+
lineList=f.readlines()
|
|
61
|
+
programData=[]
|
|
62
|
+
for line in lineList:
|
|
63
|
+
if(line[-1]=="\n"):
|
|
64
|
+
notBreakLine=line[:-1]
|
|
65
|
+
else:
|
|
66
|
+
notBreakLine=line
|
|
67
|
+
argList=notBreakLine.split(",")
|
|
68
|
+
programData.append(argList)
|
|
69
|
+
self.program=programData
|
|
70
|
+
self.programLength=len(programData)
|
|
71
|
+
|
|
72
|
+
def visualizeProgram(self,filename):
|
|
73
|
+
"""
|
|
74
|
+
@Summ: programをgraph構造で可視化する関数。
|
|
75
|
+
"""
|
|
76
|
+
digraph=graphviz.Digraph()
|
|
77
|
+
digraph.filename=filename
|
|
78
|
+
digraph.format="svg"
|
|
79
|
+
digraph.attr("graph",rankdir="LR")
|
|
80
|
+
digraph.node(name="start")
|
|
81
|
+
digraph.node(name="end")
|
|
82
|
+
digraph.edge("start","row_0:command")
|
|
83
|
+
for i in range(self.programLength):
|
|
84
|
+
argList=self.program[i]
|
|
85
|
+
command=argList[0]
|
|
86
|
+
match command:
|
|
87
|
+
case "gn":
|
|
88
|
+
nodeLabel=f"<command> {command}|{argList[1]}|<jump1> {argList[2]}"
|
|
89
|
+
if(int(argList[2])<self.programLength):
|
|
90
|
+
digraph.edge(tail_name=f"row_{i}:jump1",head_name=f"row_{argList[2]}:head")
|
|
91
|
+
else:
|
|
92
|
+
digraph.edge(tail_name=f"row_{i}:jump1",head_name="end")
|
|
93
|
+
case "ge":
|
|
94
|
+
nodeLabel=f"<command> {command}|{argList[1]}|{argList[2]}|<jump1> {argList[3]}"
|
|
95
|
+
if(int(argList[3])<self.programLength):
|
|
96
|
+
digraph.edge(tail_name=f"row_{i}:jump1",head_name=f"row_{argList[3]}:head")
|
|
97
|
+
else:
|
|
98
|
+
digraph.edge(tail_name=f"row_{i}:jump1",head_name="end")
|
|
99
|
+
case "se":
|
|
100
|
+
nodeLabel=f"<command> {command}|{argList[1]}|{argList[2]}|<jump1> {argList[3]}"
|
|
101
|
+
if(int(argList[3])<self.programLength):
|
|
102
|
+
digraph.edge(tail_name=f"row_{i}:jump1",head_name=f"row_{argList[3]}:head")
|
|
103
|
+
else:
|
|
104
|
+
digraph.edge(tail_name=f"row_{i}:jump1",head_name="end")
|
|
105
|
+
case "del":
|
|
106
|
+
nodeLabel=f"<command> {command}|{argList[1]}|<jump1> {argList[2]}"
|
|
107
|
+
if(int(argList[2])<self.programLength):
|
|
108
|
+
digraph.edge(tail_name=f"row_{i}:jump1",head_name=f"row_{argList[2]}:head")
|
|
109
|
+
else:
|
|
110
|
+
digraph.edge(tail_name=f"row_{i}:jump1",head_name="end")
|
|
111
|
+
case "if":
|
|
112
|
+
nodeLabel=f"<command> {command}|{argList[1]}|{argList[2]}|<jump1> {argList[3]}|<jump2> {argList[4]}"
|
|
113
|
+
if(int(argList[3])<self.programLength):
|
|
114
|
+
digraph.edge(tail_name=f"row_{i}:jump1",head_name=f"row_{argList[3]}:head")
|
|
115
|
+
else:
|
|
116
|
+
digraph.edge(tail_name=f"row_{i}:jump1",head_name="end")
|
|
117
|
+
if(int(argList[4])<self.programLength):
|
|
118
|
+
digraph.edge(tail_name=f"row_{i}:jump2",head_name=f"row_{argList[4]}:head")
|
|
119
|
+
else:
|
|
120
|
+
digraph.edge(tail_name=f"row_{i}:jump2",head_name="end")
|
|
121
|
+
case _:
|
|
122
|
+
raise PGVMSyntaxError(self.programCounter, f"{command} is invalid command.")
|
|
123
|
+
digraph.node(name=f"row_{i}",label=nodeLabel, shape="record")
|
|
124
|
+
digraph.render()
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def __issueNewNode(self):
|
|
128
|
+
"""
|
|
129
|
+
@Summ: 新しいnode番号を発行する関数。
|
|
130
|
+
|
|
131
|
+
@Desc: 最大値を利用して新しい番号を生成している。
|
|
132
|
+
|
|
133
|
+
@Returns:
|
|
134
|
+
@Summ: 新しいnode番号。
|
|
135
|
+
@Type: Int
|
|
136
|
+
"""
|
|
137
|
+
if(self.maxNodeIdx is None):
|
|
138
|
+
self.maxNodeIdx=max(self.graph.keys())
|
|
139
|
+
newNodeIdx=self.maxNodeIdx+1
|
|
140
|
+
self.maxNodeIdx=newNodeIdx
|
|
141
|
+
self.graph[newNodeIdx]={}
|
|
142
|
+
return newNodeIdx
|
|
143
|
+
|
|
144
|
+
def visualize(self,filename):
|
|
145
|
+
"""
|
|
146
|
+
@Summ: graphvizで可視化する関数。
|
|
147
|
+
"""
|
|
148
|
+
digraph=graphviz.Digraph()
|
|
149
|
+
digraph.filename=filename
|
|
150
|
+
digraph.format="svg"
|
|
151
|
+
digraph.attr("graph",rankdir="LR")
|
|
152
|
+
for nodeIdx,edgeDict in self.graph.items():
|
|
153
|
+
# print(nodeIdx)
|
|
154
|
+
digraph.node(str(nodeIdx))
|
|
155
|
+
for edgeLabel,destNode in edgeDict.items():
|
|
156
|
+
digraph.edge(tail_name=str(nodeIdx), head_name=str(destNode), label=edgeLabel)
|
|
157
|
+
digraph.render()
|
|
158
|
+
|
|
159
|
+
def accessNode(self,path:list):
|
|
160
|
+
"""
|
|
161
|
+
@Summ: pathからedgeを参照する関数。
|
|
162
|
+
|
|
163
|
+
@Args:
|
|
164
|
+
path:
|
|
165
|
+
@Summ: 参照するedge.
|
|
166
|
+
@SemType: Str型List.
|
|
167
|
+
@Returns:
|
|
168
|
+
- @Summ: nodeの到達履歴。
|
|
169
|
+
@Desc: 到達不可能になったらそこで終了する。
|
|
170
|
+
@Type: Int型List
|
|
171
|
+
- @Summ: 使わなかったedgeLabelの数。この数字を未到達度と呼ぶ。
|
|
172
|
+
@Desc: 0で到達完了を表す。
|
|
173
|
+
@Type: Int
|
|
174
|
+
"""
|
|
175
|
+
history=[self.rootNode]
|
|
176
|
+
curEdgeDict=self.graph[self.rootNode]
|
|
177
|
+
pathLength=len(path)
|
|
178
|
+
unreached=pathLength
|
|
179
|
+
for i in range(pathLength):
|
|
180
|
+
edgeLabel=path[i]
|
|
181
|
+
curNode=curEdgeDict.get(edgeLabel)
|
|
182
|
+
if(curNode is None):
|
|
183
|
+
break
|
|
184
|
+
history.append(curNode)
|
|
185
|
+
curEdgeDict=self.graph[curNode]
|
|
186
|
+
unreached-=1
|
|
187
|
+
return history,unreached
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def execute(self):
|
|
191
|
+
"""
|
|
192
|
+
@Summ: programを実行する関数。
|
|
193
|
+
"""
|
|
194
|
+
self.programCounter=0
|
|
195
|
+
while(True):
|
|
196
|
+
if(self.programLength<=self.programCounter):
|
|
197
|
+
break
|
|
198
|
+
argList=self.program[self.programCounter]
|
|
199
|
+
command=argList[0]
|
|
200
|
+
arglen=len(argList)
|
|
201
|
+
match command:
|
|
202
|
+
case "se":
|
|
203
|
+
if(arglen!=4):
|
|
204
|
+
raise PGVMSyntaxError(self.programCounter, "the argument count is should be 4.")
|
|
205
|
+
self.switchEdge(argList[1],argList[2])
|
|
206
|
+
self.programCounter=int(argList[3])
|
|
207
|
+
case "del":
|
|
208
|
+
if(arglen!=3):
|
|
209
|
+
raise PGVMSyntaxError(self.programCounter, "the argument count is should be 3.")
|
|
210
|
+
self.executeDeletion(argList[1])
|
|
211
|
+
self.programCounter=int(argList[2])
|
|
212
|
+
case "gn":
|
|
213
|
+
if(arglen!=3):
|
|
214
|
+
raise PGVMSyntaxError(self.programCounter, "the argument count is should be 3.")
|
|
215
|
+
self.generateNode(argList[1])
|
|
216
|
+
self.programCounter=int(argList[2])
|
|
217
|
+
case "ge":
|
|
218
|
+
if(arglen!=4):
|
|
219
|
+
raise PGVMSyntaxError(self.programCounter, "the argument count is should be 4.")
|
|
220
|
+
self.generateEdge(argList[1],argList[2])
|
|
221
|
+
self.programCounter=int(argList[3])
|
|
222
|
+
case "if":
|
|
223
|
+
if(arglen!=5):
|
|
224
|
+
raise PGVMSyntaxError(self.programCounter, "the argument count is should be 5.")
|
|
225
|
+
isSame=self.executeIf(argList[1],argList[2])
|
|
226
|
+
if(isSame):
|
|
227
|
+
self.programCounter=int(argList[3])
|
|
228
|
+
else:
|
|
229
|
+
self.programCounter=int(argList[4])
|
|
230
|
+
case _:
|
|
231
|
+
raise PGVMSyntaxError(self.programCounter, "unknown command.")
|
|
232
|
+
return
|
|
233
|
+
|
|
234
|
+
def executeDeletion(self,path:str):
|
|
235
|
+
"""
|
|
236
|
+
@Summ: 到達可能なedgeを削除する関数。
|
|
237
|
+
|
|
238
|
+
@Args:
|
|
239
|
+
path:
|
|
240
|
+
@Summ: 既存のpath。このedgeを削除する。
|
|
241
|
+
@Type: Str
|
|
242
|
+
"""
|
|
243
|
+
edgeList=path.split(self.PATH_DELIMITER)
|
|
244
|
+
lastNodePath=edgeList[:-1]
|
|
245
|
+
lastEdge=edgeList[-1]
|
|
246
|
+
history,unreached=self.accessNode(lastNodePath)
|
|
247
|
+
if(unreached==0):
|
|
248
|
+
lastNode=history[-1]
|
|
249
|
+
del self.graph[lastNode][lastEdge]
|
|
250
|
+
else:
|
|
251
|
+
raise PGVMInvalidPath(self.programCounter, path)
|
|
252
|
+
|
|
253
|
+
def generateNode(self,path):
|
|
254
|
+
"""
|
|
255
|
+
@Summ: 未知のnodeへの新しいedgeを生成する関数。
|
|
256
|
+
|
|
257
|
+
@Args:
|
|
258
|
+
path:
|
|
259
|
+
@Summ: 新規生成するpathを表す。
|
|
260
|
+
@Desc: 未到達度=1である必要がある。
|
|
261
|
+
@Type: Str
|
|
262
|
+
"""
|
|
263
|
+
edgeList=path.split(self.PATH_DELIMITER)
|
|
264
|
+
history,unreached=self.accessNode(edgeList)
|
|
265
|
+
if(unreached!=1):
|
|
266
|
+
raise PGVMInvalidPath(self.programCounter, path)
|
|
267
|
+
lastEdge=edgeList[-1]
|
|
268
|
+
lastNode=history[-1]
|
|
269
|
+
newNode=self.__issueNewNode()
|
|
270
|
+
self.graph[lastNode][lastEdge]=newNode
|
|
271
|
+
|
|
272
|
+
def generateEdge(self,path1,path2):
|
|
273
|
+
"""
|
|
274
|
+
@Summ: 既知のnodeへの新しいedgeを生成する関数。
|
|
275
|
+
|
|
276
|
+
@Args:
|
|
277
|
+
path1:
|
|
278
|
+
@Summ: 終点を動かすpathを指定する。
|
|
279
|
+
@Desc: 未知のpathしか受け付けない。
|
|
280
|
+
@Type: Str
|
|
281
|
+
path2:
|
|
282
|
+
@Summ: edgeの新しい終着点を指定する。
|
|
283
|
+
@Desc: 既知のpathしか受け付けない。
|
|
284
|
+
@Type: Str
|
|
285
|
+
"""
|
|
286
|
+
path1EdgeList=path1.split(self.PATH_DELIMITER)
|
|
287
|
+
path2EdgeList=path2.split(self.PATH_DELIMITER)
|
|
288
|
+
# self.changeEdgeDestination(path1EdgeList,path2EdgeList)
|
|
289
|
+
path1history,unused1=self.accessNode(path1EdgeList)
|
|
290
|
+
lastEdgeLabel=path1EdgeList[-1]
|
|
291
|
+
if(unused1!=1):
|
|
292
|
+
raise PGVMInvalidPath(self.programCounter, path1)
|
|
293
|
+
edgeStartNode=path1history[-1]
|
|
294
|
+
path2history,unused2=self.accessNode(path2EdgeList)
|
|
295
|
+
if(unused2>0):
|
|
296
|
+
raise PGVMInvalidPath(self.programCounter, path2)
|
|
297
|
+
newEndNode=path2history[-1]
|
|
298
|
+
self.graph[edgeStartNode][lastEdgeLabel]=newEndNode
|
|
299
|
+
|
|
300
|
+
def switchEdge(self,path1,path2):
|
|
301
|
+
"""
|
|
302
|
+
@Summ: 既知のpathの終点を既知のnodeに切り替える関数。
|
|
303
|
+
|
|
304
|
+
@Desc: 始点と終点が同じ辺(二重辺)は禁止。
|
|
305
|
+
|
|
306
|
+
@Args:
|
|
307
|
+
path1:
|
|
308
|
+
@Summ: 終点を動かすpathを指定する。
|
|
309
|
+
@Desc: 既知のpathしか受け付けない。
|
|
310
|
+
@Type: Str
|
|
311
|
+
path2:
|
|
312
|
+
@Summ: edgeの新しい終着点を指定する。
|
|
313
|
+
@Desc: 既知のpathしか受け付けない。
|
|
314
|
+
@Type: Str
|
|
315
|
+
"""
|
|
316
|
+
path1EdgeList=path1.split(self.PATH_DELIMITER)
|
|
317
|
+
path2EdgeList=path2.split(self.PATH_DELIMITER)
|
|
318
|
+
path1history,unused1=self.accessNode(path1EdgeList)
|
|
319
|
+
lastEdge=path1EdgeList[-1]
|
|
320
|
+
if(unused1>0):
|
|
321
|
+
raise PGVMInvalidPath(self.programCounter, path1)
|
|
322
|
+
newStartNode=path1history[-2]
|
|
323
|
+
path2history,unreached2=self.accessNode(path2EdgeList)
|
|
324
|
+
if(unreached2>0):
|
|
325
|
+
raise PGVMInvalidPath(self.programCounter, path2)
|
|
326
|
+
newEndNode=path2history[-1]
|
|
327
|
+
edgeInfo=self.graph[newStartNode]
|
|
328
|
+
if(newEndNode in edgeInfo.values()):
|
|
329
|
+
raise PGVMRuntimeError(self.programCounter, "Edge endpoint is duplicated.") #既存のedgeの終端が被る。
|
|
330
|
+
self.graph[newStartNode][lastEdge]=newEndNode
|
|
331
|
+
|
|
332
|
+
def executeIf(self,path1,path2):
|
|
333
|
+
"""
|
|
334
|
+
@Summ: if文を実行する関数。
|
|
335
|
+
|
|
336
|
+
@Args:
|
|
337
|
+
path1:
|
|
338
|
+
@Summ: if文の第一引数。比較するpathその1。
|
|
339
|
+
@Type: Str
|
|
340
|
+
path2:
|
|
341
|
+
@Summ: if文の第二引数。比較するpathその2。
|
|
342
|
+
@Type: Str
|
|
343
|
+
@Returns:
|
|
344
|
+
@Summ: 同じ値の時にTrue.
|
|
345
|
+
@Type: Bool
|
|
346
|
+
"""
|
|
347
|
+
path1EdgeList=path1.split(self.PATH_DELIMITER)
|
|
348
|
+
path1History,path1unreached=self.accessNode(path1EdgeList)
|
|
349
|
+
path2EdgeList=path2.split(self.PATH_DELIMITER)
|
|
350
|
+
path2History,path2unreached=self.accessNode(path2EdgeList)
|
|
351
|
+
if(path1unreached!=0):
|
|
352
|
+
raise PGVMInvalidPath(self.programCounter, path1)
|
|
353
|
+
if(path2unreached!=0):
|
|
354
|
+
raise PGVMInvalidPath(self.programCounter, path2)
|
|
355
|
+
path1LastNode=path1History[-1]
|
|
356
|
+
path2LastNode=path2History[-1]
|
|
357
|
+
if(path1LastNode==path2LastNode):
|
|
358
|
+
return True
|
|
359
|
+
else:
|
|
360
|
+
return False
|
|
361
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
class PGVMRuntimeError(RuntimeError):
|
|
3
|
+
"""
|
|
4
|
+
@Summ: PGNM用の例外class.
|
|
5
|
+
"""
|
|
6
|
+
def __init__(self, line:int, message:str):
|
|
7
|
+
super().__init__()
|
|
8
|
+
self.line=line
|
|
9
|
+
self.message=message
|
|
10
|
+
|
|
11
|
+
def __str__(self):
|
|
12
|
+
return f"line: {self.line}:\n\t{self.message}"
|
|
13
|
+
|
|
14
|
+
class PGVMInvalidPath(PGVMRuntimeError):
|
|
15
|
+
"""
|
|
16
|
+
@Summ: pathが参照できない時のerror.
|
|
17
|
+
"""
|
|
18
|
+
def __init__(self, line:int, path:str):
|
|
19
|
+
super().__init__(line,message=f"{path} is not accesible.")
|
|
20
|
+
self.line=line
|
|
21
|
+
self.path=path
|
|
22
|
+
|
|
23
|
+
class PGVMSyntaxError(PGVMRuntimeError):
|
|
24
|
+
"""
|
|
25
|
+
@Summ: PGLの構文解析上のerror.
|
|
26
|
+
"""
|
|
27
|
+
def __init__(self, line:int, message:str):
|
|
28
|
+
super().__init__(line,message)
|
|
@@ -3,9 +3,10 @@ README.md
|
|
|
3
3
|
pyproject.toml
|
|
4
4
|
src/pgvm/__init__.py
|
|
5
5
|
src/pgvm/editable_graph.py
|
|
6
|
+
src/pgvm/error.py
|
|
6
7
|
src/pgvm.egg-info/PKG-INFO
|
|
7
8
|
src/pgvm.egg-info/SOURCES.txt
|
|
8
9
|
src/pgvm.egg-info/dependency_links.txt
|
|
9
10
|
src/pgvm.egg-info/requires.txt
|
|
10
11
|
src/pgvm.egg-info/top_level.txt
|
|
11
|
-
tests/
|
|
12
|
+
tests/test_graph_execution.py
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import sys
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
|
|
5
|
+
import yaml
|
|
6
|
+
|
|
7
|
+
#sys.pathを弄る。
|
|
8
|
+
projectDir=Path(__file__).parent.parent
|
|
9
|
+
packageDir=projectDir/"src"
|
|
10
|
+
sys.path.append(str(packageDir))
|
|
11
|
+
|
|
12
|
+
from pgvm import EditableGraph
|
|
13
|
+
|
|
14
|
+
def testSuitExecution(suitDir:Path):
|
|
15
|
+
"""
|
|
16
|
+
@Summ: suit単位でtestを実行する関数。
|
|
17
|
+
|
|
18
|
+
@Desc: isTestのdefault値はtrue.
|
|
19
|
+
|
|
20
|
+
@Args:
|
|
21
|
+
suitDir:
|
|
22
|
+
@Summ: test suitのdirectory.
|
|
23
|
+
@Type: Path
|
|
24
|
+
"""
|
|
25
|
+
suitInputFile=suitDir/"suit_input.yaml"
|
|
26
|
+
suitOutputFile=suitDir/"suit_output.yaml"
|
|
27
|
+
with open(suitInputFile,mode="r", encoding="utf-8") as f:
|
|
28
|
+
inputDict=yaml.safe_load(f)
|
|
29
|
+
resultDict={"detail":{}}
|
|
30
|
+
abstract=True
|
|
31
|
+
defaultConfig=inputDict.get("default")
|
|
32
|
+
if(defaultConfig is None):
|
|
33
|
+
defaultConfig={}
|
|
34
|
+
for caseDir in suitDir.iterdir():
|
|
35
|
+
if(caseDir.is_file()):
|
|
36
|
+
continue
|
|
37
|
+
caseName=caseDir.name
|
|
38
|
+
# inputDictでfiltering.
|
|
39
|
+
caseValue=inputDict.get(caseName,{})
|
|
40
|
+
caseConfig=deepcopy(defaultConfig)
|
|
41
|
+
for key,value in caseValue.items():
|
|
42
|
+
caseConfig[key]=value
|
|
43
|
+
isTest=caseConfig.get("test",False)
|
|
44
|
+
if(isTest==False):
|
|
45
|
+
continue
|
|
46
|
+
else:
|
|
47
|
+
del caseConfig["test"]
|
|
48
|
+
result=testCaseExecution(caseDir,**caseConfig)
|
|
49
|
+
if(result==False):
|
|
50
|
+
abstract=False
|
|
51
|
+
resultDict["detail"][caseName]=result
|
|
52
|
+
resultDict["abstract"]=abstract
|
|
53
|
+
with open(suitOutputFile,mode="w",encoding="utf-8") as f:
|
|
54
|
+
yaml.safe_dump(resultDict,f)
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def testCaseExecution(caseDir:Path,outputName:str="output.yaml",expectedName:str|None=None,isDetail:bool=False)->bool:
|
|
59
|
+
"""
|
|
60
|
+
@Summ: caes単位でtestを実行する関数。
|
|
61
|
+
|
|
62
|
+
@Args:
|
|
63
|
+
caseDir:
|
|
64
|
+
@Summ: test caseのdirectoryを指定する。
|
|
65
|
+
ouputName:
|
|
66
|
+
@Summ: 出力するfile名。
|
|
67
|
+
expectedName:
|
|
68
|
+
@Summ: 期待する出力file名。
|
|
69
|
+
@Desc: Noneの時は出力飲みする。
|
|
70
|
+
isDetail:
|
|
71
|
+
@Summ: 期待file以外のfileも出力する。
|
|
72
|
+
@Default: false
|
|
73
|
+
@Returns:
|
|
74
|
+
@Summ: 実際出力と期待出力が同じ時にtrue.
|
|
75
|
+
@Desc: 実際出力するだけの時もtrue.
|
|
76
|
+
"""
|
|
77
|
+
graphFile=caseDir/"graph.yaml"
|
|
78
|
+
programFile=caseDir/"program.csv"
|
|
79
|
+
beforeDot=caseDir/"before.dot"
|
|
80
|
+
afterDot=caseDir/"after.dot"
|
|
81
|
+
outputFile=caseDir/outputName
|
|
82
|
+
with open(graphFile,mode="r",encoding="utf-8") as f:
|
|
83
|
+
graphDict=yaml.safe_load(f)
|
|
84
|
+
eg01=EditableGraph(graphDict,10)
|
|
85
|
+
if(isDetail):
|
|
86
|
+
eg01.visualize(beforeDot)
|
|
87
|
+
eg01.loadProgramCSV(programFile)
|
|
88
|
+
eg01.execute()
|
|
89
|
+
with open(outputFile,mode="w",encoding="utf-8") as f:
|
|
90
|
+
yaml.safe_dump(eg01.graph,f)
|
|
91
|
+
if(isDetail):
|
|
92
|
+
eg01.visualize(afterDot)
|
|
93
|
+
# 期待fileと比較する場合。
|
|
94
|
+
if(expectedName is None):
|
|
95
|
+
return True
|
|
96
|
+
else:
|
|
97
|
+
expectedFile=caseDir/expectedName
|
|
98
|
+
with open(expectedFile,mode="r",encoding="utf-8") as f:
|
|
99
|
+
expDict=yaml.safe_load(f)
|
|
100
|
+
# 2つのdict型を比較する。
|
|
101
|
+
return expDict==eg01.graph
|
|
102
|
+
|
|
103
|
+
if(__name__=="__main__"):
|
|
104
|
+
suitDir=Path(__file__).parent/"graph_execution"
|
|
105
|
+
caseDir=Path(__file__).parent/"graph_execution"/"invalid_command"
|
|
106
|
+
# isSuccess=testCaseExecution(caseDir,"expected.yaml",None,True)
|
|
107
|
+
# print(isSuccess)
|
|
108
|
+
# testSuitExecution(suitDir)
|
|
109
|
+
testCaseExecution(caseDir,isDetail=True)
|
|
110
|
+
|
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
import graphviz
|
|
2
|
-
|
|
3
|
-
class EditableGraph():
|
|
4
|
-
"""
|
|
5
|
-
@Summ: 編集可能なgraph構造を与えるclass.
|
|
6
|
-
|
|
7
|
-
@Desc:
|
|
8
|
-
- node番号は非負整数である。負の値はerror用。
|
|
9
|
-
"""
|
|
10
|
-
ROOT_NODE=0
|
|
11
|
-
NEW_NODE=1
|
|
12
|
-
DELETE_NODE=2
|
|
13
|
-
NEW_EDGE_LABEL="new"
|
|
14
|
-
DELETE_EDGE_LABEL="delete"
|
|
15
|
-
PATH_DELIMITER="/"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@classmethod
|
|
19
|
-
def __initializeGraph(cls):
|
|
20
|
-
"""
|
|
21
|
-
@Summ: 初期状態のgraphを出力する関数。
|
|
22
|
-
"""
|
|
23
|
-
initGraph={cls.ROOT_NODE:{"new":cls.NEW_NODE, "delete":cls.DELETE_NODE},
|
|
24
|
-
cls.NEW_NODE:{},
|
|
25
|
-
cls.DELETE_NODE:{}
|
|
26
|
-
}
|
|
27
|
-
return initGraph
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def __init__(self,data,connectingNode:int,program:list=None):
|
|
31
|
-
"""
|
|
32
|
-
@Summ: constructor.
|
|
33
|
-
|
|
34
|
-
@InsVars:
|
|
35
|
-
data:
|
|
36
|
-
@Summ: {node番号(int):{edgeLabel(str):node番号(int)}}。
|
|
37
|
-
@Type: Dict
|
|
38
|
-
maxNodeIdx:
|
|
39
|
-
@Summ: node番号の最大値を記録する。
|
|
40
|
-
@Type: Int
|
|
41
|
-
program:
|
|
42
|
-
@Summ: programのdataを入れる。
|
|
43
|
-
@Type: Str型二次元配列。
|
|
44
|
-
programCounter:
|
|
45
|
-
@Summ: 次に実行するprogramの行番号を記録する。
|
|
46
|
-
@Desc: 例外messageを出すために必要。
|
|
47
|
-
@Type: Int
|
|
48
|
-
programLength:
|
|
49
|
-
@Summ: 次に実行するprogramの行数を記録する。
|
|
50
|
-
@Type: Int
|
|
51
|
-
"""
|
|
52
|
-
initGraph=self.__initializeGraph()
|
|
53
|
-
initGraph[self.ROOT_NODE]["file"]=connectingNode
|
|
54
|
-
initGraph|=data
|
|
55
|
-
self.data=initGraph
|
|
56
|
-
self.maxNodeIdx=None
|
|
57
|
-
self.program=program
|
|
58
|
-
self.programCounter=0
|
|
59
|
-
if(program is None):
|
|
60
|
-
self.programLength=0
|
|
61
|
-
else:
|
|
62
|
-
self.proramLength=len(program)
|
|
63
|
-
|
|
64
|
-
def loadProgramCSV(self,csvFile):
|
|
65
|
-
"""
|
|
66
|
-
@Summ: programが書かれたCSV fileをloadする関数。
|
|
67
|
-
|
|
68
|
-
@Args:
|
|
69
|
-
csvFile:
|
|
70
|
-
@Summ: programが書かれたcsv file名。
|
|
71
|
-
@SemType: str型の二次元配列。
|
|
72
|
-
"""
|
|
73
|
-
with open(csvFile,mode="r",encoding="utf-8") as f:
|
|
74
|
-
lineList=f.readlines()
|
|
75
|
-
programData=[]
|
|
76
|
-
for line in lineList:
|
|
77
|
-
if(line[-1]=="\n"):
|
|
78
|
-
notBreakLine=line[:-1]
|
|
79
|
-
else:
|
|
80
|
-
notBreakLine=line
|
|
81
|
-
argList=notBreakLine.split(",")
|
|
82
|
-
programData.append(argList)
|
|
83
|
-
self.program=programData
|
|
84
|
-
self.programLength=len(programData)
|
|
85
|
-
|
|
86
|
-
def __issueNewNode(self):
|
|
87
|
-
"""
|
|
88
|
-
@Summ: 新しいnode番号を発行する関数。
|
|
89
|
-
|
|
90
|
-
@Desc: 最大値を利用して新しい番号を生成している。
|
|
91
|
-
|
|
92
|
-
@Returns:
|
|
93
|
-
@Summ: 新しいnode番号。
|
|
94
|
-
@Type: Int
|
|
95
|
-
"""
|
|
96
|
-
if(self.maxNodeIdx is None):
|
|
97
|
-
self.maxNodeIdx=max(self.data.keys())
|
|
98
|
-
newNodeIdx=self.maxNodeIdx+1
|
|
99
|
-
self.maxNodeIdx=newNodeIdx
|
|
100
|
-
self.data[newNodeIdx]={}
|
|
101
|
-
return newNodeIdx
|
|
102
|
-
|
|
103
|
-
def visualize(self,filename):
|
|
104
|
-
"""
|
|
105
|
-
@Summ: graphvizで可視化する関数。
|
|
106
|
-
"""
|
|
107
|
-
graph=graphviz.Digraph()
|
|
108
|
-
graph.filename=filename
|
|
109
|
-
graph.format="svg"
|
|
110
|
-
graph.attr("graph",rankdir="LR")
|
|
111
|
-
for nodeIdx,edgeDict in self.data.items():
|
|
112
|
-
# print(nodeIdx)
|
|
113
|
-
graph.node(str(nodeIdx))
|
|
114
|
-
for edgeLabel,destNode in edgeDict.items():
|
|
115
|
-
graph.edge(tail_name=str(nodeIdx), head_name=str(destNode), label=edgeLabel)
|
|
116
|
-
graph.render()
|
|
117
|
-
|
|
118
|
-
def accessNode(self,path:list):
|
|
119
|
-
"""
|
|
120
|
-
@Summ: pathからedgeを参照する関数。
|
|
121
|
-
|
|
122
|
-
@Args:
|
|
123
|
-
path:
|
|
124
|
-
@Summ: 参照するedge.
|
|
125
|
-
@SemType: Str型List.
|
|
126
|
-
@Returns:
|
|
127
|
-
- @Summ: nodeの到達履歴。
|
|
128
|
-
@Desc: 到達不可能になったらそこで終了する。
|
|
129
|
-
@Type: Int型List
|
|
130
|
-
- @Summ: 使わなかったedgeLabelの数。この数字を未到達度と呼ぶ。
|
|
131
|
-
@Desc: 0で到達完了を表す。
|
|
132
|
-
@Type: Int
|
|
133
|
-
"""
|
|
134
|
-
history=[self.ROOT_NODE]
|
|
135
|
-
curEdgeDict=self.data[self.ROOT_NODE]
|
|
136
|
-
pathLength=len(path)
|
|
137
|
-
unreached=pathLength
|
|
138
|
-
for i in range(pathLength):
|
|
139
|
-
edgeLabel=path[i]
|
|
140
|
-
curNode=curEdgeDict.get(edgeLabel)
|
|
141
|
-
if(curNode is None):
|
|
142
|
-
break
|
|
143
|
-
history.append(curNode)
|
|
144
|
-
curEdgeDict=self.data[curNode]
|
|
145
|
-
unreached-=1
|
|
146
|
-
return history,unreached
|
|
147
|
-
|
|
148
|
-
def changeEdgeDestination(self,oldPath,newPath):
|
|
149
|
-
"""
|
|
150
|
-
@Summ: edgeの終点を変更する関数。
|
|
151
|
-
|
|
152
|
-
@Desc:
|
|
153
|
-
- nodeの生成を伴わないedgeの生成まではできる。
|
|
154
|
-
- 始点と終点が同じ辺(二重辺)は禁止。
|
|
155
|
-
|
|
156
|
-
@Args:
|
|
157
|
-
oldPath:
|
|
158
|
-
@Summ: 現在のedgeの終着点。
|
|
159
|
-
@Desc:
|
|
160
|
-
- 未到達度0の時は、既存のedgeの終点を付け替える作業。
|
|
161
|
-
- 未到達度1の時は、新しいedgeLabelを生成する。
|
|
162
|
-
- それ以上の未到達度は受け付けない。
|
|
163
|
-
@SemType: Str型List.
|
|
164
|
-
newPath:
|
|
165
|
-
@Summ: edgeの新しい終着点を指定する。
|
|
166
|
-
@Desc: 未到達度0のpathしか受け付けない。
|
|
167
|
-
@SemType: Str型List.
|
|
168
|
-
"""
|
|
169
|
-
oldDestHistory,oldUnused=self.accessNode(oldPath)
|
|
170
|
-
lastEdgeLabel=oldPath[-1]
|
|
171
|
-
if(oldUnused==0):
|
|
172
|
-
edgeStartNode=oldDestHistory[-2]
|
|
173
|
-
elif(oldUnused==1):
|
|
174
|
-
edgeStartNode=oldDestHistory[-1]
|
|
175
|
-
else:
|
|
176
|
-
raise RuntimeError(f"line: {self.programCounter}:\n\t{oldPath} is not accessable.")
|
|
177
|
-
newDestHistory,newUnused=self.accessNode(newPath)
|
|
178
|
-
if(newUnused>0):
|
|
179
|
-
raise RuntimeError(f"line: {self.programCounter}:\n\t{newPath} is not accessable.")
|
|
180
|
-
newEndNode=newDestHistory[-1]
|
|
181
|
-
edgeInfo=self.data[edgeStartNode]
|
|
182
|
-
if(newEndNode in edgeInfo.values()):
|
|
183
|
-
raise RuntimeError(f"line: {self.programCounter}:\n\t Edge endpoint is duplicated.") #既存のedgeの終端が被る。
|
|
184
|
-
self.data[edgeStartNode][lastEdgeLabel]=newEndNode
|
|
185
|
-
|
|
186
|
-
def deleteEdge(self,path):
|
|
187
|
-
"""
|
|
188
|
-
@Summ: 到達可能なedgeを削除する関数。
|
|
189
|
-
|
|
190
|
-
@Args:
|
|
191
|
-
path:
|
|
192
|
-
@Summ: 削除するedge.
|
|
193
|
-
@SemType: Str型List.
|
|
194
|
-
"""
|
|
195
|
-
lastNodePath=path[:-1]
|
|
196
|
-
lastEdge=path[-1]
|
|
197
|
-
history,unreached=self.accessNode(lastNodePath)
|
|
198
|
-
if(unreached==0):
|
|
199
|
-
lastNode=history[-1]
|
|
200
|
-
del self.data[lastNode][lastEdge]
|
|
201
|
-
else:
|
|
202
|
-
raise RuntimeError(f"line: {self.programCounter}:\n\t{path} is not accessable.")
|
|
203
|
-
|
|
204
|
-
def generateEdge(self,path):
|
|
205
|
-
"""
|
|
206
|
-
@Summ: nodeの生成を伴うedgeの生成を行う関数。
|
|
207
|
-
|
|
208
|
-
@Args:
|
|
209
|
-
path:
|
|
210
|
-
@Summ: 新しく生成するpath.
|
|
211
|
-
@Desc: 未到達度=1である必要がある。
|
|
212
|
-
@SemType: Str型List.
|
|
213
|
-
"""
|
|
214
|
-
history,unreached=self.accessNode(path)
|
|
215
|
-
if(unreached!=1):
|
|
216
|
-
raise RuntimeError(f"line: {self.programCounter}:\n\t{path} is not accessable.")
|
|
217
|
-
lastEdge=path[-1]
|
|
218
|
-
lastNode=history[-1]
|
|
219
|
-
newNode=self.__issueNewNode()
|
|
220
|
-
self.data[lastNode][lastEdge]=newNode
|
|
221
|
-
|
|
222
|
-
def execute(self):
|
|
223
|
-
"""
|
|
224
|
-
@Summ: programを実行する関数。
|
|
225
|
-
"""
|
|
226
|
-
self.programCounter=0
|
|
227
|
-
while(True):
|
|
228
|
-
if(self.programLength<=self.programCounter):
|
|
229
|
-
break
|
|
230
|
-
argList=self.program[self.programCounter]
|
|
231
|
-
if(argList[0]=="cp"):
|
|
232
|
-
self.executeCopy(argList[1],argList[2])
|
|
233
|
-
self.programCounter=int(argList[3])
|
|
234
|
-
continue
|
|
235
|
-
elif(argList[0]=="if"):
|
|
236
|
-
isSame=self.executeIf(argList[1],argList[2])
|
|
237
|
-
if(isSame):
|
|
238
|
-
self.programCounter=int(argList[3])
|
|
239
|
-
else:
|
|
240
|
-
self.programCounter=int(argList[4])
|
|
241
|
-
return
|
|
242
|
-
|
|
243
|
-
def executeCopy(self,path1,path2):
|
|
244
|
-
"""
|
|
245
|
-
@Summ: copy文を実行する関数。
|
|
246
|
-
|
|
247
|
-
@Args:
|
|
248
|
-
path1:
|
|
249
|
-
@Summ: copy文の第一引数。編集するpathを表す。
|
|
250
|
-
@Type: Str
|
|
251
|
-
path2:
|
|
252
|
-
@Summ: copy文の第二引数。上書きするpathを表す。
|
|
253
|
-
@Type: Str
|
|
254
|
-
"""
|
|
255
|
-
path1EdgeList=path1.split(self.PATH_DELIMITER)
|
|
256
|
-
if(path2==self.NEW_EDGE_LABEL):
|
|
257
|
-
self.generateEdge(path1EdgeList)
|
|
258
|
-
elif(path2==self.DELETE_EDGE_LABEL):
|
|
259
|
-
self.deleteEdge(path1EdgeList)
|
|
260
|
-
else:
|
|
261
|
-
path2EdgeList=path2.split(self.PATH_DELIMITER)
|
|
262
|
-
self.changeEdgeDestination(path1EdgeList,path2EdgeList)
|
|
263
|
-
|
|
264
|
-
def executeIf(self,path1,path2):
|
|
265
|
-
"""
|
|
266
|
-
@Summ: if文を実行する関数。
|
|
267
|
-
|
|
268
|
-
@Desc:
|
|
269
|
-
- path1==未到達度1∧path2==DELETE_NODEの時、edgeが存在しない時にTrueを出力する。
|
|
270
|
-
|
|
271
|
-
@Args:
|
|
272
|
-
path1:
|
|
273
|
-
@Summ: if文の第一引数。比較するpathその1。
|
|
274
|
-
@Type: Str
|
|
275
|
-
path2:
|
|
276
|
-
@Summ: if文の第二引数。比較するpathその2。
|
|
277
|
-
@Type: Str
|
|
278
|
-
@Returns:
|
|
279
|
-
@Summ: 同じ値の時にTrue.
|
|
280
|
-
@Type: Bool
|
|
281
|
-
"""
|
|
282
|
-
path1EdgeList=path1.split(self.PATH_DELIMITER)
|
|
283
|
-
path1History,path1unreached=self.accessNode(path1EdgeList)
|
|
284
|
-
if(path2==self.DELETE_EDGE_LABEL):
|
|
285
|
-
if(path1unreached==0):
|
|
286
|
-
return False
|
|
287
|
-
elif(path1unreached==1):
|
|
288
|
-
return True
|
|
289
|
-
else:
|
|
290
|
-
raise RuntimeError(f"line {self.programCounter}:\n\t{path2} is not accessable.")
|
|
291
|
-
path2EdgeList=path2.split(self.PATH_DELIMITER)
|
|
292
|
-
path2History,path2unreached=self.accessNode(path2EdgeList)
|
|
293
|
-
if(path1unreached!=0):
|
|
294
|
-
raise RuntimeError(f"line {self.programCounter}:\n\t{path1} is not accessable.")
|
|
295
|
-
if(path2unreached!=0):
|
|
296
|
-
raise RuntimeError(f"line {self.programCounter}:\n\t{path2} is not accessable.")
|
|
297
|
-
path1LastNode=path1History[-1]
|
|
298
|
-
path2LastNode=path2History[-1]
|
|
299
|
-
if(path1LastNode==path2LastNode):
|
|
300
|
-
return True
|
|
301
|
-
else:
|
|
302
|
-
return False
|
|
303
|
-
|
pgvm-0.1.0/tests/test_main.py
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
import sys
|
|
3
|
-
|
|
4
|
-
#sys.pathを弄る。
|
|
5
|
-
projectDir=Path(__file__).parent.parent.parent
|
|
6
|
-
sys.path.append(str(projectDir))
|
|
7
|
-
|
|
8
|
-
from library import module1
|
|
9
|
-
|
|
10
|
-
testDir=Path(__file__).parent/"test"
|
|
11
|
-
|
|
12
|
-
def test_example_file(exampleName:str,outName:str=None,isTest:bool=None)->bool:
|
|
13
|
-
"""
|
|
14
|
-
Abst: 1つの事例をtestする関数。
|
|
15
|
-
|
|
16
|
-
Expl: 入力dataや出力dataがfileの場合。
|
|
17
|
-
|
|
18
|
-
Args:
|
|
19
|
-
exampleName(str): test caseのdirecotry名。
|
|
20
|
-
outName(str): 出力先のfile名。拡張子は不要。
|
|
21
|
-
isTest(bool): 期待出力と実際出力を比較する⇒True。
|
|
22
|
-
Returns:
|
|
23
|
-
bool: 期待通りの出力⇒True。
|
|
24
|
-
"""
|
|
25
|
-
if(outName is None):
|
|
26
|
-
outName="output"
|
|
27
|
-
if(isTest is None):
|
|
28
|
-
isTest=True
|
|
29
|
-
exampleDir=testDir/exampleName
|
|
30
|
-
inFile=exampleDir/"input.txt"
|
|
31
|
-
outFile=exampleDir/f"{outName}.txt"
|
|
32
|
-
expFile=exampleDir/"expected.txt"
|
|
33
|
-
parser1=module1()
|
|
34
|
-
tree=parser1.parseFromFile(inFile)
|
|
35
|
-
with open(outFile,mode="w",encoding="utf-8") as f:
|
|
36
|
-
f.write(tree.pretty())
|
|
37
|
-
if(isTest):
|
|
38
|
-
with open(outFile,mode="r",encoding="utf-8") as f:
|
|
39
|
-
outText=f.read()
|
|
40
|
-
with open(expFile,mode='r',encoding="utf-8") as f:
|
|
41
|
-
expText=f.read()
|
|
42
|
-
return outText==expText
|
|
43
|
-
|
|
44
|
-
def test_example_dir(exampleName:str,outName:str=None,isTest:bool=None)->bool:
|
|
45
|
-
"""
|
|
46
|
-
Abst: 1つの事例をtestする関数。
|
|
47
|
-
|
|
48
|
-
Expl: 入力dataや出力dataがdirectoryの場合。
|
|
49
|
-
|
|
50
|
-
Args:
|
|
51
|
-
exampleName(str): test caseのdirecotry名。
|
|
52
|
-
outName(str): 出力先のfile名。拡張子は不要。
|
|
53
|
-
isTest(bool): 期待出力と実際出力を比較する⇒True。
|
|
54
|
-
Returns:
|
|
55
|
-
bool: 期待通りの出力⇒True。
|
|
56
|
-
"""
|
|
57
|
-
if(outName is None):
|
|
58
|
-
outName="output"
|
|
59
|
-
if(isTest is None):
|
|
60
|
-
isTest=True
|
|
61
|
-
exampleDir=testDir/exampleName
|
|
62
|
-
inDir=exampleDir/"in"
|
|
63
|
-
inFile=inDir/"in.txt"
|
|
64
|
-
outDir=exampleDir/f"{outName}"
|
|
65
|
-
if(not outDir.is_dir()):
|
|
66
|
-
outDir.mkdir()
|
|
67
|
-
outFile=outDir/"out.txt"
|
|
68
|
-
expDir=exampleDir/"exp"
|
|
69
|
-
expFile=expDir/"exp.txt"
|
|
70
|
-
parser1=module1()
|
|
71
|
-
tree=parser1.parseFromFile(inFile)
|
|
72
|
-
with open(outFile,mode="w",encoding="utf-8") as f:
|
|
73
|
-
f.write(tree.pretty())
|
|
74
|
-
if(isTest):
|
|
75
|
-
with open(outFile,mode="r",encoding="utf-8") as f:
|
|
76
|
-
outText=f.read()
|
|
77
|
-
with open(expFile,mode='r',encoding="utf-8") as f:
|
|
78
|
-
expText=f.read()
|
|
79
|
-
return outText==expText
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def test_all(outName:str=None,isTest:bool=None)->dict:
|
|
83
|
-
"""
|
|
84
|
-
Abst: 全てのtest caseをtestする関数。
|
|
85
|
-
|
|
86
|
-
Args:
|
|
87
|
-
outName(str): 出力先のfile名。拡張子は不要。
|
|
88
|
-
isTest(bool): 期待出力と実際出力を比較する⇒True。
|
|
89
|
-
Returns:
|
|
90
|
-
dict: {testCase名(str):真理値(bool)}
|
|
91
|
-
"""
|
|
92
|
-
resultDict={}
|
|
93
|
-
for exampleDir in testDir.iterdir():
|
|
94
|
-
exampleName=exampleDir.name
|
|
95
|
-
result=test_example_dir(exampleName,outName=outName,isTest=isTest)
|
|
96
|
-
resultDict[exampleName]=result
|
|
97
|
-
return resultDict
|
|
98
|
-
|
|
99
|
-
def test_clear(deleteList:list):
|
|
100
|
-
"""
|
|
101
|
-
Abst: test caseのdirectoryを綺麗にする関数。
|
|
102
|
-
|
|
103
|
-
Expl: fileの削除にしか対応していない。
|
|
104
|
-
|
|
105
|
-
Notes: blacklist方式にすることで、意図しないfile削除を起こさないようにしている。
|
|
106
|
-
|
|
107
|
-
Args:
|
|
108
|
-
deleteList(list): 削除するfile名のlist。file名はexample directoryからの相対pathで指定する。
|
|
109
|
-
"""
|
|
110
|
-
for exampleDir in testDir.iterdir():
|
|
111
|
-
for fileDir in exampleDir.iterdir():
|
|
112
|
-
fileName=fileDir.name
|
|
113
|
-
if(fileName in deleteList):
|
|
114
|
-
fileDir.unlink()
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if(__name__=="__main__"):
|
|
118
|
-
tup=("case2", 70)
|
|
119
|
-
x=tup[0]
|
|
120
|
-
y=tup[1]
|
|
121
|
-
test_example_file("case01","out",isTest=False)
|
|
122
|
-
# test_all("out",isTest=False)
|
|
123
|
-
|
|
124
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|