Namespace funnyqt.model2model

Rule-base out-place transformations similar to ATL or QVTo.

Other Namespaces

Show/Hide
funnyqt.bidi
Bidirectional transformations (BX).
funnyqt.coevo.tg
Co-Evolution transformations on TGraphs.
funnyqt.edn
Printing/persisting and reading/loading query results and transformation traces as EDN.
funnyqt.emf
Core functions for accessing and manipulating EMF models.
funnyqt.extensional
Specify models extensionally.
funnyqt.generic
Generic protocols extended upon many different types, and generic functions.
funnyqt.in-place
In-place transformation stuff.
funnyqt.pmatch
Graph Pattern Matching on arbitrary models.
funnyqt.polyfns
Polymorphic functions dispatching on types of model elements.
funnyqt.query
Generic query functions like regular path expressions & quantified expressions.
funnyqt.query.emf
EMF-specific query functions
funnyqt.query.tg
TG-specific query functions
funnyqt.relational
Relational Model Querying.
funnyqt.tg
Core functions for accessing and manipulating TGraphs.
funnyqt.utils
Generic utility functions, e.g., for signaling errors, debugging, and profiling,
funnyqt.visualization
Model visualization functions.
funnyqt.xmltg
Convert XML to DOM-like TGraphs.
Index Page
Alphabetic Var Index

Public Vars

Usage Documentation

Show/Hide
Rule-base out-place transformations similar to ATL or QVTo.
Back to top

Details of Public Vars

Dynamic Var: *initial-trace*

  A map {rule {input output}} used for initializing *trace* if bound.
Back to top View Source

Dynamic Var: *trace*

  An atom holding a map {rule {input output}}. Used internally.
Back to top View Source

Macro: deftransformation

Arglists:
=========

  (deftransformation name [args] extends-clause? & rules-and-fns)

