parser.py 18.4 KB
Newer Older
1 2 3 4
import re
import copy
import importlib as imp

5
from comsdk.graph import Graph, Func, State, Selector
6 7
from comsdk.edge import Edge

8

9 10 11 12 13 14 15 16 17 18
class Params():
    __slots__=(
        'module',
        'entry_func',
        'predicate',
        'selector',
        'function',
        'morphism',
        'parallelism',
        'comment',
19 20
        'order',
        'subgraph'
21 22 23 24 25 26 27 28
    )
    def __init__(self):
        for slot in self.__slots__:
            setattr(self, slot, None)
    
    def __str__(self):
        stri = ""
        for s in self.__slots__:
29
            stri += ((s+": {}, ".format(getattr(self, s))) if getattr(self, s) is not None else "")
30 31
        return stri

32
# entities = {}
33 34 35

class GraphFactory():
    __slots__ = (
36
        'name',
37 38
        'states', 
        'graph',
39
        'issub',
40 41 42 43 44
        'tocpp',
        'entities'
    )
    def __init__(self, tocpp=False):
        self.states = {}
45
        self.entities = {}
46
        self.tocpp = tocpp
47 48 49
        self.name = None
        self.issub = False

50
    def add_state(self, statename):
51
        if statename not in self.states:
52
            self.states[statename] = State(statename)
Savva Golubitsky's avatar
Savva Golubitsky committed
53 54
            if statename in self.entities:
                self.states[statename].comment = self.entities[statename].comment
55 56
    
    def _create_morphism(self, morphname=None):
57
        comment = ""
58
        if morphname is None:
59
            return Func(), Func(), comment
60
        pred_f, func_f = Func(), Func() 
61
        morph = self.entities[morphname]
62
        for m in morph.__slots__:
63 64 65 66 67
            if getattr(morph,m) is not None:
                if m!="predicate" and m!="function" and m!="comment":
                    raise Exception("ERROR: Morphisms could not have any params exept comment, predicate and function!\n{}".format(morphname))
                if m=="comment":
                    comment=getattr(morph, m).replace("\0", " ")
68
                if m=="predicate":
69 70 71
                    if getattr(morph,m) not in self.entities:
                        raise Exception("\tERROR: Predicate {} is not defined!".format(getattr(morph, m)))
                    pred = self.entities[getattr(morph, m)]
72
                    if self.tocpp:
73
                        pred_f = Func(pred.module, pred.entry_func, dummy=True, comment=pred.comment)
74
                    else:
75
                        pred_f = Func(pred.module, pred.entry_func, comment=pred.comment)
76
                if m=="function":
77 78 79
                    if getattr(morph,m) not in self.entities:
                       raise Exception("\tERROR: Function: {} is not defined!".format(getattr(morph, m)))
                    fu = self.entities[getattr(morph, m)]
80
                    if self.tocpp:
81
                        func_f = Func(fu.module, fu.entry_func, dummy=True, comment=fu.comment)
82
                    else:
83 84
                        func_f = Func(fu.module, fu.entry_func,comment=fu.comment)
        return pred_f, func_f, comment
85 86


87
    def add_connection(self, st1, st2, morphism=None, ordr=0):
88 89
        pred, entr, comm = self._create_morphism(morphism)
        self.states[st1].connect_to(self.states[st2], edge=Edge(pred, entr, order=ordr, comment=comm))
90
        print("{} -> {}".format(st1, st2))
91

92
    def build(self, nsub):
93 94 95 96
        print("BUILDING {}\nStates:".format(self.name))
        for s in self.states:
            print("\t"+ s)
        if self.issub:
97
            self.graph = Graph(self.states[self.name+str(nsub)+"_"+"__BEGIN__"], self.states[self.name+str(nsub)+"_"+"__END__"])
98 99 100
        else:    
            self.graph = Graph(self.states["__BEGIN__"], self.states["__END__"])
        self.graph.init_graph()
101 102 103 104 105 106 107 108
        if self.issub:
            oldkeys = []
            for e in self.entities:
                oldkeys.append(e)
            for old in oldkeys:
                if self.entities[old].selector is not None or self.entities[old].subgraph is not None:
                    self.entities[self.name + str(Parser.subgr_count)+"_"+old] = self.entities[old]
                    del self.entities[old] 
109
        for s in self.states:
110
            if s in self.entities and self.entities[s].selector is not None:
111
                selname = self.entities[s].selector
112
                if self.tocpp:
113 114 115
                    self.states[s].selector = Selector(len(self.states[s].transfers), self.entities[selname].module, self.entities[selname].entry_func, dummy=True)
                else:
                    self.states[s].selector = Selector(len(self.states[s].transfers), self.entities[selname].module, self.entities[selname].entry_func)
