Erik's ToolBox Documentation
Index

Limitations and Differences from Standard AL

ToolBox AL is a runtime-interpreted subset of Business Central AL. Most everyday code runs unchanged, but a number of language features are deliberately not part of the interpreter, and a few behave differently. This page collects them in one place so you can check a script before running it.

Program Shape

  • A script is a single unit: an optional var block (or several), optional procedures, and one

main begin ... end; block. There are no object declarations (codeunit 50100 ...), no trigger OnRun(), and no attributes such as [TryFunction] or [EventSubscriber].

  • Procedures cannot have a Record, Codeunit, or array return type. Return such values

through a var parameter instead.

  • exit(value) is only valid inside a procedure that declares a return type. In the main block,

exit; ends the script.

Keywords and Identifiers

  • Identifiers, keywords, table names, and field names are case-insensitive.
  • The reserved words are: `and or xor not div mod break else false true for foreach to downto

procedure trigger if then exit var while do repeat until begin end case of page codeunit report run runmodal action. To use one as a name, quote it: "Report"`.

  • in and temporary are not reserved; they are recognized by position.

Types That Are Not Available

The following standard AL types cannot be declared in a ToolBox script. Where a replacement exists it is listed.

Not available Use instead
TextBuilder Text with += (see Text Methods)
Label, TextConst Text literals or Text variables
Enum types and Enum::Name::Value syntax Rec.Field::Member or the integer ordinal
Interface --
Variant --
RecordRef, FieldRef, KeyRef, RecordId Record variables; Rec.RecordId() returns the formatted id as Text
HttpClient, HttpRequestMessage, HttpResponseMessage, HttpHeaders, HttpContent Codeunit "Rest Client"
XmlDocument and the other Xml types --
Query, Page, Report, XmlPort variables Page.Run(Id), Report.Run(Id) statements
Blob, BigText, Media, MediaSet variables Codeunit "Temp Blob" with streams (Blob fields open streams directly)
DotNet, SecretText, ErrorInfo, Notification, SessionSettings, ModuleInfo, TestPage --
Multi-dimensional arrays One-dimensional arrays or a List
Nested collections such as List of [List of [Text]] A Dictionary or a JSON structure
Codeunit subtypes other than the supported list Codeunit.Run(Id[, Rec])

Statements and Expressions

  • Precedence follows standard AL, not C. and and xor bind like *, or binds like +,

and comparisons bind loosest. Always parenthesize each comparison inside a logical expression: if (Qty > 0) and (Price > 0) then. Writing if Qty > 0 and Price > 0 then is a type error.

  • and and or do not short-circuit. Both operands are always evaluated, so the left side

cannot guard the right side. Use nested if statements when the right side may fail.

  • not applies only to the primary expression that follows it: write not (X in [1, 2]).
  • foreach runs over a List, a JsonArray (with a JsonToken loop variable), or an expression

that returns a list. It does not run over a Dictionary (use Keys() or Values()) or over an array (use an indexed for loop).

  • Whole-array assignment (A := B) is a compile error, as in standard AL. Use CopyArray.
  • with ... do, asserterror, labels/goto, and event subscriptions are not available.
  • There is no try/catch. Error() stops the script; GetLastErrorText, ClearLastError, and

[TryFunction] do not exist.

Literals

  • Only 0DT is a valid DateTime literal. Build other values with

CreateDateTime(20240131D, 120000T).

  • Text literals use single quotes with '' as the only escape. A literal cannot span lines.

To embed a line break, assign character code 10 through string indexing: NL[1] := 10;.

  • Numbers have no hexadecimal, exponent, or thousands-separator forms.

Text and Code

  • Text methods (ToLower, Split, IndexOf, ...) require a Text variable as the receiver, or

the Text result of a chained call. They are not available on Code variables, string literals, or record fields directly.

  • Positions in text methods are 1-based and IndexOf returns 0 when nothing is found, matching

native AL.

  • Assigning a value that is longer than a Text[N] or Code[N] variable or field raises an

overflow error. Use CopyStr(Value, 1, MaxStrLen(Target)).

  • A Decimal with a fractional part cannot be stored in an Integer; round it first.

Records

  • Fields of type Blob, Media, MediaSet, RecordId, and TableFilter cannot be read or assigned as

values. Blob fields open streams (Rec.BlobField.CreateOutStream/CreateInStream/HasValue/ Length); Import/Export are unavailable. Codeunit "Temp Blob" also moves Blob content with FromRecordRef and ToRecordRef.

  • Rename is not supported. The following record members are also unavailable: SetLoadFields,

SetAutoCalcFields, SetView, GetView, GetPosition, SetPosition, FieldNo, CurrentKey, AddLink, CopyFilters, CopyLinks, SetPermissionFilter, ReadIsolation, SetSecurityFilter, Consistent.

  • Get used as a statement raises an error when the record is not found; used in an expression

(if Rec.Get(...) then) it returns Boolean. Insert, Modify, and Delete behave the same way.

  • Option and enum fields are compared and assigned with Rec.Field::Member or an integer. There

is no Enum::Type::Value syntax.

  • Writes to database tables require write access on the ToolBox license and may ask the user for

consent per table. Temporary records never do.

  • A record routed to another environment with ChangeEnvironment supports only reading:

Reset, SetRange, SetFilter, GetFilter(s), SetCurrentKey, Ascending, CalcFields, FindSet, Find, FindFirst, FindLast, Next, Get, and Count.

User Interaction

Message output goes to the console. Confirm, StrMenu, Dialog, UploadIntoStream, DownloadFromStream, Page.Run, Page.RunModal, and exopen need a user session and fail in Job Queue runs. Test GuiAllowed before calling them in a program that also runs in the background.

Extensions ToolBox Adds to Standard AL

These are conveniences that do not exist in standard AL:

  • The ternary operator Cond ? A : B.
  • Multi-line comments /* ... */ alongside //.
  • Rest Client members Get and Delete return the response body as Text, and the *AsJson

members return a JsonToken.

  • JsonObject and JsonArray typed getters with a DefaultIfNotFound overload

(GetText(Key, true)).

  • Format Address members have a text-only form returning List of [Text].
  • InStream.ReadText() without arguments returns the line, and OutStream.WriteText() without

arguments writes a line terminator.

  • ModifyAll takes UseValidate as its third argument.
  • Rec.ChangeEnvironment(...) reads data from another environment.
  • The Excel functions exget, exset, exclear, exopen and the eval(Source) function that

runs a script fragment and returns its console output.