Leonardus
ARCHITECTURE

The project Leonardus coding and architectural documentation is split into
a part CODING covering notes on more general aspects of
programming that could also be applied in other C++ projects.

And these very specific notes.

Structural Guide

LeoScript will not have a garbage collection, but a controlled tree of all
existing semantic objects.

Compatibilty Goals

IDEA: Details

PostScript

Forth

Semantic Object Types

This is a table of the SOs with details.

All objects are executable or non-executable aka literal.
This executable status becomes manifest with different OTCodes for
name objects and array objects.

For simple objects a duplicate of an object duplicates the value of the object.

For composite objects a duplicate of an object shares its value with the
original object.

There's an enum class OTCode in the source to list all the codes.

The column type shows the result of the operator type which is used to
identify objects in LeoScript code.

The column class shows the C++ class name of the SOs.

The Table

objects class exe status OTCode type description
simple SOL L nulltype The null object is used as placeholder in arrays.
SOB B booleantype A boolean value.
SOI I integertype A 128-bit integer.
SOM M marktype A mark object.
SON literal N nametype for non-executable name objects.
executeable Nx nametype for executable name objects.
SOO O operatortype A regular registered code code operator.
SOo o operatortype An unregistered core code operator.
SOR R realtype A 128-bit IEEE real number.
SOQ Q rationaltype A rational number with arbitrary precision.
composite SOS S stringtype A string.
SOD D dicttype A dictionary is a list of key-value pairs of SOs.
SOA literal A arraytype For a non-executable array of any SOs in any order.
executable Ax arraytype For an executable array aka procedure.
SOK K stacktype A stack of SOs.

Abbreviations

The system also makes use of abbreviations (pseudo OTCodes) to determine
groups of OTCodes.

abbreviation description
Z I or R or Q
X any SO

The Test Approach

See TESTS

Tools Directory

A development tool overview will be generated by the build process
as TOOLS.md.

Build Types

There are three so-called build types. They denote build and, in particular,
compilation settings for different environments.

<tt>DEVELOP</tt>

Compilation without time-consuming optimizations.
Used to develop and debug the code.

<tt>PRODUCTION</tt>

Compilation with good optimization. All debug-code is removed.
All asserts are removed.
Used for the GitLab pipeline.

<tt>PROFILE</tt>

Compilation without any optimization. No inlining of functions.
Compilation with instrumented code to generate profiling data.
Used to analyze function, line and branch test coverage.
make lcov only succeeds with this build type.

Notes

The make utility can be directed by: BUILD_TYPE=PRODUCTION make leon or
as in the GitLab pipeline configuration by: make BUILD_TYPE=PRODUCTION leon
or by: export BUILD_TYPE=PRODUCTION.

If you change the build type a make clean is recommended.

The command line tools lc, lb, leon, and parser print the build type
with which they were generated as the first line in their help text.

Program Exit Codes

User Programs

These are: lb, lc, parser, leon.

EC_SUCCESS = 0 ... Success.
EC_OPERATOR = 1 ... leon pass: operator error.
EC_INTERPRETER = 2 ... leon pass: parser, semantics or internal error;
parser pass: parsing error;
Scripts: environment and setup issues.
EC_CMDLINE = 3 ... Command line issues
EC_PANIC = 4 ... leon pass: panic exit due to memory restrictions.

Test Programs

These are: e2e.sh, ctest.sh.

EC_SUCCESS = 0 ... Success.
EC_TESTFAIL = 5 ... Errors from test harnisch or found by test harnish.

Scripts in Tools/ Directory

EC_SUCCESS = 0 ... Success
EC_ERROR = 1 ... General error

Release Strategy

Version Numbering Scheme

