❌ About FreshRSS

Normal view

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

Advent of Code 2023, day 10

10 December 2023 at 17:12

The puzzle: https://adventofcode.com/2023/day/10

My Ada solution: here.

In a picture (generated by the program linked above)...

Click to enlarge

 

It is always a pleasure to display the data created by Eric Wastl.

Here, an improved representation with the five cases (outside tile, inside tile, inside pixel on a path tile, outside pixel on a path tile, path pixel) in shown different colors:

Click to enlarge


LEA, a tiny but smart editor

14 October 2023 at 13:39

Note for subscribers: if you are interested in my Ada programming articles only, you can use this RSS feed link.


LEA, the Lightweight Editor for Ada, is based on the Scintilla text editor widget, which excels at colouring syntactic elements and letting the user rework his or her programs very swiftly, including manipulating rectangular selections and adding text on multiple points (usually vertically aligned).
But until recently, the cool features were limited to automatic indentation, adding comment delimiters (--) at the right column, or highlighting portions of text that match a selection.
Users were frustrated by not finding in LEA programming helpers (commonly grouped under the term "smart editor" or "intellisense") that they enjoy in their programming "studios".
An excuse would have been to dismiss such features as out of the scope of a "lightweight" editor.
But we know we can make of LEA a powerful mini-studio, with the ease of use of a simple editor, thanks to its "projectless" mode and the integrated HAC compiler.
So now, it happened.
From revision 385 of LEA (and revision 886 of HAC), you have now a good sense of intellisense:

  • mouse-hover tips: when you let the mouse pointer on an identifier for a little while, a tip appears about that identifier (typically, where it was declared)
  • context menu with a go-to-declaration entry, when applicable
  • call tips: on typing '(' after a subprogram name, a tip appears with the subprogram's parameters
  • auto-complete: on typing characters that belong to identifiers, a list of possible declared identifier appears; that list depends on where in the source code the text cursor is: declarations below the cursor are invisible and local declarations, possibly within multiple nested subprograms. 

Some screenshots will help illustrate the above points. Let's start with the sudoku_sample.adb example shipped with the HAC project. What's that "Sudo_Strings" type, for instance?
Just let the mouse pointer hang out around an occurrence of that keyword.

Mouse hover tip - Click image to enlarge it

Want to know more about that declaration? Right-click on it...

Go to declaration - Click image to enlarge it

...and here you go.

Declaration in the Sudoku package - Click image to enlarge it

That's it, so far, for features enabling navigation within existing code.
For writing code, here are two other key helpers. First, the call tips.
If we type '(' after a subprogram's name, we see the parameters list appear:


Call tip - Click image to enlarge it

If we type the beginning of an identifier, a list of possible completions appears:

Auto-complete - Click image to enlarge it

A few remarks about the present state of LEA's "smart editor" features:
  • Currently, LEA only supports the HAC subset - it was the first priority. GNAT and other Ada compilers have their own editors and navigation features, so there is a lesser pressure to support them in LEA.
  • The "smart editor" features are quite new and still under test and development. If you want to have them, you need to build LEA on a fresh clone (instructions are given in lea.gpr), then set (via regedit) the registry key SMART_EDITOR, in the branch HKEY_CURRENT_USER\SOFTWARE\LEA\, to "true", before starting LEA.
  • You will notice a few missing things, like a list of fields on typing '.' after a record variable. They are not forgotten and just still "under construction".
  • The "smart editor" features work on incomplete Ada sources. They might be just less present after the point of an error, especially a syntax error.

In order to develop the last point, here are a few examples on how LEA understands your program "on the fly", while you are typing it:

Smart editor on an incomplete piece of code - Click image to enlarge it

Smart editor on an incomplete piece of code, sample 2 - Click image to enlarge it

Smart editor on an incomplete piece of code, sample 3 - Click image to enlarge it

One more thing: the Windows version of LEA is contained in a single executable holding in currently 4.2 MiB, including data such as code samples and templates, and runs as a portable application (no installation required).

Some Web links:

LEA

Web site: http://l-e-a.sf.net/
Sources, site #1: https://sf.net/p/l-e-a/code/HEAD/tree/
Sources, site #2: https://github.com/zertovitch/lea
Alire Crate: Alire - LEA

HAC

