blob: 8bc1e3b0de6f490a066ff963b076dc05eba3de22 [file] [log] [blame]
-- @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
}