❌ About FreshRSS

Reading view

There are new articles available, click to refresh the page.

LEA, a tiny but smart editor

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!

LEA: first steps as a "smart editor"

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

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

HAC version 0.26

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)



New colour theme with LEA: Solarized Light

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

Today, a topic that is guaranteed frivolous: a new colour theme for LEA (a Lightweight Editor for Ada).

Actually, it is a serious subject if you spend a large share of your time at programming (for work, for fun, or both...). For the theoretically brighter season (spring and summer), some prefer to give up the trendy Dark Side mode (which not so long ago was considered as horribly old-fashioned):

...and use a dark-font-bright-background scheme.

However, the contrast is a bit violent. A convincing and carefully designed low-contrast theme is called Solarized Light (which can be turned, with an astute swap within the same 8-colours palette for base colours, into a dark-backgrounded Solarized Dark). The theme is described here.

The Solarized Light theme is incorporated since today in LEA.

Enjoy!


LEA, a Lightweight Editor for Ada, aims to provide an easy, script-world-like, "look & feel" for developing Ada projects of any size and level, while enabling access to full-scale development tools like GNAT.

  • Quick start and reactivity
  • Uses the Scintilla editor widget (like Notepad++)
  • Multi-document
  • Multiple undo's & redo's
  • Multi-line edit, rectangular selections
  • Color themes, easy to switch
  • Duplication of lines and selections
  • Syntax highlighting
  • Parenthesis matching
  • Bookmarks
  • Free, Open-Source
  • Programmed in Ada
  • Includes HAC, the HAC Ada Compiler
  • Includes numerous examples of Ada programs, ready to be run
  • Single executable, runs without installation




Advent of Code 2022 in pictures

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.

LEA 0.85 - the Tabs are here!

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!

AZip, GWindows, the Windows API and another surprise!

 

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


Last post was about an improvement of the GWindows.Common_Controls.Ex_List_View widget which is distributed with the GWindows framework. Without changing the specification, it was possible to avoid Windows API calls during the performance-sensitive comparison function, leading to impressive speedup factors: from 3.3x with 4096 items to 16.5x with 32768 items.

But actually, it was only the prelude!

If you can live with data duplication, which is not nice and not always practical, it is possible to do much better. Concretely, you store the same strings in the cells of the List_View widget and in the payload data associated with each row. Then, the sorting for all columns (text, dates, numbers, ...) is using the payload data. An addition to the Ex_List_View package later (this time, enriching a bit the specification), the comparison function may exclusively use the payload of both compared rows, directly, thus avoiding any API call during the lifespan of the comparison function.

Without going into too many details, here are the performance gains in charts:



This represents a factor of ~100x to ~300x on top of the previous improvement, depending on the test machine.

Here are links to the sources:

AZip:

Project site & Subversion repository:
https://sf.net/projects/azip/
GitHub clone with Git repository:
https://github.com/zertovitch/azip

GWindows:

Project site & Subversion repository:
https://sf.net/projects/gnavi/
GitHub clone with Git repository:
https://github.com/zertovitch/gwindows

LEA 0.84 - the Build & Run button!

 

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


The 0.84 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

This version is essentialy a rework of LEA's ergonomy - we had recently the chance of having a group of young first-time programmers and first-time LEA users to test it!

The main addition is the green Build & Run button, which matches a (less intuitive) menu entry and the F9 key.

Click screenshot to enlarge

Another addition is a dedicated box for the "User_Abort" internal Virtual Machine exception, so that the LEA user doesn't take the exception message for an actual error (which would be the case for any other exception).

Before:


Now:

Enjoy!


AZip, GWindows, the Windows API and a good surprise!

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


Programming user interfaces is often a bizarre experience where you program only one side and the other side is done by other people (for instance the programmers of the Windows components) that you cannot contact for asking questions. Even if it was possible (like working at Microsoft in Redmond), those people are probably already retired for long. So the only possibility is to experiment with the black box and skim the Internet in the hope someone else has encountered the same issues and found a solution. Fortunately, as time goes by, there are more and more solutions appearing.

Here is a typical example.

The AZip Zip archive manager is designed to operate with different user interface systems, like native Windows (it is done through the GWindows framework), or the multi-platform Gtk system (there is a draft version of that).

AZip uses the Ex_List_View_Control_Type widget (package GWindows.Common_Controls.Ex_List_View) which is an extension of List_View_Control_Type (package GWindows.Common_Controls), developed by the company KonAd GmbH, with cool features like individually coloured cells, and sorting.

