โŒ About FreshRSS

Normal view

There are new articles available, click to refresh the page.
Before yesterdayNews from the Ada programming language world

Reentrant scanner and parser with Aflex and Ayacc

14 May 2023 at 19:02
[Aflex and Ayacc](Ada/Aflex-Ayacc-code.jpg)
    1. What's new in Aflex 1.6

- Support the flex options `%option output`, `%option nooutput`, `%option yywrap`, `%option noinput`,

 `%option noyywrap`, `%option unput`, `%option nounput`, `%option bufsize=NNN` to better control the
 generated `_IO` package.
- Aflex(https://github.com/Ada-France/aflex) templates provide more control for tuning the code generation and
 they are embedded with [Advanced Resource Embedder](https://gitlab.com/stcarrez/resource-embedder)
- Support to define Ada code block in the scanner that is inserted in the generated scanner - New option -P to generate a private Ada package for DFA and IO - New directive `%option reentrant` and `%yyvar` to generate a recursive scanner - New directive `%yydecl` to allow passing parameters to `YYLex`
 or change the default function name

Example of `%option` directives to tell Aflex(https://github.com/Ada-France/aflex) to avoid generating several function or procedures and customize the buffer size.

```Ada %option nounput %option noinput %option nooutput %option noyywrap %option bufsize=1024 ```

The tool supports some code block injection at various places in the generated scanner. The code block has the following syntax where `<block-name>` is the name of the code block:

```Ada %<block-name> {

 -- Put Ada code here

} ```

The `%yytype` code block can contain type declaration, function and procedure declarations. It is injected within the `YYLex` function in the declaration part. The `%yyinit` code block can contain statements that are executed at beginning of the `YYLex` function. The `%yyaction` code block can contain statements that are executed before running any action. The `%yywrap` code block can contain statements which are executed when the end of current file is reached to start parsing a next input.

    1. What's new in Ayacc 1.4

- Support the Bison `%define variable value` option to configure the parser generator - Support the Bison `%code name { ... }` directive to insert code verbatim into the output parser - Recognize some Bison variables `api.pure`, `api.private`, `parse.error`, `parse.stacksize`,

 `parse.name`, `parse.params`, `parse.yyclearin`, `parse.yyerrok`, `parse.error`
- New option `-S skeleton` to allow using an external skeleton file for the parser generator - Ayacc(https://github.com/Ada-France/ayacc) templates provide more control for tuning the code generation and
 they are embedded with [Advanced Resource Embedder](https://gitlab.com/stcarrez/resource-embedder)
- New option `-P` to generate a private Ada package for the tokens package - Improvement to allow passing parameters to `YYParse` for the grammar rules - New `%lex` directive to control the call of `YYLex` function - Fix #6: ayacc gets stuck creating an infinitely large file after encountering a comment in an action

The generator supports two code block injections, the first one `decl` is injected in the `YYParse` procedure declaration and the `init` is injected as first statements to be executed only once when the procedure is called. The syntax is borrowed from the Bison parser:

```Ada %code decl {

  -- Put Ada declarations

} %code init {

  -- Put Ada statements

} ```

Some other Bison like improvements have been introduced to control the generation of the parser code.

``` %define parse.error true %define parse.stacksize 256 %define parse.yyclearin false %define parse.yyerrok false %define parse.name MyParser ```

    1. How to use

The easiest way to use Ayacc(https://github.com/Ada-France/ayacc) and Aflex(https://github.com/Ada-France/aflex) is to use Alire(https://github.com/alire-project/alire), get the sources, build them and install them. You can do this as follows:

``` alr get aflex cd aflex_1.6.0_b3c21d99 alr build alr install alr get ayacc cd ayacc_1.4.0_c06f997f alr build alr install ```

  • UPDATE*: the `alr install` command is available only with Alire(https://github.com/alire-project/alire) 2.0.

Using these tools is done in two steps:

1. a first step to call `aflex` or `ayacc` command with the scanner file or grammar file, 2. a second step to call `gnatchop` to split the generated file in separate Ada files

For example, with a `calc_lex.l` scanner file, you would use:

``` aflex calc_lex.l gnatchop -w calc_lex.ada ```

And with a `calc.y` grammar file:

``` ayacc calc.y gnatchop -w calc.ada ```

To know more about how to write a scanner file or grammar file, have a look at Aflex 1.5 and Ayacc 1.3.0(https://blog.vacs.fr/vacs/blogs/post.html?post=2021/12/18/Aflex-1.5-and-Ayacc-1.3.0) which explains more into details some of these aspects.

    1. Highlight on reentrancy

By default Aflex(https://github.com/Ada-France/aflex) and Ayacc(https://github.com/Ada-France/ayacc) generate a scanner and a parser which use global variables declared in a generated Ada package. These global variables contain some state about the scanner such as the current file being scanned. The Ayacc(https://github.com/Ada-France/ayacc) parser generates on its side two global variables `YYLVal` and `YYVal`.

Using global variables creates some strong restrictions when using the generated scanner and parser: we can scan and parse only one file at a time. It cannot be used in a multi-thread environment unless the scan and parse is protected from concurrent access. We cannot use easily some grammars that need to recurse and parse another file such as an included file.

      1. Reentrant scanner

The reentrant scanner is created by using the `-R` option or the `%option reentrant` directive. The scanner will then need a specific declaration with a context parameter that will hold the scanner state and variables. The context parameter has its type generated in the `Lexer_IO` package. The `%yydecl` directive in the scanner file must be used to declare the `YYLex` function with its parameters. By default the name of the context variable is `Context` but you can decide to customize and change it to another name by using the `%yyvar` directive.

``` %option reentrant %yyvar Context %yydecl function YYLex (Context : in out Lexer_IO.Context_Type) return Token ```

When the `reentrant` option is activated, Aflex(https://github.com/Ada-France/aflex) will generate a first `Context_Type` limited type in the `Lexer_DFA` package and another one in the `Lexer_IO` package. The generator can probably be improved in the future to provide a single package with a single type declaration. The `Lexer_DFA` package contains the internal data structures for the scanner to maintain its state and the `Lexer_IO` package holds the input file as well as the `YYLVal` and `YYVal` values.

      1. Reentrant parser

On its side, Ayacc(https://github.com/Ada-France/ayacc) uses the `YYLVal` and `YYVal` variables. By default, it generates them in the `_tokens` package that contains the list of parser symbols. It must not generate them and it must now use the scanner `Context_Type` to hold them as well as the scanner internal state. The setup requires several steps:

1. The reentrant parser is activated by using the `%define api.pure`

  directive similar to the [bison %define](https://www.gnu.org/software/bison/manual/html_node/_0025define-Summary.html).

2. The `%lex` directive must be used to define how the `YYLex` function must be called since it now has some

  context parameter.

3. The scanner context variable must be declared somewhere, either as parameter to the `YYParse`

  procedure or as a local variable to `YYParse`.  This is done using the new `%code decl` directive
  and allows to customize the local declaration part of the `YYParse` generated procedure.

4. We must give visibility of the `YYLVal` and `YYVal` values defined in the scanner context variable.

  Again, we can do this within the `%code decl` directive.

A simple reentrant parser could be defined by using:

```Ada %define api.pure true %lex YYLex (Scanner) %code decl {

     Scanner : Lexer_IO.Context_Type;
     YYLVal  : YYSType renames Scanner.YYLVal;
     YYVal   : YYSType renames Scanner.YYVal;

} ```

However, this simple form is not really useful as you may need to open the file and setup the scanner to read from it. It is probably better to pass the scanner context as parameter to the `YYParse` procedure. For this, we can use the `%define parse.params` directive to control the procedure parameters. The reentrant parser is declared as follows:

```Ada %lex YYLex (Scanner) %define api.pure true %define parse.params "Scanner : in out Lexer_IO.Context_Type" %code decl {

     YYLVal : YYSType renames Scanner.YYLVal;
     YYVal  : YYSType renames Scanner.YYVal;

} ```

To use the reentrant parser and scanner, we only need to declare the scanner context, open the file by using the `Lexer_IO.Open_Input` procedure and call the `YYParse` procedure as follows:

```Ada

 Scanner : Lexer_IO.Context_Type;
 ...
   Lexer_IO.Open_Input (Scanner, "file-to-scan");
   YYParse (Scanner);

```

      1. Grammar examples:

To have a more complete example of a reentrant parser, you may have a look at the following files:

Advanced Resource Embedder 1.2.0

23 January 2022 at 09:04
[Embedding SQL in binary](Ada/resource-embedder-bin.png)

The new release contains the following fixes:

- Fix Ada generator to be able to use other binary content types

 such as `System.Storage_Elements.Storage_Array`
- Fix Ada generator to escape special characters in strings

Since the previous version, Fabien Chouteau asked to be able to use an Ada system type to represent a binary content. This is now possible by using the following XML extract for the description to tell the resource embedder how to integrate the file and generate the Ada source code:

```XML <package>

 <resource name='Resources.Help'
           format='binary'
           type='access constant System.Storage_Elements.Storage_Array'>
   <install mode='copy'>
     <fileset dir="help">
       <include name="**/*.txt"/>
     </fileset>
   </install>
 </resource>
 ...

</package> ```

With the above description, the Ada code generator produces the following package specification:

```Ada with System.Storage_Elements; package Resources.Help is

  type Content_Access is access constant System.Storage_Elements.Storage_Array;
  type Name_Access is access constant String;
  type Name_Array is array (Natural range <>) of Name_Access;
  Names : constant Name_Array;
  function Get_Content (Name : String)
     access constant System.Storage_Elements.Storage_Array;

private

  ...

end Resources.Help; ```

This example is available in Embedding help and documentation in Ada (binary)(https://gitlab.com/stcarrez/resource-embedder/tree/master/examples/ada-help-binary). If you prefer to use `String` instead of a `Storage_Array`, have a look at the Embedding help and documentation in Ada(https://gitlab.com/stcarrez/resource-embedder/tree/master/examples/ada-help). Both examples are similar but they are exposing the file using different Ada types.

To install the tool, follow the instructions given in the initial announcement: Advanced Resource Embedder for Ada, C and Go(https://blog.vacs.fr/vacs/blogs/post.html?post=2021/06/11/Advanced-Resource-Embedder).

If you want to know more about the tool, have a look at its documentation:

  • Resource Embedder Guide(https://resource-embedder.readthedocs.io/en/latest/) PDF(https://gitlab.com/stcarrez/resource-embedder/blob/master/docs/are-book.pdf)
  • Man page: are (1)(https://gitlab.com/stcarrez/resource-embedder/blob/master/docs/are.md)

and if you have ideas for improvements, fork the project and submit a pull request!

Advanced Resource Embedder 1.1.0

4 July 2021 at 16:52

The resource embedder allows to embed files in binaries by producing C, Ada or Go source files that contain the original files. The first version of the tool was representing the file content as an array of bytes. In Ada, they are accessed through an `Ada.Streams.Stream_Element_Array` which is not easy to use when you need the content as a string.

The new release introduces the customization of the format for each resource. The format is controlled through the XML description by the `format` attribute. The following data formats are supported:

  • `binary` format provides the file content as a binary data,
  • `string` format provides the file content as string,
  • `lines` format splits the content in several lines and according to a set of customizable rules.

With the `string` format, the Ada code generator can generate the following function:

```

 function Get_Content (Name : in String)
   return access constant String;

```

The `lines` format tells the code generator to represent the content as an array of separate lines. For this integration, some control is available to indicate how the content must be split and optionally apply some filter on the input content. These controls are made within the XML description by using the `line-separator` and `line-filter` description: The `line-separator` indicates the characters that represent a line separation. There can be several `line-separator` definition. The `line-filter` defines a regular expression that when matched must be replaced by an empty string or a specified content. The `line-filter` are applied in the order of the XML definition.

The example below is intended to integrate an SQL scripts with:

  • a separate line for each SQL statement,
  • remove spurious empty lines and SQL comments.

The SQL statements are separated by `;` (semi-colon) and the `line-separator` indicates to split lines on that character. By splitting on the `;` we allow to have an SQL statement on multiple lines in the original SQL source file.

```XML <package>

 <resource name='Scripts'
           format='lines'
           type='access constant String'>
   <line-separator>;</line-separator>
   <!-- Remove new lines -->
   <line-filter>[\r\n]</line-filter>
   <!-- Remove C comments -->
   <line-filter>/\*[^/]*\*/</line-filter>
   <!-- Remove contiguous spaces after C comments removal -->
   <line-filter replace=' '>[ \t][ \t]+</line-filter>
   <install mode='copy' strip-extension='yes'>
     <fileset dir="sql">
       <include name="**/*.sql"/>
     </fileset>
   </install>
 </resource>

</package> ```

Then the first `line-filter` will remove the `r` and `n` characters.

The regular expression `/\*[./]*\*/` matches a C style comment and remove it.

The last `line-filter` replaces multiple tabs and spaces by a single occurrence.

Below is an example of generated code with the above resource description. Each file is accessed through a separate variable whose name is built from the base name of the original file.

[Resource Embedder Overview](Ada/are-scripts.png)

The command used to generate this code was:

``` are lang=Ada -o src var-access content-only rule=package.xml . ```

and the `sql` directory contains only two files: `create-database.sql` and `drop-database.sql`.

The complete example is available for two languages:

To install the tool, follow the instructions given in the initial announcement: Advanced Resource Embedder for Ada, C and Go(https://blog.vacs.fr/vacs/blogs/post.html?post=2021/06/11/Advanced-Resource-Embedder).

If you want to know more about the tool, have a look at its documentation:

  • Resource Embedder Guide(https://resource-embedder.readthedocs.io/en/latest/) PDF(https://gitlab.com/stcarrez/resource-embedder/blob/master/docs/are-book.pdf)
  • Man page: are (1)(https://gitlab.com/stcarrez/resource-embedder/blob/master/docs/are.md)

and if you have ideas for improvements, fork the project and submit a pull request!

Advanced Resource Embedder for Ada, C and Go

11 June 2021 at 12:43

The tool is able to apply some transformations on the collected files:

  • it can run a Javascript minifier such as `closure`,
  • it can compress CSS files by running `yui-compressor`,
  • it can compress files by running `gzip` or another compression tool.

Once these transformations are executed, it invokes a target generator to produce a source file either in C, Ada or Go language. The generated source file can then be used in the final program and taken into account during the compilation process of that program. At the end, the binary will contain the embedded files with their optional transformations.

[Resource Embedder Overview](Ada/resource-embedder.png)

The process to use ARE is simple:

  • You describe the resources that you want to embed.
 The description is either made on command line arguments or by writing an XML file.
 The XML description gives more flexibility as it allows to define a transformation rule that
 must be executed on the original file before being embedded.  This allows to minify a Javascript
 or CSS file, compress some files and even encrypt a file before its integration.
  • You run the ARE command with your target language and rule description and you give the tool
  •  a list of directories that must be scanned to identify the files that must be collected.
     The ARE tool scan the directories according to the patterns that you have given either on
     the command line or in the XML rule description.  After identifying the files, the tool applies
     the rules and execute the transformations.
     The ARE tool then invokes the target language generator that writes one or several files depending
     on the list of resources.
    
  • Once the files are generated, you use them in your program and add them in your build process
  •  as they are now part of your sources.  After building your program, it now embeds the
     resource files that were collected and optionally transformed.
    
      1. Defining resources to embed

    The first step is to write some `package.xml` file which describes a list of resources with their content. The root XML element is *`package`* and each resource is described by a `resource` XML element. The resource is assigned a name that will be used by the code generator. The C generator will use the name as a prefix for the C function and C type declaration. The Ada and Go generator will use that name for the Ada or Go package.

    The following resource definition declares the `Help` resource. It contains an installation rule that will copy the files under the `help` directory in the resource set. Only files matching the `.txt` pattern will be taken into account.

    ```XML <package>

     <resource name='Help'>
       <install mode='copy'>
         <fileset dir="help">
           <include name="**/*.txt"/>
         </fileset>
       </install>
     </resource>
     ...
    

    </package> ```

    The next resource definition will run an external program to get the content that must be embedded. The `man` directory is scanned and it will execute the command `man #{name}` on each filename found. That directory contains the empty files `ls`, `pwd` and `sh` and this will run and embed the man page for these commands.

    ```XML <package>

      ...
     <resource name='Man'>
       <install mode='exec'>
         <command output='#{dst}'>man #{name}</command>
         <fileset dir="man">
           <include name="*"/>
         </fileset>
       </install>
     </resource>
    

    </package> ```

    You may run other commands such as:

    ``` <command>closure charset UTF-8 #{src} js_output_file #{dst}</command> <command>yui-compressor type css charset UTF-8 -o #{dst} #{src}</command> <command output="#{dst}">gzip --no-name -c #{src}</command> ```

      1. Running the Advanced Resource Embedder

    The tool has several options that allows you to control the target code generator and tune the generation according to your needs. Assuming that the files to embed are located in the current directory, you would use the following command to generate C source files in the `src` directory:

    ``` are lang=c -o src rule=package.xml list-access name-access . ```

    For C, this would generate a `src/man.h`, `src/man.c`, `src/help.h` and `src/help.c`. You now have to include these files in the compilation of your program.

    You would use the following command for Go:

    ``` are lang=go rule=package.xml list-access name-access . ```

    and it would generate in `man/man.go` and `help/help.go` the Go source files.

    You would use the following command for Ada:

    ``` are lang=Ada -o src rule=package.xml list-access name-access . ```

    and it would generate `src/man.ads`, `src/man.adb`, `src/help.ads` and `src/help.adb`.

      1. Using the resource

    The code generator emits by default a set of type and function declaration that give access to the resource. For C, the following structure and declaration are generated in the header for the `man` resource:

    ```C struct man_content {

     const unsigned char *content;
     size_t size;
     time_t modtime;
     int format;
    

    };

    extern const struct man_content* man_get_content(const char* name); ```

    But for the Go language, the `man` generated package declares:

    ```Go type Content struct {

       Content  []byte
       Size  int64
       Modtime  int64
       Format   int
    

    } ... func Get_content(name string) (*Content) {...} ```

    And for Ada, it will generate:

    ```Ada package Resources.Man is

      type Content_Access is access constant Ada.Streams.Stream_Element_Array;
      type Name_Access is access constant String;
      type Format_Type is (FILE_RAW, FILE_GZIP);
      type Content_Type is record
         Name    : Name_Access;
         Content : Content_Access;
         Modtime : Interfaces.C.long := 0;
         Format  : Format_Type := FILE_RAW;
      end record;
      Null_Content : constant Content_Type;
    
      type Name_Array is array (Natural range <>) of Name_Access;
      Names : constant Name_Array;
    
      function Get_Content (Name : String) return Content_Type;
    

    private

      ...
    

    end Resources.Man; ```

    You can avoid the type declaration by using the `--no-type-declaration` option and in that case you have to make available these types somehow. In Ada, this can easily be made by providing these types in a parent Ada package (see the Embedding help and documentation in Ada(https://gitlab.com/stcarrez/resource-embedder/tree/master/examples/ada-help) for the example).

    Now with the generated code, you only need to call the generated get content method to obtain the embedded content. Simple!

      1. Examples

    The project proposes several detailed examples to illustrate various integrations.

    This first set of example shows how to you can embed configuration files in a C, Ada or Go program. The Advance Resource Embedder simply puts the configuration files in an array of bytes that can easily be retrieved by a generated function.

    A second set of example is more advanced by the use of an XML file that describes what must be embedded with the transformations that must be made. It creates two distinct resource sets `help` and `man`. The `help` resource set is composed of a set of fixed documentation files provided in the example. The `man` resource set is created by running the `man` Unix command on various names to embed the man page of `ls`, `pwd` and `sh`.

    More specific examples show how to make specific transformations on the files before integrating them:

      1. License considerations

    The generated code produced by Advance Resource Embedder(https://gitlab.com/stcarrez/resource-embedder) is not covered by any license. However, when you integrate some resource files with the tool, the generated code will contain the original file in some binary form and therefore it may be covered by the license associated with these resource files.

    For example, if you include a file covered by the GNU General Public license, then the generated file is covered by the GPL.

      1. How can you get Advanced Resource Embedder?
        1. Use the source Luke!

    Quick instructions to build the tool:

    ``` git clone --recursive https://gitlab.com/stcarrez/resource-embedder.git cd resource-embedder make build test install ```

        1. Debian Packages for x86_64

    You can install ARE by using the Debian 10 and Ubuntu 20.04 or 18.04 packages. First, setup to accept the signed packages:

    ``` wget -O - https://apt.vacs.fr/apt.vacs.fr.gpg.key | sudo apt-key add - ```

    and choose one of the `echo` command according to your Linux distribution:

    Ubuntu 20.04 ``` echo "deb https://apt.vacs.fr/ubuntu-focal focal main" | sudo tee -a /etc/apt/sources.list.d/vacs.list ```

    Ubuntu 18.04 ``` echo "deb https://apt.vacs.fr/ubuntu-bionic bionic main" | sudo tee -a /etc/apt/sources.list.d/vacs.list ```

    Debian 10 ``` echo "deb https://apt.vacs.fr/debian-buster buster main" | sudo tee -a /etc/apt/sources.list.d/vacs.list ```

    Then, launch the apt update command:

    ``` sudo apt-get update ```

    and install the tool using:

    ``` sudo apt-get install -y are ```

      1. Conclusion

    Embedding files, scripts, documentation and other contents in C and Ada is easily done by using the Advance Resource Embedder(https://gitlab.com/stcarrez/resource-embedder). Go developers are already familiar with these mechanisms thanks to the `go:embed` directive. But the tool provides custom transformations that gives more flexibility for the integration.

    Some benefits in embedding contents in final binaries:

    • the content is embedded within the readonly `.rodata` section,
    • you avoid at least 3 system calls to access the content: an `open`, a `read` and a `close`,
    • you reduce the size of your application on the disk: contents are contiguous within the `.rodata` section
     whereas written on a file system they each require full dedicated data blocks (4K in most cases),
    
  • you get a portable `mmap` of your files for free and without pain.
  • If you want to know more about the tool, have a look at its documentation:

    • Resource Embedder Guide(https://resource-embedder.readthedocs.io/en/latest/) PDF(https://gitlab.com/stcarrez/resource-embedder/blob/master/docs/are-book.pdf)
    • Man page: are (1)(https://gitlab.com/stcarrez/resource-embedder/blob/master/docs/are.md)

    and if you have ideas for improvements, fork the project and submit a pull request!

    Generating a REST Ada client with OpenAPI and Swagger Codegen

    8 October 2017 at 18:32

    swagger-ada-generator.png

    Writing an OpenAPI document

    The OpenAPI document is either a JSON or a YAML file that describes the REST API operations. The document can be used both for the documentation of the API and for the code generation in several programming language. We will see briefly through the Petstore example how the OpenAPI document is organized. The full OpenAPI document is available in petstore.yaml.

    General description

    A first part of the OpenAPI document provides a general description of the API. This includes the general description, the terms of service, the license and some contact information.

    swagger: '2.0'
    info:
      description: 'This is a sample server Petstore server.  You can find out more about Swagger at [http://swagger.io](http://s
    wagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/).  For this sample, you can use the api key `special-key
    ` to test the authorization filters.'
      version: 1.0.0
      title: Swagger Petstore
      termsOfService: 'http://swagger.io/terms/'
      contact:
        email: [email protected]
      license:
        name: Apache 2.0
        url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
    host: petstore.swagger.io
    basePath: /v2
    

    Type description

    The OpenAPI document can also describe types which are used by the REST operations. These types provide a description of how the data is organized and passed through the API operations.

    It is possible to describe almost all possible types from simple properties, group of properties up to complex types including arrays. For example a Pet type is made of several properties each of them having a name, a type and other information to describe how the type is serialized.

    definitions:
      Pet:
        title: a Pet
        description: A pet for sale in the pet store
        type: object
        required:
          - name
          - photoUrls
        properties:
          id:
            type: integer
            format: int64
          category:
            $ref: '#/definitions/Category'
          name:
            type: string
            example: doggie
          photoUrls:
            type: array
            xml:
              name: photoUrl
              wrapped: true
            items:
              type: string
          tags:
            type: array
            xml:
              name: tag
              wrapped: true
            items:
              $ref: '#/definitions/Tag'
          status:
            type: string
            description: pet status in the store
            enum:
              - available
              - pending
              - sold
        xml:
          name: Pet
    

    In this example, the Pet type contains 6 properties (id, category, name, photoUrls, tags, status) and refers to two other types Category and Tag.

    Operation description

    Operations are introduced by the paths object in the OpenAPI document. This section describes the possible paths that can be used by URL and the associated operation. Some operations receive their parameter within the path and this is represented by the {name} notation.

    The operation description indicates the HTTP method that is used get, post, put or delete.

    The following definition describes the getPetById operation.

    paths:
      '/pet/{petId}':
        get:
          tags:
            - pet
          summary: Find pet by ID
          description: Returns a single pet
          operationId: getPetById
          produces:
            - application/xml
            - application/json
          parameters:
            - name: petId
              in: path
              description: ID of pet to return
              required: true
              type: integer
              format: int64
          responses:
            '200':
              description: successful operation
              schema:
                $ref: '#/definitions/Pet'
            '400':
              description: Invalid ID supplied
            '404':
              description: Pet not found
          security:
            - api_key: []
    

    The summary and description are used for the documentation purposes. The operationId is used by code generators to provide an operation name that a target programming language can use. The produces section indicates the media types that are supported by the operation and which are generated for the response. The parameters section represents all the operation parameters. Some parameters can be extracted from the path (which is the case for the petId parameter) and some others can be passed as query parameter.

    The responses section describes the possible responses for the operation as well as the format used by the response. In this example, the operation returns an object described by the Pet type.

    Using Swagger Codegen

    The documentation and the Ada client are generated from the OpenAPI document by using the Swagger Codegen generator. The generator is a Java program that is packaged within a jar file. It must be launched by the Java 7 or Java 8 runtime.

    Generating the documentation

    The HTML documentation is generated from the OpenAPI document by using the following command:

     java -jar swagger-codegen-cli.jar generate -l html -i petstore.yaml -o doc
    

    Generating the Ada client

    To generate the Ada client, you will use the -l ada option to use the Ada code generator. The OpenAPI document is passed with the -i option.

     java -jar swagger-codegen-cli.jar generate -l ada -i petstore.yaml -o client \
           -DprojectName=Petstore --model-package Samples.Petstore
    

    The Ada generator uses two options to control the generation. The -DprojectName=Petstore option allows to control the name of the generated GNAT project and the --model-package option controls the name of the Ada package for the generated code.

    The Ada generator will create the following Ada packages:

    • Samples.Petstore.Models is the package that contains all the types described in the OpenAPI document. Each OpenAPI type is represented by an Ada record and it

    is also completed by an instantiation of the Ada.Containers.Vectors package for the representation of arrays of the given type. The Models package also provides Serialize and Deserialize procedures for the serialization and deserialization of the data over JSON or XML streams.

    • Samples.Petstore.Clients is the package that declares the Client_Type tagged record which provides all the operations for the OpenAPI document.

    For the Pet type describe previously, the Ada generator produces the following code extract:

    package Samples.Petstore.Models is
       ...
       type Pet_Type is
         record
           Id : Swagger.Long;
           Category : Samples.Petstore.Models.Category_Type;
           Name : Swagger.UString;
           Photo_Urls : Swagger.UString_Vectors.Vector;
           Tags : Samples.Petstore.Models.Tag_Type_Vectors.Vector;
           Status : Swagger.UString;
         end record;
         ...
    end Samples.Petstore.Models;
    

    and for the operation it generates the following code:

    package Samples.Petstore.Clients is
       ...
       type Client_Type is new Swagger.Clients.Client_Type with null record;
       procedure Get_Pet_By_Id
          (Client : in out Client_Type;
           Pet_Id : in Swagger.Long;
           Result : out Samples.Petstore.Models.Pet_Type);
       ...
    end Samples.Petstore.Clients;
    

    Using the REST Ada client

    Initialization

    The HTTP/REST support is provided by Ada Util and encapsulated by Swagger Ada. The Ada Util library also takes care of the JSON and XML serialization and deserialization. If you want to use Curl, you should initialize with the following:

    with Util.Http.Clients.Curl;
    ...
       Util.Http.Clients.Curl.Register;
    

    But if you want to use AWS, you will initialize with:

    with Util.Http.Clients.Web;
    ...
       Util.Http.Clients.Web.Register;
    

    After the initialization is done, you will declare a client instance to access the API operations:

    with Samples.Petstore.Clients;
    ...
       C : Samples.Petstore.Clients.Client_Type;
    

    And you should initialize the server base URL you want to connect to. To use the live Swagger Petstore service you can set the server base URL as follows:

      C.Set_Server ("http://petstore.swagger.io/v2");
    

    At this stage, you can use the generated operation by calling operations on the client.

    Calling a REST operation

    Let's retrieve some pet information by calling the Get_Pet_By_Id operation described previously. This operation needs an integer as input parameter and returns a Pet_Type object that contains all the pet information. You will first declare the pet instance as follows:

    with Samples.Petstore.Models;
    ...
      Pet  : Samples.Petstore.Models.Pet_Type;
    

    And then call the Get_Pet_By_Id operation:

      C.Get_Pet_By_Id (768, Pet);
    

    At this stage, you can access information from the Pet instance:

    with Ada.Text_IO;
    ...
      Ada.Text_IO.Put_Line ("Id      : " & Swagger.Long'Image (Pet.Id));
      Ada.Text_IO.Put_Line ("Name    : " & Swagger.To_String (Pet.Name));
      Ada.Text_IO.Put_Line ("Status  : " & Swagger.To_String (Pet.Status));
    

    The Swagger Ada Petstore illustrates other uses of the generated operations. It allows to list the inventory, list the pets with a given status, add a pet and so on...

    Conclusion and references

    The OpenAPI Specification provides a standard way to describe REST operations. The Swagger Codegen is the generator to be used to simplify the implementation of REST clients in many programming languages and to generate the documentation of the API. The Ada code generator only supports the client side but the server code generation is under work.

    The sources of the petstore samples are available:

    The APIs.guru lists more than 550 API descriptions from various providers such as Amazon, Google, Microsoft and many other online services. They are now available to the Ada community!

    โŒ
    โŒ