We use a two-part Version Number consisting of a major and minor
version, separated by a period. We started with "0.9".
Release Numbers are assigned to these version numbers by appending
a sequential number starting from 1. Therefore, the first release number
was 0.9.1. When the version number is increased, the last number is reset
to 1. Consequently, the first release of version 1.0 is 1.0.1.
Release numbers are stored in git as Release Tags prefixed with 'v'.
Thus, the first release tag is "v0.9.1". In git, the first commit intended
for a release is tagged with this release tag.
The last commit in the release cycle receives a Release Completion Tag
in git. This release completion tag in git has the form of the release tag
with the appended text "--release". Therefore, the first release
completion tag is named "v0.9.1--release".
For each release completion tag, a GitLab Release Object, i.e., a
release in the sense of GitLab, is created.

CHANGELOG

When a new release is created, a new section is opened in the
CHANGELOG.md for this release. The Release Date of the previous
release is also assigned then. This is the date the respective Git
release object was created.
The section of the previous release in the CHANGELOG is manually revised
when a new release is created. The revised content of the CHANGELOG section
for a release serves as the Release Notes.

For each commit, the corresponding commit message should be appended to
the CHANGELOG. This can be done automatically by installing a git hook
from TOOLS.

What is a Release?

A release consists of:

  • a release number
  • a release tag in Git
  • a release completion tag in Git
  • all associated commits
  • a section in the CHANGELOG
  • the revised release notes
  • a GitLab release object

Release Change Check List

  • build an up-to-date set of custom docker images
  • check for FIXME's in the task tags
  • check markdownlint output
  • check SAST artifact-report
  • assess the lcov HTML output
  • update CHANGELOG with release date
  • revise CHANGELOG section to create the release notes
  • last commit of remaining changes
  • git push
  • with current vx.y.z add vx.y.z–release tag to this last commit
  • git push origin tag vx.y.z--release
  • check pipelines in GitLab
  • create a GitLab release object
  • open new section in CHANGELOG with date "open"
  • first commit of new release cycle
  • git push
  • add vx.y.z+1 tag
  • git push origin tag vx.y.z+1
  • make clean
  • review make release-info output
  • make e2e
  • review ./leon -h release-info output

makefile and GitLab Integration

The available makefile targets can be displayed by using the the
make command without options.

The makefile will be triggered from the GitLab pipelines. All process
details are implemented in the makefile and its helping scripts.
Intenionally there are no details coded in the pipeline YAML configuration.

Integration Strategy

Both the makefile and the CI/CD pipeline are structured into the same five stages:

  • build,
  • test,
  • predeploy,
  • deploy, and
  • l2test

The makefile supports two different dependency mechanisms:

  • When used on the shell, the makefile enforces dependencies and builds them
  • When used in the pipeline, the makefile expects the artifacts to be copied
    by the CI/CD pipeline and does not build them itself

Interpreter Pass 2 Exection Model

main() loop

Main interpreter loop

  • Reads a leon-format line and calls Interpreter::leonline().
  • Then processes the execution stack until its empty.
    SOOs will be executed
    Executable SONs calls their SON::load_exec()
    All other SOs will be push onto the operand stack.
  • Repeat.

Interpreter::leonline()

Processes one line of the leon-format input.

  • Executable names outside of procedures and {, } are pushed onto
    the execution stack.
  • Everything else is pushed onto the operand stack.

SON::load_exec()

Looks up a name and executes it.

  • Procedures will be unfolded onto the execution stack.
  • Operators will be executed.
  • Executable names will be called recursively.
  • Other SOs will be pushed to the operand stack.

SOO::exec()

Calls the C++ machine code associated with the SOO and SOo.

SOA::unfold2exec()

Unfolds duplicates of the array-content to the execution stack.

operator bind

Replaces executable names with operator objects recursively into elements
that are SOA. Also does further optimization if Interpreter::odo_
ist set to true.

operators begin and end

These dictionary stack manipulations influence what is found by
SON::load_exec().

operators exec

The exec operator pushes

onto the execution stack.

loop operators

The loop operators push

onto the execution stack.

Boost Library

Installation Note

The installation of the Boost library in a GitLab pipeline image requires a
apt -y install gfortran- libboost-all-dev, because of Fortran config issues.

