❌ About FreshRSS

Normal view

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

HAC - recursive maze generator demo

28 May 2020 at 19:53
New example/demo, adapted from Rosetta Code's recursive maze generator.
HAC's Web site is here, source code is mirrored here.
Behind the scenes, the compiler was improved a little bit for displaying fixed-sized strings.

C:\Ada\hac\exm>..\hax -v2 maze_gen.adb

*******[ HAX ]******* Compiler version: 0.061 dated 28-May-2020.
*******[ HAX ]******* Caution: HAC is not a real Ada compiler. Type "hax" for license.
. . . .[ HAX ]. . . . Compiling from file: maze_gen.adb
. . . .[ HAX ]. . . . Compilation finished in 0.000414700 seconds.
. . . .[ HAX ]. . . . Starting p-code VM interpreter...
Height: 15, Width: 24
Starting generation at 7, 12
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | | | | |
+ +---+ +---+---+ + +---+ +---+ +---+---+ + + + + +---+---+---+---+---+ +
| | | | | | | | | | | | | | |
+---+ +---+ +---+ + +---+---+ + + +---+---+---+ + +---+---+ + +---+ +---+
| | | | | | | | | | | |
+ +---+ +---+---+---+ +---+ + + +---+---+---+ + +---+ +---+ + +---+---+ +
| | | | | | | | | | | | | | |
+ +---+ + + + + + + +---+---+ + + + +---+ +---+ +---+ +---+---+ +
| | | | | | | | | | | | | | | | | | |
+---+ + + + +---+ + + + + + + + +---+ +---+ + + + + +---+ +
| | | | | | | | | | | | | | | | |
+ +---+ +---+---+ + + +---+ + +---+ + + +---+ +---+---+ +---+ + +---+
| | | | | | X | | | | | | | | |
+ + +---+---+ +---+---+ + +---+---+ + + + + + +---+ +---+ +---+ + +
| | | | | | | | | | | | | | | | |
+ +---+ + +---+---+ +---+---+---+ +---+---+ +---+ +---+ +---+---+ + + + +
| | | | | | | | | |
+ + +---+ + +---+---+ +---+---+---+ + +---+ + +---+---+ +---+---+---+---+ +
| | | | | | | | | | | | | | |
+ + + + + + + + + + +---+ + + +---+---+---+ +---+---+---+---+ +---+
| | | | | | | | | | | | | |
+---+ + + +---+---+---+---+ +---+ +---+ + + + + +---+---+---+---+ +---+ +
| | | | | | | | | | | | |
+ +---+---+---+ +---+---+ +---+ + + +---+ +---+ +---+---+---+ +---+---+ + +
| | | | | | | | | | |
+ + +---+---+---+---+ +---+---+---+ +---+---+---+ + +---+---+---+---+ +---+---+ +
| | | | | | | | | | | | |
+ + + + + + + +---+ + +---+---+---+ + +---+---+---+ +---+ + +---+ +
| | | | | | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
-------[ HAX ]------- VM interpreter done after 0.080759500 seconds.

C:\Ada\hac\exm>

HAC v.0.07 - Exceptions and trace-backs

1 June 2020 at 14:35
Since yesterday, errors are propagated by HAC, as expected, down the call stack.
You get by default a detailed trace-back, like the following one. HAC's output is:
  HAC VM: raised Constraint_Error
Out of range
Trace-back locations:
exception_04.adb: Exception_04.Nest.NTF.Add_n_shift.Shift_n_add at line 18
exception_04.adb: Exception_04.Nest.NTF.Add_n_shift at line 23
exception_04.adb: Exception_04.Nest.NTF.Add_n_shift.Shift_n_add at line 16
exception_04.adb: Exception_04.Nest.NTF.Add_n_shift at line 23
exception_04.adb: Exception_04.Nest.NTF.Add_n_shift.Shift_n_add at line 16
exception_04.adb: Exception_04.Nest.NTF.Add_n_shift at line 23
exception_04.adb: Exception_04.Nest.NTF.Add_n_shift.Shift_n_add at line 16
exception_04.adb: Exception_04.Nest.NTF.Add_n_shift at line 23
exception_04.adb: Exception_04.Nest.NTF.Add_n_shift.Shift_n_add at line 16
exception_04.adb: Exception_04.Nest.NTF.Add_n_shift at line 23
exception_04.adb: Exception_04.Nest.NTF at line 27
exception_04.adb: Exception_04.Nest at line 34
exception_04.adb: Exception_04 at line 42
GNAT's output on the same program is:
  raised CONSTRAINT_ERROR : exception_04.adb:18 range check failed
