Interactive AL – A pipedream or madness?

What if AL could be entered, compiled and executed in chunks, down to as little as a single line? And what if state and memory would persist between chunks? In this video, I pursue that idea, check it out:

https://youtu.be/xFsFXiBvHJ4

In this video, Erik explores a fascinating experiment: bringing interactive, REPL-style programming to AL in Business Central. Inspired by the immediate feedback loops of vintage computers like the Commodore 64, as well as modern tools like PowerShell ISE and SQL editors, Erik demonstrates how he repurposed his custom AL compiler and interpreter to create a live, interactive AL console — running directly inside Business Central.

The Problem: The Code-Compile-Deploy-Debug Loop

Anyone who works with AL knows the cycle well: you write code, compile it, deploy it to Business Central, debug it, and then repeat. There’s no shortcut — that’s simply the process. But Erik found himself wondering: what if there was a more immediate way to interact with AL?

The inspiration came from multiple directions. Old home computers like the Commodore 64 gave you a blinking cursor and let you type commands that executed immediately. Modern tools like PowerShell ISE and SQL Management Studio let you highlight lines of code and execute them on the spot. That kind of interactive workflow — type something, see the result, iterate — is something AL developers have never had access to. Until now, apparently.

The Interactive AL Console

What Erik built is a terminal-style interface embedded directly into a Business Central page. The role center is replaced with a mostly black screen featuring a blinking cursor — a deliberate homage to the Commodore 64. At this prompt, you can type AL code and have it executed immediately, with state persisting between commands.

Declaring Variables and Evaluating Expressions

The syntax is straightforward: you type code and hit Enter. The code is executed, and the result lives in a persistent state between commands. For example, you can declare a variable:

var i: Integer

Lines that start with an equal sign (=) are treated as expressions — the system evaluates whatever follows and prints the result:

= i

This returns 0 since i was just declared. You can assign values and evaluate again:

i := 4
= i

Now i is 4. You can even do inline math:

i := i * 4
= i

And i is now 16.

Console Commands

Special commands prefixed with a colon provide additional functionality. For instance, :list shows all variables currently in memory, including their names, types, and current values. :reset clears the interpreter state entirely — like hitting the reset button on an old computer. And of course, there’s a :clear command to clean up the screen while preserving state.

Defining Procedures

You’re not limited to simple variable assignments. You can define full procedures interactively:

procedure test(a: Integer): Integer; begin exit(a * 2); end;

After defining this, :list shows both the variable i and the procedure test in memory. Calling = test(5) returns 10.

Working with Records

This is where things get really interesting. You can declare record variables and interact with actual Business Central data:

var cust: Record Customer

Then use standard AL record operations:

= cust.FindLast()

This returns true. You can inspect fields:

= cust.Name

And it returns the actual customer name from the database. You can loop through records interactively:

if cust.FindSet() then
  repeat Message(cust.Name) until cust.Next() = 0;

This outputs all customer names. You can even modify data:

cust.Name := cust.Name + '!'
cust.Modify()

The modification is written directly to the database.

Opening Pages with Filtered Data

As a bonus, Erik demonstrated that you can apply filters to record variables and then open standard Business Central pages with those filters applied:

cust.SetFilter("Customer Posting Group", 'DOMESTIC')
Page.Run(22, cust)

This opens the Customer List page filtered to only show domestic customers — with the filter created entirely through the interactive AL session. Everything translates into real Business Central operations.

How It’s Built

The Terminal: xterm.js as a Control Add-in

The terminal interface is built using xterm.js, a well-known JavaScript terminal emulator (incidentally, the same technology that powers terminals in tools like VS Code). It’s packaged as a Business Central control add-in with three JavaScript dependencies: xterm.js itself, the xterm fit addon (for responsive sizing), and jQuery.

The startup script creates a new terminal instance, attaches it to the control add-in’s div, and displays a welcome message with a custom prompt (al-) and blinking cursor. Key presses are captured through xterm’s onKey event: regular characters are appended to a command buffer, backspace removes the last character, and Enter sends the accumulated command to Business Central via Microsoft.Dynamics.NAV.InvokeExtensibilityMethod.

The BC Page: Under 100 Lines of AL

On the Business Central side, a page receives the command through a control add-in event trigger. The command processing logic is surprisingly simple:

  • Commands starting with : are treated as console commands (like :list, :reset, :clear)
  • Commands starting with var declare variables
  • Commands starting with = evaluate the remainder as an expression and return the result
  • Everything else is executed as a statement

The key architectural insight is that both the compiler and interpreter codeunit instances are stored as global variables on the page. This means they persist across multiple command invocations for as long as the page is open, maintaining all declared variables, procedures, and state between commands. Each line of code is compiled into a structured JSON compilation unit, and if there are no errors, the interpreter runs it within the existing context.

For expression evaluation, the system passes the expression to the compiler separately, marks the expected output type as “undefined” (since it could be anything), evaluates it, and sends the result back to the JavaScript terminal via an output callback.

Persistent State Challenges

Erik noted an interesting technical challenge: he has been exploring the idea of communicating with Business Central through a simulated serial connection protocol, which would require persistent memory between web service calls. In classic NAV, you could accomplish this through some creative .NET interop hacking, but that’s not possible in Business Central’s sandboxed environment. This remains an ongoing area of exploration.