116 117
            else:
                self.states[s].selector =  Selector(len(self.states[s].transfers))
118
            if s in self.entities and self.entities[s].subgraph is not None:
119
                print("Replacing state {} with subgraph {}".format(s,self.entities[s].subgraph))
120
                parsr = Parser(subgraph=True, tocpp= self.tocpp)
121
                subgr = parsr.parse_file(self.entities[s].subgraph)
122
                self.states[s].replace_with_graph(subgr)
123
                self.graph = Graph(self.graph.init_state, self.graph.term_state)
124 125
        return self.graph

Savva Golubitsky's avatar
Savva Golubitsky committed
126

127 128
class Parser():
    __slots__ = (
129 130
        'fact',
        'issub'
131
    )
132
    subgr_count = 0
133
    def __init__(self, tocpp=False, subgraph=False):
134
        self.fact = GraphFactory(tocpp=tocpp)
135 136
        self.fact.issub = subgraph
        self.issub = subgraph
137 138
        if subgraph:
            Parser.subgr_count+=1
139 140
    
    def _check_brackets(self, rawfile):
141 142
        br = { "[":{"line":0, "count":0}, "(":{"line":0, "count":0}, "{":{"line":0, "count":0}, "\"":{"line":0, "count":0}}
        line = 1
143 144 145
        qu = 0
        for char in rawfile:
            if char == "[":
146 147
                br["["]["line"] = line
                br["["]["count"] +=1 
148
            elif char == "{":
149 150
                br["{"]["line"] = line
                br["{"]["count"] +=1 
151
            elif char == "(":
152 153
                br["("]["line"] = line
                br["("]["count"] +=1 
154
            elif char == "]":
155
                br["["]["count"] -=1 
156
            elif char == "}":
157
                br["{"]["count"] -=1 
158
            elif char == ")":
159
                br["("]["count"] -=1 
160
            elif char =="\"":
161 162 163 164 165 166 167 168 169 170 171 172
                br["\""]["line"] = line
                br["\""]["count"] += 1 if br["\""]["count"]==0 else -1
            elif char == "\n":
                line+=1
        expstr= "Brackets or quotes do not match! Missing closing brackets on lines: "
        fl = False
        for c in br:
            if br[c]["count"] != 0:
                fl= True
                expstr+=str(br[c]["line"])+" "
        if fl:
            raise Exception(expstr)
173 174

    def _split_multiple(self,param):
175
        vals = {}
176 177 178
        first=True
        for s in param.__slots__:
            attr = getattr(param,s)
179
            if attr is not None and '\0' in attr:
180 181 182 183 184 185
                vals[s] = attr.split('\0')
        l=0
        for sl in vals:
            if l==0:
                l=len(vals[sl])
            elif l!=len(vals[sl]):
186
                raise Exception("\tERROR: Number of multiple params do not match", l)
187 188 189 190
        res = [copy.copy(param) for i in range(l)]
        for sl in vals:
            for i, _ in enumerate(res):
                setattr(res[i], sl, vals[sl][i])
191 192 193 194 195
        return res

    #Props is line "[proFp=smth, ...]"
    def _param_from_props(self,props):
        parm = Params()
196
        comment = ""
197 198
        if props =="":
            return parm
199
        props = props.replace("]", '')
200
        if '\"' in props:
201 202 203
            m = [m for m in re.finditer(r'\".*\"', props)][0]
            comment = props[m.span()[0]+1:m.span()[1]-1]
            props=props[:m.span()[0]]+props[m.span()[1]:]
Savva Golubitsky's avatar
Savva Golubitsky committed
204 205 206 207
        if '(' in props:
            mchs = [m for m in re.finditer(r'\((\w+,)*\w+\)', props)]
            for m in mchs:
                props=props[:m.span()[0]]+(props[m.span()[0]:m.span()[1]]).replace(',','\0')+props[m.span()[1]:]
208 209
        props = props.replace("(","")
        props = props.replace(")","")
210 211 212 213 214 215
        rs =props.split(r",") #.split(r", ")
        for r in rs:
            r=r.split(r"=", 1)
            if r[0] in parm.__slots__:
                setattr(parm, r[0], r[1])
            else:
216
                raise Exception("\tERROR:Unknown parameter: "+ r[0])
217 218
        if comment != "": 
            setattr(parm, "comment", comment.replace("\0", " "))
219 220 221 222 223 224
        return parm

    def _param_from_entln(self, raw):
        res = re.split(r"\[", raw, 1)
        return res[0], self._param_from_props(res[1])

