Version 2 (modified by manualwiki, 10 years ago) (diff) |
---|
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