pgvm 0.1.0__py3-none-any.whl
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/__init__.py +5 -0
- pgvm/editable_graph.py +303 -0
- pgvm-0.1.0.dist-info/METADATA +31 -0
- pgvm-0.1.0.dist-info/RECORD +7 -0
- pgvm-0.1.0.dist-info/WHEEL +5 -0
- pgvm-0.1.0.dist-info/licenses/LICENSE.txt +7 -0
- pgvm-0.1.0.dist-info/top_level.txt +1 -0
pgvm/__init__.py
ADDED
pgvm/editable_graph.py
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
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
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pgvm
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: PGVM is an abbreviation for Pointer Graph Virtual Machine.
|
|
5
|
+
Author-email: masaniki <masaniki.software@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/masaniki/python-pgvm
|
|
8
|
+
Project-URL: Issues, https://github.com/masaniki/python-pgvm/issues
|
|
9
|
+
Keywords: graph,node,edge,automaton,processor
|
|
10
|
+
Classifier: Development Status :: 1 - Planning
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Natural Language :: Japanese
|
|
14
|
+
Classifier: Natural Language :: English
|
|
15
|
+
Classifier: Intended Audience :: Information Technology
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE.txt
|
|
19
|
+
Requires-Dist: PyYAML
|
|
20
|
+
Requires-Dist: graphviz
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# Introduction
|
|
24
|
+
|
|
25
|
+
This is a introduction.
|
|
26
|
+
|
|
27
|
+
Original document is [HERE](docs/README_JP.md).
|
|
28
|
+
|
|
29
|
+
# Installing
|
|
30
|
+
|
|
31
|
+
`pip install pgvm`
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
pgvm/__init__.py,sha256=GY_Au96x1eHCwTyFeo2u7ECsEo1nLIgB30UxSTX774g,75
|
|
2
|
+
pgvm/editable_graph.py,sha256=c40N4VQXM2JYudWl7-J7P6qvrpCHV2B9FBPwFH2nPJ0,9389
|
|
3
|
+
pgvm-0.1.0.dist-info/licenses/LICENSE.txt,sha256=7oOMsj5XGQQ_SippluC7AuCMB_Vrujo2jqT0QkRkN64,1061
|
|
4
|
+
pgvm-0.1.0.dist-info/METADATA,sha256=4wMpxOPO5CgCtou0yOhwme0NgwmZo8BpC1hoUTKY7OQ,965
|
|
5
|
+
pgvm-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
6
|
+
pgvm-0.1.0.dist-info/top_level.txt,sha256=rparoIlC_FHLpXxMvM7xTajsiJYPX0ghdwMbPK3XO3w,5
|
|
7
|
+
pgvm-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2026 masaniki
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pgvm
|