225 226 227 228 229 230 231
    def _multiple_morphs(self,props, n):
        p = self._param_from_props(props)
        if p.morphism is None:
            return  [copy.copy(p) for i in range(n)] 
        else:
            return self._split_multiple(p)

232 233 234 235 236
    def _topology(self,raw):
        spl = re.split(r"\s*(=>|->|\[|\])\s*", raw)
        spl = list(filter(lambda x: x!="[" and x!="]" and x!="", spl))
        left = spl[0].split(",")
        right = spl[2].split(",")
237 238
        if self.issub:
            for i in range(len(left)):
239
                left[i] = self.fact.name + str(Parser.subgr_count) + "_" + left[i]
240
            for i in range(len(right)):
241
                right[i] = self.fact.name + str(Parser.subgr_count) + "_" + right[i]
242
        if (len(left)>1) and (len(right)>1):
243
            raise Exception("ERROR: Ambigious multiple connection in line:\n\t{}".format(raw))
244 245
        # many to one conection
        elif len(left)>1:
246 247 248
            if len(spl) < 4:
                spl.append("")
            morphs = self._multiple_morphs(spl[3], len(left))
249
            if len(morphs)!=len(left):
250
                raise Exception("\tERROR: Count of edges do not match to count of states in many to one connection!\n\t\t{}".format(raw))
251 252 253 254 255 256
            self.fact.add_state(right[0])
            for i, st in enumerate(left):
                self.fact.add_state(st)
                self.fact.add_connection(st, right[0], morphs[i].morphism)
        # one to many connection, here could be selector
        elif len(right)>1:
257 258 259
            if len(spl) < 4:
                spl.append("")
            morphs = self._multiple_morphs(spl[3], len(right))
260
            self.fact.add_state(left[0])
261
            if len(morphs)!=len(right):
262
                raise Exception("\tERROR: Count of edges do not match to count of states in one to many connection!\n\t\t{}".format(raw))
263 264
            for i, st in enumerate(right):
                self.fact.add_state(st)
265
                self.fact.add_connection(left[0], st, morphs[i].morphism, morphs[i].order)
266 267 268 269 270 271
        # one to one connection
        else:
            self.fact.add_state(left[0])
            self.fact.add_state(right[0])
            if len(spl)==4:
                pr =self._param_from_props(spl[3])
Savva Golubitsky's avatar
Savva Golubitsky committed
272
                self.fact.add_connection(left[0], right[0], pr.morphism, ordr=pr.order if pr.order is not None else 0)
273 274 275 276
            elif len(spl)==3:
                self.fact.add_connection(left[0], right[0], None)

    def parse_file(self, filename):
277 278
        # @todo В случае, если на вход будет подан файл в отличной от UTF-8 кодировке программа работать не будет
        file = open(filename, encoding='utf-8')# "r")
279 280 281
        dot = file.read()
        self._check_brackets(dot)
        
282 283 284
        comments = [m for m in re.finditer(r'\".*\"', dot)]
        for m in comments:
            dot=dot[:m.span()[0]]+(dot[m.span()[0]:m.span()[1]]).replace(' ','\0')+dot[m.span()[1]:]
285
        dot = re.sub(r"[ \t\r]", "", dot) #deleting all spaces
286
        dot = re.sub(r"((digraph)|}|{)", "", dot)
287 288 289 290
        dot = re.sub(r"\/\/.*", "", dot)
        dot = re.sub(r"^\n$", "", dot)
        dotlines = dot.splitlines()
        dotlines = list(filter(None, dotlines))
291 292
        self.fact.name = dotlines[0]
        dotlines = dotlines[1:]
293 294 295 296 297 298 299 300
        # ent_re - regular expr for edges, states, functions properties
        ent_re = re.compile(r"^\w+\[.*\]$")
        # top_re - regular expr for topology properties, most time consuming one
        top_re = re.compile(r"^(\w+,?)+(->|=>)(\w+,?)+(\[(\w+=(\(?\w+,?\)?)+,?)+\])?")
        # (r"^\w[\w\s,]*(->|=>)\s*\w[\w\s,=\[\]()]*$")
        for i, ln in enumerate(dotlines):
            if ent_re.match(ln):
                name, parm = self._param_from_entln(ln)
301
                self.fact.entities[name] = parm
302 303
            elif top_re.match(ln):
                self._topology(ln)
304
        return self.fact.build(Parser.subgr_count)
305

306
    checked=[]
Savva Golubitsky's avatar
Savva Golubitsky committed
307 308
    bushes = {}
    selectorends = {}