[C:\Ada\hac\test\exception_04.exe]
Exception_04.Nest.Ntf.Add_N_Shift.Shift_N_Add at exception_04.adb:18
Exception_04.Nest.Ntf.Add_N_Shift at exception_04.adb:23
Exception_04.Nest.Ntf.Add_N_Shift.Shift_N_Add at exception_04.adb:16
Exception_04.Nest.Ntf.Add_N_Shift at exception_04.adb:23
Exception_04.Nest.Ntf.Add_N_Shift.Shift_N_Add at exception_04.adb:16
Exception_04.Nest.Ntf.Add_N_Shift at exception_04.adb:23
Exception_04.Nest.Ntf.Add_N_Shift.Shift_N_Add at exception_04.adb:16
Exception_04.Nest.Ntf.Add_N_Shift at exception_04.adb:23
Exception_04.Nest.Ntf.Add_N_Shift.Shift_N_Add at exception_04.adb:16
Exception_04.Nest.Ntf.Add_N_Shift at exception_04.adb:23
Exception_04.Nest.Ntf at exception_04.adb:27
Exception_04.Nest at exception_04.adb:34
Exception_04 at exception_04.adb:42
Main at b__exception_04.adb:294

You can look in the "test" directory at the files "exception_01.adb", "exception_02.adb", ... for playing with some examples.

HAC (HAC Ada Compiler) is available on two open-source development sites:

  https://hacadacompiler.sourceforge.io/
  https://github.com/zertovitch/hac

HAC is a small, quick, open-source Ada compiler, covering a subset of the Ada language.
Even though the HAC documentation is more or less non-existent, the good news is that you can use as a help Ada books and online documentation about Ada: HAC does not define a dialect of Ada, only a subset. A glimpse into the file "src/hac_pack.ads" gives you the currently available types and subprograms.

Enjoy!

LEA 0.71 - exception trace-back

3 June 2020 at 18:31
Following the implementation of trace-backs in HAC, this feature is now included in the LEA editor.

Better showing an example. Suppose your Ada program has a bug that was not detected at compile-time (it happens sometimes), like an array index being out-of-range. In this case HAC will raise a Constraint_Error exception (as expected from any Ada system).



Once you have closed the message box about the unhandled exception, the message list will display the trace-back, that is the chain of all subprogram calls at the moment the error has happened. Here we have an example with recursive calls, and the error was planted for the moment where the parameter Level is 0.
For your comfort, you can click on a line of the trace-back and LEA will find the correct file and the correct line as well!



Here is a link to LEA: https://l-e-a.sourceforge.io/.
You can download a read-to-use Windows executable there.

The LEA sources are available on the above site as well as on GitHub: https://github.com/zertovitch/lea .

Furthermore, the sources for the HAC system are located at
    https://hacadacompiler.sourceforge.io/
and
    https://github.com/zertovitch/hac .

Enjoy!


HAC v.0.072 - Subtype Indication

7 June 2020 at 19:44
One more step in our adventures in compiler construction...
Recently, we've moved the parsing of ranges to a new package, HAC.Parser.Ranges.
One chunk was about static ranges like "1 .. 5" for defining arrays.
The second chunk was the dynamic ranges in FOR loops (like in "for i in j + k .. l * 2 loop").
It was logical to put both pieces in the same package, especially since the Ada standard has a much more general definition: discrete_subtype_definition (RM 3.6 (6)).
Of course, with HAC, we don't try to implement the full standard or even the full dynamic aspects of a single syntactical element like discrete_subtype_definition, which allows constructs like this one:
    procedure SD3 is