A hiccup was the performance of the sorting. On Zip archives with many entries (say, 10,000 or more), the sorting became frustratingly slow. On other software sorting is MUCH faster, so there was an issue to solve.

Sorting a column in AZip - click to enlarge screenshot

Fortunately GWindows and its extensions are completely open-source, so you can inspect everything. By the way, it is also the case for GNAT (the open-source Ada compiler): you can inspect the entire run-time library. All in all, AZip is a rare software whose entire source (the program itself, the user interface, the data compression library, and the run-time library) can be comfortably browsed per mouse clicks from GNAT Studio. This amounts to more than 112,000 and 572 units (mostly packages), completely in Ada. You see here a slide from a FOSDEM presentation about AZip.

Break-down of the Ada source code of AZip

Wait, the "entire source"? Not really, since Windows is neither open-source, nor in Ada. That's where the fun begins. Back to our sorting issue (sorting is too slow). GWindows (the nice object-oriented framework) provides a Sort method, which sends behind the scenes a Windows message, LVM_SORTITEMS, which calls back a comparison function provided by GWindows, Compare_Internal, which in turn will calls a On_Compare method - built-in, or derived by the programmer, for instance for sorting the columns of a Zip archive where some columns are numerical and other ones are texts. This mechnism is convoluted but it is how it works if you want to use the List_View widget provided by Windows. It is a case where you really have to dance with the Windows API (of course, you would have the same situation for other user interface systems: Mac, Gtk, ...). After a certain amount of Internet searches, through forums, blogs, etc., it appears that there is an alternative way with a Windows message called LVM_SORTITEMSEX. Note the "EX" at the end: it is for "extended". You have probably noticed rather the last three letters, but anyway... This alternative way provides directly to the comparison call-back the indices of the rows that Windows want to compare. With the initial approach (using LVM_SORTITEMS, without EX) two messages, LVM_FINDITEM, have to be sent in order to get those row indices. So you can save those two calls and hope for a small speedup.

Now comes the surprise. The gain in terms of time is astounding!

On a certain computer, with 4096 items to be sorted, both calls (that can be skipped with the new approach) consume 70% of the entire sorting time - including the comparison itself, the sorting effort on Windows' side, the object-oriented dispatching, etc. . With 8192 items, it is 81%. With 16'384 items, it is 88%. With 32'768 items, 94% of the time is consumed by the extra calls. Seen differently, the old approach takes 14 seconds for sorting, and the new approach takes 0.85 second for the same job!

Here are performance charts on two differents computers.


 

Interestingly, the performance stays linear with the new approach.

Here are links to the sources:

AZip:

Project site & subversion repository:
https://sf.net/projects/azip/
GitHub clone with git repository:
https://github.com/zertovitch/azip

GWindows:

Project site & subversion repository:
https://sf.net/projects/gnavi/
GitHub clone with git repository:
https://github.com/zertovitch/gwindows

HAC as an embedded compiler

Here is an example to illustrate how the HAC compiler is embedded in an Ada application.

The user of the application has typically only an executable (built with a "full Ada" system like GNAT) and an "Ada-pplet" (can be a few lines) that is run from that executable.

Normally, applications have different languages for building the application and for the embedded script system: Excel is written in C/C++ but embeds VBA (Visual Basic for Applications). 3D Studio Max has MaxScript. GIMP uses Script-fu, a variant of Scheme. Others use Lua or Python. Of course nobody would think about shipping a software with a scripting language where you have to fight with pointers and conditional compilation (C/C++).

But... how about an application that is written in a compiled language, but is using the same language for its scripting purposes? Since a few days, it is possible with HAC (the HAC Ada Compiler).

If you build the demo exchange_hac_side.adb (e.g. from GNAT Studio, or via the command "gprbuild hac"), you get an executable, exchange_hac_side on Linux or exchange_hac_side.exe on Windows. If you run it, you get:

Exchange_Native_Side is started.

