Libadalang

repository·master·Indexed 41 days ago

https://github.com/adacore/libadalang

A library for parsing and semantic analysis of Ada code, serving as a building block for IDEs, static analyzers, and language servers. It provides syntactic analysis with error recovery and semantic queries for reference resolution, type resolution, and cross-references. Libadalang offers APIs for Python (3.11+), Ada (2012+), and C, with experimental support for OCaml and Java.

Tokens
16.3K
Snippets
58
Records
76
Agent score
51%

What's inside Libadalang

  1. Overview of Libadalang services

    master

    Libadalang is a library for parsing and semantic analysis of Ada code, designed as a building block for tools like IDEs and static analyzers.

    Key services include:

    • Syntactic Analysis: Complete analysis with error recovery. It produces a precise tree for correct code and a best-effort tree for incorrect code.
    • Semantic Queries:
      • Reference Resolution: Determining what a reference corresponds to.
      • Type Resolution: Determining the type of an expression.
      • Cross-references: Finding all references to a specific entity.

    Note: Libadalang does not currently provide full legality checks for the Ada language; for full legality, use a compiler like GNAT.

  2. What is Libadalang?

    master

    Libadalang is a library designed for the syntactic and semantic analysis of Ada code. It is intended to be used as a building block for other tools such as IDEs and static analyzers.

    Core Services:

    • Syntactic Analysis: Provides complete syntactic analysis with error recovery. It produces a precise syntax tree for correct source code and a best-effort tree for incorrect source code.
    • Semantic Queries: Enables queries on top of the syntactic tree, including:
      • Reference Resolution: Determining what a specific reference corresponds to.
      • Type Resolution: Determining the type of an expression.
      • Cross-Reference Queries: Finding all references to a specific entity.

    Note: Libadalang does not currently provide full legality checks for the Ada language. For full legality verification, use a full Ada compiler like GNAT.

  3. How tokens and trivias work

    master

    Libadalang preserves all formatting information by distinguishing between two types of lexemes:

    • Regular Tokens: Identifiers, keywords, etc., used by the parser to build the syntax tree.
    • Trivias: White space and comments. These are ignored by the parser but preserved for analysis.

    Libadalang exposes these as a single sequence, but they maintain distinct indexing. Getting the index of a regular token returns its position in the token list, and similarly for trivias.

    Example mapping for procedure Foo; -- Comment:

    1. procedure (token index 1)
    2. white space (trivia index 1)
    3. Foo (token index 2)
    4. ; (token index 3)
    5. white space (trivia index 2)
    6. -- Comment (trivia index 3)
  4. Identify node kinds using Kind

    master

    Every node has a Kind which identifies its syntactic role (e.g., an object declaration). The Kind returns a value from the Libadalang.Common.Ada_Node_Kind_Type enumeration.

    Note on Naming: There is a direct correspondence between kind names and type names. The kind is prefixed with Ada_. For example, the type Object_Decl has the kind Ada_Object_Decl.

    if Node.Kind = LALCO.Ada_Object_Decl then
       -- Handle object declaration
    end if;
  5. Use unit providers for name resolution

    master

    When Libadalang encounters with clauses or other references (e.g., with Ada.Text_IO;), it uses a unit provider to resolve the unit name to a specific source file.

    A unit provider takes a unit name (e.g., Ada.Strings.Unbounded) and a unit kind (specification or body) and returns the corresponding source file.

    Available Providers

    • Default Provider: Looks for files in the current directory using GNAT naming conventions (e.g., Parent.Child $\rightarrow$ parent-child.ads).
    • Project Unit Provider: Uses a GPR project file to access all Ada sources referenced in that project tree.
    • Auto Unit Provider: Searches for files matching a pattern in a list of directories.

    You can implement custom unit providers if your project uses non-standard naming conventions.

  6. Use the Nameres database schema and statistics functions

    master

    The schema.py module defines the SQLite database schema and provides functions for computing key statistics. You can use it in two ways:

    1. Programmatically: Import schema.py into an IPython terminal or a Python script to compute custom statistics.
    2. Directly: Run SQL queries against the generated SQLite database using any standard SQL client.
  7. Handle identifiers starting with $ in Ada parsing

    master

    By default, Libadalang parses identifiers starting with a dollar sign ($) as regular identifiers for backward compatibility.

    If you want these to be treated as preprocessor identifiers, you must use the Langkit_Support.File_Readers and Libadalang.Preprocessing APIs. If these APIs are not used, identifiers starting with $ will be parsed normally.

  8. Supported Languages and APIs

    master

    Libadalang provides multiple interfaces for different development needs:

    • Python (3.11+): Recommended for easy prototyping and explorative programming.
    • Ada (2012+): For native integration.
    • C API: A low-level API intended for writing bindings to other languages.
    • Experimental APIs: Available for OCaml and Java.
  9. Access syntactic fields of AdaNodes

    master

    Each kind of node has a specific set of syntactic fields representing its children in the parsing tree. These fields are accessed using the homonym attribute on the node.

    For example, an ObjectDecl node has fields such as:

    • f_ids: identifiers for the declared objects
    • f_type_expr: the type for the declared objects
    • f_default_expr: the expression providing a default value (may be null)
    • f_aspects: a list of aspects associated with the declaration

    Note: It is always valid to access syntax fields on non-null objects, but some fields themselves may return a null node if the syntax is not present (e.g., an object declaration without a default value).

    # Accessing a specific field
    obj = get_some_object_decl()
    print(obj.f_type_expr)
  10. How Libadalang's integrated preprocessor works

    master
    Libadalang handles code preprocessing through its File_Reader mechanism. Instead of preprocessing files manually before analysis, you create a specialized File_Reader that defines which files to preprocess and with which parameters (such as macro definitions). This File_Reader is then passed to the Analysis_Context during its creation. This ensures that the analysis engine is aware of the preprocessing rules and can correctly map preprocessed code back to the original source files.
  11. Understand the Standard package implementation and Target Information

    master

    Libadalang implements the Standard package as a special analysis unit. Because this implementation uses stubs for certain types (like Character), you must ensure Libadalang has the correct target information to resolve range bounds, digits, and 'Small attributes correctly.

    To provide this, you must either:

    1. Call Libadalang.Analysis.Set_Target_Information.
    2. Use Libadalang.Analysis.Create_Context_From_Project, which fetches this information from the project file.
  12. Choose between the Ada and Python APIs

    master

    Libadalang provides two primary interfaces for interacting with the library:

    • Ada API: Best for integration into existing Ada programs where high performance and static safety guarantees are required.
    • Python API: Ideal for rapid prototyping. The Python API maps directly to the Ada API, so the knowledge of tree structures and accessors is shared between both interfaces. It is highly recommended for exploring the API and available accessors.