Web site: https://hacadacompiler.sourceforge.io/
Sources, site #1: https://sf.net/p/hacadacompiler/code/HEAD/tree/
Sources, site #2: https://github.com/zertovitch/hac
Alire Crate: Alire - HAC

Enjoy!

Gcc 13.1.0 macOS Sonoma not compiling Ada or any languages on Intel or M1 Macs

I just upgraded to macOS Sonoma on my Intel and M1 Macs, and Gcc and Alire cannot compile simple Ada programs or any others so I suspect it's a compatibility issue. I am using Gcc 13.1.0 aarch64 and x86_64 on the MacBook Pro/Air M1 and MacBook Pro Core i9 respectively. The error I see among others is "-macosx_version_min has been renamed to -macosx_version_min" also: "exited with code 4" Since I cannot compile Ada programs which is most important, is there a workaround or hack until there are upgraded versions to fix it? The same errors are present on both the Intel and M1 Macs, and I had no issues at all on macOS Ventura.

I tried using Gcc 13.1.0 aarch64 and x86_64 on my MacBook M1s and only the x86_64 version on my Intel MacBook Pro Core i9. I tried using Alire with it's native compilers on both type Macs and its matching gprbuild version. The same errors are present using the terminal editing with "nano" and compiling with "gnatmake" or Gnat Studio using the Gcc 13.1.0 toolchain. It obviously is a compatibility issue between Gcc/Alire and macOS Sonoma. It's not just Ada "Hello, World!", but C and C++ "Hello, World!"

LEA: first steps as a "smart editor"

2 September 2023 at 19:53

Note for subscribers: if you are interested in my Ada programming articles only, you can use this RSS feed link.


Spot the "tool tip" on the following screenshot...

Click to enlarge

Translation: cross-references and source code navigation in LEA are becoming a reality since this evening!

 


Some Web links:

LEA

Web site: http://l-e-a.sf.net/
Sources, site #1: https://sf.net/p/l-e-a/code/HEAD/tree/
Sources, site #2: https://github.com/zertovitch/lea
Alire Crate: Alire - LEA


Since LEA embeds the HAC compiler, here are a few links about HAC as well:

HAC

Web site: https://hacadacompiler.sourceforge.io/
Sources, site #1: https://sf.net/p/hacadacompiler/code/HEAD/tree/
Sources, site #2: https://github.com/zertovitch/hac
Alire Crate: Alire - HAC

HAC for native targets

20 August 2023 at 13:57
Note for subscribers: if you are interested in my Ada programming articles only, you can use this RSS feed link.

HAC (the HAC Ada Compiler) was since the first of its previous lives, from Pascal-S, in 1973, to recent commits, translating high-level language exclusively to the machine code of a fictitious machine (a Virtual Machine, abbreviated VM).

For writing a tiny compiler, a VM is very convenient:

  • You (the compiler creator) can model and adapt it to your needs.
  • You don't depend on hardware.
  • You don't need to rewrite the code generation for each new hardware.
  • You can make the VM running on many hardwares (hence the success of the JVM and .NET around year 2000).


The HAC VM and its users are enjoying all these advantages.

However, there is a flip side:

  • A VM is much slower than a real machine (but till year 2000, it didn't matter; you could say: don't worry, just wait a few months for a more powerful computer).
  • Since year 2000, the speed of microprocessors stopped doubling every six months (hence the declining interest in Virtual Machines). Despite the improvements in cache, RAM, and the multiplication of cores, the per-core performance improvement is slower and slower.
  • Supporting only a VM gives the impression that your compiler can only compile to a VM.


Well, so far, the latter blame was objectively correct regarding HAC until a few weeks ago.
Now it is changing!
We have begun an abstract framework for emitting machine code.
With that framework, HAC can, on demand, emit code for its captive VM or for any implemented machine.
So you read well, it is not a just-in-time compiler translating VM instructions to native ones.
We are implementing a direct Ada-to-native compiler - and cross-compiler, since HAC doesn't know on which machine it is running!

The framework looks like this:

with HAC_Sys.Defs;