begin
for I in 1 .. 3 loop
declare
type T1 is new Integer range 1 .. I * 5; -- I is dynamic
subtype T2 is T1 range 1 .. T1 (I);
type T3 is array (1 .. 2 * I) of T2;
type T4 is array (1 .. 3 * I) of T3;
B: T4;
begin
null;
end;
end loop;
end;

We prefer to add piece by piece features we find cool in Ada.
For instance, the subtype_indication (RM 3.2.2).
Again, something that sounds very bureaucratic, but what it represents is a very powerful shortcut:
Instead of writing
    x : array (False .. True) of Integer;

you can write in a nicer way:
    x : array (Boolean) of Integer;

same for loops:
    for b in Boolean loop
x (b) := ...
end loop;
As an illustration, here is an excerpt of the before/after comparison on one of the HAC examples:

Click to enlarge

LEA 0.74 - embedded Ada samples collection

27 June 2020 at 20:23
From now on, LEA has got the uber-gadget: a samples collection.
Are you looking for an example to start a script, or an algorithm, or some other inspiration?
The menu selection "Actions → Code sample" will offer you a choice of programs of various sizes.
Actually, they are examples from the HAC project (the HAC compiler is embedded in LEA).
You don't even need to fish those examples in the Internet: they are embedded within LEA as well.
In a few clicks, an example will pop in a LEA window and you can run it immediately.

LEA (Lightweight Editor for Ada) is a free, open-source software. It can be found here.

Feedback is appreciated.
Don't hesitate to sponsor the project with a small donation (from the link above: "Donate" link).

Enjoy!

New menu entry: Code sample (click to enlarge image)

Sample selector (click to enlarge image)

HAC v.0.075: time functions: goodies for scripting tasks

20 October 2020 at 19:44
Today, HAC has a few more functions, from Ada.Calendar. I have added them in order to translate a Windows cmd script to Ada (with HAC_Pack). More precisely, it's "save.cmd", which takes a snapshot of the sources of the HAC system and other key files. This snapshot is a Zip archive and has a time stamp in its name, like "hac-2020-10-20-20-27-36-.zip". Hence the addition of standard functions like Year, Month, etc. The script is very practical for making backups between commits via subversion or git, and for other purposes. Now the script is called "save.adb" and does the same job, but not only on Windows, but also on Linux or other Operating Systems. Since the Zip compression is also programmed in Ada (Zip-Ada), you have in that script example a cool situation of Ada invading your computer 😊.

Here, a screenshot of the added functions running from the LEA editor (hum, also a full Ada software, by the way!):

HAC 0.075 running from LEA. Click to enlarge.

More to come soon with some subprograms stemming from Ada.Directories.

HAC is free and open-source, you can find it here and here.

HAC v.0.076: Ada.Directories-like subprograms

24 October 2020 at 07:12

After Ada.Calendar-like subprograms, here are now Ada.Directories-like subprograms which facilitate shell scripting with HAC in a portable way.

As usual, the easiest way to see the full set of additions is to look into HAC_Pack's specification, hac_pack.ads:

      function Current_Directory return VString;

procedure Set_Directory (Directory : String) renames Ada.Directories.Set_Directory;
procedure Set_Directory (Directory : VString);

procedure Copy_File (Source_Name : String; Target_Name : String);
procedure Copy_File (Source_Name : VString; Target_Name : String);
procedure Copy_File (Source_Name : String; Target_Name : VString);
procedure Copy_File (Source_Name : VString; Target_Name : VString);

procedure Delete_File (Name : String) renames Ada.Directories.Delete_File;
procedure Delete_File (Name : VString);

function Exists (Name : String) return Boolean renames Ada.Directories.Exists;
function Exists (Name : VString) return Boolean;