Native: building a HAC program: exchange_hac_side.adb

   Exchange_HAC_Side is started.

   HAC: I call a parameterless callback.
      Native: Parameterless_Callback is speaking!
   HAC: done calling.

   HAC: I send some stuff through callbacks.
      Native: HAC is saying: [I'm HAC and I say hello!]
      Native: HAC has sent me the numbers: 123 456 789
      Native: HAC has sent me the numbers: 1.23000000000000E+02 4.56000000000000E+02 7.89000000000000E+02

   HAC: message before call: [I'm HAC]
      Native: HAC is saying: [I'm HAC]
   HAC: message after call: [No, I'm Native, you loser!]
   HAC: integer before call: [12]
      Native: HAC has sent me the number: 12; I will send it back squared.
   HAC: integer after call: [144]
   HAC: float before call: [11.0]
      Native: HAC has sent me the number: 1.10000000000000E+01; I will send it back squared.
   HAC: float after call: [121.0]

   HAC: integer before call: [13]
        message before call: [I'm a HAC record field]
           enum before call: [BAT]
      Native: Enum = BAT
      Native: Matrix m:
           1.10 1.20
           1.30 1.40
      Native: Matrix n:
          -2.10 2.20
          -2.30 2.40
      Native: Matrix o = m * n:
          -5.07 5.30
          -5.95 6.22
      Native: Enum = CAT
   HAC: integer after call: [169]
        message after call: [I'm a Native message now (niarg niarg niarg)!]
           enum after call: [CAT]
        matrix product:
          -5.07 5.30
          -5.95 6.22

Messages as "Native" are from the main application.

Messages as "HAC" are from the small HAC program that is run from the application.

A little disadvantage of having the same language on both sides... is that it is the same language! It could be confusing at first glance. Plus, it is not in the tradition. That may be disturbing for a number of people.

A big advantage of having the same language is that you can share some pieces of code. For instance the following package is used for building the demo application, and compiled by HAC when the application is running!

------------------------------------------- -- HAC <-> Native data exchange demo -- ------------------------------------------- -- Package used by both HAC and -- -- Native sides -- ------------------------------------------- package Exchange_Common is type Animal is (ant, bat, cat, dog); subtype Beast is Animal; subtype Insect is Animal range ant .. ant; subtype Mammal is Animal range bat .. dog; end Exchange_Common;


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

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

Ann: HAC v.0.2

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

Web site: http://hacadacompiler.sf.net/
From there, links to sources, and an executable for Windows.

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

* Main improvements since v.0.1:

  - a program run by HAC can exchange data with the
      programm running HAC, through dynamically registered call-backs
      - see package HAC_Sys.Interfacing and demos:
        src/apps/exchange_native_side.adb
        src/apps/exchange_hac_side.adb

  - the compiler checks that all choices in a CASE statement
      are covered

  - the compiler performs more compile-time range checks and
      optimizes away useless run-time checks when it's safe to do so.

Enjoy!

Gautier

Zip-Ada: Zip64 extensions

On the very top of the Zip-Ada to-do list, for a while, was the support for archive containing large (more than 4 GiB data) or numerous (more than 65535) files – the so-called "Zip64" extensions.

It's now gone from the to-do list and done in the project.

The Zip archive format was defined with 32-bit file sizes and 16-bit number of files. At the time (1989) it was a perfectly sound choice, especially given that the software, PKZIP, was written for 16-bit PC's running on MS-DOS. At some point, these limits began to be problematic for archiving or backup tasks. Although the original Zip format is still the most widely used (including many file types without the .zip name extension, such as .docx, .xlsx, .pptx, .jar), the limits become, slowly, more and more frequently an issue over time. Consequently, PKWARE (the firm behind PKZIP) has introduced, around year 2000, a set of format extensions to overcome those limitations. Given the omnipresence of Zip archives and the difficulty to innovate in software, PKWARE did not opt for a fresh, new, simple, but incompatible format. Instead, they designed a set of extensions, allowed by the flexibility of the Zip format. This seems clumsy and over-engineered, but the design is actually quite clever: a program can create a Zip archive, without size limitations, and without checking in advance whether the Zip64 extensions are needed or not. When the archive is finished, either the Zip64 extensions were not needed and the archive is conform to the year 1989 original format, or they were – then, an unpacker for that archive file has to know about Zip64. Thus, the Zip64 design allowed for a progressive adoption of the new format, since the actual need went progressively from very rare to a bit less rare.

How to implement Zip64 in a new software? There are a few options, which you can combine:

1.    Follow the documentation, appnote.txt
2.    Take inspiration from some open-source software like Info-Zip or 7-Zip
3.    Reverse-engineer data (hex dumps)

All three are very time-consuming (especially, the documentation, aimed at maintaining the openness of the Zip format, is "legally" correct but lacks some indications and practical details).
Even widespread, commercial software (with paying customers and many paid programmers behind it) does not support Zip64 (e.g. Microsoft Word for .docx), or took almost two decades to do so (Microsoft Windows for instance). It reveals something about the time and costs related to the implementation…

Fortunately, a very helpful person called Yaakov not only did an implementation from scratch, but also was kind enough to share his experience in an explanatory and colorful way, with a simple example as an illustration. The article is available here: ZIP64 - Go Big Or Go Home.
I show here (with permission) the key diagram of his article:

Click to enlarge


The Zip64 extensions are in dark background and white letters.
The part with the archived (eventually compressed) files is in pink and brown. In that example, there is only one file in the archive.

As you see, an implementation of Zip64 may be quite tricky, but it is feasible, if we have "the big picture" before our eyes.

Alas, things are a little bit more complicated than it appears when reading the article too fast...

We want, with Zip-Ada, to decode and unpack correctly archives not only made by Zip-Ada, but also by Info-Zip's Zip, 7-Zip, WinZip, and so on (the converse is also true: we want the other guys to be able to unpack our Zip-Ada archives).
For that reason, we need to anticipate all possible ways the data headers have been written, not only our choices. In that respect, we have had some headache with the per-entry "size" Zip64 extensions (the parts in brown and dark green in the chart).
The documentation (4.5.3) describes, for that, a record with a variable number of values:

8 bytes    [A] Original uncompressed file size
8 bytes    [B] Size of compressed data
8 bytes    [C] Offset of local header record
4 bytes    [D] Number of the disk on which this file starts


and defines in plain English some rules about the order and in which circumstances the values are present or not.

The documentation requires both [A] and [B] for the Local Header (part in brown in the chart). Let us figure out the explanation to that rule. For a program that creates the archive, it would not be practical to put only the uncompressed size before compressing the data, because the program would have to shift all the compressed content by 8 bytes in case the compressed size (which is known only after the compression) happened to also exceed 2**32 - 2. Hence, exactly and always two values if the uncompressed size (which is usually known in advance) exceeds the limit.

When it is time for the archiving program to write the Central Directory (the part that concludes the archive, in green, light and dark, in the chart), all information about sizes is known. An archiving program may choose to put only the necessary values in 64 bits. However, traditionally, you would expect that the variable record's contents are self-describing and would contain [A], or: [A] and [B], or: [A] and [B] and [C], or: [A] and [B] and [C] and [D], given the record's size.
With Zip64, it is not the case: each field is optional. This micro-optimization is ridiculous given the fact that the size of archive is breaking the 4 GiB limit. To make things worse, the documentation is not clear about that full optionality.
An archiver (7-Zip does so for instance) may put just [A], the uncompressed size of one first large file that is well compressible, or just [C], the offset of one small file following a file with a large size in the archive. Or it could put [A], [B], [C] in both cases (as we do for Zip-Ada's archive creation). So, we've adapted the archive reader to take all possible cases into consideration.

Thanks to Nicolas Brunot for pointing that (hopefully) ultimate difficulty with Zip64.

Latest sources:
 
Link 1: https://github.com/zertovitch/zip-ada/ 
  With git: git clone https://github.com/zertovitch/zip-ada.git
  As Zip-ball: green button "Code", choice: "Download ZIP"
 
Link 2: https://sourceforge.net/p/unzip-ada/code/HEAD/tree/
  With subversion: svn checkout https://svn.code.sf.net/p/unzip-ada/code/ za
  As Zip-ball: button: "Download Snapshot"

Ann: HAC v.0.1

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

Web site: http://hacadacompiler.sf.net/
From there, links to sources and an executable for Windows.

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

* Main improvements since v.0.0996:

  - packages and subpackages are now supported
  - modularity: packages and subprograms can be standalone
      library units, stored in individual files with
      GNAT's naming convention, and accessed from other units
      via the WITH clause
  - validity checks were added for a better detection of
      uninitialized variables.

Package examples and modularity tests have been added.
Particularly, a new PDF producer package with a few demos
is located in the ./exm/pdf directory.


hakoch

Enjoy!

Gautier
__
PS: for Windows, there is an integrated editor that embeds HAC:
LEA: http://l-e-a.sf.net
PPS: HAC will be shown at the Ada-Europe conference (presentation + tutorial)
http://www.ada-europe.org/conference2022/

HAC, packages and PDF

In order to illustrate a feature, what is better than a visual example?
In our case, the feature is the modularity, recently implemented in HAC (the HAC Ada Compiler).
So, a nice exercise was to try with a "real" visual package, PDF_Out (project Ada PDF Writer, link here).
Of course, HAC is still a small compiler project which doesn't cover all the sexy features of Ada - even those of the first version of Ada, Ada 1983, which already had extensive generics, aggregates (on-the-fly composite objects as expressions), default values in records or in subprogram parameters, exceptions or user-defined operators.
The easy way of down-stripping PDF_Out for HAC was to open the source code in the LEA editor (link here), and do frenetically and iteratively:

loop
  punch the compile key (F4)
  exit when the package compiles!
  make the code HAC-compatible (sometimes, it hurts!)
end loop


A few things that needed a downgrade for the HAC 0.1 language subset were:

  • Object-oriented type extension (tagged type for PDF stream). In the simplified version, PDF files are the only supported PDF streams.
  • Object.method notation
  • Default values in records, replaced by explicit initialization.
  • Inclusion of raster images (NB: the removal of that feature was unnecessary).
  • User-defined exceptions (PDF_stream_not_created, ...).
  • User-defined operators ("+", ...).
  • Indefinite page table and offset table.

The result can be seen here:


Click to enlarge

 

You can experiment it yourself by checking out or cloning the latest version of HAC:

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

The new PDF examples are located in the subdirectory ./exm/pdf




HAC: packages - now, the whole package!

Drum rolls: HAC (the HAC Ada Compiler) now supports packages.

OK, there is still a slight limitation: library-level packages cannot contain variables and constants, nor an initialization part (begin .. end). Removing this limitation would be complex to implement and is postponed so far for HAC.

A new modularity demo can be found in the "exm" subdirectory.
The main file is "pkg_demo.adb".
It is a simple test for simulating the build of an application with numerous dependencies.
The dependencies take form of packages depending on two or more other packages, down to
a certain recursion depth.
Each package contains a procedure with the same name: "Do_it", which just writes a string related to the package name. For instance,  X_Pkg_Demo_B3.Do_it will write "[B3]", then call X_Pkg_Demo_B31.Do_it, X_Pkg_Demo_B32.Do_it, and X_Pkg_Demo_B33.Do_it.
The package dependencies can be seen the following chart:



NB: the depth and number of children can be set as you wish.

How to run the demo?
First, we generate the package tree.
For that, assume you have the "hac[.exe]" executable ready in the main directory.
Note: in what follows, replace '/' by '\' if you are on Windows.
In the "exm" directory, run from the command line: "../hac pkg_demo_gen.adb".
Alternatively, from LEA, load "pkg_demo_gen.adb" and run it (F9).
The packages ("x_*" files) will be generated.

Now, from the command line, run "../hac pkg_demo.adb" (or from LEA, load it and punch F9).

The result is:

     Specs:  [S][S1][S11][S12][S13][S2][S21][S22][S23][S3][S31][S32][S33]    
     Mixed:  [M][M1][M11][M12][M13][M2][M21][M22][M23][M3][M31][M32][M33]    
     Bodies: [B][B1][B11][B12][B13][B2][B21][B22][B23][B3][B31][B32][B33]    

    
If you are curious about how HAC does the whole build the 40 units (main procedure and 39 packages),
add the "-v2" option to the build command: "../hac -v2 pkg_demo.adb".
On an i7 machine (with HAC itself built in "Fast" mode), the time elapsed
for the entire build is around 0.08 seconds. Only one core is used.
Since GNAT builds also the library "on the fly" by automatically
finding dependencies, you can see it in action by doing:
"gnatmake -I../src -j0 pkg_demo"
Interestingly, GNAT compiles the packages for each depth successively.
Then you can see the result by doing:
"./pkg_demo"
Of course, it's identical to HAC's. 

---

HAC (the 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.

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!

Gautier


HAC: packages (work in progress)

Since a few commits, HAC parses packages! But so far there are limitations : it's "work in progress".

Nevertheless you can already play a bit with the things already programmed in HAC.

File prc.ads:

with Pkg_1, Pkg_2;

procedure Prc is
  x : Pkg_1.A;
 
  use Pkg_1, Pkg_2;

  xg : G;
  y : B;
  z : Pkg_1.E;

begin
  x := 0;
  y := 0;
  z := 2;
end;

File pkg_1.ads:

package Pkg_1 is

  subtype A is Integer;
  subtype B is Pkg_1.A;
  subtype C is B range 0 .. 1000;
 
  package Sub_Pkg is    
    subtype D is C range 1 .. 100;    
  end Sub_Pkg;  
 
  subtype E is Sub_Pkg.D range 2 .. 10;
 
end Pkg_1;

File pkg_2.ads:

with Pkg_1;

package Pkg_2 is 

  subtype F is Pkg_1.E;
  use Pkg_1.Sub_Pkg;
  subtype G is D;
 
end Pkg_2;

Now, from the command-line (assume you are in the main hac directory), do:

hac -v2 prc.adb

*******[ HAC ]*******   Compiler version: 0.0997 dated 19-Mar-2022.
*******[ HAC ]*******   Caution: HAC is not a complete Ada compiler. Type "hac" for license.
. . . .[ HAC ]. . . .   Compiling main: prc.adb
. . . .[ HAC ]. . . .   .Compiling: pkg_1.ads (specification)
. . . .[ HAC ]. . . .   .Compilation of pkg_1.ads completed
. . . .[ HAC ]. . . .   .Compiling: pkg_2.ads (specification)
. . . .[ HAC ]. . . .   .Compilation of pkg_2.ads completed
. . . .[ HAC ]. . . .   Compilation of prc.adb (main) completed
. . . .[ HAC ]. . . .   --  Compilation of eventual with'ed unit's bodies  --
. . . .[ HAC ]. . . .   Build finished in 0.001255600 seconds.
. . . .[ HAC ]. . . .   Object code size: 12 of 100000 Virtual Machine instructions.
. . . .[ HAC ]. . . .   Starting p-code VM interpreter...
-------[ HAC ]-------   VM interpreter done after 0.045569300 seconds.
Execution of prc.adb completed.
Maximum stack usage: 10 of 4000000 units, around 0%.

If you want a more complex example of how HAC can create the library "on the fly" by compiling recursively everything needed to build the main procedure, it is already in the examples (subdirectory exm):

*******[ HAC ]*******   Compiler version: 0.0997 dated 19-Mar-2022.
*******[ HAC ]*******   Caution: HAC is not a complete Ada compiler. Type "hac" for license.
. . . .[ HAC ]. . . .   Compiling main: unit_a.adb
. . . .[ HAC ]. . . .   .Compiling: unit_b.ads (specification)
. . . .[ HAC ]. . . .   .Compilation of unit_b.ads completed
. . . .[ HAC ]. . . .   .Compiling: unit_c.adb (body)
. . . .[ HAC ]. . . .   .Compilation of unit_c.adb completed
. . . .[ HAC ]. . . .   Compilation of unit_a.adb (main) completed
. . . .[ HAC ]. . . .   --  Compilation of eventual with'ed unit's bodies  --
. . . .[ HAC ]. . . .   .Compiling: unit_b.adb (body)
. . . .[ HAC ]. . . .   ..Compiling: unit_e.adb (body)
. . . .[ HAC ]. . . .   ..Compilation of unit_e.adb completed
. . . .[ HAC ]. . . .   ..Compiling: unit_f.ads (specification)
. . . .[ HAC ]. . . .   ...Compiling: unit_g.ads (specification)
. . . .[ HAC ]. . . .   ...Compilation of unit_g.ads completed
. . . .[ HAC ]. . . .   ..Compilation of unit_f.ads completed
. . . .[ HAC ]. . . .   .Compilation of unit_b.adb completed
. . . .[ HAC ]. . . .   --  Compiled bodies: 1
. . . .[ HAC ]. . . .   .Compiling: unit_f.adb (body)
. . . .[ HAC ]. . . .   .Compilation of unit_f.adb completed
. . . .[ HAC ]. . . .   .Compiling: unit_g.adb (body)
. . . .[ HAC ]. . . .   .Compilation of unit_g.adb completed
. . . .[ HAC ]. . . .   --  Compiled bodies: 2
. . . .[ HAC ]. . . .   Build finished in 0.006483700 seconds.
. . . .[ HAC ]. . . .   Object code size: 350 of 100000 Virtual Machine instructions.
. . . .[ HAC ]. . . .   Starting p-code VM interpreter...
Unit_A: demo of modularity features for subprograms.

------

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.

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!


Gautier
❌