compiler.py 26 KB

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