package HAC_Sys.Targets is

  type Machine is limited interface;

  type Abstract_Machine_Reference is access Machine'Class;

  --------------------
  --  Informations  --
  --------------------

  function Name (m : Machine) return String is abstract;
  function CPU (m : Machine) return String is abstract;
  function OS (m : Machine) return String is abstract;
  function Null_Terminated_String_Literals (m : Machine) return Boolean is abstract;

  ----------------------------
  --  Machine Instructions  --
  ----------------------------

  procedure Emit_Arithmetic_Binary_Instruction
    (m         : in out Machine;
     operator  :        Defs.Arithmetic_Binary_Operator;
     base_typ  :        Defs.Numeric_Typ) is abstract;

...


The code emission in the compiler is being changed (very slowly, be patient!) for going through this new abstract machine mechanism.
So far we have two implementations:

  • The HAC VM - that way, we ensure HAC-for-HAC-VM works exactly as previously and can pass the test suite.
  • A real target: the AMD64, running under Windows; the machine code is emitted in Assembler form, for the Flat Assembler (FASM).


The development for multiple targets is embryonic so far.
However, we can already compile a "hello world"-style program:

with HAT;

procedure Native is
  use HAT;
  a : Integer;
begin
  --  a := 1;  --  Variables: TBD.
  Put_Line ("Hello ...");
  Put_Line ("... world!");
  Put_Line (12340 + 5);
  Put_Line (12350 - 5);
  Put_Line (2469 * 5);
  Put_Line (61725 / 5);
end Native;


With the command:
hac -tamd64_windows_console_fasm native.adb

HAC produces this:

;  Assembler file for the Flat Assembler - https://flatassembler.net/

format PE64 console
entry _start
include 'include\win64a.inc'

section '.code' code readable executable

_start:
         push                9
         push                1
         push                -1
         push                -1
         pop                 r14
         pop                 r13
         pop                 r12
         pop                 r11
         add                 r12, _hac_strings_pool
         ccall               [printf], r12
         ccall               [printf], _hac_end_of_line
         push                10
         push                11
         push                -1
         push                -1
         pop                 r14
         pop                 r13
         pop                 r12
         pop                 r11
         add                 r12, _hac_strings_pool
         ccall               [printf], r12
         ccall               [printf], _hac_end_of_line
         push                12340
         push                5
         pop                 r11
         pop                 rax
         add                 rax, r11
         push                rax
         push                20
         push                10
         push                -1
         pop                 r14
         pop                 r13
         pop                 r12
         pop                 r11
         ccall               [printf], _hac_decimal_format, r11
         ccall               [printf], _hac_end_of_line
         push                12350
         push                5
         pop                 r11
         pop                 rax
         sub                 rax, r11
         push                rax
         push                20
         push                10
         push                -1
         pop                 r14
         pop                 r13
         pop                 r12
         pop                 r11
         ccall               [printf], _hac_decimal_format, r11
         ccall               [printf], _hac_end_of_line
         push                2469
         push                5
         pop                 r11
         pop                 rax
         imul                rax, r11
         push                rax
         push                20
         push                10
         push                -1
         pop                 r14
         pop                 r13
         pop                 r12
         pop                 r11
         ccall               [printf], _hac_decimal_format, r11
         ccall               [printf], _hac_end_of_line
         push                61725
         push                5
         pop                 r11
         pop                 rax
         xor                 rdx, rdx
         idiv                r11
         push                rax
         push                20
         push                10
         push                -1
         pop                 r14
         pop                 r13
         pop                 r12
         pop                 r11
         ccall               [printf], _hac_decimal_format, r11
         ccall               [printf], _hac_end_of_line
         stdcall             [ExitProcess],0

section '.data' data readable writeable
_hac_end_of_line  db 10, 0
_hac_decimal_format  db "%d", 0
_hac_strings_pool db "XHello ...", \
    0, "... world!", 0

section '.idata' import data readable
library kernel,'kernel32.dll',\
        msvcrt,'msvcrt.dll'
import  kernel,\
        ExitProcess,'ExitProcess'
import  msvcrt,\
        printf,'printf'

As you can see, the assembler code needs badly some simplification, but, anyway: it works.
FASM produces from it a relatively small 2048-byte executable which writes

Hello ...
... world!
12345
12345
12345
12345


The executable is full of zeroes, due to alignments. The non-zero bytes (more or less, the actual machine code and data) take 605 bytes.

Some Web links for HAC:

Main URL: https://hacadacompiler.sourceforge.io/
Sources, site #1: HAC Ada Compiler download | SourceForge.net
Sources, site #2: GitHub - zertovitch/hac: HAC Ada Compiler - a small, quick Ada compiler fully in Ada
Alire Crate: Alire - Hac

