wiki:SemanticQuery/Variables

Version 1 (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. A query always has the starting point mods. Moving on we select functions with funs. Here 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, that is, 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 can only be created using properties in filters. It is valid to write

mods[name=A]

or

mods.funs[A=arity]

but it is semantic error to write

mods[A=not_a_property]

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. The first = or == binds, the others compare.

The general form of binding is the following:

[property=Variable]

or

[Variable=property]

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

Occurences

Filters

Variables can only 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.

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