Following Boost modules are in use:

Boost.Assert

BOOST_ASSERT, BOOST_ASSERT_MSG, BOOST_ASSERT_IS_VOID
are used as building blocks for
DBC_PRE, BDC_POST, DBC_INV, DBC_INV_CTRO, DBC_INV_RAII and DBC_IS_VOID

Boost.Test

See TESTS

GitLab Notes

The following GitLab features are used:

  • Issues & Incidents for planning
  • CI/CD Pipelines
    • with build, test, predeploy and deploy stages
    • CI/CD variables
  • Pages for hosting the program documentation
  • Issue Boards
  • Milestones for a thematic structuring of tickets
  • Releases
  • Tags to mark the beginning and the end of a release cycle.
  • Components to import ready to run CI/CD steps:
    • to-be-continuous/bash
    • components/sast
    • components/markdownlint

Additional features will be integrated over time.

KDevelop

KDevelop creates a .kdev4 file and a ./kdev4 directory. These files
are integrated into the git repository to share them.

Visual Studio Code

VSC creates a ./vscode directory. This directory is integrated into
the git repository to share it as reference.

The following helpful adjustments can be made from the example configuration files c_cpp_properties.json und settings.json:

  • defines of GITRELEASE and BUILD_TYPE
  • cppStandard
  • .leo as PostScript file
  • Doxygen section tags
  • the todo-tree configuration

The 'Todo Tree' extension can handle our task tags appropriately.
The 'Lex' extension improves syntax highlighting for the flex source.
The 'Docker' extension supports Dockerfile editing.

Docker

The Dockerfiles to build the images are located in ./Docker.
The images are hosted on Docker hub under the user hagenbund2.

Deployment to a Docker Image

The makefile target dockerize builds a Docker image with a Leonardus
installation and pushes it to the Docker hub.

Build Custom CI/CD Images

We build our own custom CI/CD images with preinstalled software to improve
CI/CD speed of the GitLab pipelines.

These are the steps to build them:

Step 1: Store Docker images localy

docker pull gcc
docker pull ubuntu:noble
docker pull dockershelf/latex:full

Step 2: Build the Docker images

docker build -t hagenbund2/gcc_boost:latest -f gcc_boost.Dockerfile .
docker build -t hagenbund2/ubuntunoble_man:latest -f ubuntunoble_man.Dockerfile .
docker build -t hagenbund2/ubuntunoble_gcc_boost:latest -f ubuntunoble_gcc_boost.Dockerfile .
docker build -t hagenbund2/ubuntunoble_gcc:latest -f ubuntunoble_gcc.Dockerfile .
docker build -t hagenbund2/latexfull_doxygen:latest -f latexfull_doxygen.Dockerfile .

Step 3: Tests

docker run -it hagenbund2/gcc_boost:latest
docker run -it hagenbund2/ubuntunoble_man:latest
docker run -it hagenbund2/ubuntunoble_gcc_boost:latest
docker run -it hagenbund2/ubuntunoble_gcc:latest
docker run -it hagenbund2/latexfull_doxygen:latest

Step 4: Push the Docker images to Docker hub

docker login -u hagenbund2
docker push hagenbund2/gcc_boost:latest
docker push hagenbund2/ubuntunoble_man:latest
docker push hagenbund2/ubuntunoble_gcc_boost:latest
docker push hagenbund2/ubuntunoble_gcc:latest
docker push hagenbund2/latexfull_doxygen:latest

Further Notes and Details

To run Docker in Docker on a workstation:

docker run --privileged docker

To run Docker in Docker on GitLab we use in the .gitlab-ci.yml:

image: docker
services:
- docker:dind

$CI_REGISTRY_USER and $CI_REGISTRY_PASSWORD are stored in the GiLab project
as CI/CD variables.

To run Docker commands as a non-root user, add your user to the "docker" group.
This is particularly necessary for integration with the makefile to trigger
the "dockerize" target locally during development. To do this, enter the following:

sudo usermod -aG docker ${USER}