Skip to content

Commit

Permalink
Merge branch 'topic/adapt_doc' into 'master'
Browse files Browse the repository at this point in the history
Document language changes

See merge request eng/libadalang/langkit-query-language!214
  • Loading branch information
raph-amiard committed Apr 30, 2024
2 parents a012d02 + f16f007 commit b571ab3
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 35 deletions.
64 changes: 34 additions & 30 deletions lkql_checker/doc/gnatcheck_rm/writing_your_own_rules.rst
Expand Up @@ -159,8 +159,10 @@ Here is an example rule:
|" Will flag object declarations for which the type is the standard
|" ``Integer`` type
node is o@ObjectDecl(
p_type_expression() is SubtypeIndication(
p_designated_type_decl() is t@* when t == o.p_std_entity("Integer")))
p_type_expression(): SubtypeIndication(
p_designated_type_decl(): t@* when t == o.p_std_entity("Integer")
)
)
Debugging Your Rules
--------------------
Expand Down Expand Up @@ -303,7 +305,7 @@ by using the libadalang ``p_is_int_type`` property:
.. code-block:: lkql
@check(message="integer type may be replaced by an enumeration")
fun integer_types_as_enum(node) = node is TypeDecl(p_is_int_type() is true)
fun integer_types_as_enum(node) = node is TypeDecl(p_is_int_type(): true)
Now, we'll add a first criteria to consider: there should be no use
of any arithmetic or bitwise operator on this type anywhere in the sources. To
Expand All @@ -313,10 +315,10 @@ operators:
.. code-block:: lkql
select BinOp(f_op is OpDiv or OpMinus or OpMod or OpMult or
OpPlus or OpPow or OpRem or OpXor or
OpAnd or OpOr)
or UnOp(f_op is OpAbs or OpMinus or OpPlus or OpNot)
select (
BinOp(f_op: OpDiv | OpMinus | OpMod | OpMult | OpPlus | OpPow | OpRem | OpXor | OpAnd | OpOr)
| UnOp(f_op: OpAbs | OpMinus | OpPlus | OpNot)
)
we then create a function that will compute all the types associated with
these expressions in a list:
Expand All @@ -326,11 +328,13 @@ these expressions in a list:
fun arithmetic_ops() =
|" Return a list of all types referenced in any arithmetic operator
[op.p_expression_type()
for op in select
BinOp(f_op is OpDiv or OpMinus or OpMod or OpMult or
OpPlus or OpPow or OpRem or OpXor or
OpAnd or OpOr) or
UnOp(f_op is OpAbs or OpMinus or OpPlus or OpNot)].to_list
for op in select (
BinOp(f_op: OpDiv | OpMinus | OpMod | OpMult |
OpPlus | OpPow | OpRem | OpXor |
OpAnd | OpOr)
| UnOp(f_op: OpAbs | OpMinus | OpPlus | OpNot)
)
].to_list
and we update our rule accordingly to find all integer types for which no
arithmetic operator is found. To achieve that, we use a list comprehension
Expand All @@ -342,7 +346,7 @@ element evaluates to ``true``:
.. code-block:: lkql
fun integer_types_as_enum(node) =
node is TypeDecl(p_is_int_type() is true)
node is TypeDecl(p_is_int_type(): true)
when not [t for t in arithmetic_ops() if t == node]
Running this rule we realize that it finds some interesting matches, but
Expand All @@ -357,14 +361,14 @@ referenced declaration (``p_referenced_decl`` property) is a type declaration
fun types() =
[c.p_referenced_decl()
for c in select CallExpr(p_referenced_decl() is TypeDecl)].to_list
for c in select CallExpr(p_referenced_decl(): TypeDecl)].to_list
And we update our rule accordingly:
.. code-block:: lkql
fun integer_types_as_enum(node) =
node is TypeDecl(p_is_int_type() is true)
node is TypeDecl(p_is_int_type(): true)
when not [t for t in arithmetic_ops() if t == node]
and not [t for t in types() if t == node]
Expand All @@ -386,7 +390,7 @@ declarations with both source and target types of conversions:
fun types() =
concat([[c.p_referenced_decl(), c.f_suffix[1].f_r_expr.p_expression_type()]
for c in select CallExpr(p_referenced_decl() is TypeDecl)].to_list)
for c in select CallExpr(p_referenced_decl(): TypeDecl)].to_list)
This gives much better results and much fewer false positives! We then
realize that we need to perform a similar filtering on subtype declarations:
Expand All @@ -406,7 +410,7 @@ We combine this with the previous results:
|" Return a list of TypeDecl matching all type conversions (both as source
|" and target) and subtype declarations in the project.
concat([[c.p_referenced_decl(), c.f_suffix[1].f_r_expr.p_expression_type()]
for c in select CallExpr(p_referenced_decl() is TypeDecl)].to_list)
for c in select CallExpr(p_referenced_decl(): TypeDecl)].to_list)
& [s.f_subtype.f_name.p_referenced_decl() for s in select SubtypeDecl].to_list
We're getting even less false positives now, and quickly realize that we need
Expand All @@ -415,7 +419,7 @@ to do the same for type derivations:
.. code-block:: lkql
[c.f_type_def.f_subtype_indication.f_name.p_referenced_decl()
for c in select TypeDecl(f_type_def is DerivedTypeDef)].to_list
for c in select TypeDecl(f_type_def: DerivedTypeDef)].to_list
We combine again the results, which gives us our final ``types`` function:
Expand All @@ -425,10 +429,10 @@ We combine again the results, which gives us our final ``types`` function:
|" Return a list of TypeDecl matching all type conversions (both as source
|" and target), subtype declarations and type derivations in the project.
concat([[c.p_referenced_decl(), c.f_suffix[1].f_r_expr.p_expression_type()]
for c in select CallExpr(p_referenced_decl() is TypeDecl)].to_list)
for c in select CallExpr(p_referenced_decl(): TypeDecl)].to_list)
& [s.f_subtype.f_name.p_referenced_decl() for s in select SubtypeDecl].to_list
& [c.f_type_def.f_subtype_indication.f_name.p_referenced_decl()
for c in select TypeDecl(f_type_def is DerivedTypeDef)].to_list
for c in select TypeDecl(f_type_def: DerivedTypeDef)].to_list
Running our rule again, we find a final source of false positives: types
referenced as parameter of generic instantiations also need to be filtered
Expand Down Expand Up @@ -461,7 +465,7 @@ Updating our rule this gives us:
.. code-block:: lkql
fun integer_types_as_enum(node) =
node is TypeDecl(p_is_int_type() is true)
node is TypeDecl(p_is_int_type(): true)
when not [t for t in arithmetic_ops() if t == node]
and not [t for t in types() if t == node]
and not [t for t in instantiations() if t == node]
Expand Down Expand Up @@ -491,7 +495,7 @@ order of the tests:
.. code-block:: lkql
fun integer_types_as_enum(node) =
node is TypeDecl(p_is_int_type() is true)
node is TypeDecl(p_is_int_type(): true)
when not [t for t in types() if t == node]
and not [t for t in instantiations() if t == node]
and not [t for t in arithmetic_ops() if t == node]
Expand All @@ -504,11 +508,11 @@ which gives us this complete rule:
fun arithmetic_ops() =
|" Return a list of all types referenced in any arithmetic operator
unique([op.p_expression_type()
for op in select
BinOp(f_op is OpDiv or OpMinus or OpMod or OpMult or
OpPlus or OpPow or OpRem or OpXor or
OpAnd or OpOr) or
UnOp(f_op is OpAbs or OpMinus or OpPlus or OpNot)].to_list)
for op in select (
BinOp(f_op: OpDiv | OpMinus | OpMod | OpMult |
OpPlus | OpPow | OpRem | OpXor |
OpAnd | OpOr) |
UnOp(f_op: OpAbs | OpMinus | OpPlus | OpNot))].to_list)
@memoized
fun instantiations() =
Expand All @@ -522,14 +526,14 @@ which gives us this complete rule:
|" and target), subtype declarations and type derivations in the project.
unique(concat([[c.p_referenced_decl(),
c.f_suffix[1].f_r_expr.p_expression_type()]
for c in select CallExpr(p_referenced_decl() is TypeDecl)].to_list) &
for c in select CallExpr(p_referenced_decl(): TypeDecl)].to_list) &
[s.f_subtype.f_name.p_referenced_decl() for s in select SubtypeDecl].to_list &
[c.f_type_def.f_subtype_indication.f_name.p_referenced_decl()
for c in select TypeDecl(f_type_def is DerivedTypeDef)].to_list)
for c in select TypeDecl(f_type_def: DerivedTypeDef)].to_list)
@check(message="integer type may be replaced by an enumeration")
fun integer_types_as_enum(node) =
node is TypeDecl(p_is_int_type() is true)
node is TypeDecl(p_is_int_type(): true)
when not [t for t in types() if t == node]
and not [t for t in instantiations() if t == node]
and not [t for t in arithmetic_ops() if t == node]
Expand Down
103 changes: 99 additions & 4 deletions user_manual/source/language_reference.rst
Expand Up @@ -11,6 +11,105 @@ technology. As such, it is theoretically capable of running queries on any
language with a Langkit frontend. In practice for the moment, LKQL is hardwired
for Ada (and Libadalang).