Savva Golubitsky's avatar
Savva Golubitsky committed
309
    def generate_cpp(self, filename=None):
310
        self.fact.graph.init_state.input_edges_number =0
311 312 313 314 315 316 317
        states_to_check = [self.fact.graph.init_state]
        while len(states_to_check)!=0:
            for st in states_to_check:
                self.checked.append(st)
                states_to_check.remove(st)
                bush = _Bush(st)
                bush.grow_bush()
Savva Golubitsky's avatar
Savva Golubitsky committed
318
                self.bushes[st] = bush
319 320 321
                for outs in bush.outstates:
                    if outs not in states_to_check and outs not in self.checked:
                        states_to_check.append(outs)
322
        send_token(self.fact.graph.init_state, self.bushes, [])
Savva Golubitsky's avatar
Savva Golubitsky committed
323 324
        preds, morphs, sels, st, body = print_graph(self.fact.graph.init_state, self.fact.entities, self.bushes)
        from mako.template import Template
Savva Golubitsky's avatar
Savva Golubitsky committed
325 326 327 328
        if filename is not None:
            f = open(filename, "w")
        else:
            f= open(self.fact.name + ".cpp", "w")
Savva Golubitsky's avatar
Savva Golubitsky committed
329
        print(Template(filename="./cpp/template.cpp").render(preds=preds, morphs = morphs, sels = sels, states=st, body=body), file=f)
330

Savva Golubitsky's avatar
Savva Golubitsky committed
331
def print_graph(cur_state, entities, bushes):
332
    checked = []
Savva Golubitsky's avatar
Savva Golubitsky committed
333 334 335
    toloadpred = []
    toloadmorph = []
    toloadsel =[]
336
    tocheck = [cur_state]
Savva Golubitsky's avatar
Savva Golubitsky committed
337
    body = ""
338 339 340
    while len(tocheck) !=0:
        cur_state=tocheck[0]
        cur_b = bushes[cur_state]
341 342 343 344 345 346 347 348 349
        cur_b.token+=1
        if cur_b.token < cur_b.state.input_edges_number - cur_b.state.looped_edges_number:
            tocheck.remove(cur_state)
            tocheck.append(cur_state)
            continue
        if cur_state in checked:
            tocheck.remove(cur_state)
            continue
        if len(cur_b.branches)>1 or len(cur_b.incomes)>1:
Savva Golubitsky's avatar
Savva Golubitsky committed
350
            body+="{}:\n".format(cur_state.name)
351
        if len(cur_b.incomes)!=0:
352 353 354
            if cur_b.state.comment!="" and cur_b.state.comment is not None:
                print("STcomm:", cur_b.state.comment)
                body+="//"+cur_b.state.comment+"\n"
355 356 357
            stri = "false "
            for inc in cur_b.incomes:
                stri += "|| SEL_{}[{}] ".format(inc["st"].name, inc["i"])
Savva Golubitsky's avatar
Savva Golubitsky committed
358 359
            body+="if (!({}))".format(stri)
            body+="{\n\tfor (int seli = 0;"+" seli < {};".format(len(cur_state.transfers))+" seli++)\n"+ "\t\tSEL_{}[seli]=false;".format(cur_state.name)+"\n}"
360 361
            if cur_state.selector.name != "":
                # print(cur_state.name, cur_state.selector)
Savva Golubitsky's avatar
Savva Golubitsky committed
362 363
                if cur_state.selector not in toloadsel:
                    toloadsel.append(cur_state.selector)
Savva Golubitsky's avatar
Savva Golubitsky committed
364
                body+="else {\n"+ "\tSEL_{} = {}(&data);//{}\n".format(cur_state.name, cur_state.selector, cur_state.selector.comment )+"}\n"
365
            else:
Savva Golubitsky's avatar
Savva Golubitsky committed
366
                body+="else {\n\tfor (int seli = 0;"+" seli < {};".format(len(cur_state.transfers))+" seli++)\n"+"\t\tSEL_{}[seli]=true;".format(cur_state.name)+"\n}\n"
367
        for i, br in enumerate(cur_b.branches):
Savva Golubitsky's avatar
Savva Golubitsky committed
368
            body+="if (SEL_{}[{}])".format(cur_state.name, i)+"{\n"
