compiler.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. '''*****************************************************************************
  2. AToMPM - A Tool for Multi-Paradigm Modelling
  3. Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
  4. This file is part of AToMPM.
  5. AToMPM is free software: you can redistribute it and/or modify it under the
  6. terms of the GNU Lesser General Public License as published by the Free Software
  7. Foundation, either version 3 of the License, or (at your option) any later
  8. version.
  9. AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  11. PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with AToMPM. If not, see <http://www.gnu.org/licenses/>.
  14. *****************************************************************************'''
  15. import re, json, uuid, itertools
  16. from dcal import DesignerCodeAbstractionLayer
  17. from utils import Utilities as utils
  18. from tconstants import TConstants as TC
  19. from pytcore.core.himesis import Himesis, HConstants as HC
  20. from pytcore.core.himesis import HimesisPreConditionPatternLHS
  21. from pytcore.core.himesis import HimesisPreConditionPatternNAC
  22. from pytcore.core.himesis import HimesisPostConditionPattern
  23. '''
  24. implements an atompm-to-himesis model compiler and an
  25. atompm-to-himesis rule compiler
  26. _username the user's username, needed to access files from user's
  27. folder
  28. _aswid the user's backend's aswid, needed to support httpreqs to
  29. backend from rule code
  30. _defaultDCL the user's preferred designer code language
  31. _subtypes a mapping from metamodel types to their subtypes
  32. _connectorTypes a set of known 'connector' fulltypes
  33. _knownMMs a set of known metamodels (i.e., metamodels for which we've
  34. computed subtype and connectorType information)
  35. _loadedMMs a set of currently loaded (on the asworker) metamodels
  36. _mmTypeData a mapping of fulltypes to default attributes
  37. _dcal an instance of the DesignerCodeAbstractionLayer
  38. _compiledRules a mapping between rule filenames and compiled data
  39. *runtime configuration flags*
  40. RC__looseSubtypingMM (see _computeSubtypes() for details) '''
  41. class ModelAndRuleCompiler :
  42. def __init__(self,username,aswid,defaultDCL,mtwid) :
  43. self._username = username
  44. self._aswid = aswid
  45. self._defaultDCL = defaultDCL
  46. self._subtypes = {}
  47. self._connectorTypes = set()
  48. self._knownMMs = set()
  49. self._loadedMMs = set()
  50. self._mmTypeData = {}
  51. self._mtwid = mtwid;
  52. self._dcal = DesignerCodeAbstractionLayer(username,aswid,mtwid)
  53. self._compiledRules = {}
  54. self.RC__looseSubtypingMM = None
  55. '''
  56. add a node to himesis graph 'hg' that reflects the contents of asworker
  57. node 'n' with id 'id' '''
  58. def addNode(self,hg,n,id) :
  59. newNodeIndex = \
  60. hg.add_node(n['$type'],n['$type'] in self._connectorTypes)
  61. hg.vs[newNodeIndex]['$atompmId'] = str(id)
  62. for attr in n :
  63. attr = str(attr)
  64. if attr.startswith('$') :
  65. hg.vs[newNodeIndex][attr] = n[attr]
  66. elif attr.startswith('____'):
  67. hg.vs[newNodeIndex][attr[2:]] = n[attr]['value']
  68. elif 'value' in n[attr]:
  69. hg.vs[newNodeIndex][attr] = n[attr]['value']
  70. return newNodeIndex
  71. '''
  72. compile an atompm json model into a pytcore himesis graph
  73. 0. compute and store the relevant metamodels' subtypes and connector
  74. types if we haven't already done so
  75. 1. initialise a Himesis/HimesisPreConditionPatternLHS/... graph 'hg'
  76. 2. in the special case where compileModel() is called as a result of a
  77. user loading a model (i.e., from ptcal.loadModel(), detectable because
  78. himesisBaseClass==Himesis), we also (re)set self._loadedMMs to match
  79. the given model's loaded metamodels
  80. 3. if input model is empty, return 'hg'
  81. 4. for each node in input model, create appropriate node in 'hg'
  82. 5. for each edge in input model, create appropriate edge in 'hg'
  83. 6. return 'hg'
  84. NOTE: igraph.add_vertices() crashes when attribute names are unicode
  85. strings (i.e., u'...')... this is why we convert attr names to
  86. ascii via 'attr = str(attr)' '''
  87. def compileModel(self,m,mmsData=None,name=None,himesisBaseClass=Himesis) :
  88. if m.__class__ != {}.__class__ :
  89. m = json.loads(m)
  90. if mmsData != None :
  91. if mmsData.__class__ != {}.__class__ :
  92. mmsData = json.loads(mmsData)
  93. for mm in mmsData :
  94. if mm not in self._knownMMs :
  95. self.parseMetamodel(mm,mmsData[mm])
  96. else :
  97. for mm in m['metamodels'] :
  98. if mm not in self._knownMMs :
  99. mmData = utils.fread(
  100. '/users/%s/%s.metamodel'%(self._username,mm))
  101. self.parseMetamodel(mm,mmData)
  102. hg = himesisBaseClass(name)
  103. hg[HC.METAMODELS] = set(m['metamodels'])
  104. hg[HC.MISSING_METAMODELS] = \
  105. lambda : hg[HC.METAMODELS] - self._loadedMMs
  106. hg[HC.GUID] = uuid.uuid4()
  107. if himesisBaseClass == Himesis :
  108. self._loadedMMs = hg[HC.METAMODELS].copy()
  109. if len(m['nodes']) == 0 :
  110. return hg
  111. atompmIds2himesisIndices = {}
  112. for id in m['nodes'] :
  113. atompmIds2himesisIndices[id] = self.addNode(hg,m['nodes'][id],id)
  114. for e in m['edges'] :
  115. hg.add_edges(
  116. [(atompmIds2himesisIndices[str(e['src'])],
  117. atompmIds2himesisIndices[str(e['dest'])])])
  118. return hg
  119. '''
  120. compile one rule (this function defines a number of inner functions, see
  121. bottom for actual logic) given its atompm model 'r' and its filename
  122. 'fname'
  123. 1. compile LHS, NACs and RHS into himesis graphs
  124. 2. add remaining properties to compiled patterns (e.g., LHS.NACs) '''
  125. def compileRule(self,r,fname) :
  126. if fname in self._compiledRules :
  127. return self._compiledRules[fname]
  128. elif r == None :
  129. r = utils.fread('/users/%s/%s'%(self._username,fname))
  130. '''
  131. compile a pattern (e.g. LHS) into a himesis graph
  132. 1. retrieve atompm model(s) of pattern(s) contents
  133. 2. validate __pLabels
  134. 3. foreach pattern>contents pair from step 1
  135. a) rename metamodels from (.*).pattern to just $1
  136. b) foreach node in contents
  137. i. rename type from (.*).pattern/__p(.*) to $1/$2
  138. ii. rename __pLabel to HC.MT_LABEL (and delete __pLabel attr)
  139. iii. rename __pMatchSubtypes to HC.MT_SUBTYPE_MATCH, if any (and
  140. delete __pMatchSubtypes)
  141. (the model is now ready to be fed to compileModel)
  142. c) compile contents into himesis graph, 'hg'
  143. d) wrap attribute and pattern action/condition code into python
  144. functions
  145. NOTE: to complete pattern compilation, certain properties still need to
  146. be added (e.g., pointer to NACs in LHS) '''
  147. def compilePattern(patternType,himesisBaseClass) :
  148. p2pcm = getPatternContents(patternType)
  149. ''' hergin :: motif-integration :: modify start '''
  150. if p2pcm==None or len(p2pcm)==0 or len(p2pcm.keys()) == 0 :
  151. ''' hergin :: motif-integration :: modify end '''
  152. return []
  153. res = validateLabels(p2pcm)
  154. if '$err' in res :
  155. return res
  156. hgs = []
  157. for p,pcm in p2pcm.iteritems() :
  158. mms = []
  159. for mm in pcm['metamodels'] :
  160. if re.search('.pattern$',mm):
  161. mms.append(mm[:-len('.pattern')])
  162. elif re.search('.ramified$',mm):
  163. mms.append(mm[:-len('.ramified')])
  164. pcm['metamodels'] = mms
  165. for id in pcm['nodes'] :
  166. n = pcm['nodes'][id]
  167. matches = re.match('(.*)\.pattern/__p(.*)',n['$type']) or re.match('(.*)\.ramified/__p(.*)',n['$type'])
  168. n['$type'] = matches.group(1)+'/'+matches.group(2)
  169. n[HC.MT_LABEL] = n['__pLabel']['value']
  170. del n['__pLabel']
  171. ''' hergin :: motif-integration start '''
  172. if '__pPivotIn' in n:
  173. if n['__pPivotIn']['value']:
  174. n[HC.MT_PIVOT_IN] = n['__pPivotIn']['value']
  175. del n['__pPivotIn']
  176. if '__pPivotOut' in n:
  177. if n['__pPivotOut']['value']:
  178. n[HC.MT_PIVOT_OUT] = n['__pPivotOut']['value']
  179. del n['__pPivotOut']
  180. ''' hergin :: motif-integration end '''
  181. if 'value' in n['__pMatchSubtypes'] :
  182. n[HC.MT_SUBTYPE_MATCH] = n['__pMatchSubtypes']['value']
  183. del n['__pMatchSubtypes']
  184. hg = self.compileModel(
  185. pcm,
  186. name=fname+'_'+patternType,
  187. himesisBaseClass=himesisBaseClass)
  188. if patternType == 'LHS' or patternType == 'NAC' :
  189. wrapAttributeDesignerCode(hg,'attrCondition')
  190. hg[HC.MT_CONSTRAINT] = r['nodes'][p]['Condition']['value']
  191. wrapPatternConditionDesignerCode(hg)
  192. for v in hg.vs :
  193. v[HC.MT_DIRTY] = False
  194. v[HC.MT_SUBTYPES] = (self._subtypes[v[HC.FULLTYPE]] if v[HC.FULLTYPE] in self._subtypes else [])
  195. elif patternType == 'RHS' :
  196. wrapAttributeDesignerCode(hg,'attrAction')
  197. hg[HC.MT_ACTION] = r['nodes'][p]['Action']['value']
  198. wrapPatternActionDesignerCode(hg)
  199. elif patternType == 'RHSImport' :
  200. def wrapImportedModelAttribute(val) :
  201. return lambda arg1,arg2 : val
  202. for v in hg.vs :
  203. for attr,val in v.attributes().iteritems() :
  204. if Himesis.is_RAM_attribute(attr) and val != None :
  205. v[attr] = wrapImportedModelAttribute(val)
  206. hg[HC.MT_ACTION] = ''
  207. wrapPatternActionDesignerCode(hg)
  208. hgs.append(hg)
  209. return hgs
  210. '''
  211. read in and slightly alter a model such that it can pass as a
  212. traditional RHS's contents... this functions return format is
  213. this identical to that of getPatternContents(..)
  214. 1. find the RHSImport node
  215. 2. read in the model it refers to
  216. 3. update each of that model's nodes s.t.
  217. a) they have valid __pLabels
  218. b) they have empty __matchSubtypes
  219. c) they have pattern types
  220. 4. update each of that model's metamodels s.t. that become pattern
  221. metamodels '''
  222. def getImportedModelAsPatternContents() :
  223. pc = {}
  224. for id in r['nodes'] :
  225. if re.search('/RHSImport$',r['nodes'][id]['$type']) :
  226. pc[id] = utils.fread(
  227. '/users/%s/%s'%(self._username,
  228. r['nodes'][id]['filename']['value']))
  229. for nid in pc[id]['nodes'] :
  230. n = pc[id]['nodes'][nid]
  231. n['__pLabel'] = {'type':'string','value':'$IM_'+str(nid)}
  232. n['__pMatchSubtypes'] = {}
  233. matches = re.match('(.*)/(.*)',n['$type'])
  234. n['$type'] = matches.group(1)+'.pattern/__p'+matches.group(2)
  235. pc[id]['metamodels'] = \
  236. map(lambda mm : mm+'.pattern', pc[id]['metamodels'])
  237. return pc
  238. '''
  239. return a dict of the form {...,id:contents,...} where 'id' describes a
  240. node of pattern-type 'pt' (e.g., LHS) and 'contents' is an atompm model
  241. that contains only 'id''s contents
  242. 0. if 'pt' is not LHS, NAC, RHS, outsource operation
  243. 1. identify all nodes of type 'pt'
  244. 2. return {} if no matches
  245. 3. map each of step 1's results to every single node that is
  246. [transitively] connected to it (except its PatternContents links)...
  247. this effectively maps patterns to their contents
  248. 4. based on results from step 3, map each match from step 1 to an
  249. atompm model that contains only the pattern's contents
  250. 5. return map from step 4
  251. NOTE:: the outNeighbors() and getConnectedNodes() inner functions are
  252. translated from identical javascript functions in
  253. mmmk.compileToIconDefinitionMetamodel
  254. '''
  255. def getPatternContents(pt) :
  256. if pt == 'RHSImport' :
  257. return getImportedModelAsPatternContents()
  258. def outNeighbors(source) :
  259. return map(lambda x: str(x['dest']),
  260. filter(lambda y : y['src'] == source, r['edges']))
  261. def getConnectedNodes(container,contents) :
  262. _contents = set()
  263. for n in outNeighbors(container) :
  264. if not n in contents :
  265. _contents.add(n)
  266. if len(_contents) == 0 :
  267. return contents
  268. contents = contents | _contents
  269. return set(utils.flatten(
  270. map(lambda x: getConnectedNodes(x,contents),_contents)))
  271. pc = {}
  272. for id in r['nodes'] :
  273. if re.search('/'+pt+'$',r['nodes'][id]['$type']) :
  274. pc[id] = []
  275. if len(pc) == 0 :
  276. return {}
  277. for p in pc :
  278. pc[p] = filter(
  279. lambda x: \
  280. r['nodes'][x]['$type'] != TC.RULEMM+'/PatternContents', \
  281. getConnectedNodes(p,set()))
  282. m = {'nodes':{},'edges':[],'metamodels':[]}
  283. mms = []
  284. for id in pc[p] :
  285. m['nodes'][id] = r['nodes'][id]
  286. mms.append( utils.getMetamodel(r['nodes'][id]['$type']) )
  287. m['metamodels'] = list(set(mms))
  288. m['edges'] = \
  289. filter(
  290. lambda e : e['src'] in m['nodes'], r['edges'])
  291. pc[p] = m
  292. return pc
  293. '''
  294. ensure none of the nodes specified in the provided list of {patternId:
  295. patternContentsModel} have empty or duplicate __pLabels... return error
  296. if any '''
  297. def validateLabels(p2pcm) :
  298. for p,pcm in p2pcm.iteritems() :
  299. for id in pcm['nodes'] :
  300. if '__pLabel' not in pcm['nodes'][id] :
  301. return {'$err':'missing __pLabel attribute'}
  302. l = pcm['nodes'][id]['__pLabel']['value']
  303. if l == '' :
  304. return {'$err':'empty __pLabel'}
  305. elif len(
  306. filter(
  307. lambda x: pcm['nodes'][x]['__pLabel']['value'] == l,
  308. pcm['nodes']) ) > 1 :
  309. return {'$err':'duplicate __pLabel :: '+l}
  310. return {}
  311. '''
  312. store a function that evaluates designer-specified javascript attribute
  313. action/condition code as the value of every RAM attribute (i.e., of
  314. every non-Himesis attribute or atompm $ attribute)
  315. NOTE: a little quirk of igraph is that all vertices have all attributes
  316. (e.g., v1['a']=5, v2['b']=5
  317. >> v1:{'a':5,'b':None}, v2:{'a':None,'b':6})... thus, we make
  318. sure to only wrap 'real' attributes to avoid 'false' attributes
  319. becoming non-None '''
  320. def wrapAttributeDesignerCode(hg,type) :
  321. '''
  322. return a python function that will properly execute designer-
  323. specified javascript action/condition code '''
  324. def wrap(code,pLabel,attr) :
  325. def evalAttrCode(pLabel2graphIndexMap,graph):
  326. if code == '' :
  327. if type == 'attrCondition':
  328. return True
  329. else :
  330. return
  331. ex = {}
  332. try :
  333. self._dcal.configure(
  334. self._dcal.identifyLanguage(code) or self._defaultDCL,
  335. graph,
  336. type,
  337. pLabel2graphIndexMap,
  338. ex,
  339. pLabel,
  340. attr)
  341. return self._dcal.eval(code)
  342. except Exception as e :
  343. if '$err' in ex :
  344. raise RuntimeError(ex['$err'])
  345. else :
  346. raise RuntimeError(\
  347. 'unexpected error encountered while evaluating '+
  348. type+' :: '+str(e))
  349. return evalAttrCode
  350. for v in hg.vs :
  351. for attr,code in v.attributes().iteritems() :
  352. if Himesis.is_RAM_attribute(attr) and code != None :
  353. v[attr] = wrap(code,v[HC.MT_LABEL],attr)
  354. '''
  355. store a function that evaluates designer-specified javascript pattern
  356. action code as the value of pattern[MT_ACTION] '''
  357. def wrapPatternActionDesignerCode(hg) :
  358. def wrap(code) :
  359. def evalPatternCode(pLabel2graphIndexMap,graph):
  360. if code == '' :
  361. return []
  362. journal = []
  363. ex = {}
  364. try :
  365. self._dcal.configure(
  366. self._dcal.identifyLanguage(code) or self._defaultDCL,
  367. graph,
  368. 'patternAction',
  369. pLabel2graphIndexMap,
  370. ex,
  371. journal=journal)
  372. self._dcal.eval(code)
  373. return journal
  374. except Exception as e :
  375. if '$err' in ex :
  376. raise RuntimeError(ex['$err'])
  377. else :
  378. raise RuntimeError(\
  379. 'unexpected error encountered while evaluating '+
  380. 'pattern action code :: '+str(e))
  381. return evalPatternCode
  382. hg[HC.MT_ACTION] = wrap(hg[HC.MT_ACTION])
  383. '''
  384. store a function that evaluates designer-specified javascript pattern
  385. condition code as the value of pattern[MT_CONSTRAINT] '''
  386. def wrapPatternConditionDesignerCode(hg) :
  387. def wrap(code) :
  388. def evalPatternCode(pLabel2graphIndexMap,graph) :
  389. if code == '' :
  390. return True
  391. ex = {}
  392. try :
  393. self._dcal.configure(
  394. self._dcal.identifyLanguage(code) or self._defaultDCL,
  395. graph,
  396. 'patternCondition',
  397. pLabel2graphIndexMap,
  398. ex)
  399. return self._dcal.eval(code)
  400. except Exception as e :
  401. if '$err' in ex :
  402. raise RuntimeError(ex['$err'])
  403. else :
  404. raise RuntimeError(\
  405. 'unexpected error encountered while evaluating '+
  406. 'pattern condition code :: '+str(e))
  407. return evalPatternCode
  408. hg[HC.MT_CONSTRAINT] = wrap(hg[HC.MT_CONSTRAINT])
  409. lhs = compilePattern('LHS',HimesisPreConditionPatternLHS)
  410. if lhs.__class__ == {}.__class__ :
  411. raise ValueError(fname+' LHS compilation failed on :: '+lhs['$err'])
  412. nacs = compilePattern('NAC',HimesisPreConditionPatternNAC)
  413. if nacs.__class__ == {}.__class__ :
  414. raise ValueError(fname+' NAC compilation failed on :: '+nacs['$err'])
  415. rhs = compilePattern('RHS',HimesisPostConditionPattern) or \
  416. compilePattern('RHSImport',HimesisPostConditionPattern)
  417. if rhs.__class__ == {}.__class__ :
  418. raise ValueError(fname+' RHS compilation failed on :: '+rhs['$err'])
  419. #lhs[0].NACs = nacs
  420. for nac in nacs :
  421. nac.LHS = lhs[0]
  422. nac.bridge = nac.compute_bridge()
  423. #lhs[0].NACs = nacs
  424. lhs[0].addNACs(nacs)
  425. ''' hergin :: motif-integration start '''
  426. ''' check condition for RHS for query rule '''
  427. if len(rhs)>0:
  428. rhs[0].pre = lhs[0]
  429. if lhs[0].vcount() > 0 :
  430. rhs[0].pre_labels = lhs[0].vs[HC.MT_LABEL]
  431. else :
  432. rhs[0].pre_labels = []
  433. self._compiledRules[fname] = {'lhs':lhs[0],'rhs':rhs[0]}
  434. else:
  435. self._compiledRules[fname] = {'lhs':lhs[0]}
  436. ''' hergin :: motif-integration end '''
  437. return self._compiledRules[fname]
  438. '''
  439. remember the types stored in the 'connectorTypes' property of the passed
  440. metamodel '''
  441. def _computeConnectorTypes(self,mm,mmData) :
  442. for ct in mmData['connectorTypes'].keys() :
  443. self._connectorTypes.add(mm+'/'+ct)
  444. '''
  445. remember the information required to initialize nodes from any of mm's
  446. types to their default values '''
  447. def _computeMMTypeData(self,mm,mmData) :
  448. for type in mmData['types'] :
  449. fulltype = mm+'/'+type
  450. self._mmTypeData[fulltype] = {}
  451. for attr in mmData['types'][type] :
  452. self._mmTypeData[fulltype][attr['name']] = attr['default']
  453. '''
  454. using the 'types2parentTypes' property of the passed metamodel, construct
  455. and save a mapping of metamodel types to their subtypes
  456. NOTE:: to EASE SEMANTICS SHARING (paramount when working with semantic
  457. templates), we introduce the 'RC__looseSubtypingMM' compiler
  458. flag... it is used to allow mapToBaseFormalism semantics to be
  459. seamlessly used by "looseSubtyping" DSLs... see example below:
  460. 1 mapToBaseFormalism rule:
  461. match BasicState subtypes, produce BasicState
  462. 2 base formalism subtypes:
  463. SimpleStateChart/BasicState subtypes = []
  464. 3 loose subtyping formalism subtypes:
  465. MyDSL/BasicState subtypes = [AAA, BBB]
  466. 4 result
  467. SimpleStateChart/BasicState subtypes =
  468. [MyDSL/BasicState, MyDSL/AAA, MyDSL/BBB]
  469. >> rule can now match entities from MyDSL '''
  470. def _computeSubtypes(self,mm,mmData) :
  471. t2pt = mmData['types2parentTypes']
  472. types = t2pt.keys()
  473. parents = set(itertools.chain.from_iterable(t2pt.values()))
  474. children = filter(lambda t: t2pt[t] != [], types)
  475. for type in types :
  476. fulltype = mm+'/'+type
  477. if fulltype not in self._subtypes :
  478. self._subtypes[fulltype] = []
  479. if type in parents :
  480. for c in children :
  481. if type in t2pt[c] :
  482. self._subtypes[fulltype].append(mm+'/'+c)
  483. if self.RC__looseSubtypingMM and \
  484. self.RC__looseSubtypingMM+'/'+type in self._subtypes :
  485. self._subtypes[fulltype].append(self.RC__looseSubtypingMM+'/'+type)
  486. self._subtypes[fulltype].extend(
  487. self._subtypes[self.RC__looseSubtypingMM+'/'+type])
  488. '''
  489. forget all compiled rules '''
  490. def forgetCompiledRules(self) :
  491. self._compiledRules = {}
  492. '''
  493. return a reference to self._mmTypeData '''
  494. def getMMTypeData(self) :
  495. return self._mmTypeData
  496. '''
  497. compute and store the specified metamodel's default attributes, subtypes
  498. and connector types
  499. 1. if we already know 'mm' (e.g., user may be re-loading it or a newer
  500. version of it),
  501. a) clear known subtypes for that 'mm' in-place... we do this clearing
  502. in-place (i.e., del L[:] vs. L = []) because compiled pattern nodes
  503. have pointers to entries in self._subtypes... this implies that to
  504. avoid having to recompile rules when we alter self._subtypes,
  505. alterations made to existing entries need to preserve pointers
  506. b) clear known connector types for that 'mm'
  507. 2. do the deed
  508. 3. if 'loadMM' is specified (i.e., this function is called as a result of
  509. a user LOADMM changelog, not as a result of encountering an unknown
  510. metamodel while compiling a rule '''
  511. def parseMetamodel(self,mm,mmData,loadMM=False) :
  512. if mm in self._knownMMs :
  513. for type in mmData['types'] :
  514. fulltype = mm+'/'+type
  515. if fulltype in self._subtypes :
  516. self._subtypes[fulltype][:] = []
  517. if fulltype in self._connectorTypes :
  518. self._connectorTypes.remove(fulltype)
  519. self._computeSubtypes(mm,mmData)
  520. self._computeConnectorTypes(mm,mmData)
  521. self._computeMMTypeData(mm,mmData)
  522. self._knownMMs.add(mm)
  523. if loadMM :
  524. self._loadedMMs.add(mm)
  525. '''
  526. remove a metamodel from the list of currently loaded (on the asworker)
  527. metamodels '''
  528. def unloadMetamodel(self,mm):
  529. if mm in self._loadedMMs:
  530. self._loadedMMs.remove(mm)