← board

RemObjects Pascal Script — compile under pxx (embeddable scripting)

Why this is a good test case (the actual motivation)

Two wins at once:

  1. Compiler conformance. A self-contained, FPC-clean, mid-size Object Pascal codebase (lexer + compiler + bytecode runtime + import glue). Compiling it on the pinned stable is a heavyweight real-world test that exercises the dialect far past our own RTL — like Synapse, but a different shape (interpreter, not networking). Lowest-friction of the Pascal scripting engines, so it goes first.
  2. A feature for free. Once it builds, frank2 apps gain an embedded Object-Pascal scripting engine. We are not rolling our own — purely reusing.

Approach

Done when

$(PXX_STABLE) builds the Pascal Script core, and a frank2 host program runs a small script end-to-end (compile → execute → observe output) under a smoke. Stretch: host↔script binding of a hand-registered function.

License compliance (we honour it)

If we ship a demo or test app built on Pascal Script, we follow the license and give the attribution — a visible "made using RemObjects Pascal Script" line (and where to find it) in the app's aboutbox / docs / README, and we keep the upstream notice in any vendored source. Fair trade for a free engine; bake the credit line into the demo from the start, not as an afterthought.

Log

First probe 2026-06-28 (v83, --mimic-fpc)

Clone at external/pascalscript/ (remobjects/pascalscript, shallow). Core units probed with temporary lowercase copies (see [[bug-c-header-case-sensitivity-lookup]] — compiler lowercases unit name for file lookup; uPS* units have mixed-case filenames → not found without workaround).

unit state
uPSUtils [[bug-consteval-named-type-cast]]IPointer(expr) in const expr fails ConstEval (same bug as Synapse TSocket(NOT(0)))
uPSPreProcessor same — IPointer cast
uPSCompiler same — IPointer cast
uPSRuntime [[bug-mimic-fpc-version-defines-missing]]{$IF DEFINED(FPC) and (FPC_VERSION >= 3)} fails; FPC_VERSION not defined as integer under --mimic-fpc

3 Track A bugs gate the core (1 shared with Synapse, 1 new, 1 infrastructure):

  1. [[bug-c-header-case-sensitivity-lookup]] — unit name lowercasing blocks all uPS* units on Linux
  2. [[bug-consteval-named-type-cast]] — IPointer(expr) in const, blocks uPSUtils/uPSPreProcessor/uPSCompiler
  3. [[bug-mimic-fpc-version-defines-missing]] — FPC_VERSION integer missing, blocks uPSRuntime

When Track A fixes these, re-probe for the next wall.

Open questions

Probe log 2026-07-12 (opus-p)

Clone at github.com/remobjects/pascalscript, probe --mimic-fpc -Fu<clone>/Source -Fulib/rtl -Fulib/rtl/platform/posix, target unit uPSUtils. Walls burned this session:

  1. const array-of-RECORD with named-field element inits ((name: 'AND'; c: CSTII_and) keyword table) — LANDED (parser, test_const_array_of_record).
  2. SysUtils.CurrToStr / Currency — LANDED (sysutils shim: Currency=Double).
  3. Pos(tbtstring(' '), s) — string-typed ALIAS casts were pointer reinterprets (arg matched nothing) — LANDED: value no-op passthrough.

Current wall: CheckReserved(FLastUpToken, CurrTokenId) — a managed (tyAnsiString) field passed to a Const S: ShortString param: the const frozen-string param is by-ref for ABI, the managed→frozen conversion produces a non-lvalue, and the by-ref argument check rejects it. Needs the const-frozen-string param path to materialize a conversion temp (mirror the const-record temp rule) — parser/ir slice, file/pick up next session.

Probe log 2026-07-12 (later, opus-p)

uPSUtils compiles (walls 4-8): FPC variable typecast as var arg (Cardinal(len)), Dec(Byte(p^),32) cast-deref/type-keyword targets, TObject(x).Free statement, FreeAndNil, managed→ShortString param conversion.

uPSCompiler wall: IUnknown_Guid: TGuid = (D1:0; ...; D4:($C0,...)) — pxx has NO builtin TGuid record (it's a System type; interfaces reference it). The array-valued-field record const shape itself now works (LANDED, test_record_const_array_field — TGuid's D4 array field). What's missing is the builtin TGuid type + interface-GUID semantics. -dPS_NOINTERFACES skips the GUID consts and reaches the next wall (uPSCompiler:1963, a Decl.Params shape). Pascal Script core (uPSCompiler+uPSRuntime) is a multi-wall haul past this — needs builtin TGuid, ole2/Variant surface (uPSRuntime uses ole2), and more. Parked; uPSUtils is the concrete milestone reached.

Update: builtin TGuid landed (RegisterBuiltinTGuid — System record, SizeOf 16); uPSCompiler advances past the GUID consts to uPSCompiler:1963, a {$IFDEF CPU64}...Result := False block (CPU64 IS defined; the wall is the surrounding record-field expression Decl.Params[i].Mode shape after include expansion — needs isolation). Full Pascal Script core remains a multi-wall haul (ole2/Variant for uPSRuntime, InvokeCall.inc assembly). The generally-useful spinoffs all landed: array-field record consts, builtin TGuid, variable typecasts as var args, cast-deref Dec targets, TObject(x).Free, managed→ShortString param conversion, FreeAndNil.