.. attention:: While mostly stable, LKQL is not completely done yet. The
language will keep being extended with new constructs, and from time to time
syntax might change to accomodate new language constructs/enhance the
language ergonomics/fix design mistakes.

Language changes
================

Under this section, we'll document language changes chronogically, and
categorize them by AdaCore GNATcheck release.

25.0
----

Syntax of pattern details (breaking)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Pattern details were specified with the syntax `<left_part> is <pattern>`, and
are now specified with the syntax `<left_part>: <pattern>`.

Syntax of selectors recursion definition (breaking)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The syntax for defining a recursion in selectors has completely changed. The
old `rec` and `skip` keywords have been replaced by a single `rec` construct
that allows to specify what elements will be recursed upon, and what elements
will be yielded by the selector.

.. code-block:: lkql
selector parent
| AdaNode => rec(*this.parent, this)
# ^ Add parent to the recurse list
# ^ ^ Add this to the return list
| * => ()
.. warning:: This syntax is more general than the previous one, but is still
not optimal, and might change again in a further release. Please take that
into account when using selectors in your own code.

More details in the `Selector Declaration`_ section.

Or patterns syntax (breaking)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Or patterns were defined with the `<pattern> or <pattern>` syntax, and are now
defined with the `<pattern> | <pattern>` syntax.