procedure Rename (Old_Name : String; New_Name : String) renames Ada.Directories.Rename;
procedure Rename (Old_Name : VString; New_Name : String);
procedure Rename (Old_Name : String; New_Name : VString);
procedure Rename (Old_Name : VString; New_Name : VString);
HAC is free and open-source, you can find it here and here.

The HAC scripts invasion

27 October 2020 at 12:42

Perhaps you were already confronted to this problem:

  • You have "housekeeping" shell scripts for cleaning files, building tools, listing results, etc. . 
  • You would like to have the project, that these scripts are serving, running on multiple systems: Linux, MacOS, Windows, ... Especially for highly portable Ada projects, it is almost a must.
  • But in the end, you have lots of duplicate scripts: for each script one version for Linux, one version for Windows.
The solution: use HAC (the HAC Ada Compiler).

One practical example of script simplification can be found in the Zip-Ada project.
In the test directory, there were test_za.cmd and test_za.sh, meant to do the same thing: testing the compression side of the library. But the scripts were out of sync, and it was a pain to make them converge. So it was a perfect opportunity to switch to HAC, which has since its 0.076 version standard subprograms for file management. The unified script is test_za.adb, can be run with the hac test_za.adb command.
Now test_rz.cmd and test_rz.sh (for testing the Zip archive recompression tool, ReZip) are also unified, and so are make_za.cmd and make_za.sh for building everything.

More generally HAC scripts have tremendous advantages:
  • They are plain Ada, so there is no need to learn a new language.
  • If you need it, they can be compiled with an Ada compiler. It can be for different reasons:
    • You need performance (nested loops, for instance). You'll get C-level performance, at least with the GNAT compiler.
    • You need more functionalities that are not present in HAC.
    • You are afraid HAC is not developed or supported further.
Here are a few screenshots:



The screenshots are taken from TeXCAD and LEA - other Ada projects 😏...

Advent of Code 2020 with HAC and LEA

2 December 2020 at 20:27

First use of HAC, via the LEA editor, for the famous Advent of Code contest.

LEA screenshot: a parser for Day 2 Advent of Code's puzzle

In my (of course biased) opinion, LEA is perfect (among other tasks...) for developing quickly Ada solutions to the Advent of Code problems.

After the rush, I tidy up the source code with GNAT's style checks. Hence the presence of a GNAT project file, aoc_2020.gpr .

Then I post my solutions as HAC examples, here and here


HAC built with ObjectAda 10.2

6 December 2020 at 14:31

When you think that your software is highly compiler- and operating-system-independent, it is only a guess until you can verify it.

For HAC (the HAC Ada Compiler), the OS-independence is easy to verify thanks to GNAT, the well-known free Ada compiler.

Regarding the full independence, it is a bit trickier. There are two items (which are subprograms in the HAC_Pack package), that are intrinsically non-portable and not covered by the standard Ada libraries whose authors bear the burden of finding implementations:

  package Non_Standard is
    procedure Sys (Command : String; Result : out Integer);
    function Directory_Separator return Character;
  end Non_Standard;

