mirror of
https://github.com/github/codeql.git
synced 2026-05-02 12:15:17 +02:00
ql language ref: change code blocks
This commit is contained in:
@@ -35,7 +35,9 @@ Use the following syntax to define an alias for a :ref:`module <modules>`::
|
||||
module ModAlias = ModuleName;
|
||||
|
||||
For example, if you create a new module ``NewVersion`` that is an updated version
|
||||
of ``OldVersion``, you could deprecate the name ``OldVersion`` as follows::
|
||||
of ``OldVersion``, you could deprecate the name ``OldVersion`` as follows:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
deprecated module OldVersion = NewVersion;
|
||||
|
||||
@@ -59,7 +61,9 @@ For example, you can use an alias to abbreviate the name of the primitive type `
|
||||
class bool = boolean;
|
||||
|
||||
Or, to use a class ``OneTwo`` defined in a :ref:`module <explicit-modules>` ``M`` in
|
||||
``OneTwoThreeLib.qll``, you could create an alias to use the shorter name ``OT`` instead::
|
||||
``OneTwoThreeLib.qll``, you could create an alias to use the shorter name ``OT`` instead:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import OneTwoThreeLib
|
||||
|
||||
@@ -92,12 +96,16 @@ You can use an alias to abbreviate the name to ``succ``::
|
||||
predicate succ = getSuccessor/1;
|
||||
|
||||
As an example of a predicate without result, suppose you have a predicate that holds
|
||||
for any positive integer less than ten::
|
||||
for any positive integer less than ten:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
predicate isSmall(int i) {
|
||||
i in [1 .. 9]
|
||||
}
|
||||
|
||||
You could give the predicate a more descriptive name as follows::
|
||||
You could give the predicate a more descriptive name as follows:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
predicate lessThanTen = isSmall/1;
|
||||
@@ -5,7 +5,9 @@ Annotations
|
||||
|
||||
An annotation is a string that you can place directly before the declaration of a QL entity or name.
|
||||
|
||||
For example, to declare a module ``M`` as private, you could use::
|
||||
For example, to declare a module ``M`` as private, you could use:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
private module M {
|
||||
...
|
||||
@@ -20,7 +22,9 @@ For example, if you annotate an entity with ``private``, then only that particul
|
||||
private. You could still access that entity under a different name (using an :ref:`alias <aliases>`).
|
||||
On the other hand, if you annotate an entity with ``cached``, then the entity itself is cached.
|
||||
|
||||
Here is an explicit example::
|
||||
Here is an explicit example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
module M {
|
||||
private int foo() { result = 1 }
|
||||
@@ -61,7 +65,9 @@ class, and should be :ref:`overridden <overriding-member-predicates>` in non-abs
|
||||
Here is an example that uses abstract predicates. A common pattern when writing data flow
|
||||
analysis in QL is to define a configuration class. Such a configuration must describe, among
|
||||
other things, the sources of data that it tracks. A supertype of all such configurations might
|
||||
look like this::
|
||||
look like this:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
abstract class Configuration extends string {
|
||||
...
|
||||
@@ -125,7 +131,9 @@ alternatives.
|
||||
Typically, deprecated names have a QLDoc comment that tells users which updated element they
|
||||
should use instead.
|
||||
|
||||
For example, the name ``DataFlowNode`` is deprecated and has the following QLDoc comment::
|
||||
For example, the name ``DataFlowNode`` is deprecated and has the following QLDoc comment:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `DataFlow::Node` instead.
|
||||
@@ -177,7 +185,9 @@ This is useful if you don't want subclasses to change the meaning of a particula
|
||||
|
||||
For example, the predicate ``hasName(string name)`` holds if an element has the name ``name``.
|
||||
It uses the predicate ``getName()`` to check this, and it wouldn't make sense for a subclass to
|
||||
change this definition. In this case, ``hasName`` should be final::
|
||||
change this definition. In this case, ``hasName`` should be final:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class Element ... {
|
||||
string getName() { result = ... }
|
||||
|
||||
@@ -163,7 +163,9 @@ being "bound" or "unbound" is a global concept—a single binding occurrence is
|
||||
variable bound.
|
||||
|
||||
Therefore, you could fix the "infinite" examples above by providing a binding occurrence. For
|
||||
example, instead of ``int timesTwo(int n) { result = n * 2 }``, you could write::
|
||||
example, instead of ``int timesTwo(int n) { result = n * 2 }``, you could write:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
int timesTwo(int n) {
|
||||
n in [0 .. 10] and
|
||||
|
||||
@@ -33,7 +33,9 @@ You can express certain values directly in QL, such as numbers, booleans, and st
|
||||
|
||||
- :ref:`Integer <int>` literals: These are sequences of decimal digits (``0`` through ``9``),
|
||||
possibly starting with a minus sign (``-``).
|
||||
For example::
|
||||
For example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
0
|
||||
42
|
||||
@@ -50,7 +52,9 @@ You can express certain values directly in QL, such as numbers, booleans, and st
|
||||
- :ref:`String <string>` literals: These are finite strings of 16-bit characters. You can
|
||||
define a string literal by enclosing characters in quotation marks (``"..."``). Most
|
||||
characters represent themselves, but there are a few characters that you need to "escape"
|
||||
with a backslash. The following are examples of string literals::
|
||||
with a backslash. The following are examples of string literals:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
"hello"
|
||||
"They said, \"Please escape quotation marks!\""
|
||||
@@ -177,7 +181,9 @@ Aggregations
|
||||
An aggregation is a mapping that computes a result value from a set of input values that are
|
||||
specified by a formula.
|
||||
|
||||
The general syntax is::
|
||||
The general syntax is:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
<aggregate>(<variable declarations> | <formula> | <expression>)
|
||||
|
||||
@@ -203,7 +209,9 @@ The following aggregates are available in QL:
|
||||
each possible assignment of the aggregation variables.
|
||||
|
||||
For example, the following aggregation returns the number of files that have more than
|
||||
``500`` lines::
|
||||
``500`` lines:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
count(File f | f.getTotalNumberOfLines() > 500 | f)
|
||||
|
||||
@@ -217,7 +225,9 @@ The following aggregates are available in QL:
|
||||
In this case, ``<expression>`` must be of numeric type or of type ``string``.
|
||||
|
||||
For example, the following aggregation returns the name of the ``.js`` file (or files) with the
|
||||
largest number of lines::
|
||||
largest number of lines:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
max(File f | f.getExtension() = "js" | f.getBaseName() order by f.getTotalNumberOfLines())
|
||||
|
||||
@@ -260,7 +270,9 @@ The following aggregates are available in QL:
|
||||
the formula, then ``concat`` defaults to the empty string.
|
||||
|
||||
For example, the following aggregation returns the string ``"3210"``, that is, the
|
||||
concatenation of the strings ``"0"``, ``"1"``, ``"2"``, and ``"3"`` in descending order::
|
||||
concatenation of the strings ``"0"``, ``"1"``, ``"2"``, and ``"3"`` in descending order:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
concat(int i | i = [0 .. 3] | i.toString() order by i desc)
|
||||
|
||||
@@ -378,7 +390,9 @@ aggregation in a simpler form:
|
||||
|
||||
#. If you want to write an aggregation of the form ``<aggregate>(<type> v | <expression> = v | v)``,
|
||||
then you can omit the ``<variable declarations>`` and ``<formula>`` parts and write it
|
||||
as follows::
|
||||
as follows:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
<aggregate>(<expression>)
|
||||
|
||||
@@ -403,17 +417,23 @@ aggregation in a simpler form:
|
||||
count(int i, int j | i in [1 .. 3] and j in [1 .. 3] | 1)
|
||||
count(int i, int j | i in [1 .. 3] and j in [1 .. 3])
|
||||
|
||||
#. You can omit the ``<formula>`` part, but in that case you should include two vertical bars::
|
||||
#. You can omit the ``<formula>`` part, but in that case you should include two vertical bars:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
<aggregate>(<variable declarations> | | <expression>)
|
||||
|
||||
This is useful if you don't want to restrict the aggregation variables any further.
|
||||
For example, the following aggregation returns the maximum number of lines across all files::
|
||||
For example, the following aggregation returns the maximum number of lines across all files:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
max(File f | | f.getTotalNumberOfLines())
|
||||
|
||||
#. Finally, you can also omit both the ``<formula>`` and ``<expression>`` parts. For example,
|
||||
the following aggregations are equivalent ways to count the number of files in a database::
|
||||
the following aggregations are equivalent ways to count the number of files in a database:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
count(File f | any() | 1)
|
||||
count(File f | | 1)
|
||||
@@ -498,7 +518,9 @@ Any
|
||||
***
|
||||
|
||||
The general syntax of an ``any`` expression is similar to the syntax of an
|
||||
:ref:`aggregation <aggregations>`, namely::
|
||||
:ref:`aggregation <aggregations>`, namely:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
any(<variable declarations> | <formula> | <expression>)
|
||||
|
||||
@@ -536,7 +558,9 @@ Unary operations
|
||||
****************
|
||||
|
||||
A unary operation is a minus sign (``-``) or a plus sign (``+``) followed by an expression of type
|
||||
``int`` or ``float``. For example::
|
||||
``int`` or ``float``. For example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
-6.28
|
||||
+(10 - 4)
|
||||
@@ -552,7 +576,9 @@ Binary operations
|
||||
*****************
|
||||
|
||||
A binary operation consists of an expression, followed by a binary operator, followed by
|
||||
another expression. For example::
|
||||
another expression. For example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
5 % 2
|
||||
(9 + 1) / (-2)
|
||||
@@ -621,7 +647,9 @@ Since the predicate ``getASupertype()`` is defined for ``Class``, but not for ``
|
||||
can't call ``t.getASupertype()`` directly. The cast ``t.(Class)`` ensures that ``t`` is
|
||||
of type ``Class``, so it has access to the desired predicate.
|
||||
|
||||
If you prefer to use a prefix cast, you can rewrite the ``where`` part as::
|
||||
If you prefer to use a prefix cast, you can rewrite the ``where`` part as:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
where ((Class)t).getASupertype().hasName("List")
|
||||
|
||||
|
||||
@@ -22,7 +22,9 @@ The following sections describe the kinds of formulas that are available in QL.
|
||||
Comparisons
|
||||
***********
|
||||
|
||||
A comparison formula is of the form::
|
||||
A comparison formula is of the form:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
<expression> <operator> <expression>
|
||||
|
||||
@@ -109,7 +111,9 @@ As a consequence, ``A != B`` has a very different meaning to the :ref:`negation
|
||||
Type checks
|
||||
***********
|
||||
|
||||
A type check is a formula that looks like::
|
||||
A type check is a formula that looks like:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
<expression> instanceof <type>
|
||||
|
||||
@@ -121,7 +125,9 @@ example, ``x instanceof Person`` holds if the variable ``x`` has type ``Person``
|
||||
Range checks
|
||||
************
|
||||
|
||||
A range check is a formula that looks like::
|
||||
A range check is a formula that looks like:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
<expression> in <range>
|
||||
|
||||
@@ -186,7 +192,9 @@ mathematical logic.
|
||||
``exists``
|
||||
----------
|
||||
|
||||
This quantifier has the following syntax::
|
||||
This quantifier has the following syntax:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
exists(<variable declarations> | <formula>)
|
||||
|
||||
@@ -204,7 +212,9 @@ type ``int`` and holds if any value of that variable has type ``OneTwoThree``.
|
||||
``forall``
|
||||
----------
|
||||
|
||||
This quantifier has the following syntax::
|
||||
This quantifier has the following syntax:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
forall(<variable declarations> | <formula 1> | <formula 2>)
|
||||
|
||||
@@ -224,11 +234,15 @@ logically the same as ``not exists(<vars> | <formula 1> | not <formula 2>)``.
|
||||
``forex``
|
||||
---------
|
||||
|
||||
This quantifier has the following syntax::
|
||||
This quantifier has the following syntax:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
forex(<variable declarations> | <formula 1> | <formula 2>)
|
||||
|
||||
This quantifier exists as a shorthand for::
|
||||
This quantifier exists as a shorthand for:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
forall(<vars> | <formula 1> | <formula 2>) and
|
||||
exists(<vars> | <formula 1> | <formula 2>)
|
||||
@@ -296,7 +310,7 @@ You can use the keyword ``not`` before a formula. The resulting formula is calle
|
||||
|
||||
The following query selects files that are not HTML files.
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
from File f
|
||||
where not f.getFileType().isHtml()
|
||||
@@ -318,7 +332,9 @@ notation: ``if A then B else C`` is the same as writing ``(A and B) or ((not A)
|
||||
**Example**
|
||||
|
||||
With the following definition, ``visibility(c)`` returns ``"public"`` if ``x`` is
|
||||
a public class and returns ``"private"`` otherwise::
|
||||
a public class and returns ``"private"`` otherwise:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
string visibility(Class c){
|
||||
if c.isPublic()
|
||||
@@ -340,7 +356,9 @@ conjunction.
|
||||
**Example**
|
||||
|
||||
The following query selects files that have the ``js`` extension and contain fewer
|
||||
than 200 lines of code::
|
||||
than 200 lines of code:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from File f
|
||||
where f.getExtension() = "js" and
|
||||
@@ -383,7 +401,7 @@ implication. This is just a simplified notation: ``A implies B`` is the same as
|
||||
|
||||
The following query selects any ``SmallInt`` that is odd, or a multiple of ``4``.
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
class SmallInt extends int {
|
||||
SmallInt() { this = [1 .. 10] }
|
||||
@@ -396,11 +414,15 @@ The following query selects any ``SmallInt`` that is odd, or a multiple of ``4``
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#] The difference between ``A != B`` and ``not A = B`` is due to the underlying quantifiers.
|
||||
If you think of ``A`` and ``B`` as sets of values, then ``A != B`` means::
|
||||
If you think of ``A`` and ``B`` as sets of values, then ``A != B`` means:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
exists( a, b | a in A and b in B | a != b )
|
||||
|
||||
On the other hand, ``not A = B`` means::
|
||||
On the other hand, ``not A = B`` means:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
not exists( a, b | a in A and b in B | a = b )
|
||||
|
||||
|
||||
@@ -21,7 +21,9 @@ You can also write another kind of comment, namely **QLDoc comments**. These com
|
||||
QL entities and are displayed as pop-up information in QL editors. For information about QLDoc
|
||||
comments, see the ":doc:`QLDoc comment specification <qldoc-comment-specification>`."
|
||||
|
||||
The following example uses these three different kinds of comments::
|
||||
The following example uses these three different kinds of comments:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
/**
|
||||
* A QLDoc comment that describes the class `Digit`.
|
||||
|
||||
@@ -60,7 +60,7 @@ For example, consider the following QL library:
|
||||
|
||||
**OneTwoThreeLib.qll**
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
class OneTwoThree extends int {
|
||||
OneTwoThree() {
|
||||
@@ -90,7 +90,7 @@ For example:
|
||||
|
||||
**OneTwoQuery.ql**
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
import OneTwoThreeLib
|
||||
|
||||
@@ -162,7 +162,9 @@ into the :ref:`namespace <namespaces>` of the current module.
|
||||
Import statements
|
||||
=================
|
||||
|
||||
Import statements are used for importing modules. They are of the form::
|
||||
Import statements are used for importing modules. They are of the form:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import <module_expression1> as <name>
|
||||
import <module_expression2>
|
||||
|
||||
@@ -38,7 +38,9 @@ in the :ref:`namespaces <namespaces>` of the current module.
|
||||
|
||||
In an :ref:`import statement <import-statements>`, name resolution is slightly more complicated.
|
||||
For example, suppose you define a :ref:`query module <query-modules>` ``Example.ql`` with the
|
||||
following import statement::
|
||||
following import statement:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -57,7 +59,9 @@ only use such an expression in :ref:`import statements <import-statements>`, to
|
||||
library module defined by a relative path.
|
||||
|
||||
For example, suppose you define a :ref:`query module <query-modules>` ``Example.ql`` with the
|
||||
following import statement::
|
||||
following import statement:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import examples.security.MyLibrary
|
||||
|
||||
@@ -84,7 +88,9 @@ Selections
|
||||
**********
|
||||
|
||||
You can use a selection to refer to a module, type, or predicate inside a particular
|
||||
module. A selection is of the form::
|
||||
module. A selection is of the form:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
<module_expression>::<name>
|
||||
|
||||
@@ -200,7 +206,7 @@ This is easiest to understand in an example:
|
||||
|
||||
**OneTwoThreeLib.qll**
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
import MyFavoriteNumbers
|
||||
|
||||
|
||||
@@ -65,7 +65,9 @@ Predicates without result
|
||||
These predicate definitions start with the keyword ``predicate``. If a value satisfies the
|
||||
logical property in the body, then the predicate holds for that value.
|
||||
|
||||
For example::
|
||||
For example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
predicate isSmall(int i) {
|
||||
i in [1 .. 9]
|
||||
@@ -84,7 +86,9 @@ Predicates with result
|
||||
You can define a predicate with result by replacing the keyword ``predicate`` with the type
|
||||
of the result. This introduces the special variable ``result``.
|
||||
|
||||
For example::
|
||||
For example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
int getSuccessor(int i) {
|
||||
result = i + 1 and
|
||||
@@ -97,7 +101,9 @@ is the successor of ``i``.
|
||||
Note that you can use ``result`` in the same way as any other argument to the predicate.
|
||||
You can express the relation between ``result`` and other variables in any way you like.
|
||||
For example, given a predicate ``getAParentOf(Person x)`` that returns parents of ``x``, you can
|
||||
define a "reverse" predicate as follows::
|
||||
define a "reverse" predicate as follows:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
Person getAChildOf(Person p) {
|
||||
p = getAParentOf(result)
|
||||
@@ -105,7 +111,9 @@ define a "reverse" predicate as follows::
|
||||
|
||||
|
||||
It is also possible for a predicate to have multiple results (or none at all) for each value
|
||||
of its arguments. For example::
|
||||
of its arguments. For example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
string getANeighbor(string country) {
|
||||
country = "France" and result = "Belgium"
|
||||
@@ -164,7 +172,9 @@ For more information about the other kinds of predicates, see :ref:`characterist
|
||||
<characteristic-predicates>` and :ref:`member predicates <member-predicates>` in the
|
||||
":ref:`Classes <classes>`" topic.
|
||||
|
||||
Here is an example showing a predicate of each kind::
|
||||
Here is an example showing a predicate of each kind:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
int getSuccessor(int i) { // 1. Non-member predicate
|
||||
result = i + 1 and
|
||||
@@ -201,7 +211,9 @@ is not usually allowed to be infinite. In other words, a predicate can only cont
|
||||
The QL compiler reports an error when it can prove that a predicate contains variables that
|
||||
aren't constrained to a finite number of values. For more information, see ":ref:`binding`."
|
||||
|
||||
Here are a few examples of infinite predicates::
|
||||
Here are a few examples of infinite predicates:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
/*
|
||||
Compilation errors:
|
||||
@@ -239,7 +251,9 @@ use it on a restricted set of arguments. In that case, you can specify an explic
|
||||
set using the ``bindingset`` :ref:`annotation <bindingset>`. This annotation is valid for any
|
||||
kind of predicate.
|
||||
|
||||
For example::
|
||||
For example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
bindingset[i]
|
||||
int multiplyBy4(int i) {
|
||||
@@ -256,7 +270,9 @@ finite provided that ``i`` is bound to a finite number of values. Then it uses t
|
||||
a context where ``i`` is restricted to to the range ``[1 .. 10]``.
|
||||
|
||||
It is also possible to state multiple binding sets for a predicate. This can be done by adding
|
||||
multiple binding set annotations, for example::
|
||||
multiple binding set annotations, for example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
bindingset[x] bindingset[y]
|
||||
predicate plusOne(int x, int y) {
|
||||
@@ -276,7 +292,9 @@ be bound, is different from ``bindingset[x, y]``, which states that both ``x`` a
|
||||
bound.
|
||||
|
||||
The latter can be useful when you want to declare a :ref:`predicate with result <predicates-with-result>` that takes multiple input arguments.
|
||||
For example, the following predicate takes a string ``str`` and truncates it to a maximum length of ``len`` characters::
|
||||
For example, the following predicate takes a string ``str`` and truncates it to a maximum length of ``len`` characters:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
bindingset[str, len]
|
||||
string truncate(string str, int len) {
|
||||
@@ -285,7 +303,9 @@ For example, the following predicate takes a string ``str`` and truncates it to
|
||||
else result = str
|
||||
}
|
||||
|
||||
You can then use this in a :ref:`select clause <select-clauses>`, for example::
|
||||
You can then use this in a :ref:`select clause <select-clauses>`, for example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
select truncate("hello world", 5)
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ Module definitions
|
||||
|
||||
A QL module definition has the following syntax:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
module ::= annotation* "module" modulename "{" moduleBody "}"
|
||||
|
||||
@@ -182,7 +182,7 @@ A module definition extends the current module's declared module environment wit
|
||||
|
||||
QL files consist of simply a module body without a name and surrounding braces:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
ql ::= moduleBody
|
||||
|
||||
@@ -204,7 +204,7 @@ Import directives
|
||||
|
||||
An import directive refers to a module identifier:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
import ::= annotations "import" importModuleId ("as" modulename)?
|
||||
|
||||
@@ -276,7 +276,7 @@ Type references
|
||||
|
||||
With the exception of class domain types and character types (which cannot be referenced explicitly in QL source), a reference to a type is written as the name of the type. In the case of database types, the name includes the at sign (``@``, U+0040).
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
type ::= (moduleId "::")? classname | dbasetype | "boolean" | "date" | "float" | "int" | "string"
|
||||
|
||||
@@ -373,7 +373,7 @@ A *named tuple* is a finite map of variables to values. Each variable in a named
|
||||
|
||||
A *variable declaration list* provides a sequence of variables and a type for each one:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
var_decls ::= var_decl ("," var_decl)*
|
||||
var_decl ::= type simpleId
|
||||
@@ -389,7 +389,7 @@ QL programs evaluate in the context of a *store*. This section specifies several
|
||||
|
||||
A *fact* is a predicate or type along with an ordered tuple. A fact is written as the predicate name or type name followed immediately by the tuple. Here are some examples of facts:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
successor(0, 1)
|
||||
Tree.toString(@method_tree(12), "def println")
|
||||
@@ -439,13 +439,13 @@ There are two kinds of comments in QL: one-line and multiline.
|
||||
|
||||
A one-line comment is two slash characters (``/``, U+002F) followed by any sequence of characters other than line feeds (U+000A) and carriage returns (U+000D). Here is an example of a one-line comment:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
// This is a comment
|
||||
|
||||
A multiline comment is a *comment start*, followed by a *comment body*, followed by a *comment end*. A comment start is a slash (``/``, U+002F) followed by an asterisk (``*``, U+002A), and a comment end is an asterisk followed by a slash. A comment body is any sequence of characters that does not include a comment end. Here is an example multiline comment:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
/*
|
||||
It was the best of code.
|
||||
@@ -458,7 +458,7 @@ Keywords
|
||||
|
||||
The following sequences of characters are keyword tokens:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
and
|
||||
any
|
||||
@@ -513,7 +513,7 @@ Operators
|
||||
|
||||
The following sequences of characters are operator tokens:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
<
|
||||
<=
|
||||
@@ -548,7 +548,7 @@ An identifier cannot have the same sequence of characters as a keyword, nor can
|
||||
|
||||
Here are some examples of identifiers:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
width
|
||||
Window_width
|
||||
@@ -567,7 +567,7 @@ There are several kinds of identifiers:
|
||||
|
||||
Identifiers are used in following syntactic constructs:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
simpleId ::= lowerId | upperId
|
||||
modulename ::= simpleId
|
||||
@@ -583,7 +583,7 @@ Integer literals (int)
|
||||
|
||||
An integer literal is a possibly negated sequence of decimal digits (``0`` through ``9``, U+0030 through U+0039). Here are some examples of integer literals:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
0
|
||||
42
|
||||
@@ -595,7 +595,7 @@ Float literals (float)
|
||||
|
||||
A floating-point literals is a possibly negated two non-negative integers literals separated by a dot (``.``, U+002E). Here are some examples of float literals:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
0.5
|
||||
2.0
|
||||
@@ -619,7 +619,7 @@ A string literal denotes a sequence of characters. It begins and ends with a dou
|
||||
|
||||
Here are some examples of string literals:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
"hello"
|
||||
"He said, \"Logic clearly dictates that the needs of the many...\""
|
||||
@@ -629,7 +629,7 @@ Annotations
|
||||
|
||||
Various kinds of syntax can have *annotations* applied to them. Annotations are as follows:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
annotations ::= annotation*
|
||||
|
||||
@@ -745,7 +745,7 @@ Non-member predicates
|
||||
|
||||
A *predicate* is declared as a sequence of annotations, a head, and an optional body:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
predicate ::= annotations head optbody
|
||||
|
||||
@@ -757,13 +757,13 @@ A valid non-member predicate can be annotated with ``cached``, ``deprecated``, `
|
||||
|
||||
The head of the predicate gives a name, an optional *result type*, and a sequence of variables declarations that are *arguments*:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
head ::= ("predicate" | type) predicateName "(" (var_decls)? ")"
|
||||
|
||||
The body of a predicate is of one of three forms:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
optbody ::= ";"
|
||||
| "{" formula "}"
|
||||
@@ -780,7 +780,7 @@ Classes
|
||||
|
||||
A class definition has the following syntax:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
class ::= annotations "class" classname "extends" type ("," type)* "{" member* "}"
|
||||
|
||||
@@ -818,7 +818,7 @@ Members
|
||||
|
||||
Each member of a class is either a *character*, a predicate, or a field:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
member ::= character | predicate | field
|
||||
character ::= annotations classname "(" ")" "{" formula "}"
|
||||
@@ -870,7 +870,7 @@ Select clauses
|
||||
|
||||
A QL file may include at most one *select clause*. That select clause has the following syntax:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
select ::= ("from" var_decls)? ("where" formula)? "select" select_exprs ("order" "by" orderbys)?
|
||||
|
||||
@@ -884,7 +884,7 @@ The ``where`` keyword, if present, is followed by the *formula* of the select cl
|
||||
|
||||
The ``select`` keyword is followed by a number of *select expressions*. Select expressions have the following syntax:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
as_exprs ::= as_expr ("," as_expr)*
|
||||
as_expr ::= expr ("as" simpleId)?
|
||||
@@ -893,7 +893,7 @@ The keyword ``as`` gives a *label* to the select expression it is part of. No tw
|
||||
|
||||
The ``order`` keyword, if present, is followed by a number of *ordering directives*. Ordering directives have the following syntax:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
orderbys ::= orderby ("," orderby)*
|
||||
orderby ::= simpleId ("asc" | "desc")?
|
||||
@@ -923,7 +923,7 @@ Given a named tuple and a store, each expression has one or more *values*. This
|
||||
|
||||
There are several kinds of expressions:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
exprs ::= expr ("," expr)*
|
||||
|
||||
@@ -947,7 +947,7 @@ Parenthesized expressions
|
||||
|
||||
A parenthesized expression is an expression surrounded by parentheses:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
eparen ::= "(" expr ")"
|
||||
|
||||
@@ -958,7 +958,7 @@ Don't-care expressions
|
||||
|
||||
A don't-care expression is written as a single underscore:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
dontcare ::= "_"
|
||||
|
||||
@@ -969,7 +969,7 @@ Literals
|
||||
|
||||
A literal expression is as follows:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
literal ::= "false" | "true" | int | float | string
|
||||
|
||||
@@ -980,7 +980,7 @@ Unary operations
|
||||
|
||||
A unary operation is the application of ``+`` or ``-`` to another expression:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
unop ::= "+" expr
|
||||
| "-" expr
|
||||
@@ -996,7 +996,7 @@ Binary operations
|
||||
|
||||
A binary operation is written as a *left operand* followed by a *binary operator*, followed by a *right operand*:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
binop ::= expr "+" expr
|
||||
| expr "-" expr
|
||||
@@ -1023,7 +1023,7 @@ Variables
|
||||
|
||||
A variable has the following syntax:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
variable ::= varname | "this" | "result"
|
||||
|
||||
@@ -1036,7 +1036,7 @@ Super
|
||||
|
||||
A super expression has the following syntax:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
super_expr ::= "super" | type "." "super"
|
||||
|
||||
@@ -1055,7 +1055,7 @@ Casts
|
||||
|
||||
A cast expression is a type in parentheses followed by another expression:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
cast ::= "(" type ")" expr
|
||||
|
||||
@@ -1070,7 +1070,7 @@ Postfix casts
|
||||
|
||||
A postfix cast is a primary expression followed by a dot and then a class or primitive type in parentheses:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
postfix_cast ::= primary "." "(" type ")"
|
||||
|
||||
@@ -1081,7 +1081,7 @@ Calls with results
|
||||
|
||||
An expression for a call with results is of one of two forms:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
callwithresult ::= predicateRef (closure)? "(" (exprs)? ")"
|
||||
| primary "." predicateName (closure)? "(" (exprs)? ")"
|
||||
@@ -1124,7 +1124,7 @@ Aggregations
|
||||
|
||||
An aggregation can be written in one of two forms:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
aggregation ::= aggid ("[" expr "]")? "(" (var_decls)? ("|" (formula)? ("|" as_exprs ("order" "by" aggorderbys)?)?)? ")"
|
||||
| aggid ("[" expr "]")? "(" as_exprs ("order" "by" aggorderbys)? ")"
|
||||
@@ -1218,7 +1218,7 @@ Any
|
||||
|
||||
The ``any`` expression is a special kind of quantified expression.
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
any ::= "any" "(" var_decls ("|" (formula)? ("|" expr)?)? ")"
|
||||
|
||||
@@ -1231,7 +1231,7 @@ Ranges
|
||||
|
||||
Range expressions denote a range of values.
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
range ::= "[" expr ".." expr "]"
|
||||
|
||||
@@ -1246,7 +1246,7 @@ Set literals
|
||||
|
||||
Set literals denote a choice from a collection of values.
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
setliteral ::= "[" expr ("," expr)* "]"
|
||||
|
||||
@@ -1275,7 +1275,7 @@ A formula is a form of syntax used to *match* a named tuple given a store.
|
||||
|
||||
There are several kinds of formulas:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
formula ::= fparen
|
||||
| disjunction
|
||||
@@ -1296,7 +1296,7 @@ Parenthesized formulas
|
||||
|
||||
A parenthesized formula is a formula enclosed by a pair of parentheses:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
fparen ::= "(" formula ")"
|
||||
|
||||
@@ -1307,7 +1307,7 @@ Disjunctions
|
||||
|
||||
A disjunction is two formulas separated by the ``or`` keyword:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
disjunction ::= formula "or" formula
|
||||
|
||||
@@ -1318,7 +1318,7 @@ Conjunctions
|
||||
|
||||
A conjunction is two formulas separated by the ``and`` keyword:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
conjunction ::= formula "and" formula
|
||||
|
||||
@@ -1329,7 +1329,7 @@ Implications
|
||||
|
||||
An implication formula is two formulas separated by the ``implies`` keyword:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
implies ::= formula "implies" formula
|
||||
|
||||
@@ -1342,7 +1342,7 @@ Conditional formulas
|
||||
|
||||
A conditional formula has the following syntax:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
ifthen ::= "if" formula "then" formula "else" formula
|
||||
|
||||
@@ -1355,7 +1355,7 @@ Negations
|
||||
|
||||
A negation formula is a formula preceded by the ``not`` keyword:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
negated ::= "not" formula
|
||||
|
||||
@@ -1366,7 +1366,7 @@ Quantified formulas
|
||||
|
||||
A quantified formula has several syntaxes:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
quantified ::= "exists" "(" expr ")"
|
||||
| "exists" "(" var_decls ("|" formula)? ("|" formula)? ")"
|
||||
@@ -1390,7 +1390,7 @@ Comparisons
|
||||
|
||||
A comparison formula is two expressions separated by a comparison operator:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
comparison ::= expr compop expr
|
||||
compop ::= "=" | "!=" | "<" | ">" | "<=" | ">="
|
||||
@@ -1408,7 +1408,7 @@ Type checks
|
||||
|
||||
A type check formula has the following syntax:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
instanceof ::= expr "instanceof" type
|
||||
|
||||
@@ -1423,7 +1423,7 @@ Range checks
|
||||
|
||||
A range check has the following syntax:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
inrange ::= expr "in" range
|
||||
|
||||
@@ -1434,7 +1434,7 @@ Calls
|
||||
|
||||
A call has the following syntax:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
call ::= predicateRef (closure)? "(" (exprs)? ")"
|
||||
| primary "." predicateName (closure)? "(" (exprs)? ")"
|
||||
@@ -1471,7 +1471,7 @@ Aliases
|
||||
|
||||
Aliases define new names for existing QL entities.
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
alias ::= annotations "predicate" literalId "=" predicateRef "/" int ";"
|
||||
| annotations "class" classname "=" type ";"
|
||||
@@ -1891,7 +1891,7 @@ Summary of syntax
|
||||
|
||||
The complete grammar for QL is as follows:
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
ql ::= moduleBody
|
||||
|
||||
|
||||
@@ -37,7 +37,9 @@ Apart from the expressions described in ":ref:`expressions`," you can also inclu
|
||||
|
||||
.. TODO: link to topics on formulas and expressions in QL
|
||||
|
||||
For example::
|
||||
For example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from int x, int y
|
||||
where x = 3 and y in [0 .. 2]
|
||||
@@ -76,7 +78,9 @@ Query predicates
|
||||
A query predicate is a :ref:`non-member predicate <non-member-predicates>` with a ``query``
|
||||
annotation. It returns all the tuples that the predicate evaluates to.
|
||||
|
||||
For example::
|
||||
For example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
query int getProduct(int x, int y) {
|
||||
x = 3 and
|
||||
|
||||
@@ -132,7 +132,9 @@ If a recursive predicate evaluates to the empty set of values, there is usually
|
||||
wrong.
|
||||
|
||||
For example, you might try to define the predicate ``getAnAncestor()`` (from the
|
||||
:ref:`above <transitive-closures>` example) as follows::
|
||||
:ref:`above <transitive-closures>` example) as follows:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
Person getAnAncestor() {
|
||||
result = this.getAParent().getAnAncestor()
|
||||
@@ -141,7 +143,9 @@ For example, you might try to define the predicate ``getAnAncestor()`` (from the
|
||||
In this case, the QL compiler gives an error stating that this is an empty recursive call.
|
||||
|
||||
Since ``getAnAncestor()`` is initially assumed to be empty, there is no way for new values to
|
||||
be added. The predicate needs a starting point for the recursion, for example::
|
||||
be added. The predicate needs a starting point for the recursion, for example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
Person getAnAncestor() {
|
||||
result = this.getAParent()
|
||||
@@ -158,7 +162,9 @@ A valid recursive predicate must also be `monotonic <https://en.wikipedia.org/wi
|
||||
This means that (mutual) recursion is only allowed under an even number of :ref:`negations <negation>`.
|
||||
|
||||
Intuitively, this prevents "`liar's paradox <https://en.wikipedia.org/wiki/Liar_paradox>`_"
|
||||
situations, where there is no solution to the recursion. For example::
|
||||
situations, where there is no solution to the recursion. For example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
predicate isParadox() {
|
||||
not isParadox()
|
||||
@@ -181,7 +187,9 @@ For example, consider the following (slightly macabre) member predicate of class
|
||||
|
||||
The recursive call to ``isExtinct()`` is nested in an even number of negations, so this is a
|
||||
valid definition.
|
||||
In fact, you could rewrite the second part of the definition as follows::
|
||||
In fact, you could rewrite the second part of the definition as follows:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
forall(Person descendant | descendant.getAParent+() = this |
|
||||
descendant.isExtinct()
|
||||
|
||||
@@ -79,7 +79,9 @@ To define a class, you write:
|
||||
#. The types to extend.
|
||||
#. The :ref:`body of the class <class-bodies>`, enclosed in braces.
|
||||
|
||||
For example::
|
||||
For example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class OneTwoThree extends int {
|
||||
OneTwoThree() { // characteristic predicate
|
||||
@@ -146,7 +148,9 @@ Member predicates
|
||||
|
||||
These are :ref:`predicates <predicates>` that only apply to members of a particular class.
|
||||
You can :ref:`call <calls>` a member predicate on a value. For example, you can use the member
|
||||
predicate from the :ref:`above <defining-a-class>` class::
|
||||
predicate from the :ref:`above <defining-a-class>` class:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
1.(OneTwoThree).getAString()
|
||||
|
||||
@@ -166,7 +170,9 @@ This call returns ``"ONE, TWO OR THREE: 1"``.
|
||||
.. index:: this
|
||||
.. _this:
|
||||
|
||||
.. note::
|
||||
.. note:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
Characteristic predicates and member predicates often use the variable ``this``.
|
||||
This variable always refers to a member of the class—in this case a value belonging to the
|
||||
@@ -240,7 +246,9 @@ abstract class, you can add more subclasses to it.
|
||||
|
||||
If you are writing a security query, you may be interested in identifying
|
||||
all expressions that can be interpreted as SQL queries.
|
||||
You can use the following abstract class to describe these expressions::
|
||||
You can use the following abstract class to describe these expressions:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
abstract class SqlExpr extends Expr {
|
||||
...
|
||||
@@ -343,7 +351,9 @@ Multiple inheritance
|
||||
|
||||
A class can extend multiple types. In that case, it inherits from all those types.
|
||||
|
||||
For example, using the definitions from the above section::
|
||||
For example, using the definitions from the above section:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class Two extends OneTwo, TwoThree {}
|
||||
|
||||
@@ -401,7 +411,9 @@ of values that satisfy the argument types and the body.
|
||||
|
||||
A benefit of this is that each branch can have a different structure. For example, if you want
|
||||
to define an "option type" that either holds a value (such as a ``Call``) or is empty, you
|
||||
could write this as follows::
|
||||
could write this as follows:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
newtype OptionCall = SomeCall(Call c) or NoCall()
|
||||
|
||||
@@ -415,7 +427,9 @@ To define an algebraic datatype, use the following general syntax::
|
||||
|
||||
newtype <TypeName> = <branches>
|
||||
|
||||
The branch definitions have the following form::
|
||||
The branch definitions have the following form:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
<BranchName>(<arguments>) { <body> }
|
||||
|
||||
@@ -429,7 +443,9 @@ The branch definitions have the following form::
|
||||
Note that branch bodies are evaluated fully, so they must be finite. They should be kept small
|
||||
for good performance.
|
||||
|
||||
For example, the following algebraic datatype has three branches::
|
||||
For example, the following algebraic datatype has three branches:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
newtype T =
|
||||
Type1(A a, B b) { body(a, b) }
|
||||
@@ -495,7 +511,9 @@ Type unions of :ref:`database types <database-types>` are also supported.
|
||||
You can use a type union to give a name to a subset of the branches from an algebraic datatype.
|
||||
In some cases, using the type union over the whole algebraic datatype can avoid spurious
|
||||
:ref:`recursion <recursion>` in predicates.
|
||||
For example, the following construction is legal::
|
||||
For example, the following construction is legal:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
newtype InitialValueSource =
|
||||
ExplicitInitialization(VarDecl v) { exists(v.getInitializer()) } or
|
||||
|
||||
@@ -34,7 +34,9 @@ and many more.
|
||||
Conceptually, you can think of a variable as holding all the values that its type allows, subject
|
||||
to any further constraints.
|
||||
|
||||
For example, consider the following select clause::
|
||||
For example, consider the following select clause:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from int i
|
||||
where i in [0 .. 9]
|
||||
@@ -44,7 +46,9 @@ Just based on its type, the variable ``i`` could contain all integers. However,
|
||||
constrained by the formula ``i in [0 .. 9]``. Consequently, the result of the select clause is
|
||||
the ten numbers between ``0`` and ``9`` inclusive.
|
||||
|
||||
As an aside, note that the following query leads to a compile-time error::
|
||||
As an aside, note that the following query leads to a compile-time error:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from int i
|
||||
select i
|
||||
@@ -64,7 +68,9 @@ affect the value of an :ref:`expression <expressions>` that uses them, or whethe
|
||||
Other variables, called **bound** variables, are restricted to specific sets of values.
|
||||
|
||||
It might be easiest to understand this distinction in an example. Take a look at the following
|
||||
expressions::
|
||||
expressions:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
"hello".indexOf("l")
|
||||
|
||||
@@ -90,7 +96,9 @@ variables ``i`` and ``x`` respectively. In other words, the value of the variabl
|
||||
on the value of the expression. These are examples of **free variables**.
|
||||
|
||||
Similarly, if a formula contains free variables, then the formula can hold or not hold
|
||||
depending on the values assigned to those variables [#]_. For example::
|
||||
depending on the values assigned to those variables [#]_. For example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
"hello".indexOf("l") = 1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user