Binding patterns without value pattern
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Patterns binding any value to a name can simply be expressed with a binding
name now:

.. code-block:: lkql
match d
| BasicDecl(p_doc(): doc) => print(doc)
More patterns
^^^^^^^^^^^^^

So far, only node values had corresponding patterns to match them. Now,
patterns can be used to match other values:

.. code-block:: lkql
v is 12
v is true
v is "hello"
v is "hello.*?world"
match i
| (1, 2, 3) => print("un, dos, tres")
| * => print("un pasito adelante maria")
match i
| (1, a@*, b@*, 4) => { print(a); print(b) }
match lst
| [1, 2, 3] => "[1, 2, 3]"
| [1, a@*, 3] => "[1, a@*, 3], with a = " & img(a)
match lst
| [11, 12, ...] => "[11, 12, ...]"
| [1, c@...] => "[1, c@...] with b = " & img(b) & " & c = " & img(c)
| [...] => "Any list"
match obj
| {a: 12} => "{a: 12}"
| {a: a@*} => "Any object with an a key. Bind the result to a"
match obj
| {a@..., b: "hello"} => "Bind keys that are not b to var a"
| {a@...} => "Bind all the object to a"
General Purpose Language Subset
===============================

LKQL today is the mixture of two language subsets:

* The first is a dynamically typed, functional, small but general
Expand All @@ -26,8 +125,6 @@ Those two languages will be documented separately. The general language will be
documented first, because its knowledge is needed for understanding the tree
query language.

General Purpose Language Subset
===============================

This language subset is composed of a reduced set of declarations and
expressions that forms a minimal but turing complete language.
Expand Down Expand Up @@ -1062,8 +1159,6 @@ Selector Declaration
.. raw:: html
:file: ../../lkql/build/railroad-diagrams/selector_arm.svg

.. raw:: html

Selectors are a special form of functions that return a lazy stream of values.
They're at the basis of the query DSL of LKQL, allowing the easy expression of
traversal blueprints.
Expand Down
2 changes: 1 addition & 1 deletion user_manual/source/lkql_lexer.py
Expand Up @@ -18,7 +18,7 @@ class LKQLPygmentsLexer(RegexLexer):
(r"\|\".+", token.String),
(r"#(.?)+", token.Comment),
(r"(\-\>|=|\=\>|\<\=|\>\=|\=|\!\=|\+|\-|\*|\/|\&|"
r"\@|\||\>|\<)", token.Operator),
r"\@|\||\>|\<|:)", token.Operator),
(r"\b(and|or|not)\b", token.Operator),
(r"\{|\}|\(|\)|\[|\]|;|\.|,", token.Punctuation),
(r'"(\\.|[^"])*"', token.String),
Expand Down

0 comments on commit b571ab3

Please sign in to comment.