Savva Golubitsky's avatar
Savva Golubitsky committed
369 370 371 372 373 374 375 376
            if br[len(br)-1].output_state not in tocheck:
                tocheck.append(br[len(br)-1].output_state)
            if br[len(br)-1].output_state in checked or br[len(br)-1].output_state is cur_state:
                stri, toloadpred, toloadmorph = cur_b.cpp_branch(i, toloadpred, toloadmorph)
                body+=stri+"\tgoto {};\n".format(br[len(br)-1].output_state.name)+"}\n"
            else:
                stri, toloadpred, toloadmorph = cur_b.cpp_branch(i, toloadpred, toloadmorph) 
                body+=stri+"}\n"
377
        tocheck.remove(cur_state)
Savva Golubitsky's avatar
Savva Golubitsky committed
378
        checked.append(cur_state)
Savva Golubitsky's avatar
Savva Golubitsky committed
379
    return _unique(toloadpred), _unique(toloadmorph), _unique(toloadsel), checked, body
380

Savva Golubitsky's avatar
Savva Golubitsky committed
381
def _unique(lst):
Savva Golubitsky's avatar
Savva Golubitsky committed
382 383 384 385 386
    for i, el in enumerate(lst):
        for el2 in lst[i+1:]:
            if el2.module == el.module and el2.name == el.name:
                lst.remove(el2)
    return lst
387

388
def send_token(cur_state, bushes, checked):
Savva Golubitsky's avatar
Savva Golubitsky committed
389 390 391 392 393
    cur_b = bushes[cur_state]
    if cur_state in checked:
        return
    if len(cur_b.outstates)==0:
        return
394
    if len(cur_b.incomes) == cur_b.state.input_edges_number - cur_b.state.looped_edges_number:
Savva Golubitsky's avatar
Savva Golubitsky committed
395
        checked.append(cur_state)
396
        for i,br in enumerate(cur_b.branches):
Savva Golubitsky's avatar
Savva Golubitsky committed
397 398
            bushes[br[len(br)-1].output_state].incomes.append({"st":cur_state, "i":i})
            send_token(br[len(br)-1].output_state,bushes, checked)
399 400 401 402 403 404

class _Bush():
    __slots__=(
        'state', 
        'selector',
        'branches',
Savva Golubitsky's avatar
Savva Golubitsky committed
405 406 407 408
        'outstates',
        'token',
        'incomes',
        'selectorfin'
409 410 411 412 413 414 415
    )

    def __init__(self, state):
        self.state = state
        self.selector = state.selector
        self.branches = []
        self.outstates = []
Savva Golubitsky's avatar
Savva Golubitsky committed
416
        self.token = 0
417
        self.incomes = []
418 419 420

    def grow_bush(self):
        for t in self.state.transfers:
Savva Golubitsky's avatar
Savva Golubitsky committed
421
            branch = [t]
422 423 424 425
            self._gen_branch(t.output_state, branch)
        
    def _gen_branch(self, cur_state, branch):
        while len(cur_state.transfers)==1 and cur_state.input_edges_number==1:
426 427
            if cur_state._proxy_state is not None:
                cur_state=cur_state._proxy_state
428
            tr = cur_state.transfers[0]
Savva Golubitsky's avatar
Savva Golubitsky committed
429
            branch.append(tr)
430 431 432
            cur_state = tr.output_state
        self.branches.append(branch)
        if cur_state not in self.outstates:
Savva Golubitsky's avatar
Savva Golubitsky committed
433
            self.outstates.append(cur_state)
Savva Golubitsky's avatar
Savva Golubitsky committed
434 435 436 437 438 439 440 441 442 443
        
    def cpp_branch(self, i, toloadpred, toloadmorph):
        res = ""
        for tr in self.branches[i]:
            edge = tr.edge
            if edge.comment!="":
                res+="\t//{}\n".format(edge.comment)
            if edge.pred_f.name != "":
                if edge.pred_f not in toloadpred:
                    toloadpred.append(edge.pred_f)
Savva Golubitsky's avatar
Savva Golubitsky committed
444
                res+="\tcheck_pred({}(&data), \"{}\");".format(edge.pred_f, edge.pred_f)
Savva Golubitsky's avatar
Savva Golubitsky committed
445 446 447 448
                res+="//{}\n".format(edge.pred_f.comment) if edge.pred_f.comment != "" else "\n"
            if edge.morph_f.name != "":
                if edge.morph_f not in toloadmorph:
                    toloadmorph.append(edge.morph_f)
Savva Golubitsky's avatar
Savva Golubitsky committed
449
                res+="\t{}(&data);".format(edge.morph_f)
Savva Golubitsky's avatar
Savva Golubitsky committed
450 451 452 453
                res+="//{}\n".format(edge.morph_f.comment) if edge.morph_f.comment != "" else "\n"
        return res, toloadpred, toloadmorph


Savva Golubitsky's avatar
Savva Golubitsky committed
454 455