wiki:SemanticQuery/Variables

Using variables in semantic queries

While writing semantic queries sometimes we need to refer back to a property of an entity at some earlier point. To make it clear suppose we want to write such query: we would like to know about functions with the same name as the module it resides in, like mymod:mymod/1. Lets start with all the functions of all modules:

mods.funs

This query has starting point mods. Moving on we select functions with funs. At this point we need to filter all the functions based on its module. Here come the variables in:

The problem can easily solved by the following query:

mods[name=A].funs[name==A]

Here for each module the variable A binds the module's name. Unlike in Erlang, the value of A changes.

In the following sections we show how to make variable bindings and present some examples of using variables in semantic queries.

Binding

Variables may only be defined using properties in filters or selectors.

Properties

It is valid to write

mods[name=A]

or

mods.funs[A=arity]

but it is semantic error to write

mods[A=this_is_not_a_property_of_modules]

or

mods[A=2].funs[arity==A]

As you may noticed, the binding operator = is commutative. Also, one may interchangeably use = and == to bind and compare. In case of variables the first = or == binds, the others test equality.

The general form of binding to a property is the following:

[property=Variable]

or

[Variable=property]

Variables may bind to values of any type. They can hold atoms, integers, strings etc.

Selectors

It is also possible to bind a variable to a selector. In this case the variable holds a database node.

mods->M

Variable M can function as a selector in the rest of a query. Its semantics is to set the state of current entities to the bound entity.

mods->M.funs[name=foo].M.path

is equivalent to

mods[.funs[name=foo]].path

Variables bound this way can be used to filter entities. It comes handy when we would like to know all the recursive functions:

mods.funs->A.calls?A

Of course one cannot mix variables bound to properties and selectors. The following query will make the type checker complain:

mods[name=M].funs?M

Occurences

Filters

Variables may occur in filters. In its simplest form a semantic query is sequence of selectors and filters. When a variable binding takes place the variable can be used in any following filter. In other words, the scope of a variable is right from its binding.

mods.funs[name=A].calls[name==A]

yields all function that called by a function with the same name. Example output would be:

module_a:f/0
  module_b:f/1
module_b:g/2
  module_c:g/1

means in the program code f/0 calls f/1.

In comparisons it is allowed to use every operator that standard semantic query language allows. This means ==, /=, >, >=, <, <= and ~ . A variable can be compared to property, variable and literal.

mods.funs[arity=A, A>2]
mods.funs[exported=A, dirty/=A, A==true]
mods[name=A].funs[name=B, A==B]

Conversion

It is important to note that any form of conversion between types is not supported. When you use a value of a variable in a comparison, you have to make sure that both operands have the same type.

mods.funs[name=A].vars[name==A]

yields type error, because vars.name has type of string.

At the end of a query

Ending a query with a variable make RefactorErl? show the set of values of that variable.

mods[name=M].M

Results in

Mymod1
  M = Mymod1
Mymod2
  M = Mymod2

Iterations

Writing such query as

mods.funs.{calls[name=A]}2.name

is exactly the same as writing

mods.funs.calls[name=A].calls[name=A].name

It sheds light on that binding always happens in the first iteration.

One may use previously bound variable or use variable bound inside the iteration later:

mods.funs[name=A].{calls[name==A]}3
mods.funs.{calls[name=A]}2.mod[name==A]

Closures

Similarly to iterations, variables can be used in transitive closures. Here are some examples:

mods.funs.(calls[name=A)+
mods.funs[name=A].(calls[name=A])2.name

The first means all possible call chains that consist of functions with the same name from the second chain link onwards. Example output would be:

module_a:f/0 module_a:g/0 module_a:g/1
module_a:g/0 module_a:g/1
Last modified 10 years ago Last modified on Apr 17, 2015, 2:58:31 PM