Docstring:
==========

  Creates a model-to-model transformation named `name` with the declared
  input, output, input-output models, and further arguments in `args`.  For
  example,

    (deftransformation foo2bar [^:in in1 ^:in in2 ^:out out x y z]
      ...)

  declares a transformation named foo2bar that receives 2 input models in1 and
  in2, creates elements in the single output model out, and has three
  additional parameters x, y, and z.

  In the rest of the transformation spec, rules and functions are defined.

  Rules
  =====

  Rules are defined similarily, but they are identified by several keywords.
  A plain mapping rule has the form:

    (a2b
      :from [a 'InClass, x]
      :id   [id [(aval a :name) x]]
      :dup-id-eval true
      :when (some-predicate? a)
      :when-let [v (some-fn a)]
      :let  [y (some-other-fn a x)
             z (some-other-fn2 a x y)]
      :to   [b 'OutClass
             c 'OutClass2 {:name id}
             d 'OutClass3 :in out)]
      (do-stuff-with a x v b y z b c d))

  :from declares the number and types of elements for which this rule is
  applicable.  Providing types is optional thus making it possible to have
  strings or numbers as input elements.  So the rule above has to be called
  with 2 arguments a and x, and the first one needs to be of metamodel type
  InClass.

  :id is a var-expression tuple.  The expression should compute an identity of
  the rule's input elements.  Traceability links are managed also from the
  elements' identity to the elements created in that rule.  :id is optional.
  By default, if the rule is called again with different elements that have the
  same identity, the results of the first evaluation are immediately returned.
  If :dup-id-eval is true and the rule is called with different elements that
  have the same identity, then the constraints are checked again, :let-bindings
  are established, the existing output elements are retrieved and bound to the
  vars in :to (without creating new elements nor setting properties as
  specified by the output elements' prop-maps), and then the body is evaluated
  again (with the new similar input elements and the old existing output
  elements).  This can be used to merge duplicate elements in the source to one
  canonical output element that subsumes the properties of all input elements.
  To distinguish if body is evaluated the first time or an additional time for
  a similar element, check if (resolve-in :rule-name id) returns nil.  If so,
  it's the first time.  Else, it's an additional time and you might want to use
  only add!-operations and no set!-operations.

  :when constrains the input elements to those satisfying some predicate.
  The :when clause is optional.

  :when-let has a vector of variable-expression pairs.  The expressions are
  evaluated and bound to the respective vars.  The rule may only be applied if
  all the vars are non-nil (which makes the "when"-part in :when-let).
  The :when-let is optional, and it is evaluated after :from and :when already
  matched.

  :let has a vector of variable-expression pairs.  The expressions are
  evaluated and bound to the respective vars.  The :let is evaluated
  after :when-let has matched, that is, the vars bound by :when-let may be used
  in the :let expressions, but not the other way round.

  :to is a vector of output elements paired with their type that are to be
  created.

    :to [b 'OutClass,
         c (type-expr ...)]

  As can be seen, the type may be the value of an expression which should
  return either a qualified name as a symbol or the actual metamodel class
  object.

  If there are multiple output models, the :to spec may state in which model a
  given object has to be created, e.g.,

    :to [b 'OutClass :in out2,
         c (type-expr ...) :in out2]

  If there are multiple output models but an element in :to doesn't specify the
  target model explicitly, the first output model in the transformation's
  argument vector is used as a default.

  The :to vector may also specify values for the newly created element's
  properties (attributes and references).  Those a specified using a map.

    :to [b 'OutClass :in out2 {:name "Some Name", ...},
         c (type-expr ...) {:name "Other name", :links b} :in out2]

  As can be seen, if a target element specification contains both a property
  map and a :in spec, they may occur in any order.

  Following these special keyword-clauses, arbitrary code may follow, e.g., to
  set attributes and references of the newly created objects by calling other
  rules.

  A rule always returns the elements that where created for the given input
  elements (or rather their identity) in terms of the :to clause.  If the
  called rule's :to clause creates only one object, the result of a call is
  this object.  If its :to clause creates multiple objects, the result of a
  call is a vector of the created objects in the order of their declaration
  in :to.

  Disjunctive Rules
  =================

  Besides normal mapping rules, there are disjunctive rules.  Those have the
  following form:

    (x2y
      :from [x]
      :disjuncts [a2b c2d ... :as y]
      (optional-body-using y))

  Disjunctive rules mustn't have a :to clause, but :let/:when/:when-let are
  supported.  When a disjunctive rule is applied, it tries the given disjunct
  rules in the declared order.  The first one whose constraints and :from type
  match gets applied.  An optional :as clause may be the last thing in
  the :disjuncts vector.  If a disjunct rule could be applied, the result is
  bound to that clause's var and can be used in the optional body.  The spec
  may be a symbol or any destructuring form supported by let.

  Top-level Rules
  ===============

  A rule can be declared as top-level rule using ^:top metadata:

    (^:top a2b
       :from [a 'A]
       ;; same as above
       ...)

  When the transformation gets executed, all top-level rules are applied to
  matching elements automatically (unless there's a main function; see below).
  All other rules have to be called from the top-level rules explicitly.
  Top-level rules must have exactly one element declared in their :from clause.

  Functions
  =========

  Functions are just arbitrary local helpers.  They are to be defined using the
  syntax of function definitions in `letfn`, that is, they support for
  overloading, etc.

  There may be one special function called `main`.  This function must not have
  parameters.  If defined, it acts as the entry point to the transformation,
  i.e., it is called automatically and is responsible for calling the
  transformation's rules appropriately.  If there's a `main` function, the
  ^:top metadata attached to rules has no effect, that is, they are not called
  automatically anymore, but now that's the job of the `main` function.

  So you usually have either top-level rules or a `main` function.  The former
  is simpler and usually suffices while the latter provides more control to the
  user.  For example, a transformation needs to bind additional dynamic vars.
  In this case, defining a `main` function allows you to do so.

  Transformation Trace
  ====================

  The transformation function returns the trace of the transformation as a map
  of the form:

    {:rule1 {[in1 in2] out1, ...}
     :rule2 {in [out1 out2], ...}
     ...}

  In that example, it is obvious that rule1 creates just one target element for
  two given input elements, whereas rule2 creates two output elements for one
  given input element.  In other words, rule1 has 2 elements declared in :from
  and 1 element in :to, and for rule2 it's the other way round.

  Transformation Inheritance
  ==========================

  By providing :extends clause after the transformation's argument vector (a
  symbol or a vector of symbols denoting other transformations), one can
  declare the new transformation to extend one or many other transformations.

    (deftransformation foo2bar
     "Transforms a foo model to a bar model."
      [^:in foo ^:out bar]
      :extends foo2bar-base
     ...)

  The transformation foo2bar extends foo2bar-base here.  This means that
  foo2bar contains its own rules and functions plus the ones defined in
  foo2bar-base.  If foo2bar defines a function or rule that's already defined
  by foo2bar-base, then foo2bar's version overrides the version from
  foo2bar-base.  foo2bar calls all top-level relations defined by itself and
  foo2bar-base.

  Note that transformation inheritance doesn't mangle transformation parameters
  in any way.  If a base transformation uses an argument vector [src trg] but
  the extending transformation uses [s t], you'll get a compile error if
  inherited/non-overridden rules use either one of them.  So in general, it is
  advisable that extending transformations declare the very same parameters in
  the same order as the extended transformations, plus optionally some more
  parameters.
Back to top View Source

Function: resolve-in

Arglists:
=========

  (resolve-in rule in)

Docstring:
==========

  Resolve the input `in` of `rule` (given as keyword) in the transformation
  trace.  If the `rule` has only one input element, that has to be provided.
  If it has multiple input elements, a vector containing all of them in the
  order of the rule's :from clause has to be provided.  The return value is
  either the single output element of `rule` or a vector of output elements in
  the order of the rule's :to clause if the rule creates many output elements.
  If the rule has no :to clause at all, then the value of the rule's body is
  returned.

  Note that resolve-in doesn't work for disjunctive rules because those have no
  trace mappings themselves and just delegate to the disjunct rules.

  Also note that resolve-in is seldomly useful because (some-rule in) either
  transforms `in` or resolves it if it has been transformed already.  The only
  place where resolve-in is really needed is with rules with :id
  and :dup-id-eval clauses where one wants to test in the body if the current
  in elements have already been transformed.
Back to top View Source