Unable to link C source code with Ada static library (Error: libnewapi.a(unit1.o):unit1.adb:undefined reference to `__gnat_rcheck_CE_Overflow_Check')

I want to integrate my Ada static library (libnewapi.a) with my C source code (main.c). I do not have any issues generating the static library but when trying to link it with main.c. I get the below link error, libnewapi.a(unit1.o):unit1.adb:(.text+0x31): undefined reference to `__gnat_rcheck_CE_Overflow_Check' I am not using this reference in my main.c not in any of my Ada files. I do not know why this reference was added automatically. It is crucial that I need to link my main.c using a static Ada library for my project. I do not know where I am going wrong. Help is much appreciated. Thanks!

I am generating libnewapi.a using a GPR as below,

    -- ada_gen_a.gpr
    project ada_gen_a is
       for Languages use ("Ada");
       for Source_Dirs use ("./");
       for Library_Name use "newapi";
       for Library_Dir use "./Lib/";
       for Library_Kind use "static";

       package Naming is
          for Spec_Suffix ("ada") use ".ads";
          for Body_Suffix ("ada") use ".adb";
          for Separate_Suffix use ".adb";
          for Dot_Replacement use ".";
          for Casing use "mixedcase";
       end Naming;

       Ada_Switches := ("-gnato", "-O2");

       package Compiler is
          for Default_Switches ("ada") use Ada_Switches;
       end Compiler;

       package Binder is
          for Default_Switches ("Ada") use ("-n","-Lada");
       end Binder;
    end ada_gen_a;

Ada Sourcefiles:

    --  unit1.ads
    package Unit1 is
       function Add (A, B : Integer) return Integer;
       pragma Export (C, Add, "ada_add");
    end Unit1;
    -- unit1.adb
    package body Unit1 is
       function Add (A, B : Integer) return Integer is
       begin
          return A + B;
       end Add;
    end Unit1;

C Source File:

    /* main.c */
    #include <stdio.h>
    extern void ada_add (void);
    int main (int argc, char *argv[])
    {
       int a = 21, b = 7, c = 0;
       printf ("%d", a);
       printf ("%d", b);
       c = ada_add(a,b);
       printf ("%d", c);
       return 0;
    }

I am using the below GPR to link the above main.c with the Ada static library generated using ada_gen_a.gpr.

    -- Ada_Use_A.gpr
    with "newapi.gpr";
    project Ada_Use_A is

       for Languages use ("C");
       for Source_Dirs use (".");
       for Source_Files use ("main.c");

       package Naming is
          for Casing use "mixedcase";
       end Naming;

       Ada_Switches := ("-gnato", "-O2");

       package Compiler is
          for Default_Switches ("C") use ("-O2", "-Wall");
          for Default_Switches ("Ada") use Ada_Switches;
       end Compiler;

       package Binder is
          for Default_Switches ("Ada") use ("-n","-Lada");
       end Binder;

       for Main use ("main.c");

    end Ada_Use_A;
    -- newapi.gpr
    project newapi is
       for Externally_Built use "true";
       for Source_Files use ();
       for Library_Dir use ".\lib\";
       for Library_Name use "newapi";
       for Library_Kind use "static";
    end newapi;

When I try to build the Ada_Use_A.gpr GPS I get the below linker error, libnewapi.a(unit1.o):unit1.adb:(.text+0x31): undefined reference to __gnat_rcheck_CE_Overflow_Check' gprbuild: link of main.c failed`

HAC version 0.26

8 July 2023 at 06:40
Note for subscribers: if you are interested in my Ada programming articles only, you can use this RSS feed link.

Main URL: https://hacadacompiler.sourceforge.io/
Sources, site #1: HAC Ada Compiler download | SourceForge.net
Sources, site #2: GitHub - zertovitch/hac: HAC Ada Compiler - a small, quick Ada compiler fully in Ada
Alire Crate: Alire - Hac

What’s new:

  • You can use a loop’s name for the exit statement: exit Loop_Name.
  • You can exit multiple, nested, loops, by using exit Loop_Name.
  • Ada semantics are better verified:
    • Array indexing (i)(j) (array of array) and (i, j) (multi-dimensional array) are no more treated as equivalent (this feature was a remnant of the Pascal syntax).
    • Separation between Type and Subtype declarations (anonymous types are allowed only in the few cases foreseen by the language).
    • Operators of the HAT package (+, -, & for strings) are visible without prefix only in the scope of a use HAT clause.

Note that correct Ada programs, in relation to the above points, were already accepted and parsed correctly by HAC before that change.

Finally, a bit of cosmetics in the build messages:

C:\Ada\hac\exm>..\hac -v2 -c pkg_demo.adb

*******[ HAC ]*******   HAC is free and open-source. Type "hac" for license.
       [ HAC ]          HAC Ada Compiler version 0.26, 08-Jul-2023
       [ HAC ]          Compiling main: pkg_demo.adb
       [ HAC ]          | Compiling x_pkg_demo_s.ads (specification)
       [ HAC ]           \ Compiling x_pkg_demo_s1.ads (specification)
       [ HAC ]            \ Compiling x_pkg_demo_s11.ads (specification)
       [ HAC ]            /           x_pkg_demo_s11.ads: done.
       [ HAC ]            \ Compiling x_pkg_demo_s12.ads (specification)
       [ HAC ]            /           x_pkg_demo_s12.ads: done.
       [ HAC ]            \ Compiling x_pkg_demo_s13.ads (specification)
       [ HAC ]            /           x_pkg_demo_s13.ads: done.
       [ HAC ]           /           x_pkg_demo_s1.ads: done.
       [ HAC ]           \ Compiling x_pkg_demo_s2.ads (specification)
       [ HAC ]            \ Compiling x_pkg_demo_s21.ads (specification)
       [ HAC ]            /           x_pkg_demo_s21.ads: done.
       [ HAC ]            \ Compiling x_pkg_demo_s22.ads (specification)
       [ HAC ]            /           x_pkg_demo_s22.ads: done.
       [ HAC ]            \ Compiling x_pkg_demo_s23.ads (specification)
       [ HAC ]            /           x_pkg_demo_s23.ads: done.
       [ HAC ]           /           x_pkg_demo_s2.ads: done.
       [ HAC ]           \ Compiling x_pkg_demo_s3.ads (specification)
       [ HAC ]            \ Compiling x_pkg_demo_s31.ads (specification)
       [ HAC ]            /           x_pkg_demo_s31.ads: done.
       [ HAC ]            \ Compiling x_pkg_demo_s32.ads (specification)
       [ HAC ]            /           x_pkg_demo_s32.ads: done.
       [ HAC ]            \ Compiling x_pkg_demo_s33.ads (specification)
       [ HAC ]            /           x_pkg_demo_s33.ads: done.
       [ HAC ]           /           x_pkg_demo_s3.ads: done.
       [ HAC ]          |           x_pkg_demo_s.ads: done.
       [ HAC ]          | Compiling x_pkg_demo_m.ads (specification)
       [ HAC ]          |           x_pkg_demo_m.ads: done.
       [ HAC ]          | Compiling x_pkg_demo_b.ads (specification)
       [ HAC ]          |           x_pkg_demo_b.ads: done.
       [ HAC ]          Compilation of pkg_demo.adb (main) completed
       [ HAC ]          ------  Compilation of eventual with'ed unit's bodies
       [ HAC ]          | Compiling x_pkg_demo_s.adb (body)
       [ HAC ]          |           x_pkg_demo_s.adb: done.
       [ HAC ]          | Compiling x_pkg_demo_s1.adb (body)



Stack corruption in Ada C binding to OpenGL function

I'm using SDL to retrieve the address of the function. That much seems to be working, since I'm using the same subprogram that's working for all my other function calls.

Here are all the relevant code snippits in the spec:

type GLenum is new Uint32;

subtype glTexImage2D_Target is GLenum with Static_Predicate => glTexImage2D_Target in GL_TEXTURE_2D | GL_TEXTURE_RECTANGLE | GL_PROXY_TEXTURE_RECTANGLE;
subtype glTexImage2D_Dtype is GLenum with Static_Predicate => glTexImage2D_Dtype in GL_BYTE | GL_UNSIGNED_BYTE;
subtype glTexImage2D_InternalFormat is GLenum with Static_Predicate => glTexImage2D_InternalFormat in GL_DEPTH_COMPONENT | GL_DEPTH_STENCIL | GL_RED | GL_RG | GL_RGB | GL_RGBA;
subtype glTexImage2D_Format is GLenum with Static_Predicate => glTexImage2D_Format in GL_RED | GL_RG | GL_RGB | GL_BGR | GL_RGBA | GL_BGRA | GL_RED_INTEGER | GL_RG_INTEGER | GL_RGB_INTEGER | GL_BGR_INTEGER | GL_RGBA_INTEGER | GL_BGRA_INTEGER | GL_STENCIL_INDEX | GL_DEPTH_COMPONENT | GL_DEPTH_STENCIL;

procedure glTexImage2D(target : glTexImage2D_Target; level : Integer; internalFormat : glTexImage2D_InternalFormat; width : Integer; height : Integer; format : glTexImage2D_Format; dtype : glTexImage2D_Dtype; pixels : System.Address) with Pre => (target /= GL_TEXTURE_RECTANGLE and target /= GL_PROXY_TEXTURE_RECTANGLE) or Level = 0;

procedure Set_OpenGL_Subprogram_Address(Addr : in out System.Address; Name : String);

glTexImage2D_C_Address : System.Address := System.Null_Address;

Could_Not_Load_OpenGL_Subprogram : exception;

And here are the relevant snippits in the body:

procedure glTexImage2D(target : glTexImage2D_Target; level : Integer; internalFormat : glTexImage2D_InternalFormat; width : Integer; height : Integer; format : glTexImage2D_Format; dtype : glTexImage2D_Dtype; pixels : System.Address) is
begin
    Set_OpenGL_Subprogram_Address(glTexImage2D_C_Address, "glTexImage2D");
    declare
        procedure glTexImage2D_C(target : GLenum; level : GLint; internalFormat : GLint; width : GLsizei; height : GLsizei; border : GLint; format : GLenum; dtype : GLenum; data : System.Address)
        with Import, Convention => Stdcall, Address => glTexImage2D_C_Address;
    begin
        glTexImage2D_C(GLenum(target), GLint(level), GLint(internalFormat), GLsizei(width), GLsizei(height), GLint'(0), GLenum(format), GLenum(dtype), pixels);
    end;
end glTexImage2D;

--This seems to work... but here it is just in case.
procedure Set_OpenGL_Subprogram_Address(Addr : in out System.Address; Name : String) is
begin
    if Addr = System.Null_Address then
        Addr := SDL_GL_GetProcAddress(Value(New_String(Name)));
        if Addr = System.Null_Address then
            raise Could_Not_Load_OpenGL_Subprogram with Name;
        end if;
    end if;
end Set_OpenGL_Subprogram_Address;

Finally, the pixels I'm passing in is an address indicating an object of type:

type Uc_Array is array(Integer range <>) of aliased Interfaces.C.unsigned_char;

The stack is being corrupted on the call to glTexImage2D_C. I discovered this by using gdb:

enter image description here

Here you can see the parameters I'm passing as well; target 3553, level 0, internalformat 6408, width 63, height 63, format 6408, dtype 5121, non-zero pixels address. The subprogram address is being set to a non-zero value. But after the call to glTexImage2D_C, the value of $sp (the stack pointer) has increased by 16#24#.

I've tried adjusting the height and width I pass to glTexImage2D_C, both to 10 and both to 1000, just to see if giving it more (or less) buffer prevents stack corruption, but in every case, the stack pointer is different before and after the procedure call.

I'm hoping this is a simple matter of an incorrect datatype or something... Here's the prototype of glTexImage2D on the OpenGL side:

void glTexImage2D(  GLenum target,
    GLint level,
    GLint internalformat,
    GLsizei width,
    GLsizei height,
    GLint border,
    GLenum format,
    GLenum type,
    const void * data);

(As a final note, I do intend to clean up the awkward lazy loading tactic you see above, if I can ever get this to actually work. For now I'm leaving it this way so I can eliminate the chance of trying to call a function that hasn't been loaded yet, while I'm still trying to debug the call itself. The tactic of using a nested procedure declared inside a declare...begin...end block is working for other OpenGL functions.)

How to pass complex data types, like records between Ada and C via a DLL?

I am trying to get Ada code to compile to a DLL to be callable in C. So far I have managed to get this to work for simple types such as integers but i'm having difficulty with more complex types such as array's or records/structs.

Below is my Ada code for an record/struct. It is a dynamic library which i compile to a dll using the command "gprbuild -P ./math.gpr -p":

person.adb
with Interfaces.C; use Interfaces.C;

package body Person is

   function Set_Age_To_Five(P : Person_Record) return Person_Record is
      Result : Person_Record;
   begin
      Result := P;
      Result.Age := 5;
      return Result;
   end Set_Age_To_Five;

end Person;

person.ads

with Interfaces.C; use Interfaces.C;

package Person is

   type Person_Record is record
      Name    : String(1 .. 100);
      Age     : Interfaces.C.int;
      Address : String(1 .. 100);
   end record with Convention => C_Pass_By_Copy;

   function Set_Age_To_Five(P : Person_Record) return Person_Record;
   pragma Export (C, Set_Age_To_Five, "Set_Age_To_Five");

end Person;

math.gpr

library project Math is
    for Languages use ("Ada");
    for Library_Name use "Person";
    for Source_Dirs use ("src");
    for Object_Dir use "obj";
    for Library_Dir use "lib";
    for Library_Kind use "Dynamic";
end Math;

I then have a C header file math.h:

#ifndef MATH_H
#define MATH_H

#ifdef __cplusplus
extern "C"
#endif

typedef struct {
    char Name[101];
    int Age;
    char Address[101];
} Person_Record;

Person_Record Set_Age_To_Five(Person_Record P);

#ifdef __cplusplus

#endif

#endif /* MATH_H */

and finally my C code:

#include <stdio.h>
#include "math.h"

int main() {
    Person_Record p, q, r;

    // Initialize the person record
    snprintf(p.Name, sizeof(p.Name), "John");
    p.Age = 25;
    snprintf(p.Address, sizeof(p.Address), "123 Main St");

    // Call the Set_Age_To_Five function from the DLL
    q = Set_Age_To_Five(p);

    // Print the modified person record
    printf("Name: %s\n", q.Name);
    printf("Age: %d\n", q.Age);
    printf("Address: %s\n", q.Address);

    return 0;
}

This should when executed return

Name: John
Age: 5
Address: 123 Main St

Instead its returning:

Name: John
Age: 25
Address: 123 Main St

I've tried passing by variable, passing by reference. using convention C and convention pass by c in ada.

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:

Libadalang, Alire, and macOS

Background

This exercise was prompted by the need for Scripted Testing to be supported by – as far as possible – code generation. The need is for the public or interfacing view of a supporting part (domain) of a system to be able to log calls made to it and to provide values on calls to it, using a scripting mechanism.

Read more »

Advent of Code 2022 in pictures

14 January 2023 at 09:25

Note for subscribers: if you are interested in my Ada programming articles only, you can use this RSS feed link.


Just as a teaser for the next Advent of Code in 10 1/2 months, we would like to show a few pictures related to last edition. The 25 puzzles, data and solutions can be found here and here. They are programmed with HAC (the HAC Ada Compiler), thus in a small subset of the Ada language. The HAC compiler is very fast, so you run your program without noticing it was ever compiled, which is perfect for completing a programming puzzle.

Day 22 run with HAC (here, embedded in the LEA environment)

However, the program will run substantially slower than compiled with a native, optimizing compiler like GNAT.
This is not an issue for most Advent of Code puzzles, but for some, it is, especially on the later days. Fortunately, changing from HAC to GNAT is trivial (just switch compilers), unlike the traditional reprogramming of Python prototypes in C++, for instance.

The pictures

Day 8's data is a map representing the height of trees. Once the puzzle was solved, I was curious how the forest looked like. Note that different users get different data, so you are unlikely to find a visualisation of exactly your data on the internet.


Day 12's puzzle is a shortest path problem with specific rules and a nice data - a terrain with a hill - which seems designed to trap depth-first-search algorithms into an almost infinite search. The yellowish path is the shortest from the green dot, elevation 'a', to blue dot, elevation 'z'.
The pinkish path is the shortest from the blue dot to any dot with elevation 'a'. Fortunately Dijkstra's algorithm (and perhaps others) allows for such a special criterion regarding the end point.

Click to enlarge


For day 22’s puzzle, a walk on a cube’s surface is involved, so it is helpful to sketch it on a piece of paper, cut it and glue the faces. A banana skin of that puzzle is that the example’s layout may be different from the data’s layout. We slipped on that one and ended up gluing and programming the face-to-face transitions for two layouts…


Click to enlarge

Other Adaists’ solutions, discussions and pictures can be found here and in the "2022 Day x" threads in the same forum.

How would I define the __builtin_blendvps256 GCC intrinsic in Ada using GNAT?

I am trying to define a library in Ada (built on GNAT specifically) for x86 ISA extensions. (This question is specific to AVX/AVX2).

Here is some example code below:

-- 256-bit Vector of Single Precision Floating Point Numbers
type Vector_256_Float_32 is array (0 .. 7) of IEEE_Float_32 with
  Alignment => 32, Size => 256, Object_Size => 256;
pragma Machine_Attribute (Vector_256_Float_32, "vector_type");
pragma Machine_Attribute (Vector_256_Float_32, "may_alias");

-- 256-bit Vector of 32-bit Signed Integers
type Vector_256_Integer_32 is array (0 .. 7) of Integer_32 with
  Alignment => 32, Size => 256, Object_Size => 256;
pragma Machine_Attribute (Vector_256_Integer_32, "vector_type");
pragma Machine_Attribute (Vector_256_Integer_32, "may_alias");

-- 256-bit Vector of 32-bit Unsigned Integers
type Vector_256_Unsigned_32 is array (0 .. 7) of Unsigned_32 with
  Alignment => 32, Size => 256, Object_Size => 256;
pragma Machine_Attribute (Vector_256_Unsigned_32, "vector_type");
pragma Machine_Attribute (Vector_256_Unsigned_32, "may_alias");

function vblendvps
  (Left, Right, Mask : Vector_256_Float_32)
   return Vector_256_Float_32 with
  Inline_Always => True, Convention => Intrinsic, Import => True,
  External_Name => "__builtin_ia32_blendvps256";

For the sake of education, I want to know how to do this in assembly.

I have tried to define the vblendvps function using the Asm function from System.Machine_Code. However, as I am not knowledgeable about assembly programming, I am struggling to find resources on how to do this.

This is what I have so far:

with System.Machine_Code; use System.Machine_Code;

function vblendvps
(Left, Right, Mask : Vector_256_Float_32)
return Vector_256_Float_32
is
result : Vector_256_Float_32;
begin
Asm
(Template => "vblendvps %3, %0, %1, %2",
 Outputs  => Vector_256_Float_32'Asm_Output ("=g", result),
 Inputs   =>
   (Vector_256_Float_32'Asm_Input ("g", Left),
    Vector_256_Float_32'Asm_Input ("g", Right),
    Vector_256_Unsigned_32'Asm_Input ("g", Mask)));
return result;
end vblendvps;

When compiling the complete code, I get

Error: too many memory references for `vblendvps'

I believe this means that I need to move the arguments from memory to registers, but I am not sure. If there are some helpful references that explain every instruction, I would greatly appreciate that. (I had quite some trouble looking up the arguments to vblendvps).

My understanding is that the instruction is of the form (from ymm registers in my case)

vblendvps RESULT, LEFT, RIGHT, MASK

Please let me know how I would do this. Even if it is not in Ada, I'm sure I can figure out how to translate it.

LEA 0.85 - the Tabs are here!

20 November 2022 at 20:05
Note for subscribers: if you are interested in my Ada programming articles only, you can use this RSS feed link.

The 0.85 version of LEA (the Lightweight Editor for Ada) is available!

Web site: http://l-e-a.sf.net/
Source repository #1: https://sf.net/p/l-e-a/code/HEAD/tree/
Source repository #2: https://github.com/zertovitch/lea

The new version has two main new features:
  - Tabs (screenshot below)
  - LEA doesn't write scilexer.dll as a file; thus, it runs as a portable application (in the sense: you can run it from a read-only drive directly, without installation)

Click to enlarge

Enjoy!

Building GCC 12.2.0 on Ventura for aarch64

These are notes on building GCC 12.2.0 and GNAT tools for Apple silicon.

There were two main problems:

  • the base package was built on an Intel machine (lockheed - named after Shadowcat’s companion dragon), running Monterey (macOS 12).
  • the build machine, an M1 mac Mini (temeraire - named after Naomi Novik’s dragon) was by this time running Ventura (macOS 13), but I wanted to make sure that users could continue to run on Monterey.
Read more »
❌
❌