| -- @authors Frédéric Jouault |
| -- @date 2007/07/26 |
| -- @description This ACG code generator generates .asm files that run on the ATL VM from ATL models. |
| acg ATL startsWith Unit { |
| |
| -- @begin Type Encoding |
| function OclType::encode() = '<DUMMY>'; |
| function IntegerType::encode() = 'I'; |
| function OclAnyType::encode() = 'J'; |
| function RealType::encode() = 'D'; |
| function BooleanType::encode() = 'B'; |
| function StringType::encode() = 'S'; |
| function Module::encode() = 'A'; |
| function OclModelElement::encode() = 'M' + self.model.name + '!' + self.name + ';'; |
| function SequenceType::encode() = 'Q' + self.elementType.encode(); |
| function BagType::encode() = 'G' + self.elementType.encode(); |
| function CollectionType::encode() = 'C' + self.elementType.encode(); |
| function SetType::encode() = 'E' + self.elementType.encode(); |
| function OrderedSetType::encode() = 'O' + self.elementType.encode(); |
| function TupleType::encode() = 'T' + self.attributes->collect(e | e.type.encode() + e.name + ';').prepend('').sum() + ';'; |
| -- @end Type Encoding |
| |
| asm Module name self.name { |
| field 'links' : 'NTransientLinkSet;' |
| field 'col' : 'J' |
| if(self.isRefining) { |
| field 'enumLiteralType' : 'J' |
| } |
| |
| -- if(self.isRefining) { |
| -- report error 'refining mode not supported by ATL 2006 yet' |
| -- } |
| |
| operation |
| context 'A' |
| name 'main' { |
| getasm |
| push 'OclParametrizedType' |
| push '#native' |
| new |
| dup |
| push 'Collection' |
| pcall 'J.setName(S):V' |
| dup |
| push 'OclSimpleType' |
| push '#native' |
| new |
| dup |
| push 'OclAny' |
| pcall 'J.setName(S):V' |
| pcall 'J.setElementType(J):V' |
| set 'col' |
| |
| if(self.isRefining) { |
| getasm |
| push 'EnumLiteral' |
| push '#native' |
| new |
| call 'J.oclType():J' |
| set 'enumLiteralType' |
| |
| -- OclType.registerWeavingHelper(name : String, persistTo : String) |
| push 'Element' |
| push 'RefiningTrace' |
| findme |
| push 'sourceElement' |
| push 'persistedSourceElement' |
| pcall 'J.registerWeavingHelper(SS):V' |
| } |
| |
| analyze self.elements->select(e | e isa Helper) mode register |
| |
| getasm |
| push 'TransientLinkSet' |
| push '#native' |
| new |
| set 'links' |
| |
| let ep = self.elements->select(e | e isa CalledRule)->select(e | e.isEntrypoint).first() { |
| if(not ep.oclIsUndefined()) { |
| getasm |
| pcall 'A.' + ep.name + '():V' |
| } |
| } |
| |
| getasm |
| pcall 'A.__matcher__():V' |
| getasm |
| pcall 'A.__exec__():V' |
| |
| if(self.isRefining) { |
| getasm |
| pcall 'A.__applyRefiningTrace__():V' |
| } |
| |
| let ep = self.elements->select(e | e isa CalledRule)->select(e | e.isEndpoint).first() { |
| if(not ep.oclIsUndefined()) { |
| getasm |
| pcall 'A.' + ep.name + '():V' |
| } |
| } |
| } |
| operation |
| context 'A' |
| name '__resolve__' { |
| param 'value' : 'J' |
| |
| load 'value' |
| getasm |
| get 'col' |
| call 'J.oclIsKindOf(J):B' |
| if thn |
| -- not a collection |
| getasm |
| get 'links' |
| load 'value' |
| call 'NTransientLinkSet;.getLinkBySourceElement(S):QNTransientLink;' |
| dup |
| call 'J.oclIsUndefined():B' |
| if thn2 |
| load 'value' |
| call 'NTransientLink;.getTargetFromSource(J):J' |
| goto eoi2 |
| thn2: |
| pop |
| load 'value' -- TODO: refining_copy |
| eoi2: |
| goto eoi |
| thn: |
| push 'Sequence' |
| push '#native' |
| new |
| load 'value' |
| iterate |
| variable self named 'e' { |
| getasm |
| load self |
| call 'A.__resolve__(J):J' |
| call 'QJ.including(J):QJ' |
| } |
| enditerate |
| call 'QJ.flatten():QJ' |
| eoi: |
| } |
| operation |
| context 'A' |
| name 'resolveTemp' { |
| param 'value' : 'J' |
| param 'name' : 'S' |
| |
| getasm |
| get 'links' |
| load 'value' |
| call 'NTransientLinkSet;.getLinkBySourceElement(S):QNTransientLink;' |
| load 'value' |
| load 'name' |
| call 'NTransientLink;.getNamedTargetFromSource(JS):J' |
| } |
| operation |
| context 'J' |
| name '__applyReverseBinding' { |
| param 'propertyName' : 'S' |
| param 'target' : 'J' |
| |
| load 'target' |
| getasm |
| get 'col' |
| call 'J.oclIsKindOf(J):B' |
| if col |
| -- not col |
| if(self.isRefining) { |
| load 'target' -- tgtTraceElement |
| call 'J.__asElement():J' -- tgtTraceElement |
| load 'propertyName' -- tgtTraceElement, propertyName |
| load 'self' -- tgtTraceElement, propertyName, traceElement |
| -- tgtTraceElement, propertyName, traceElement |
| call 'MRefiningTrace!Element;.setProperty(SJ):V' |
| } else { |
| getasm -- thisModule |
| load 'target' -- thisModule, target |
| call 'A.__resolve__(J):J' -- resolvedTarget |
| load 'propertyName' -- resolvedTarget, propertyName |
| load 'self' -- resolvedTarget, propertyName, self |
| pcall 'J.refSetValue(SJ):J' |
| } |
| goto end |
| col: |
| load 'target' |
| iterate |
| load 'self' |
| swap |
| load 'propertyName' |
| swap |
| pcall 'J.__applyReverseBinding(SJ):V' |
| enditerate |
| end: |
| } |
| operation |
| context 'A' |
| name '__matcher__' { |
| foreach(r in self.elements->select(e | (e isa MatchedRule) and (not (e isa LazyMatchedRule)))->select(e | e.superRule.oclIsUndefined())) { |
| getasm |
| pcall 'A.__match' + r.name + '():V' |
| } |
| } |
| operation |
| context 'A' |
| name '__exec__' { |
| foreach(r in self.elements->select(e | e isa MatchedRule and not (e isa LazyMatchedRule))) { |
| getasm |
| get 'links' |
| push r.name |
| call 'NTransientLinkSet;.getLinksByRule(S):QNTransientLink;' |
| iterate |
| variable self named 'e' { |
| getasm |
| load self |
| pcall 'A.__apply' + r.name + '(NTransientLink;):V' |
| } |
| enditerate |
| } |
| } |
| if(self.isRefining) { |
| operation |
| context 'MRefiningTrace!Element;' |
| name 'setProperty' { |
| param 'propertyName' : 'S' |
| param 'value' : 'J' |
| param 'isAssignment' : 'B' |
| |
| load 'self' |
| push 'Slot' |
| push 'RefiningTrace' |
| new |
| dup |
| load 'isAssignment' |
| set 'isAssignment' |
| dup |
| load 'propertyName' |
| set 'name' -- traceElement, slot |
| dup -- traceElement, slot, slot |
| load 'value' -- traceElement, slot, slot, value |
| dup |
| getasm |
| get 'col' |
| call 'J.oclIsKindOf(J):B' |
| if setPropertyThn |
| -- not a collection |
| call 'J.__toValue():J' |
| goto setPropertyEoi |
| setPropertyThn: |
| getasm |
| swap |
| call 'A.__collectionToValue(QJ):J' |
| setPropertyEoi: -- element, slot, slot, value |
| set 'value' |
| set 'slots' |
| } |
| operation |
| context 'A' |
| name '__applyRefiningTrace__' { |
| push 'Element' |
| push 'RefiningTrace' |
| findme |
| push 'refiningTrace' |
| call 'MMOF!Classifier;.allInstancesFrom(S):QJ' |
| dup |
| iterate |
| -- traceElement |
| dup -- traceElement, traceElement |
| get 'sourceElement' -- traceElement, element|OclUndefined |
| call 'J.oclIsUndefined():B' -- traceElement, oclIsUndefined? |
| call 'B.not():B' -- traceElement, oclIsDefined? |
| if sourceElement |
| -- create the new element |
| -- traceElement |
| dup -- traceElement, traceElement |
| get 'type' -- traceElement, typeName |
| swap -- typeName, traceElement |
| dup_x1 -- traceElement, typeName, traceElement |
| get 'metamodel' -- traceElement, typeName, metamodelName |
| new -- traceElement, element |
| set 'sourceElement' -- nothing |
| goto next |
| --TODO:use id? |
| sourceElement: |
| -- traceElement |
| pop |
| next: |
| enditerate |
| dup -- We dup refining trace to loop again and execute deletion |
| iterate |
| -- traceElement |
| dup -- traceElement, traceElement |
| get 'sourceElement' -- traceElement, element |
| swap -- element, traceElement |
| get 'slots' |
| iterate |
| -- element, slot |
| dup |
| get 'isAssignment' -- sourceElement, slot, isAssignment |
| call 'B.not():B' |
| if noAssign |
| -- sourceElement, slot |
| dup_x1 -- slot, sourceElement, slot |
| get 'name' -- slot, sourceElement, name |
| call 'J.refUnsetValue(S):J' -- slot, sourceElement |
| swap -- sourceElement, slot |
| noAssign: |
| -- sourceElement, slot |
| dup |
| get 'name' -- sourceElement, slot, name |
| swap -- sourceElement, name, slot |
| get 'value' -- sourceElement, name, value |
| call 'J.__fromValue():J' -- sourceElement, name, actualValue |
| call 'J.refSetValue(SJ):J' |
| enditerate |
| pop |
| enditerate |
| iterate -- Iteration to delete everything. We do another loop to be sure to do the deletion |
| -- after all the other modifications to prevent errors |
| -- traceElement |
| dup -- traceElement, traceElement |
| get 'toDelete' -- traceElement, boolean |
| call 'B.not():B' -- traceElement, boolean |
| if noDelete |
| dup -- traceElement, traceElement |
| get 'sourceElement' -- traceElement, element |
| delete -- traceElement |
| push 'Sequence' |
| push '#native' |
| new |
| call 'QJ.first():J' -- traceElement, OclUndefined |
| set 'sourceElement' -- Unset sourceElement reference to avoid problems when serializing the refining trace |
| goto endDeleteLoop |
| noDelete: |
| -- traceElement |
| pop |
| endDeleteLoop: |
| enditerate |
| } |
| operation |
| context 'A' |
| name '__collectionToValue' { |
| param 'c' : 'CJ' |
| |
| push 'CollectionVal' |
| push 'RefiningTrace' |
| new |
| load 'c' |
| iterate |
| -- collectionVal, element |
| call 'J.__toValue():J' -- collectionVal, value |
| swap -- value, collectionVal |
| dup_x1 -- collectionVal, value, collectionVal |
| swap -- collectionVal, collectionVal, value |
| set 'elements' -- collectionVal |
| enditerate |
| } |
| operation |
| context 'B' |
| name '__toValue' { |
| push 'BooleanVal' |
| push 'RefiningTrace' |
| new |
| dup |
| load 'self' |
| set 'value' |
| } |
| operation |
| context 'I' |
| name '__toValue' { |
| push 'IntegerVal' |
| push 'RefiningTrace' |
| new |
| dup |
| load 'self' |
| set 'value' |
| } |
| operation |
| context 'D' |
| name '__toValue' { |
| push 'RealVal' |
| push 'RefiningTrace' |
| new |
| dup |
| load 'self' |
| set 'value' |
| } |
| operation |
| context 'S' |
| name '__toValue' { |
| push 'StringVal' |
| push 'RefiningTrace' |
| new |
| dup |
| load 'self' |
| set 'value' |
| } |
| operation |
| context 'MRefiningTrace!Element;' |
| name '__toValue' { |
| push 'ElementVal' |
| push 'RefiningTrace' |
| new |
| dup |
| load 'self' |
| set 'value' |
| } |
| operation |
| context 'J' |
| name '__toValue' { |
| load 'self' |
| call 'J.oclType():J' |
| getasm |
| get 'enumLiteralType' |
| call 'J.=(J):B' |
| if thn3 |
| -- not enum literal |
| load 'self' |
| call 'J.oclIsUndefined():B' |
| if oclUndefined |
| -- This point should only be reached for model elements |
| |
| push 'ElementVal' |
| push 'RefiningTrace' |
| new |
| dup |
| load 'self' |
| call 'J.__asElement():J' |
| set 'value' |
| goto eoi3 |
| oclUndefined: |
| push 'NullVal' |
| push 'RefiningTrace' |
| new |
| goto eoi3 |
| thn3: |
| push 'EnumLiteralVal' |
| push 'RefiningTrace' |
| new |
| dup |
| load 'self' |
| call 'J.toString():S' |
| set 'value' |
| eoi3: |
| } |
| -- should only be called on model elements |
| operation |
| context 'J' |
| name '__asElement' { |
| -- TODO: use a Map as cache |
| push 'Element' |
| push 'RefiningTrace' |
| new |
| dup |
| load 'self' |
| set 'sourceElement' |
| } |
| -- may notably be called from __applyReverseBinding if target has already been resolved (e.g., using resolveTemp) |
| operation |
| context 'MRefiningTrace!Element;' |
| name '__asElement' { |
| load 'self' |
| } |
| operation |
| context 'MRefiningTrace!CollectionVal;' |
| name '__fromValue' { |
| push 'Sequence' |
| push '#native' |
| new |
| load 'self' |
| get 'elements' |
| iterate |
| call 'J.__fromValue():J' |
| call 'QJ.append(J):QJ' |
| enditerate |
| } |
| operation |
| context 'MRefiningTrace!BooleanVal;' |
| name '__fromValue' { |
| load 'self' |
| get 'value' |
| } |
| operation |
| context 'MRefiningTrace!IntegerVal;' |
| name '__fromValue' { |
| load 'self' |
| get 'value' |
| } |
| operation |
| context 'MRefiningTrace!RealVal;' |
| name '__fromValue' { |
| load 'self' |
| get 'value' |
| } |
| operation |
| context 'MRefiningTrace!StringVal;' |
| name '__fromValue' { |
| load 'self' |
| get 'value' |
| } |
| operation |
| context 'MRefiningTrace!NullVal;' |
| name '__fromValue' { |
| push 'Sequence' |
| push '#native' |
| new |
| call 'QJ.first():J' |
| } |
| operation |
| context 'MRefiningTrace!ElementVal;' |
| name '__fromValue' { |
| load 'self' |
| get 'value' |
| get 'sourceElement' |
| } |
| operation |
| context 'MRefiningTrace!EnumLiteralVal;' |
| name '__fromValue' { |
| push 'EnumLiteral' |
| push '#native' |
| new |
| dup |
| load 'self' |
| get 'value' |
| set 'name' |
| } |
| } |
| analyze self.elements |
| } |
| |
| asm Query name self.name { |
| operation |
| context 'A' |
| name 'main' { |
| analyze self.helpers mode register |
| analyze self.body |
| } |
| analyze self.helpers |
| } |
| |
| asm Library name self.name { |
| -- we have a main operation to register the attribute helpers |
| operation |
| context 'A' |
| name 'main' { |
| analyze self.helpers mode register |
| } |
| analyze self.helpers |
| } |
| |
| -- @begin Helpers |
| Helper { |
| analyze self.definition.feature |
| } |
| |
| Helper mode register { |
| analyze self.definition.feature mode register |
| } |
| |
| Operation mode register { |
| -- nothing to do: only attributes need registration |
| } |
| |
| Operation { |
| operation |
| context |
| if self.definition.context_.oclIsUndefined() then |
| 'A' |
| else |
| self.definition.context_.context_.encode() |
| endif |
| name self.name { |
| foreach(p in self.parameters) { |
| param p.varName : 'J' |
| } |
| analyze self.body |
| } |
| } |
| |
| -- module attribute |
| Attribute mode register | self.definition.context_.oclIsUndefined() { |
| getasm |
| analyze self.initExpression |
| set self.name |
| } |
| |
| Attribute | self.definition.context_.oclIsUndefined() { |
| field self.name : 'J' |
| } |
| |
| Attribute mode register { |
| analyze self.definition.context_.context_ |
| push self.name |
| push '__init' + self.name |
| pcall 'J.registerHelperAttribute(SS):V' |
| } |
| |
| Attribute { |
| operation |
| context self.definition.context_.context_.encode() |
| name '__init' + self.name { |
| analyze self.initExpression |
| } |
| } |
| -- @end Helpers |
| |
| -- @begin Declarative Part |
| -- @begin Standard Rules |
| function Rule::ruleOutPatternElements() = |
| if self.outPattern.oclIsUndefined() then |
| Sequence {} |
| else |
| self.outPattern.elements |
| endif; |
| |
| MatchedRule { |
| analyze self mode matcher |
| analyze self mode apply |
| } |
| |
| MatchedRule mode matcher | not self.superRule.oclIsUndefined() { |
| -- do nothing because subRules are matched by their superRules |
| } |
| |
| MatchedRule mode matcher | self.superRule.oclIsUndefined() { |
| operation |
| context 'A' |
| name '__match' + self.name { |
| foreach(ipe in self.inPattern.elements) { -- equivalent to self.inPatternElements because self.isRoot() |
| let sourceModels = |
| if ipe.models.isEmpty() then |
| self.module.inModels->select(e | |
| -- TODO: compare models instead of names? |
| e.metamodel.name = ipe.type.model.name |
| ) |
| else |
| ipe.models |
| endif { |
| if(sourceModels.isEmpty()) { |
| report error ipe.type.model.name + ' is not a source model' |
| } |
| foreach(m in sourceModels) { |
| push ipe.type.name -- TODO: optimize this out of loop? |
| push ipe.type.model.name |
| findme |
| push m.name |
| call 'MMOF!Classifier;.allInstancesFrom(S):QJ' |
| if(m <> sourceModels.first()) { |
| call 'CJ.union(CJ):CJ' |
| } |
| } |
| } |
| iterate |
| variable ipe named ipe.varName { |
| [ |
| if(not self.inPattern.filter.oclIsUndefined()) { |
| analyze self.inPattern.filter |
| call 'B.not():B' |
| if end |
| } |
| |
| analyze self mode checkSubRules |
| end: |
| ] |
| } |
| enditerate |
| } |
| } |
| } |
| |
| function MatchedRule::allSubRules() = |
| if self.isAbstract then |
| self.children |
| else |
| self.children.append(self) |
| endif; |
| |
| -- makes sure the default rule is last |
| function MatchedRule::subRules() = |
| self.allSubRules()->select(e | not e.inPattern.filter.oclIsUndefined()).union( |
| self.allSubRules()->select(e | e.inPattern.filter.oclIsUndefined()) |
| ); |
| |
| function MatchedRule::rootRule() = |
| if self.superRule.oclIsUndefined() then |
| self |
| else |
| self.superRule.rootRule() |
| endif; |
| |
| function MatchedRule::mostAbstractOutPatternElements() = |
| if self.superRule.oclIsUndefined() then |
| self.ruleOutPatternElements() |
| else |
| let super = self.superRule.mostAbstractOutPatternElements() in |
| let superNames = super->collect(e | e.varName) in |
| super.union( |
| self.ruleOutPatternElements()->select(e | |
| not superNames.includes(e.varName) |
| ) |
| ) |
| endif; |
| |
| function MatchedRule::mostConcreteOutPatternElements() = |
| if self.superRule.oclIsUndefined() then |
| self.ruleOutPatternElements() |
| else |
| let this = self.ruleOutPatternElements() in |
| let thisNames = this->collect(e | e.varName) in |
| this.union( |
| self.superRule.mostConcreteOutPatternElements()->select(e | |
| not thisNames.includes(e.varName) |
| ) |
| ) |
| endif; |
| |
| function MatchedRule::mostConcreteVariables() = |
| if self.superRule.oclIsUndefined() then |
| self.variables |
| else |
| let this = self.variables in |
| let thisNames = this->collect(e | e.varName) in |
| this.union( |
| self.superRule.mostConcreteVariables()->select(e | |
| not thisNames.includes(e.varName) |
| ) |
| ) |
| endif; |
| |
| function MatchedRule::inPatternElements() = |
| self.rootRule().inPattern.elements; |
| |
| function SimpleInPatternElement::actualDeclaration() = |
| self.inPattern.rule.inPatternElements()->select(e | e.varName = self.varName).first(); |
| |
| function OutPatternElement::actualDeclaration() = |
| if self.outPattern.rule isa MatchedRule then |
| self.outPattern.rule.mostAbstractOutPatternElements()->select(e | e.varName = self.varName).first() |
| else |
| self |
| endif; |
| |
| -- subRules should be ordered with the only one without guard last (including self if not abstract) |
| -- their should be only one with no guard (including self) |
| -- only one should match (except the one without guard, which is "default") |
| -- a subrule inpattern must have the same number of elements with the same names |
| MatchedRule mode checkSubRules | self.children.size() > 0 { |
| foreach(subRule in self.subRules()) { |
| foreach(ipe in subRule.inPattern.elements) { |
| load ipe.actualDeclaration() |
| push ipe.type.name |
| push ipe.type.model.name |
| findme |
| call 'J.oclIsKindOf(J):B' |
| call 'B.not():B' |
| if unmatched |
| } |
| if(not subRule.inPattern.filter.oclIsUndefined()) { |
| analyze subRule.inPattern.filter |
| call 'B.not():B' |
| if unmatched |
| } |
| |
| -- we only select the first subRule that matches (there should be only one anyway) |
| if(subRule = self) { |
| analyze self mode create |
| } else { |
| analyze subRule mode checkSubRules |
| } |
| goto end |
| unmatched(subRule): |
| } |
| end: |
| } |
| |
| MatchedRule mode checkSubRules { |
| analyze self mode create |
| } |
| |
| MatchedRule mode create { |
| getasm |
| get 'links' |
| |
| push 'TransientLink' |
| push '#native' |
| new |
| |
| dup |
| push self.name |
| pcall 'NTransientLink;.setRule(MATL!Rule;):V' |
| |
| foreach(ipe in self.inPattern.elements) { |
| dup |
| push ipe.varName |
| load ipe.actualDeclaration() |
| pcall 'NTransientLink;.addSourceElement(SJ):V' |
| } |
| |
| foreach(v in self.mostConcreteVariables()) { |
| dup |
| push v.varName |
| analyze v.initExpression |
| dup |
| variable v named v.varName { |
| pcall 'NTransientLink;.addVariable(SJ):V' |
| [ |
| let opes = self.mostConcreteOutPatternElements() { |
| if(self.module.isRefining) { |
| if (not opes.first().oclIsUndefined()){ |
| -- there is no first element because the first element is going to be dropped |
| -- so no different treatment for the first element |
| if (not self.outPattern.dropPattern.oclIsUndefined()){ |
| analyze opes mode match |
| } else { |
| analyze opes.first() mode matchFirstRefining |
| analyze opes.subSequence(2, opes.size()) mode match |
| } |
| |
| } |
| } else { |
| analyze opes mode match |
| } |
| } |
| ] |
| } |
| } |
| |
| if (not self.outPattern.dropPattern.oclIsUndefined()){ |
| analyze self.outPattern.dropPattern |
| } |
| |
| if(self.isNoDefault) { |
| pushf |
| } else { |
| pusht |
| } |
| pcall 'NTransientLinkSet;.addLink2(NTransientLink;B):V' |
| } |
| |
| MatchedRule mode apply | self.isAbstract { |
| -- do nothing |
| } |
| |
| MatchedRule mode apply | not self.isAbstract { |
| operation |
| context 'A' |
| name '__apply' + self.name { |
| param 'link' : 'NTransientLink;' |
| -- TODO: only one source element for refining mode |
| foreach(ipe in self.inPatternElements()) { |
| load 'link' |
| push ipe.varName |
| call 'NTransientLink;.getSourceElement(S):J' |
| variable ipe named ipe.varName { |
| [ |
| foreach(ope in self.mostAbstractOutPatternElements()) { |
| load 'link' |
| push ope.varName |
| call 'NTransientLink;.getTargetElement(S):J' |
| variable ope named ope.varName { |
| [ |
| foreach(v in self.mostConcreteVariables()) { |
| load 'link' |
| push v.varName |
| call 'NTransientLink;.getVariable(S):J' |
| variable v named v.varName { |
| [ |
| analyze self.mostConcreteOutPatternElements() mode apply |
| if(not self.actionBlock.oclIsUndefined()) { |
| if(self.module.isRefining) { |
| report error 'do block not supported in refining mode' |
| } else { |
| analyze self.actionBlock |
| } |
| } |
| ] |
| } |
| } |
| ] |
| } |
| } |
| ] |
| } |
| } |
| } |
| } |
| |
| attribute OutPatternElement::isRefiningMode = |
| self.outPattern.rule.module.isRefining; |
| |
| -- @begin SimpleOutPatternElement |
| code SimpleOutPatternElement mode match { |
| dup |
| push self.varName |
| if(self.isRefiningMode) { |
| push 'Element' |
| push 'RefiningTrace' |
| new |
| dup |
| push self.type.name |
| set 'type' |
| dup |
| push self.type.model.name |
| set 'metamodel' |
| } else { |
| push self.type.name |
| push self.type.model.name |
| if (not self.model.oclIsUndefined()) { |
| push self.model.name |
| newin |
| } else { |
| new |
| } |
| } |
| |
| pcall 'NTransientLink;.addTargetElement(SJ):V' |
| } |
| |
| -- TODO: removal, change... |
| code SimpleOutPatternElement mode matchFirstRefining { |
| dup |
| push self.varName |
| |
| push 'Element' |
| push 'RefiningTrace' |
| new |
| dup |
| push self.type.name |
| set 'type' |
| dup |
| push self.type.model.name |
| set 'metamodel' |
| dup |
| load self.outPattern.rule.inPatternElements().first() |
| set 'sourceElement' |
| |
| pcall 'NTransientLink;.addTargetElement(SJ):V' |
| } |
| |
| function SimpleOutPatternElement::superElement() = |
| if self.outPattern.rule isa MatchedRule then |
| let superRule = self.outPattern.rule.superRule in |
| if superRule.oclIsUndefined() then |
| OclUndefined |
| else |
| superRule.mostConcreteOutPatternElements()->select(e | e.varName = self.varName).first() |
| endif |
| else |
| OclUndefined |
| endif; |
| |
| function SimpleOutPatternElement::allReverseBindings() = |
| let superElement = self.superElement() in |
| if superElement.oclIsUndefined() then |
| Sequence {} |
| else |
| superElement.allReverseBindings() |
| endif.union(self.reverseBindings); |
| |
| -- We create a normal template instead of a code template because this template will be |
| -- called from another code template matching the same model element. If this were |
| -- a code template an error will be raised when the vm tries to remove the entrance |
| -- for this code template (it is registered twice, but as it is a map is has no effect |
| -- so we can not remove twice) |
| SimpleOutPatternElement mode applyReverseBindings { -- element -> element |
| foreach(rb in self.allReverseBindings()) { |
| if(rb isa NavigationOrAttributeCallExp) { |
| dup -- element, element |
| push rb.name |
| analyze rb.source -- element, element, propertyName, value |
| pcall 'J.__applyReverseBinding(SJ):V' |
| -- element |
| } else { |
| if(rb isa OperationCallExp) { |
| if(rb.operationName = 'refGetValue' and rb.arguments.size() = 1) { |
| dup |
| analyze rb.arguments.first() |
| analyze rb.source |
| pcall 'J.__applyReverseBinding(SJ):V' |
| } else { |
| report error 'only navigations (including refGetValue) are allowed in reversebindings' |
| } |
| } else { |
| report error 'only navigations (including refGetValue) are allowed in reversebindings' |
| } |
| } |
| } |
| } |
| |
| function MatchedRule::getBindings(varName, ret) = |
| if self.outPattern.oclIsUndefined() then |
| Sequence {} |
| else |
| let ope = |
| self.outPattern.elements->select(e | |
| e.varName = varName |
| ).first() in |
| let retNames = ret->collect(e | e.propertyName) in |
| let newRet = |
| if ope.oclIsUndefined() then |
| ret |
| else |
| ret.union( |
| ope.bindings->select(e | |
| not retNames.includes(e.propertyName) |
| ) |
| ) |
| endif in |
| if self.superRule.oclIsUndefined() then |
| newRet |
| else |
| self.superRule.getBindings(varName, newRet) |
| endif |
| endif; |
| |
| function OutPatternElement::allBindings() = |
| let superRule = self.outPattern.rule.superRule in |
| if superRule.oclIsUndefined() then |
| self.bindings |
| else |
| self.outPattern.rule.getBindings(self.varName, Sequence {}) |
| endif; |
| |
| code SimpleOutPatternElement mode apply { |
| load self.actualDeclaration() -- element |
| analyze self mode applyReverseBindings |
| if(self.isRefiningMode) { |
| analyze self.allBindings() mode refining |
| } else { |
| analyze self.allBindings() |
| } |
| pop |
| } |
| |
| code Binding { |
| dup |
| if(self.isAssignment) { |
| push self.propertyName |
| call 'J.refUnsetValue(S):J' |
| } |
| getasm |
| analyze self.value |
| call 'A.__resolve__(J):J' |
| set self.propertyName |
| } |
| |
| code Binding mode refining { |
| dup |
| push self.propertyName |
| getasm |
| analyze self.value |
| call 'A.__resolve__(J):J' |
| |
| -- traceElement, propertyName, value |
| if(self.isAssignment) { |
| pusht |
| } else { |
| pushf |
| } |
| pcall 'MRefiningTrace!Element;.setProperty(SJB):V' |
| } |
| -- @end SimpleOutPatternElement |
| |
| -- ForEachOutPatternElement does not support rule inheritance. |
| -- It is now deprecated anyway. |
| -- @begin ForEachOutPatternElement |
| code ForEachOutPatternElement mode match { |
| if(self.isRefiningMode) { |
| report error 'distinct-foreach target pattern elements are not supported in refining mode, use lazy rules instead' |
| } else { |
| report warning 'distinct-foreach target pattern elements are deprecated, use lazy rules instead' |
| dup |
| push self.varName |
| |
| push 'Sequence' |
| push '#native' |
| new |
| analyze self.collection |
| iterate |
| pop |
| push self.type.name |
| push self.type.model.name |
| -- NOTE: foreach patterns syntax can't handle output model specification, so "newin" instructions are not generated here |
| new |
| call 'CJ.including(J):CJ' |
| enditerate |
| pcall 'NTransientLink;.addTargetElement(SJ):V' |
| } |
| } |
| |
| code ForEachOutPatternElement mode apply { |
| pushi 1 |
| variable self.type named 'counter' { -- self.type is not a VariableDeclaration but this shouldn't matter |
| analyze self.collection |
| call 'CJ.asSequence():QJ' |
| variable self.collection named 'collection' { -- self.collection is not a VariableDeclaration but this shouldn't matter |
| load self |
| iterate |
| load self.collection -- 'collection' |
| load self.type -- 'counter' |
| call 'QJ.at(I):J' |
| variable self.iterator named self.iterator.varName { |
| analyze self.bindings mode forEach |
| } |
| pop |
| load self.type -- 'counter' |
| pushi 1 |
| call 'I.+(I):I' |
| store self.type -- 'counter' |
| enditerate |
| } |
| } |
| } |
| |
| code Binding mode forEach { |
| dup |
| getasm |
| analyze self.value |
| dup |
| getasm |
| get 'col' |
| call 'J.oclIsKindOf(J):B' |
| call 'B.not():B' |
| if thn |
| load self.outPatternElement.type -- 'counter' |
| call 'QJ.at(I):J' |
| thn: |
| call 'A.__resolve__(J):J' |
| set self.propertyName |
| } |
| -- @end ForEachOutPatternElement |
| |
| -- @begin DropPattern |
| -- |
| -- We have to set toDelete flag to true |
| -- |
| code DropPattern { |
| push 'Element' |
| push 'RefiningTrace' |
| new |
| dup |
| push self.outPattern.rule.inPatternElements().first().type.name |
| set 'type' |
| dup |
| push self.outPattern.rule.inPatternElements().first().type.model.name |
| set 'metamodel' |
| dup |
| pusht |
| set 'toDelete' |
| load self.outPattern.rule.inPatternElements().first() |
| set 'sourceElement' |
| } |
| |
| -- @end DropPattern |
| |
| -- @end Standard Rules |
| |
| -- @begin Lazy Rules |
| LazyMatchedRule | self.isRefining { |
| report error 'refining lazy matched rules not supported in ATL 2006 yet' |
| } |
| |
| LazyMatchedRule | not self.superRule.oclIsUndefined() { |
| -- do nothing because subRules are matched by their superRules |
| } |
| |
| LazyMatchedRule | self.superRule.oclIsUndefined() { |
| if(self.module.isRefining) { |
| report error 'lazy rules not supported in refining mode yet' |
| } else { |
| operation |
| context 'A' |
| name self.name { |
| foreach(ipe in self.inPattern.elements) { |
| param ipe.varName : ipe.type.encode() |
| } |
| |
| if(self.isUnique) { |
| getasm |
| get 'links' |
| push self.name |
| --TODO: finish this (also in VM code) |
| -- if(self.inPattern.elements.size() = 1) { |
| load self.inPattern.elements.first().varName |
| -- } else { |
| -- push 'Tuple' |
| -- push '#native' |
| -- new -- links, name, Tuple |
| -- foreach(e in self.inPattern.elements) { -- TODO: change TransientLinkSet implementation accordingly |
| -- dup |
| -- load e.varName |
| -- set e.varName |
| -- } |
| -- } |
| call 'NTransientLinkSet;.getLinkByRuleAndSourceElement(SJ):QNTransientLink;' |
| dup |
| call 'J.oclIsUndefined():B' |
| if thn |
| load self.inPattern.elements.first().varName |
| call 'NTransientLink;.getTargetFromSource(J):J' |
| goto eoo |
| thn: |
| } |
| |
| getasm |
| get 'links' |
| |
| push 'TransientLink' |
| push '#native' |
| new |
| |
| dup |
| push self.name |
| pcall 'NTransientLink;.setRule(MATL!Rule;):V' |
| |
| foreach(ipe in self.inPattern.elements) { |
| dup |
| push ipe.varName |
| load ipe.varName |
| pcall 'NTransientLink;.addSourceElement(SJ):V' |
| } |
| |
| analyze self mode checkSubRules |
| eoo: |
| } |
| } |
| } |
| |
| LazyMatchedRule mode checkSubRules | self.children.size() > 0 { |
| foreach(subRule in self.subRules()) { |
| foreach(ipe in subRule.inPattern.elements) { |
| load ipe.varName |
| push ipe.type.name |
| push ipe.type.model.name |
| findme |
| call 'J.oclIsKindOf(J):B' |
| call 'B.not():B' |
| if unmatched |
| } |
| if(not subRule.inPattern.filter.oclIsUndefined()) { |
| analyze subRule.inPattern.filter |
| call 'B.not():B' |
| if unmatched |
| } |
| |
| -- we only select the first subRule that matches (there should be only one anyway) |
| if(subRule = self) { |
| foreach(v in self.mostConcreteVariables()) { |
| analyze v.initExpression |
| variable v named v.varName { |
| [ |
| analyze self mode create |
| ] |
| } |
| } |
| } else { |
| analyze subRule mode checkSubRules |
| } |
| goto end |
| unmatched(subRule): |
| } |
| end: |
| } |
| |
| LazyMatchedRule mode checkSubRules { |
| foreach(v in self.mostConcreteVariables()) { |
| analyze v.initExpression |
| variable v named v.varName { |
| [ |
| analyze self mode create |
| ] |
| } |
| } |
| } |
| |
| LazyMatchedRule mode create { -- TODO: REFINING |
| foreach(ope in self.mostConcreteOutPatternElements()) { |
| if(ope isa ForEachOutPatternElement) { |
| -- TODO: compiler crashes |
| report error 'distinct-foreach target pattern elements are deprecated and not compatible with lazy rules' |
| } else { |
| |
| dup |
| push ope.varName |
| |
| push ope.type.name |
| push ope.type.model.name |
| if (not ope.model.oclIsUndefined()) { |
| push ope.model.name |
| newin |
| } else { |
| new |
| } |
| dup |
| variable ope.actualDeclaration() named ope.varName { |
| pcall 'NTransientLink;.addTargetElement(SJ):V' |
| [ |
| pushf |
| pcall 'NTransientLinkSet;.addLink2(NTransientLink;B):V' |
| |
| analyze self.mostConcreteOutPatternElements() mode apply |
| |
| if(not self.actionBlock.oclIsUndefined()) { |
| analyze self.actionBlock |
| } |
| |
| load self.mostConcreteOutPatternElements().first().actualDeclaration() |
| ] |
| } |
| } |
| } |
| } |
| -- @end Lazy Rules |
| -- @end Declarative Part |
| |
| -- @begin OCL |
| -- @begin LiteralExps |
| -- @begin PrimitiveExps |
| code BooleanExp | self.booleanSymbol { |
| pusht |
| } |
| |
| code BooleanExp { |
| pushf |
| } |
| |
| code IntegerExp { |
| pushi self.integerSymbol |
| } |
| |
| code RealExp { |
| pushd self.realSymbol |
| } |
| |
| code StringExp { |
| push self.stringSymbol |
| } |
| -- @end PrimitiveExps |
| |
| code EnumLiteralExp { |
| push 'EnumLiteral' |
| push '#native' |
| new |
| dup |
| push self.name |
| set 'name' |
| } |
| |
| code OclUndefinedExp { |
| push 'Sequence' |
| push '#native' |
| new |
| call 'QJ.first():J' |
| } |
| |
| -- @begin CollectionExps |
| code BagExp { |
| push 'Bag' |
| push '#native' |
| new |
| analyze self.elements { |
| call 'CJ.including(J):CJ' |
| } |
| } |
| |
| code SequenceExp { |
| push 'Sequence' |
| push '#native' |
| new |
| analyze self.elements { |
| call 'CJ.including(J):CJ' |
| } |
| } |
| |
| code SetExp { |
| push 'Set' |
| push '#native' |
| new |
| analyze self.elements { |
| call 'CJ.including(J):CJ' |
| } |
| } |
| |
| code OrderedSetExp { |
| push 'OrderedSet' |
| push '#native' |
| new |
| analyze self.elements { |
| call 'CJ.including(J):CJ' |
| } |
| } |
| -- @end CollectionExps |
| |
| code TupleExp { |
| push 'Tuple' |
| push '#native' |
| new |
| analyze self.tuplePart |
| } |
| |
| code TuplePart { |
| dup |
| analyze self.initExpression |
| set self.varName |
| } |
| |
| code MapExp { |
| push 'Map' |
| push '#native' |
| new |
| analyze self.elements |
| } |
| |
| code MapElement { |
| analyze self.key |
| analyze self.value |
| call 'J.including(JJ):J' |
| } |
| -- @end LiteralExps |
| |
| -- @begin Types |
| -- @begin SimpleTypes |
| code OclAnyType { |
| push 'OclAny' |
| push '#native' |
| findme |
| } |
| |
| code IntegerType { |
| push 'Integer' |
| push '#native' |
| findme |
| } |
| |
| code BooleanType { |
| push 'Boolean' |
| push '#native' |
| findme |
| } |
| |
| code RealType { |
| push 'Real' |
| push '#native' |
| findme |
| } |
| |
| code StringType { |
| push 'String' |
| push '#native' |
| findme |
| } |
| -- @end Simple Types |
| |
| code OclModelElement { |
| push self.name |
| push self.model.name |
| findme |
| } |
| |
| code TupleType { |
| push 'TupleType' |
| push '#native' |
| new |
| analyze self.attributes |
| } |
| |
| code TupleTypeAttribute { |
| dup |
| push self.name |
| push 'OclAny' |
| push '#native' |
| findme |
| pcall 'J.addAttribute(SJ):V' |
| } |
| |
| -- @begin Parameterized Types |
| code CollectionType { |
| push 'OclParametrizedType' |
| push '#native' |
| new |
| dup |
| push 'Collection' |
| pcall 'J.setName(S):V' |
| dup |
| analyze self.elementType |
| pcall 'J.setElementType(J):V' |
| } |
| |
| code SequenceType { |
| push 'OclParametrizedType' |
| push '#native' |
| new |
| dup |
| push 'Sequence' |
| pcall 'J.setName(S):V' |
| dup |
| analyze self.elementType |
| pcall 'J.setElementType(J):V' |
| } |
| |
| code SetType { |
| push 'OclParametrizedType' |
| push '#native' |
| new |
| dup |
| push 'Set' |
| pcall 'J.setName(S):V' |
| dup |
| analyze self.elementType |
| pcall 'J.setElementType(J):V' |
| } |
| |
| code OrderedSetType { |
| push 'OclParametrizedType' |
| push '#native' |
| new |
| dup |
| push 'OrderedSet' |
| pcall 'J.setName(S):V' |
| dup |
| analyze self.elementType |
| pcall 'J.setElementType(J):V' |
| } |
| |
| code BagType { |
| push 'OclParametrizedType' |
| push '#native' |
| new |
| dup |
| push 'Bag' |
| pcall 'J.setName(S):V' |
| dup |
| analyze self.elementType |
| pcall 'J.setElementType(J):V' |
| } |
| -- @end Parameterized Types |
| -- @end Types |
| |
| code LetExp { |
| analyze self.variable.initExpression |
| variable self.variable named self.variable.varName { |
| analyze self.in_ |
| } |
| } |
| |
| code IfExp { |
| analyze self.condition |
| if thn |
| analyze self.elseExpression |
| goto eoi |
| thn: |
| analyze self.thenExpression |
| eoi: |
| } |
| |
| -- @begin VariableExp |
| code VariableExp | self.referredVariable.varName = 'thisModule' { |
| getasm |
| } |
| |
| code VariableExp | self.referredVariable.varName = 'self' { |
| load 'self' |
| } |
| |
| -- is redefined for rule inheritance |
| function VariableDeclaration::actualDeclaration() = self; |
| |
| code VariableExp | |
| (self.referredVariable isa Parameter) or ( |
| if self.referredVariable isa InPatternElement then |
| self.referredVariable.inPattern.rule isa LazyMatchedRule |
| else |
| false |
| endif |
| ) { |
| load self.referredVariable.varName |
| } |
| |
| code VariableExp { |
| load self.referredVariable.actualDeclaration() |
| } |
| -- @end VariableExp |
| |
| -- @begin PropertyCallExps |
| code OperationCallExp | |
| if self.operationName = '-' then -- use a if to optimize |
| self.arguments.size() = 0 and self.source isa NumericExp |
| else |
| false |
| endif { |
| pushi 0 |
| analyze self.source |
| call 'J.-(J):J' |
| } |
| |
| code OperationCallExp | self.source isa SuperExp { |
| load 'self' |
| analyze self.arguments |
| supercall self.operationSignature |
| } |
| |
| attribute OperationCallExp::operationSignature = |
| 'J.' + self.operationName + '(' + self.arguments->collect(e | 'J').prepend('').sum() + '):J'; |
| |
| code OperationCallExp { |
| analyze self.source |
| analyze self.arguments |
| call self.operationSignature |
| } |
| |
| code NavigationOrAttributeCallExp { |
| analyze self.source |
| get self.name |
| } |
| |
| code IterateExp | self.iterators.size() = 1 { |
| analyze self.result.initExpression |
| variable self.result named self.result.varName { |
| analyze self.source |
| iterate |
| variable self.iterators.first() named self.iterators.first().varName { |
| analyze self.body |
| store self.result |
| } |
| enditerate |
| load self.result |
| } |
| } |
| |
| -- WITH SEVERAL ITERATORS |
| code IterateExp { |
| analyze self.result.initExpression |
| variable self.result named self.result.varName { |
| analyze self.source |
| dup |
| foreach(i in self.iterators) { |
| if(i <> self.iterators.first()) { |
| dup_x1 |
| } |
| iterate |
| variable i named i.varName { |
| [ |
| analyze self.body |
| store self.result |
| ] |
| } |
| enditerate |
| if(i = self.iterators.last()) { |
| pop |
| } else { |
| swap |
| } |
| } |
| load self.result |
| } |
| } |
| |
| -- @begin IteratorExps |
| code IteratorExp | self.name = 'exists' and self.iterators.size() = 1 { |
| pushf |
| analyze self.source |
| iterate |
| variable self.iterators.first() named self.iterators.first().varName { |
| analyze self.body |
| call 'B.or(B):B' |
| } |
| enditerate |
| } |
| |
| -- WITH TWO ITERATORS |
| -- code IteratorExp | self.name = 'exists' and self.iterators.size() = 2 { |
| -- pushf -- ret |
| -- analyze self.source -- ret, Seq |
| -- dup -- ret, Seq, Seq |
| -- iterate |
| -- variable self.iterators.first() named self.iterators.first().varName { |
| -- -- ret, Seq |
| -- dup_x1 -- Seq, ret, Seq |
| -- iterate |
| -- variable self.iterators.last() named self.iterators.last().varName { |
| -- -- Seq, ret |
| -- analyze self.body -- Seq, ret, body |
| -- call 'B.or(B):B' -- Seq, ret |
| -- } |
| -- enditerate |
| -- swap |
| -- } |
| -- enditerate |
| -- pop |
| -- } |
| |
| -- WITH SEVERAL ITERATORS |
| code IteratorExp | self.name = 'exists' { |
| pushf |
| analyze self.source |
| dup |
| foreach(i in self.iterators) { |
| if(i <> self.iterators.first()) { |
| dup_x1 |
| } |
| iterate |
| variable i named i.varName { |
| [ |
| analyze self.body |
| call 'B.or(B):B' |
| ] |
| } |
| enditerate |
| if(i = self.iterators.last()) { |
| pop |
| } else { |
| swap |
| } |
| } |
| -- ACTUAL SEMANTICS (recursive): |
| -- pushf |
| -- analyze self.source |
| -- f(iterators, statements) { |
| -- if(iterators.size() > 1) { |
| -- dup |
| -- } |
| -- iterate |
| -- variable iterators.first() named iterators.first().varName { |
| -- if(iterators.size() > 1) { |
| -- f(iterators.subSequence(2, iterators.size() - 1), statements) |
| -- } else { |
| -- statements |
| -- } |
| -- } |
| -- enditerate |
| -- } (self.iterators, { |
| -- analyze self.body |
| -- call 'B.or(B):B' |
| -- } |
| -- ) |
| } |
| |
| code IteratorExp | self.name = 'forAll' and self.iterators.size() = 1 { |
| pusht |
| analyze self.source |
| iterate |
| variable self.iterators.first() named self.iterators.first().varName { |
| analyze self.body |
| call 'B.and(B):B' |
| } |
| enditerate |
| } |
| |
| -- WITH SEVERAL ITERATORS |
| code IteratorExp | self.name = 'forAll' { |
| pusht |
| analyze self.source |
| dup |
| foreach(i in self.iterators) { |
| if(i <> self.iterators.first()) { |
| dup_x1 |
| } |
| iterate |
| variable i named i.varName { |
| [ |
| analyze self.body |
| call 'B.and(B):B' |
| ] |
| } |
| enditerate |
| if(i = self.iterators.last()) { |
| pop |
| } else { |
| swap |
| } |
| } |
| } |
| |
| -- One iterator max (from OCL spec) |
| code IteratorExp | self.name = 'isUnique' and self.iterators.size() = 1 { |
| analyze self.source |
| dup |
| call 'CJ.size():I' |
| swap |
| |
| push 'Set' |
| push '#native' |
| new |
| swap |
| iterate |
| variable self.iterators.first() named self.iterators.first().varName { |
| analyze self.body |
| call 'CJ.including(J):CJ' |
| } |
| enditerate |
| |
| call 'CJ.size():I' |
| call 'I.=(I):I' |
| } |
| |
| -- One iterator max (from OCL spec) |
| code IteratorExp | self.name = 'any' and self.iterators.size() = 1 { |
| push 'Sequence' |
| push '#native' |
| new |
| analyze self.source |
| iterate |
| variable self.iterators.first() named self.iterators.first().varName { |
| analyze self.body |
| call 'B.not():B' |
| if thn |
| load self.iterators.first() |
| call 'CJ.including(J):CJ' |
| thn: |
| } |
| enditerate |
| call 'CJ.asSequence():QJ' |
| call 'QJ.first():J' |
| } |
| |
| -- One iterator max (from OCL spec) |
| code IteratorExp | self.name = 'one' and self.iterators.size() = 1 { |
| push 'Sequence' |
| push '#native' |
| new |
| analyze self.source |
| iterate |
| variable self.iterators.first() named self.iterators.first().varName { |
| analyze self.body |
| call 'B.not():B' |
| if thn |
| load self.iterators.first() |
| call 'CJ.including(J):CJ' |
| thn: |
| } |
| enditerate |
| call 'CJ.size():I' |
| pushi 1 |
| call 'I.=(I):I' |
| } |
| |
| -- One iterator max (from OCL spec) |
| code IteratorExp | self.name = 'select' and self.iterators.size() = 1 { |
| push 'Sequence' |
| push '#native' |
| new |
| analyze self.source |
| iterate |
| variable self.iterators.first() named self.iterators.first().varName { |
| analyze self.body |
| call 'B.not():B' |
| if thn |
| load self.iterators.first() |
| call 'CJ.including(J):CJ' |
| thn: |
| } |
| enditerate |
| } |
| |
| -- One iterator max (from OCL spec) |
| code IteratorExp | self.name = 'reject' and self.iterators.size() = 1 { |
| push 'Sequence' |
| push '#native' |
| new |
| analyze self.source |
| iterate |
| variable self.iterators.first() named self.iterators.first().varName { |
| analyze self.body |
| if thn |
| load self.iterators.first() |
| call 'CJ.including(J):CJ' |
| thn: |
| } |
| enditerate |
| } |
| |
| -- One iterator max (from OCL spec) |
| code IteratorExp | self.name = 'collect' and self.iterators.size() = 1 { |
| push 'Sequence' |
| push '#native' |
| new |
| analyze self.source |
| iterate |
| variable self.iterators.first() named self.iterators.first().varName { |
| analyze self.body |
| call 'CJ.including(J):CJ' |
| } |
| enditerate |
| } |
| |
| -- One iterator max (from OCL spec) |
| code IteratorExp | self.name = 'sortedBy' and self.iterators.size() = 1 { |
| push 'Sequence' |
| push '#native' |
| new |
| analyze self.source |
| iterate |
| variable self.iterators.first() named self.iterators.first().varName { |
| |
| dup |
| call 'CJ.isEmpty():B' |
| if thn |
| dup |
| pushi 1 |
| swap |
| iterate |
| load self.iterators.first() |
| swap |
| store self.iterators.first() |
| analyze self.body |
| swap |
| store self.iterators.first() |
| analyze self.body |
| |
| call 'I.>(I):B' |
| if thn2 |
| pushi 1 |
| call 'I.+(I):I' |
| thn2: |
| enditerate |
| |
| load self.iterators.first() |
| call 'QJ.insertAt(IJ):QJ' |
| |
| goto eoi |
| thn: |
| load self.iterators.first() |
| call 'QJ.append(J):QJ' |
| eoi: |
| } |
| enditerate |
| } |
| |
| code IteratorExp | self.iterators.size() = 1 { |
| report error 'iterator \'' + self.name + '\' not supported' |
| } |
| |
| code IteratorExp { |
| report error 'iterator \'' + self.name + '\' not supported with several iterators' |
| } |
| -- @end IteratorExps |
| -- @end PropertyCallExps |
| -- @end OCL |
| |
| -- @begin Imperative Part |
| |
| CalledRule { -- TODO: REFINING |
| if(self.module.isRefining) { |
| report error 'called rules not supported in refining mode yet' |
| } else { |
| operation |
| context 'A' |
| name self.name { |
| foreach(p in self.parameters) { |
| param p.varName : 'J' |
| } |
| foreach(v in self.variables) { |
| analyze v.initExpression |
| variable v named v.varName { |
| [ |
| foreach(ope in self.ruleOutPatternElements()) { |
| push ope.type.name |
| push ope.type.model.name |
| if (not ope.model.oclIsUndefined()) { |
| push ope.model.name |
| newin |
| } else { |
| new |
| } |
| variable ope named ope.varName { |
| [ |
| foreach(ope in self.ruleOutPatternElements()) { |
| load ope |
| analyze ope.bindings |
| pop |
| } |
| if(not self.actionBlock.oclIsUndefined()) { |
| analyze self.actionBlock |
| } |
| ] |
| } |
| } |
| ] |
| } |
| } |
| } |
| } |
| } |
| |
| code ActionBlock { |
| analyze self.statements |
| } |
| |
| code BindingStat | self.source isa NavigationOrAttributeCallExp { |
| analyze self.source.source |
| if(self.isAssignment) { |
| push self.source.name |
| call 'J.refUnsetValue(S):J' |
| } |
| analyze self.value |
| set self.source.name |
| } |
| |
| code BindingStat | self.source isa VariableExp { |
| analyze self.value |
| if(self.source.referredVariable isa Parameter) { |
| store self.source.referredVariable.varName |
| } else { |
| store self.source.referredVariable |
| } |
| } |
| |
| code BindingStat { |
| report error 'only variables or navigations are allowed on the left of a binding statement' |
| } |
| |
| -- TODO: |
| -- - calling OperationCallExp in a special mode would not prevent its location to be written in the line number table |
| -- - compiler calls to operations having no return value should also be changed into pcall |
| code ExpressionStat | self.expression isa OperationCallExp { |
| analyze self.expression.source |
| analyze self.expression.arguments |
| pcall self.expression.operationSignature |
| } |
| |
| code ExpressionStat { |
| analyze self.expression |
| } |
| |
| code IfStat { |
| analyze self.condition |
| if thn |
| analyze self.elseStatements |
| goto eoi |
| thn: |
| analyze self.thenStatements |
| eoi: |
| } |
| |
| code ForStat { |
| analyze self.collection |
| iterate |
| variable self.iterator named self.iterator.varName { |
| analyze self.statements |
| } |
| enditerate |
| } |
| -- @end Imperative Part |
| } |