| | 1 | [[PageOutline]] |
| | 2 | |
| | 3 | = Using variables in semantic queries = |
| | 4 | 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. |
| | 5 | |
| | 6 | The problem can easily solved by the following query: |
| | 7 | {{{ |
| | 8 | mods[name=A].funs[name==A] |
| | 9 | }}} |
| | 10 | |
| | 11 | Here for each module the variable {{{A}}} binds the module's name, that is, the value of {{{A}}} changes. |
| | 12 | |
| | 13 | In the following sections we show how to make variable bindings and present some examples of using variables in semantic queries. |
| | 14 | |
| | 15 | == Binding == |
| | 16 | Variables can only be created using properties in filters. It is ''valid'' to write |
| | 17 | {{{ |
| | 18 | mods[name=A] |
| | 19 | }}} |
| | 20 | or |
| | 21 | {{{ |
| | 22 | mods.funs[A=arity] |
| | 23 | }}} |
| | 24 | |
| | 25 | but it is ''semantic error'' to write |
| | 26 | {{{ |
| | 27 | mods[A=not_a_property] |
| | 28 | }}} |
| | 29 | or |
| | 30 | {{{ |
| | 31 | mods[A=2].funs[arity==A] |
| | 32 | }}} |
| | 33 | |
| | 34 | 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. |
| | 35 | |
| | 36 | The general form of binding is the following: |
| | 37 | {{{ |
| | 38 | [property=Variable] |
| | 39 | }}} |
| | 40 | or |
| | 41 | {{{ |
| | 42 | [Variable=property] |
| | 43 | }}} |
| | 44 | |
| | 45 | Variables may bind to values of any type. They can hold atoms, integers, strings etc. |
| | 46 | |
| | 47 | == Occurences == |
| | 48 | |
| | 49 | === Filters === |
| | 50 | 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. |
| | 51 | {{{ |
| | 52 | mods.funs[name=A].calls[name==A] |
| | 53 | }}} |
| | 54 | yields all function that called by a function with the same name. Example output would be: |
| | 55 | {{{ |
| | 56 | module_a:f/0 |
| | 57 | module_b:f/1 |
| | 58 | module_b:g/2 |
| | 59 | module_c:g/1 |
| | 60 | }}} |
| | 61 | means in the program code {{{f/0}}} calls {{{f/1}}}. |
| | 62 | |
| | 63 | 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. |
| | 64 | |
| | 65 | {{{ |
| | 66 | mods.funs[arity=A, A>2] |
| | 67 | mods.funs[exported=A, dirty/=A, A==true] |
| | 68 | mods[name=A].funs[name=B, A==B] |
| | 69 | }}} |
| | 70 | |
| | 71 | ==== Conversion ==== |
| | 72 | 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. |
| | 73 | {{{ |
| | 74 | mods.funs[name=A].vars[name==A] |
| | 75 | }}} |
| | 76 | yields type error, because {{{vars.name}}} has type of string. |
| | 77 | |
| | 78 | |
| | 79 | === Iterations === |
| | 80 | Writing such query as |
| | 81 | {{{ |
| | 82 | mods.funs.{calls[name=A]}2.name |
| | 83 | }}} |
| | 84 | is exactly the same as writing |
| | 85 | {{{ |
| | 86 | mods.funs.calls[name=A].calls[name=A].name |
| | 87 | }}} |
| | 88 | |
| | 89 | It sheds light on that binding always happens in the first iteration. |
| | 90 | |
| | 91 | One may use previously bound variable or use variable bound inside the iteration later: |
| | 92 | {{{ |
| | 93 | mods.funs[name=A].{calls[name==A]}3 |
| | 94 | mods.funs.{calls[name=A]}2.mod[name==A] |
| | 95 | }}} |
| | 96 | |
| | 97 | === Closures === |
| | 98 | Similarly to iterations, variables can be used in transitive closures. Here are some examples: |
| | 99 | {{{ |
| | 100 | mods.funs.(calls[name=A)+ |
| | 101 | mods.funs[name=A].(calls[name=A])2.name |
| | 102 | }}} |
| | 103 | |
| | 104 | 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: |
| | 105 | {{{ |
| | 106 | module_a:f/0 module_a:g/0 module_a:g/1 |
| | 107 | module_a:g/0 module_a:g/1 |
| | 108 | }}} |