• A tiny self-hosting compiler used for teaching

    From Michael Lehn@michael.lehn@uni-ulm.de to comp.compilers on Mon Aug 24 18:32:50 2026
    From Newsgroup: comp.compilers

    Hi,

    I thought this might be of interest to readers of comp.compilers.

    I teach a high-performance computing course at Ulm University. For this course I developed a small C-like language called ABC, which we use to teach some of the basics of programming and compiler construction.

    As a course project, the students wrote a compiler in ABC for an even smaller, somewhat BCPL-like language which we called not-abc. Code generation is a separate module (or, more precisely, a separate translation unit containing
    the code generation functions), so that different backends can be used without changing the rest of the compiler.

    During the course, the students generated code for a simple RISC architecture called ULM (Ulm Lecture Machine), which exists both as a virtual machine and
    as an FPGA implementation.

    At the end of the semester, I wanted to demonstrate that essentially the same compiler could also generate code for the computers they were actually using. So I gave them another code-generator translation unit which emits LLVM IR.
    The resulting IR can simply be passed to clang to produce native code.

    This also led to a little experiment in self-hosting. The not-abc compiler originally written in ABC can itself be rewritten in not-abc. That version is about 2,000 lines of code and can be found here:

    https://github.com/michael-lehn/not-abc

    `not-abc.ll` in the repository is LLVM IR for the compiler and can be compiled with clang to obtain the initial `not-abc`executable. `examples/not-abc.abc`
    is the same compiler written in not-abc.

    It can then compile itself:

    ./not-abc < examples/not-abc.abc > not-abc-compare.ll
    diff not-abc.ll not-abc-compare.ll

    The second command produces no output: the compiler reproduces its own LLVM
    IR.

    Getting from the students' ABC implementation to the not-abc implementation
    was mostly a matter of combining the translation units into a single source file and downgrading the language features. not-abc deliberately has only one data type: a 64-bit integer, which can also be interpreted as a pointer. The compiler reads its source from stdin and writes LLVM IR to stdout, so the complete compiler can live in one small source file.

    The ABC compiler and language I developed for the course are here:

    <https://github.com/michael-lehn/abc-llvm>

    I thought the result was a nice small example of bootstrapping that students can actually follow from beginning to end.

    Best,
    Michael
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.compilers on Mon Aug 24 13:39:14 2026
    From Newsgroup: comp.compilers

    Michael Lehn <michael.lehn@uni-ulm.de> writes:
    I thought this might be of interest to readers of comp.compilers.

    I teach a high-performance computing course at Ulm University. For this course
    I developed a small C-like language called ABC, which we use to teach some of the basics of programming and compiler construction.

    FYI, there's an existing language called ABC. It influenced Python.

    https://en.wikipedia.org/wiki/ABC_(programming_language)

    I understand it's difficult to avoid name collisions, and it probably
    doesn't hurt anything in this case.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    [ABC was also the AtanasoffrCoBerry computer built in 1939-42. There
    aren't enough letters to go around. -John]
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Cóilín Nioclásín Glostéir@thanks-to@Taf.com to comp.compilers on Fri Sep 4 21:12:41 2026
    From Newsgroup: comp.compilers

    Congratulations on an FPGA implementation.

    "Interestingly, there is usually another group of students as
    well. Many of them already know me from previous mathematics courses
    and are curious to see what a programming course taught by a
    mathematician looks like."
    says
    HTTPS://Github.com/michael-lehn/not-abc/tree/main/hpc0-sessions/session00

    "No previous programming experience is assumed.

    Designing such a course is surprisingly similar to teaching first-year mathematics."
    says
    HTTPS://Github.com/michael-lehn/not-abc/tree/main/hpc0-sessions

    Michael Lehn <michael.lehn@Uni-Ulm.De> wrote: |------------------------------------------------------------------------------|
    |"[. . .] |
    | |
    |[. . .] When teaching C, |
    |I always found declarations unnecessarily difficult to explain. [. . .] |
    | |
    |[. . .] |
    | |
    |[. . .] |
    |[. . .] But for beginners this means that |
    |they already need to understand operator precedence [. . .] |
    |[. . .] |
    | |
    |[. . .] |
    | |
    |[. . .] |
    |[. . .] the subsequent HPC courses use C or C++. The idea is that once |
    |students have learned the basic concepts in ABC, moving on to C should require|
    |learning C's declaration syntax rather than learning another substantially |
    |different language. |
    | |
    |Interestingly, when I introduced ABC some years ago, several colleagues were |
    |quite concerned about this. Their argument was that students without prior |
    |programming experience would now have to learn _two_ languages instead of |
    |one, making things even harder for them. |
    | |
    |[. . .]" |
    |------------------------------------------------------------------------------|

    Normally a compiler module is in the 4th year of a degree, so students
    already used to have many "previous programming experience"s including
    with C. So why are the ABC students ignorant of C (and even with no
    "previous programming experience") at the start of this module? Are
    they mathematicians or non-computer-scientists or
    non-software-engineers?

    HTTP://Gloucester.Insomnia247.NL/
    explains an easy way to find a real email address for me.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From ram@ram@zedat.fu-berlin.de to comp.compilers on Sat Sep 5 07:02:21 2026
    From Newsgroup: comp.compilers

    Michael Lehn <michael.lehn@uni-ulm.de> wrote or quoted:
    In practice, we have seen the opposite. Since introducing ABC, students who >come to the course without previous programming experience have had a >noticeably easier time. They can first learn the programming concepts without >having to deal with some of C's syntactic peculiarities, and the later >transition to C or C++ has not been a problem.

    Writing a compiler requires implementing complex data structures
    (like symbol tables) and managing memory. If students have no prior
    programming experience, they must learn basic control flow, data
    structures, memory management and compiler theory simultaneously.
    This creates a steep learning curve, even with a simplified language.

    The claim that transitioning to C or C++ later "has not been a
    problem" is the most debatable point. While learning concepts
    first is beneficial, C requires a deep understanding of hardware
    interaction, manual memory management, and pointers - areas where
    students taught via idealized languages frequently struggle.
    [What language to teach first is an old and insoluble problem. When
    I was in school most places started with Pascal while MIT started
    with Lisp. That meant they were dealing with interesting data
    structures and introspection while we were still explaining how
    big to make an array. -John]
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From George Neuner@gneuner2@comcast.net to comp.compilers on Sat Sep 5 14:02:25 2026
    From Newsgroup: comp.compilers

    On Tue, 25 Aug 2026 08:05:06 +0200, Michael Lehn
    <michael.lehn@uni-ulm.de> wrote:


    The main motivation for A Better C is actually quite mundane. When teaching C, >I always found declarations unnecessarily difficult to explain. For example:

    int *a[10]; // array of 10 pointers to int
    int (*b)[10]; // pointer to an array of 10 ints

    There is a perfectly consistent logic behind C declarations: you declare an >object in a form resembling how it is used. But for beginners this means that >they already need to understand operator precedence rCo in particular that >postfix operators bind more strongly than prefix operators rCo just to read a >declaration.

    I learned Pascal before C myself, and always found Pascal declarations easier >to read because you can essentially read them from left to right.


    I also learned Pascal before C, and I always have preferred
    Pascal-like syntax to the line noise that is typical of C. Ironically
    most of my career was spent using C++, SQL and Scheme.


    IMO, the main reasons that Pascal declarations were easier are:

    1) variable names are not intermixed with their types

    2) complex types generally require one or more type declarations
    and can't be declared inline in a variable declaration

    WRT your examples (and ignoring array base issues), in Pascal the
    array of pointers is no problem /because/ the base type of the array
    is simple, however the pointer to array can't be declared in one step
    because the array itself is not a simple type.

    type
    tenInts = array [1..10] of integer;
    var
    a : array [1..10] of ^integer; { array of 10 pointers to int }
    b : ^tenInts; { pointer to array of 10 ints }


    Yes, reading the declaration for 'b', you do have to go search out the declaration of 'tenInts', but what you don't have to do is decipher a
    string of line noise to understand the type.

    Of course, in C you could typedef the array and declare 'b' as a
    pointer to it - but most programmers would never think to do that with
    such a "simple" declaration. Few even would bother declaring 'b' a
    "pointer to array of int" when "pointer to int" will work just as
    well.
    [ignoring potential for error checking by the compiler.]

    And, of course, if you want a Pascal-like language with more
    functional parity to C there were/are extended and OO Pascals, and
    derivatives like Modula 2, 2+, 3, etc.


    That is almost the whole idea behind A Better C: it is basically C, but with >declarations following a Pascal-like logic. Since `^` already has a meaning in >C, I used `->` for pointers.

    Yeah ... but what do the declarations look like? Unless you totally
    changed the syntax, just changing what token(s) denote a "pointer" or
    a "dereference" operation is no more helpful than is the original C.

    YMMV.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From George Neuner@gneuner2@comcast.net to comp.compilers on Mon Sep 7 15:23:12 2026
    From Newsgroup: comp.compilers


    Hi John,

    [Your moderator has only limited sympathy for arguments about the aesthetics of
    declarations, noting a strong pattern of people liking what they learned first.
    Personally, I think Fortran EQUIVALENCE and COMMON statements are totally >intuituitive, but then I would. -John]

    Understood.

    It was meant more to be a question of what was being done in the
    author's language. I thought it needed a bit of background -
    apologies if that went too far astray.

    George
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Christopher F Clark@christopher.f.clark@compiler-resources.com to comp.compilers on Mon Sep 7 23:26:20 2026
    From Newsgroup: comp.compilers

    While I agree with our esteemed moderator's point about aesthetics, it
    is worth noting that Wirth intentionally made Pascal easy to parse.
    Thus, the Pascal declaration notation was designed to be parsed with a
    one pass compiler.
    Thus, arguing that the syntax is easy to read and understand has some
    objective support.

    However, it is worth noting that picking up C declaration syntax is
    for the most part also simple and intuitive if one ignores pointers to functions where we have both a prefix and a suffix operator that need
    to be disambiguated as to their precedence. But C's notation does
    have the nice aspect that declaration syntax matches usage.

    And, one can get both, although it looks a bit tortured to my eyes,
    but it might just be that I'm not used to it, yet
    You follow C's convention, but put the result type where the variable
    goes in usage and then string the operators around it.
    Moreover, If all the operators are suffix operators, there is also no
    ambiguity over precedence..

    a: int->[10] // a pointer an array of 10 ints
    b: int [10] -> // an array of 10 pointers to ints

    ****************************************************************************** Chris Clark email: christopher.f.clark@compiler-resources.com
    18 Meadow Rd Web Site: http://world.std.com/~compres
    Bolton, MA 01740 USA voice: (508) 435-5016 ------------------------------------------------------------------------------ --- Synchronet 3.22a-Linux NewsLink 1.2
  • From ram@ram@zedat.fu-berlin.de to comp.compilers on Tue Sep 8 09:14:35 2026
    From Newsgroup: comp.compilers

    Christopher F Clark <christopher.f.clark@compiler-resources.com> wrote or quoted:
    While I agree with our esteemed moderator's point about aesthetics, it
    is worth noting that Wirth intentionally made Pascal easy to parse.
    Thus, the Pascal declaration notation was designed to be parsed with a
    one pass compiler.

    Wirth famously created another language that is even easier to
    parse, viz. "PL/0" for his 1976 book "Compilerbau", which then
    became "Oberon-0" for his 1996 book "Compiler Construction".

    C's notation does
    have the nice aspect that declaration syntax matches usage.

    For readers who might not know those rules:

    | Parsing C Declarations
    |
    | To parse any C declaration by hand, follow the "Clockwise/Spiral Rule"
    | (or Right-Left Rule). Always start at the identifier and move outward
    | using this strict operator precedence:
    |
    | Precedence Rules
    |
    | 1. Groupings: Parentheses ( . . . ) surrounding a modifier
    |
    | 2. Post-fix operators (Right): Array subscripts [] and function
    | parameters ()
    |
    | 3. Pre-fix operators (Left): Pointer stars *
    |
    | Step-by-Step Algorithm
    |
    | 1. Locate the identifier (the variable or function name). Say: "__ is
    | a . . . ". When parsing an abstract declarator (e.g., inside a
    | cast like "(int (*)[10])" or sizeof), find the location where the
    | identifier would normally be placed.
    |
    | 2. Look to the right of the current position.
    |
    | - If [], say: "array of . . . " and move past it.
    |
    | - If (), say: "function returning . . . " and move past it.
    |
    | 3. Look to the left of the current position.
    |
    | - If *, say: "pointer to . . . " and move past it.
    |
    | - If a type qualifier (const, volatile), apply it to the element
    | to the left.
    |
    | 4. Encountering Parentheses: If you hit a closing parenthesis ) on
    | the right, you must consume all modifiers to the left until you
    | hit the matching opening parenthesis (. Then, step outside the
    | parentheses and repeat from Step 2.
    |
    | 5. Final Base Type: When the identifier and all modifiers are
    | consumed, read the leftmost base type (e.g., int, char).
    |
    | Quick Reference Table
    |
    | Operator Reading Direction Meaning
    | ---------- ------------------- ------------------------
    | name Start here "name is a . . . "
    | [N] Right " . . . array of N . . . "
    | () Right " . . . function returning . . . "
    | * Left " . . . pointer to . . . "
    | const Left " . . . constant . . . "

    Lines marked with "| " come from my editing, where I start by writing
    prompts for the chatbot and then edit the generated texts and format
    them for Usenet. The chatbots might make mistakes and misrepresent
    or invent facts.

    For example, in "(int const *(*(*)( ))[ ])", we insert the "virtual
    identifier" "_" to get "(int const *(*(*_)())[ ])". So we now have
    "pointer to" and what remains is "(int const *(*_())[ ])"; this gives
    "function returning a pointer to", and "(int const *_[ ])" gives
    "array of pointers to constant ints". So it's, "pointer to function
    returning a pointer to an array of pointers to constant ints".

    From: ram@zedat.fu-berlin.de (Stefan Ram)
    --- Synchronet 3.22a-Linux NewsLink 1.2