Source Code: A Chess Engine in AL

While the interactive AL console was the star of this video, the repository also contains another remarkable demonstration of Erik’s AL compiler capabilities — a complete chess engine written entirely in AL. This showcases the kind of complex logic that can be built and executed within Business Central.

The chess engine is implemented as a codeunit with a full board representation, piece validation, and AI using the minimax algorithm with alpha-beta pruning:

codeunit 50100 "Chess Engine"
{
    var
        Empty: Integer;
        WPawn: Integer;
        WKnight: Integer;
        WBishop: Integer;
        WRook: Integer;
        WQueen: Integer;
        WKing: Integer;
        BPawn: Integer;
        BKnight: Integer;
        BBishop: Integer;
        BRook: Integer;
        BQueen: Integer;
        BKing: Integer;
        Board: array[8, 8] of Integer;
        WhiteToMove: Boolean;
        MaxDepth: Integer;

    trigger OnRun()
    begin
        InitializePieces();
        InitializeBoard();
        MaxDepth := 4;
    end;

    // ...
}

The engine includes move validation for every piece type — pawns (including two-square initial moves and diagonal captures), knights, bishops, rooks, queens, and kings:

procedure IsValidMove(FromRow: Integer; FromCol: Integer; 
                       ToRow: Integer; ToCol: Integer): Boolean
var
    Piece: Integer;
    Target: Integer;
begin
    // Boundary and basic checks...
    
    Piece := Board[FromRow, FromCol];
    Target := Board[ToRow, ToCol];

    // Can't capture own piece
    if (Piece > 0) and (Target > 0) then
        exit(false);
    if (Piece < 0) and (Target < 0) then
        exit(false);

    case Abs(Piece) of
        1: exit(IsValidPawnMove(FromRow, FromCol, ToRow, ToCol, Piece > 0));
        2: exit(IsValidKnightMove(FromRow, FromCol, ToRow, ToCol));
        3: exit(IsValidBishopMove(FromRow, FromCol, ToRow, ToCol));
        4: exit(IsValidRookMove(FromRow, FromCol, ToRow, ToCol));
        5: exit(IsValidQueenMove(FromRow, FromCol, ToRow, ToCol));
        6: exit(IsValidKingMove(FromRow, FromCol, ToRow, ToCol));
    end;
    exit(false);
end;

The AI uses a minimax algorithm with alpha-beta pruning to search for the best move, with a configurable search depth and a board evaluation function that considers both material value and positional bonuses for center control:

local procedure Minimax(Depth: Integer; Alpha: Integer; Beta: Integer; 
                        MaximizingPlayer: Boolean): Integer
var
    BestScore: Integer;
    Score: Integer;
    i, j, k, l : Integer;
    TempPiece: Integer;
begin
    if Depth = 0 then
        exit(EvaluateBoard());

    if MaximizingPlayer then begin
        BestScore := -999999;
        for i := 1 to 8 do
            for j := 1 to 8 do
                if Board[i, j] > 0 then
                    for k := 1 to 8 do
                        for l := 1 to 8 do
                            if IsValidMove(i, j, k, l) then begin
                                TempPiece := Board[k, l];
                                Board[k, l] := Board[i, j];
                                Board[i, j] := Empty;

                                Score := Minimax(Depth - 1, Alpha, Beta, false);

                                Board[i, j] := Board[k, l];
                                Board[k, l] := TempPiece;

                                if Score > BestScore then
                                    BestScore := Score;
                                if BestScore > Alpha then
                                    Alpha := BestScore;
                                if Beta <= Alpha then
                                    exit(BestScore);
                            end;
        exit(BestScore);
    end else begin
        // Mirror logic for minimizing player...
    end;
end;

The visual side uses a control add-in built with HTML/CSS and JavaScript, rendering a beautiful chess board with Unicode piece symbols and click-to-move interaction. The board communicates moves back to the AL chess engine for validation, creating a fully playable chess game running inside Business Central.

Future Directions

Erik suggested that the next evolution of the interactive AL concept would likely move away from the terminal-style interface toward something more like a PowerShell ISE or SQL editor experience — where you can write multiple lines of code, select specific sections, and execute them on demand. The terminal aesthetic is undeniably cool, but a code-editor-style interface would likely be more practical for day-to-day use.

There's also the question of creating UI on the fly. While you can open existing pages and pass filtered records to them, dynamically creating pages at runtime remains an unsolved challenge.

Conclusion

Whether you consider this a pipe dream, madness, or a genuine breakthrough, there's no denying the technical achievement here. Erik has taken his custom AL compiler and interpreter, combined them with a JavaScript terminal emulator, and created something that AL developers have never had before: a live, interactive environment for writing and executing AL code against a real Business Central database. You can declare variables, define procedures, query records, modify data, and even open pages — all from a command line, with no compile-deploy cycle in sight. The implementation is surprisingly concise, with under 100 lines of AL handling the page logic, and the persistent state management elegantly solved by keeping the compiler and interpreter as global page variables. It's the kind of project that makes you rethink what's possible within the Business Central platform.