Two subprograms in 12595 lines of code spanning over 59 source files (revision #306) - it's not a such a big deal.

So a real test was to build HAC with the ObjectAda64 for Windows development environment.

Surprise (but with Ada, it was still a bit expected...): the build went completely seamlessly once I had selected "Ada 2012" in the project settings (HAC's source use a few Ada 2012 features).

Zero error, and the real surprise: zero warning.

The ObjectAda implementation for the above package Non_Standard actually required more work than building the Ada sources (basically, a few mouse clicks). For Directory_Separator, it was easy since we could hardcode '\' given the Windows target. For the procedure Sys, we were able to use the same C function (system) as for the C library that comes with GNAT since the Microsoft run-time imitates some Unix (and then GNU) functionalities. Then, having the linker finding system was the real challenge. Basically if you reference the Win32 library in the project, the linker somehow finds system. Don't ask me how, it's part of the mysteries of the tools that emulate modularity for C (to say things politely).

Enough dirty details, here is a screenshot:

ObjectAda's Integrated Development Environment, right after a complete build of HAC. Click to enlarge.

HAC is free and open-source, sources can be found here and here.


HAC at work with shiny gold bags (Advent of Code, Day 7)

7 December 2020 at 14:13

Day 7 of the Advent of Code, titled "Handy Haversacks" is nice pair of puzzles involving recursion.

The data is a set of rules, beginning with

wavy green bags contain 1 posh black bag, 1 faded green bag, 4 wavy red bags.

dotted chartreuse bags contain 1 light beige bag.

dark white bags contain 2 dotted white bags.

... 

There are hundreds of such rules.

The puzzles involve recursion, which makes them fun.

You can see below HAC at work on the problem, from the LEA editor.

There is also a "full Ada" version. I began with that one, because the current limitations of HAC would have been too time-consuming (in terms of development time) for submitting a solution quickly enough. The limitations are not around recursion, that HAC masters like a big one, but mostly around the enumeration type I/O which is currently non-existent in HAC (v.0.081).

Click to enlarge

Solutions will be soon be posted on the HAC repositories.

First steps in modularity for HAC

5 April 2021 at 18:55

The recent development effort of HAC (the open-source HAC Ada Compiler, links here and here) is concentrated on modularity.

Some new packages were added on the compiler side:
 - HAC_Sys.Builder   : about building a program around a main procedure
 - HAC_Sys.Librarian : about registering units, compiling them if needed, and making them available to other units.
 
In order to do things step by step, we have begun with the simplest form of modularity units: subprograms. We'll do packages later.
Probably some people will be surprised that modularity units in Ada can be something else than packages.

This little-known feature is already useful for the following very simple job: making a "shell" program calling other programs. An example: someone had a series of programs:
  poly_1 (Ada name: poly_1.adb, executable: poly_1.exe)
  poly_2 (Ada name: poly_2.adb, executable: poly_2.exe)
  poly_3 (Ada name: poly_3.adb, executable: poly_3.exe)
  ...

The deployment to users of all those executables was cumbersome.
The solution was to define:

  with Poly_1, Poly_2, Poly_3, ...
  with Ada.Command_Line;

  procedure Poly_N is

    use Ada.Command_Line;
  begin
    case Argument (1) is
      when "1" => Poly_1;
      when "2" => Poly_2;
      when "3" => Poly_3;
      ...
    end case;
  end;

 
Et voilà. Of course it works because Ada doesn't have a hard-coded name for "main".

Back to HAC's development. Here is a nonsensical example, which is a bit tricky on nesting levels.

a.adb:
  with B, C, HAL;

  procedure A is
    v : Integer;
    a_msg : HAL.VString;

    use HAL;

    procedure X is
    begin
      Put("(x>");
      a_msg := +"A";
      B;
      v := v * 2;
      Put("<x)");
    end X;

  begin
    v := 111;
    a_msg := +"a";
    HAL.Put(+"(a" & a_msg & ">");
    for i in 1 .. 2 loop C; end loop;
    X;
    B;
    X;
    HAL.Put(v, 0);
    HAL.Put("<A" & a_msg & ")");
  end A;

 
b.adb:
  with C, HAL;

  procedure B is
    use HAL;
    b_msg : VString := +"b";
    --
    procedure Y is
    begin
      Put("(y>");
      C;
      HAL.Put("<y)");
      b_msg := +"B";
    end;
    procedure Y2 is
    begin
      Y;
    end;
  begin
    Put ("(b" & b_msg & ">");
    C;
    Y2;
    HAL.Put("<B" & b_msg & ")");
  end B;


c.adb:
  with HAL;
  use HAL;

  procedure C is
    c_msg : VString;
    procedure Z is
    begin
     c_msg := +"C";
     HAL.Put("z");
    end Z;
  begin
    c_msg := +"c";
    Put("(c" & c_msg & ">");
    Z;
    Put("<C" & c_msg & ")");
  end C;

 
The output is, with the GNAT compiler:
>gnatmake a -Isrc
gcc -c -Isrc a.adb
gcc -c -Isrc b.adb
gcc -c -Isrc c.adb
gcc -c -I./ -Isrc -I- src/hal.adb
gnatbind -Isrc -x a.ali
gnatlink a.ali
>a
(aa>(cc>z<CC)(cc>z<CC)(x>(bb>(cc>z<CC)(y>(cc>z<CC)<y)<BB)<x)(bb>(cc>z<CC)(y>(cc>z<CC)<y)<BB)(x>(bb>(cc>z<CC)(y>(cc>z<CC)<y)<BB)<x)444<AA)

With HAC:
>hac a.adb
(aa>(cc>z<CC)(cc>z<CC)(x>(bb>(cc>z<CC)(y>(cc>z<CC)<y)<BB)<x)(bb>(cc>z<CC)(y>(cc>z<CC)<y)<BB)(x>(bb>(cc>z<CC)(y>(cc>z<CC)<y)<BB)<x)444<AA)

If we add "with B" to C, we get the "circular unit dependency" issue. Each compiler handles it, as expected.

>hac a.adb
c.adb: 2:7-8: library error: Circular unit dependency ("->" means "depends on"): B -> C -> B

>gnatmake a -Isrc
gcc -c -Isrc a.adb
a.adb:1:08: circular unit dependency
a.adb:1:08: "A (body)" depends on "B (body)"
a.adb:1:08: "B (body)" depends on "C (body)"
a.adb:1:08: "C (body)" depends on "B (body)"
a.adb:1:08: "B (body)" depends on "B (spec)"
gnatmake: "a.adb" compilation error 

 

HAC v.0.095

7 April 2021 at 20:13

A bit of brush-up after our last post about the new modularity features of HAC (the HAC Ada Compiler), we can announce the release of version 0.095.

We have tested since then parameters to units, and the use of functions instead of procedures as units.

All that with the LEA editor, in order to experiment the behaviour of compile-time errors and run-time errors. In contrast to the command-line tool called "hac" where everything is forgotten once the command is run, whatever the outcome, in an integrated editor the compiler needs to close files properly in every case - also on errors, because the editor will open them to show the error locations. Similarily, the builder needs to clear the library information between two builds. The improvements to be made to HAC for a smooth functioning were straightforward. Just a couple of statements to add here and there.

A demo of HAC's current modularity support, in one picture:

Click to enlarge

HAC (HAC Ada Compiler) is a small, quick, open-source Ada compiler,
covering a subset of the Ada language.
HAC is itself fully programmed in Ada.

HAC web site: http://hacadacompiler.sf.net/


Source repositories:
 #1 svn: https://sf.net/p/hacadacompiler/code/HEAD/tree/trunk/
 #2 git: https://github.com/zertovitch/hac

Enjoy!

Cleaning up HAC sources with AdaControl

30 April 2021 at 14:15

AdaControl is a powerful Ada source code verification tool. It's freely available here. As a side note, its name sounds like "flight control" and it is no coincidence 😊.

Our HAC (HAC Ada Compiler) project is a good opportunity to use AdaControl from scratch on a given project and to show you how capable it is.

*** 

A note about installation. The free version of AdaControl works so far with the GNAT Ada toolset called Community Edition 2019 - not later, for reasons explained in a newsgroup post titled "Status of AdaControl".

So, for example on Windows, the installation steps include installing GNAT CE 2019 (even if you use a later version), writing a script, say, go19.cmd, with the line "path c:\gnat\2019\bin;%path%". The AdaControl installer (adactl-1.21r6b-exe_setup.exe) will work correctly if you first set the correct temporary path with "go19", then launch the installer, from the same command line session.

Then, you can write a second script, say, gps_19.cmd, with the lines:

  call go19
  start gps -P hac.gpr

That way you can start your AdaControl session comfortably through GNAT Studio by just double-clicking gps_19.cmd.

***

AdaControl works with rules that you can choose to apply (or not), sometimes with sub-rules. You list the rules you want to apply in a text file. In the HAC project (see hac.gpr) we have called it "test/verif_hac.aru".

Let's start with a first rule which seems promising: "simplifiable_statements". As you know, unnecessary complications are the biggest threat to programs of any size. Small simplifications that are automatically detected are very welcome. Of course it doesn't replace larger, conceptual simplifications - but for that, you need a brain 😉.

------------------------------------------------
--  AdaControl verification rules             --
--  https://www.adalog.fr/en/adacontrol.html  --
------------------------------------------------

-- Set for HAC (HAC Ada Compiler).

--------------------------------------
--  (1) Required ("check" command)  --
--------------------------------------

check simplifiable_statements;


------------------------------------------------------------------
--  (2) Acceptable, but should be looked at ("search" command)  --
------------------------------------------------------------------ 

All but one line of the above rules file are comments starting with "--". But since AdaControl provides 579+ possible rules and sub-rules, the comments will be useful later.

So, we courageously begin with the first rule, on the main procedure of the HAC command-line compiler and VM interpreter, hac.adb (that GNAT turns into hac.exe).

Click to enlarge

After a few seconds, AdaControl completes with 74 diagnostics in 18 files (it scans recursively all units that the initial unit (hac.adb) depends on, and the units those units depend on, and so on).

That, for the "simplifiable_statements" rule only. Ouch! 😨

Click to enlarge

The first item is a loop that is simplifiable into a "for" loop. First, a disclaimer: I did not write that loop👐🚿. Remember that HAC stems from an automatic translation of SmallAda (written in the late 1980's), itself a derivation of CoPascal, which derives from Pascal-S by Pr. Wirth himself around 1975. At the time some languages did not produce a check to skip a "for" loop from a to b when a > b. If it is not skipped, the loop parameter will be incremented to "the infinity", and practically, towards an overflow error in best cases. So probably authors of SmallAda or earlier felt better using a "while" loop instead of a "for" loop in such cases.

In Ada every situation is defined in the Reference Manual (an ISO standard!), and programmers can count on the fact that the loop "for i in a .. b" is skipped if a > b.

Back to AdaControl. Obviously, it has found a case where you can simplify a "while" loop into a "for" loop.

This finding looks easy, a posteriori, for the human reader, because it is a tiny loop. But it is not trivial. Logically, AdaControl was able to figure out that the "OC'Last" part of the exit condition is invariant during the loop's activity (if not, you must stick with the "while" loop). Clever!

Plus, it has seen that the exit condition was equivalent to an upper bound check in a "for" loop. Very clever!

Plus, it has detected simple increment (LC0 := LC0 + 1;) in the main execution path of the loop - and nothing else, nowhere else, like, for instance, a modification of LC0 in the "if" statement. That's very, very clever!

So let's do the recommended simplification. The loop in

  procedure Patch_Addresses (
    OC            : in out Object_Code_Table;
    dummy_address :        Operand_2_Type
  )
  is
    LC0 : Integer := OC'First;
    use Defs;
    use type HAC_Integer;
  begin
    while LC0 < OC'Last loop
      if OC (LC0).F in Jump_Opcode and then OC (LC0).Y = dummy_address then
        OC (LC0).Y := HAC_Integer (OC'Last);
      end if;
      LC0 := LC0 + 1;
    end loop;
  end Patch_Addresses;

becomes:

    for LC0 in OC'First .. OC'Last - 1 loop
      if OC (LC0).F in Jump_Opcode and then OC (LC0).Y = dummy_address then
        OC (LC0).Y := HAC_Integer (OC'Last);
      end if;
    end loop;

As a bonus, the variable declaration, "LC0 : Integer := OC'First;", can be deleted (it is implicitely defined in Ada for the "for" loop).

But the simplification is not enough for AdaControl! It detects, on a second round, that the loop parameter (LC0) is always used as an index to an array. It says that the "for ... of" Ada 2012 statement can be used instead. This means that instead of having a loop incrementing an index to the OC array, the loop could as well scan OC's elements directly. The job is the same, but the Ada code is shorter and cleaner for human readers. In this case the gain is small, but for an array like "foo(k).bar.x3.grogu(l)" it makes a big difference in readability.

Let's apply again the recommanded change.

    for Op of OC (OC'First .. OC'Last - 1) loop
      if Op.F in Jump_Opcode and then Op.Y = dummy_address then
        Op.Y := HAC_Integer (OC'Last);
      end if;
    end loop;

Beautiful!

Here are both changes together, in colour, via GitHub: 

More AdaControl adventures in a further post...


The HAC scripts invasion (follow-up)

26 July 2021 at 18:18

As a follow-up of another post about converting bash (Linux) or cmd (Windows) scripts to HAC scripts, here is a fresh example.

I needed to improve an existing cmd script for benchmarking compression software, with the possibility of switching various subsets separately: that is re-run this subset of methods, or that other subset, or the full tests (long!), etc.

Of course, if you use a real language instead of command-line interpreter ones, there is an obvious solution: you can define a set and you can programmatically flip the membership switches.

In Ada, it looks like

  type Category is (
    Reduce_Shrink,
    Deflate,
    Deflate_External,
    BZip2_External,
    PPMd_External,
    LZMA_7z,
    LZMA_3,
    TAR,
    Preselection
  );

  cat_set : array (Category) of Boolean;
The good news is that you can run an Ada program exactly like a script by using HAC (the HAC Ada Compiler). That is, it runs immediately (with HAC), and HAC doesn't drop .ali, .o, .bexch, .tmp, .exe files which are too much waste for the sake of running a small script-like job.

Below are screenshots of the quick development of bench.adb using the LEA editor, where you can punch F4 to check eventual errors. If there is one, you get instantly to the offending line / column point.

This script is part of the Zip-Ada project and is very helpful for developing and testing new compression methods.

 

Click to enlarge


Click to enlarge


HAC - range checks

4 September 2021 at 18:36

Up to a few hours ago, range checks in HAC (the HAC Ada Compiler) were limited to array indices.

However, a simple and cool Ada feature is the possibility of subtyping, as in

subtype Level is Integer range 1 .. 30;
It allows to describe precisely values in your program that have to stay within a certain range and would not be meaningful outside of that range. You can also use the subtype's name in many contexts like

switch : array (Level) of Boolean;

or 

for x in Level loop ...

Furthermore it opens the possibility of checking that values are actually within the range.

Typically you have at some places a statement like:

lev := lev - 1;

for a variable defined as

lev : Level;
If your program, by mistake, decrements the variable lev to 0, a standard Ada run-time range check will detect it and raise an exception. This is a very powerful help in finding bugs in complex programs, typically data compression software.

Up to a few hours ago, HAC did not have that kind of range check. Now it is fixed.

Since types in the HAC Ada subset are fully known at compile time, the addition was actually easy:

1) Add appropriate Virtual Machine instructions:


2) Implement them in the VM interpreter:


3) On the parser side, add the run-time check for (sub)type conversions (better to do an easy case as a warm-up):


4) Add the run-time check for assignment statements (:=):

The last point was a bit less trivial. Some details had to be changed in the compiler's way of memorizing and propagating information about types and their range constraints.

For instance, for a composite type like

type Rec is record

  L : Level;

  Points : Natural;

end record;

and an array

x : array (1 .. N) of Rec;

the compiler needs to track correctly that

x(i).L

is of subtype Level, with the range 1 .. 30, and not only of the broader type Integer, when it considers checking the range of the value after := in the following statement:

x(i).L := new_value;

Conversely, in an expression like  z + y * x(i).L, the subtype has to be ignored, it is an Integer expression and nothing more (Integer in that case is called the base type). A solution was to define, in the compiler, a type extension for holding subtype informations:

In places where the subtype constraint needs to be ignored, it is easy to do:


 HAC is an open-source software and can be found here and here.


 

 

❌
❌