VX/PASCAL - OpenVMS PASCAL to ANSI C++
Overview
VX/PASCAL is a source-to-source translator that rewrites OpenVMS and VAX Pascal into ANSI-compliant C++, so existing Pascal applications compile, link, and run on x86 Linux and Windows with no manual code changes. It works on a typed model of the whole program rather than doing text substitution, so it understands the VMS-specific extensions a real OpenVMS estate depends on: VARYING and packed string types, VMS string descriptors, stack argument tracking, CDD record definitions, and RMS file handling all carry across intact rather than being passed through as unrecognised text.
The migration is fully automated. Pascal type declarations become C++ classes and templates, VMS system service calls are routed through call-compatible runtime jackets, and file and record access is served by the VX/RMS runtime, so the translated program behaves the same as it did on OpenVMS. The emitted C++ is readable, standard source that the GNU or Clang toolchain builds, debugs, and maintains, and teams keep developing in Pascal while each release is migrated, so there is no code freeze during a long project.
<!-- Webflow embed (min). Source: pascal-page-technical/pascal-tech-body-embed-dark.html → python3 website/embeds/minify_webflow_embed.py … -->
<div class="s7-tech-embed" data-vxpascald-body-embed="1" data-technical-mega-embed="1" lang="en"><h2 id="architecture">Translation model and pipeline</h2><p>
VX/PASCAL is a compile-time source-to-source translator, not a runtime emulator. It reads
OpenVMS and VAX Pascal and writes ANSI C++, which the GNU (g++) or Clang toolchain then
builds into a native Linux or Windows executable. The translator parses the full Pascal
source into a typed program model with a recursive-descent parser and symbol table, so it
can tell a VMS language extension apart from a semantic difference and apply the correct
transformation to each rather than doing text substitution.
</p><p>
Two things ship together: the translator that rewrites the source, and the Pascal runtime
library the emitted C++ links against for VMS string, descriptor, file, and system-service
behaviour. Generated units include a single runtime header and link the runtime archive, so
constructs such as VARYING strings, VMS descriptors, RMS files, and <code>SYS$</code>
service calls keep working because they are routed to the runtime rather than being left to
the target compiler.
</p><div class="callout"><strong>Compile-time, not runtime.</strong> VX/PASCAL emits ordinary ANSI C++, so the
output is standard source the GNU or Clang toolchain builds, debugs, and profiles. Every
emitted unit includes <code>VxPascal.hxx</code> and links the Pascal runtime, which supplies
the string, descriptor, file, and OpenVMS system-service behaviour the code depends on.
</div><h6>Pipeline</h6><style>.s7-tech-embed .process-flow {margin: 1.5rem 0;padding: 16px;border: 1px solid var(--s7t-border);border-radius: 10px;background: rgba(255, 255, 255, 0.01);}.s7-tech-embed .process-endpoint {display: grid;grid-template-columns: 72px minmax(0, 1fr);gap: 14px;align-items: center;padding: 14px 16px;border-radius: 8px;background: var(--s7t-surface);color: var(--s7t-text);}.s7-tech-embed .process-output {border: 1px solid var(--s7t-note-bd);background: var(--s7t-note-bg);}.s7-tech-embed .process-label,.s7-tech-embed .process-number {font-family: var(--s7t-mono);color: var(--s7t-accent);font-size: 0.76em;font-weight: 700;letter-spacing: 0.08em;text-transform: uppercase;}.s7-tech-embed .process-endpoint div,.s7-tech-embed .process-stage {min-width: 0;}.s7-tech-embed .process-endpoint strong,.s7-tech-embed .process-endpoint span:last-child,.s7-tech-embed .process-stage strong,.s7-tech-embed .process-stage span:last-child {display: block;}.s7-tech-embed .process-endpoint strong,.s7-tech-embed .process-stage strong {margin-bottom: 4px;color: var(--s7t-text);}.s7-tech-embed .process-endpoint span:last-child,.s7-tech-embed .process-stage span:last-child {color: var(--s7t-muted);font-size: 0.9em;line-height: 1.45;}.s7-tech-embed .process-stages {display: grid;grid-template-columns: repeat(3, minmax(0, 1fr));gap: 10px;margin: 10px 0;}.s7-tech-embed .process-stage {padding: 15px;border-top: 3px solid var(--s7t-accent);border-radius: 6px;background: var(--s7t-surface2);color: var(--s7t-text);}.s7-tech-embed .process-stage .process-number {margin-bottom: 10px;}@media (max-width: 800px) {.s7-tech-embed .process-stages {grid-template-columns: 1fr;}.s7-tech-embed .process-endpoint {grid-template-columns: 1fr;gap: 5px;}}</style><div class="process-flow" aria-label="VX/PASCAL translation pipeline"><div class="process-endpoint"><span class="process-label">Input</span><div><strong>OpenVMS or VAX Pascal</strong><span><code>.pas</code> source</span></div></div><div class="process-stages" role="list" aria-label="Translation stages"><div class="process-stage" role="listitem"><span class="process-number">01</span><strong>Lex</strong><span>Tokenises the Pascal source and its VMS language extensions.</span></div><div class="process-stage" role="listitem"><span class="process-number">02</span><strong>Parse</strong><span>Builds the structured program model from the token stream.</span></div><div class="process-stage" role="listitem"><span class="process-number">03</span><strong>Resolve types</strong><span>Resolves symbols, declarations, and Pascal type relationships.</span></div><div class="process-stage" role="listitem"><span class="process-number">04</span><strong>Transpile</strong><span>Rewrites VMS Pascal semantics into standard C++ forms.</span></div><div class="process-stage" role="listitem"><span class="process-number">05</span><strong>Emit</strong><span>Writes ANSI C++ source as <code>.cxx</code>.</span></div><div class="process-stage" role="listitem"><span class="process-number">06</span><strong>Target compiler</strong><span>GNU C++ (<code>g++</code>) or Clang compiles and links the emitted source.</span></div></div><div class="process-endpoint process-output"><span class="process-label">Output</span><div><strong>x86 Linux or Windows binary</strong><span>Native executable using <code>VxPascal.hxx</code> and linked against the Pascal runtime</span></div></div></div><h6>What runs the migration</h6><div class="table-wrapper"><table><thead><tr><th>Stage</th><th>Role</th></tr></thead><tbody><tr><td>Source code analysis</td><td>Scans Pascal source for VMS-specific constructs, CDD record references, and complex data types, and reports diagnostics with a numbered error code rather than dropping anything silently.</td></tr><tr><td>Transpile</td><td>Rewrites VMS Pascal declarations, statements, and expressions into readable, maintainable ANSI C++ while preserving the original logic and structure.</td></tr><tr><td>Compile and link</td><td>The GNU C++ (g++) or Clang compiler builds the emitted C++ into an optimised native binary linked against the Pascal runtime.</td></tr><tr><td>Execute and validate</td><td>Automated test cases confirm identical execution behaviour between the original VMS Pascal and the translated C++.</td></tr></tbody></table></div><h6>Options and customisation</h6><p>
Translation is driven by a setup table read from a customer setup file, so output can be
tuned to house conventions without editing source: variable naming, code formatting (braces,
indentation, and line breaks), VMS logical-name translation, POSIX error-code mapping, and
inherited system-file handling are all controllable. Runtime checks (case, array bounds, nil
pointers, allocation, and file state) can be enabled together for a validation build, and a
compiler-style preset selects strict ANSI output in one switch.
</p><h2 id="dialect-coverage">Source dialect coverage</h2><p>
VX/PASCAL parses the full OpenVMS and VAX Pascal surface into a typed model, so it
understands the extensions a real estate uses rather than passing them through as text. The
table groups the VMS-specific constructs it recognises and shows how each one is carried onto
the target platform: some are rewritten inline to standard C++, others are routed to the
Pascal runtime, and the remainder are recognised and preserved through the emitted code.
</p><div class="table-wrapper"><table><thead><tr><th>VMS Pascal construct</th><th>How it is migrated</th></tr></thead><tbody><tr><td><code>VARYING [n] OF CHAR</code> and <code>PACKED ARRAY [lo..hi] OF CHAR</code> string types</td><td>Rewritten to the runtime string class templates that carry VMS string semantics.</td></tr><tr><td>String descriptor parameters (<code>[CLASS_S]</code>, by-descriptor arguments)</td><td>Bridged to the runtime descriptor class and passed with the correct cast at the call site.</td></tr><tr><td>External and system-service headers (<code>[EXTERNAL(SYS$...)]</code>, <code>SYS$</code> routines)</td><td>Emitted as <code>extern "C"</code> prototypes bound to call-compatible runtime jackets.</td></tr><tr><td>Argument mechanisms and defaults (<code>%IMMED</code>, <code>%REF</code>, <code>%DESCR</code>, <code>:= %IMMED 0</code>)</td><td>Preserved as C++ default arguments and the host calling convention.</td></tr><tr><td>Attribute-qualified declarations (<code>[VOLATILE]</code>, <code>[UNSAFE]</code>, <code>[QUAD]</code>, <code>[WORD]</code>, <code>[ASYNCHRONOUS]</code>)</td><td>Mapped to the matching C++ qualifier, packed layout, or width; <code>volatile</code> emission is switchable.</td></tr><tr><td>Variant records (<code>RECORD ... CASE tag OF ...</code>)</td><td>Rewritten to a C++ <code>struct</code> containing an anonymous <code>union</code> of the variant layouts.</td></tr><tr><td>Sets, subranges, and schema types (<code>SET OF</code>, <code>lo..hi</code>, <code>name(p) = ...</code>)</td><td>Mapped to the runtime set, subrange, and template-instantiation forms.</td></tr><tr><td>Preprocessing and inheritance (<code>%IF</code> conditional compilation, <code>%INCLUDE</code>, <code>[INHERIT]</code> system files)</td><td>Conditional blocks evaluated, includes resolved, and inherited system files (for example starlet) pulled in.</td></tr><tr><td>Embedded SQL and CDD records (<code>EXEC SQL</code>, <code>DICTIONARY</code> references)</td><td>CDD record definitions extracted and integrated so dictionary-defined types carry through.</td></tr></tbody></table></div><div class="callout"><strong>Named gaps, not silent ones.</strong> Any construct the translator cannot fully
represent surfaces a numbered diagnostic in the analysis output rather than being dropped,
so the migration is auditable and nothing disappears without a trace.
</div><h6>Built-in procedures and functions</h6><p>
The predefined Pascal procedures and functions are lowered to their runtime equivalents,
including <code>SUBSTR</code>, <code>INDEX</code>, <code>LENGTH</code>, <code>PAD</code>,
<code>ORD</code>, <code>CHR</code>, <code>PRED</code>, <code>SUCC</code>, <code>ODD</code>,
<code>ABS</code>, <code>ROUND</code>, <code>TRUNC</code>, <code>NEW</code>,
<code>DISPOSE</code>, and <code>PRESENT</code>, alongside the file verbs
<code>OPEN</code>, <code>CLOSE</code>, <code>RESET</code>, <code>REWRITE</code>,
<code>GET</code>, <code>PUT</code>, <code>FIND</code>, <code>READ</code>,
<code>READLN</code>, <code>WRITE</code>, <code>WRITELN</code>, and <code>UPDATE</code>.
</p><h2 id="type-mapping">Type mapping: Pascal to C++</h2><p>
Because the translator carries a typed model of the program, every Pascal type is mapped to
a specific C++ type rather than a generic byte layout. Scalars map to fixed-width integer and
floating types, and the structured Pascal types map to the runtime class templates that
reproduce VMS layout and semantics. The result is readable C++ where a Pascal declaration
still looks like the type it came from.
</p><div class="table-wrapper"><table><thead><tr><th>Pascal type</th><th>C++ type</th></tr></thead><tbody><tr><td><code>INTEGER</code> / <code>INTEGER8/16/64</code></td><td><code>int32_t</code> / <code>int8_t</code>, <code>int16_t</code>, <code>int64_t</code></td></tr><tr><td><code>UNSIGNED</code> / <code>UNSIGNED8/16/64</code></td><td><code>uint32_t</code> / <code>uint8_t</code>, <code>uint16_t</code>, <code>uint64_t</code></td></tr><tr><td><code>BOOLEAN</code> / <code>CHAR</code></td><td><code>bool</code> / <code>uchar_t</code></td></tr><tr><td><code>REAL</code> / <code>SINGLE</code>, <code>DOUBLE</code> / <code>LONGREAL</code>, <code>QUADRUPLE</code></td><td><code>float</code>, <code>double</code>, <code>long double</code> (or <code>__float128</code> where available)</td></tr><tr><td>subrange <code>lo..hi</code></td><td><code>_Subrange_t<base, lo, hi></code></td></tr><tr><td><code>VARYING [n] OF CHAR</code></td><td><code>_VaryingString<n></code></td></tr><tr><td><code>PACKED ARRAY [lo..hi] OF CHAR</code></td><td><code>_FixedString<lo, hi></code></td></tr><tr><td><code>ARRAY [lo..hi] OF T</code></td><td><code>_FixedArray<lo, hi, T></code></td></tr><tr><td><code>SET OF</code> subrange</td><td><code>_Set<base, lo, hi></code> bitset</td></tr><tr><td><code>RECORD ... END</code> / <code>[UNSAFE] RECORD</code></td><td><code>struct</code> (packed when unsafe)</td></tr><tr><td><code>RECORD ... CASE tag OF ...</code></td><td><code>struct</code> with an anonymous <code>union</code> of variants</td></tr><tr><td><code>^T</code> pointer</td><td><code>T *</code></td></tr><tr><td><code>FILE OF T</code> / <code>TEXT</code></td><td><code>File_t</code> / <code>_TEXT</code></td></tr><tr><td>enumeration</td><td><code>enum</code> with generated bound and size markers</td></tr><tr><td>schema <code>name(p) = ...</code></td><td><code>name<p></code> C++ template instantiation</td></tr></tbody></table></div><div class="callout"><strong>Strings keep VMS semantics.</strong> VARYING and fixed strings become runtime class
templates rather than raw <code>char</code> arrays, so length handling, padding, and
descriptor passing behave exactly as they did under VMS Pascal.
</div><h6>Program entry and exit</h6><p>
A Pascal <code>PROGRAM ... BEGIN ... END.</code> becomes a standard C++
<code>int main(int argc, const char *argv[])</code>, and the program terminates through the
runtime exit entry point rather than a bare <code>return</code>, so condition values and
exit status are reported to the host the way they were on OpenVMS.
</p><h2 id="examples">Before and after</h2><p>
These worked examples show VX/PASCAL rewriting real OpenVMS Pascal constructs into ANSI C++.
The identifiers use a banking demo estate; the syntax is exactly the kind of change the
translation applies.
</p><h6>VMS strings and a system service</h6><p>
A routine declares a VMS <code>VARYING</code> string, a fixed packed-array string, and an
external OpenVMS system service with by-descriptor arguments and immediate defaults:
</p>
<pre><span class="kw">TYPE</span>
VString255_t = <span class="kw">Varying</span>[255] <span class="kw">of</span> Char;
PString80_t = <span class="kw">Packed Array</span>[1..80] <span class="kw">OF</span> Char;
[<span class="kw">ASYNCHRONOUS</span>,<span class="kw">EXTERNAL</span>(SYS$ASCTIM)] <span class="kw">FUNCTION</span> $ASCTIM (
<span class="kw">VAR</span> TIMLEN : [<span class="kw">VOLATILE</span>] $UWORD := %<span class="kw">IMMED</span> 0;
<span class="kw">VAR</span> TIMBUF : [<span class="kw">CLASS_S</span>,<span class="kw">VOLATILE</span>] <span class="kw">Packed Array</span>[$l2..$u2:<span class="kw">INTEGER</span>] <span class="kw">OF</span> Char;
TIMADR : $UQUAD := %<span class="kw">IMMED</span> 0) : <span class="kw">INTEGER</span>; <span class="kw">EXTERNAL</span>;
<span class="kw">VAR</span>
Stmt_Time : PString80_t;
Stmt_Display : VString255_t;
Length_Time : [<span class="kw">WORD</span>] 0..65535 := 0;</pre>
<p>becomes:</p>
<pre><span class="cmt">#include "VxPascal.hxx"</span>
<span class="kw">extern</span> <span class="str">"C"</span> int32_t sys$asctim(
$UWORD* TIMLEN = 0,
DXX* TIMBUF = NULL,
$UQUAD* TIMADR = 0) ;
<span class="kw">typedef</span> _VaryingString<255> VString255_t ;
<span class="kw">typedef</span> _FixedString<1,80> PString80_t ;
PString80_t Stmt_Time;
VString255_t Stmt_Display;
uint16_t Length_Time = 0;</pre>
<p>
The <code>VARYING</code> and packed-array string types become the <code>_VaryingString</code>
and <code>_FixedString</code> runtime templates, the <code>[CLASS_S]</code> descriptor
parameter is bridged to the runtime <code>DXX</code> descriptor class, and the external VMS
service is emitted as an <code>extern "C"</code> prototype whose <code>:= %IMMED 0</code>
defaults become C++ default arguments.
</p><h6>Program body and string operations</h6>
<pre><span class="kw">BEGIN</span>
$ASCTIM (TIMLEN := Length_Time, TIMBUF := Stmt_Time);
Stmt_Display := <span class="kw">SUBSTR</span>(Stmt_Time, 1, Length_Time);
<span class="kw">WRITELN</span> (<span class="str">'Statement time [', Stmt_Time, ']'</span>);
<span class="kw">END</span>.</pre>
<p>becomes:</p>
<pre>int main(int argc, const char *argv[])
{
sys$asctim(&Length_Time, Stmt_Time);
Stmt_Display = Substr(Stmt_Time, 1, (Length_Time));
Writeln(VXP_OUTPUT, E_CONTINUE, <span class="str">"%s%D%c"</span>,
<span class="str">"Statement time ["</span>, (DXX *)(Stmt_Time), <span class="str">']'</span>);
vxrt_exit(0);
}</pre>
<p>
The <code>BEGIN ... END.</code> block becomes <code>int main</code>, <code>SUBSTR</code> and
<code>WRITELN</code> resolve to their runtime entry points, string arguments are passed as
<code>DXX</code> descriptors, and the program terminates through <code>vxrt_exit</code> so
the exit status is reported to the host as it was on OpenVMS.
</p><h6>Variant records</h6><p>
A Pascal variant record keeps its tag and case structure as a C++ struct with an anonymous
union, so overlaid layouts such as a transaction record carry across without hand editing:
</p>
<pre><span class="kw">TYPE</span>
Txn_t = <span class="kw">RECORD</span>
Account : <span class="kw">INTEGER</span>;
<span class="kw">CASE</span> Kind : Txn_Kind <span class="kw">OF</span>
Debit : (Amount : <span class="kw">INTEGER</span>);
Detail : (Ref_Text : <span class="kw">Varying</span>[40] <span class="kw">of</span> Char);
<span class="kw">END</span>;</pre>
<p>
The fixed fields become struct members and the <code>CASE</code> variants become an anonymous
<code>union</code> of the alternative layouts, preserving the original storage overlay.
</p><h2 id="runtime">Runtime, files, and system libraries</h2><p>
The emitted ANSI C++ links against the Pascal runtime library, which supplies the OpenVMS
behaviour the source relied on. String handling, descriptors, file and record access, VMS
system services, and CDD data structures all resolve against this runtime and the shared VX
libraries rather than being left to the target compiler, so the migrated program behaves as
it did on OpenVMS.
</p><div class="table-wrapper"><table><thead><tr><th>Area</th><th>What the runtime provides</th></tr></thead><tbody><tr><td>Strings and descriptors</td><td>The <code>_VaryingString</code> and <code>_FixedString</code> class templates and the <code>DXX</code> descriptor class, so VMS string length, padding, and by-descriptor passing behave as they did on VMS. OpenVMS <code>STR$</code> routines such as <code>STR$CONCAT</code> and <code>STR$COPY</code> resolve through <a href="/technical/api-str-string-interface-routines">STR$</a> and C++ operator overloading.</td></tr><tr><td>File and record I/O</td><td>Sequential, relative, indexed (ISAM), and block files with keyed access, RFA access, and record locking, served by <a href="/technical/vx-rms-sys-routines-for-rms">VX/RMS</a>.</td></tr><tr><td>System services</td><td>OpenVMS <code>SYS$</code> services emitted as <code>extern "C"</code> prototypes bound to call-compatible runtime jackets, delivered through the <a href="/technical/api-sys-interface-routines">SYS$</a> libraries in <a href="/technical/vx-rt-apis">VX/RT</a>.</td></tr><tr><td>CDD data structures</td><td>Common Data Dictionary record definitions retained and integrated through <a href="/technical/vx-cdd-vms-cdd">VX/CDD</a>, so CDD-defined types and references carry into the translated C++.</td></tr><tr><td>Build</td><td>MMS descriptions and command files converted to make and shell equivalents through <a href="/technical/vx-mms-vms-mms-to-make-and-xml">VX/MMS</a>, so the build carries across with the source.</td></tr></tbody></table></div><h6>Runtime routine families</h6><p>
The runtime library groups its entry points into the same families the emitted C++ calls:
string operations (<code>Substr</code>, <code>Index</code>, <code>Pad</code>,
<code>Chr</code>), arithmetic and radix conversion (<code>Bin</code>, <code>Dec</code>,
<code>Hex</code>, <code>Oct</code>, <code>Mod</code>, <code>Rem</code>, <code>Ord</code>),
file I/O (<code>Open</code>, <code>Close</code>, <code>Reset</code>, <code>Rewrite</code>,
<code>Find</code>, <code>Update</code>), heap and array management, condition handlers
(<code>Establish</code>, <code>Halt</code>), descriptors, and date and time. Argument
presence built-ins such as <code>PRESENT</code> are supplied so VMS stack argument tracking
keeps its meaning.
</p><h6>VMS stack argument tracking</h6><p>
Linux and Windows do not natively carry the VMS notion of a present or absent optional
argument, which would otherwise cause incorrect behaviour when a routine tests whether an
argument was supplied. VX/PASCAL preserves the <code>PRESENT</code> and not-present semantics
in the emitted C++ and its runtime so optional-argument handling behaves as it did on
OpenVMS.
</p><h6>System services and DCL</h6><p>
Applications that call OpenVMS system services keep working because those calls resolve
against the VX/RT jacket libraries rather than the VMS executive, and logical-name and
command-procedure behaviour is available through <a href="/technical/vx-dcl-vms-dcl">VX/DCL</a>,
so no source changes are needed to reach the services the application depends on. The Pascal
runtime jackets are named to stay distinct from platform natives and use C++ overloading to
bridge the VMS by-descriptor and by-reference calling mechanisms.
</p><h2 id="compatibility">Compatibility and status</h2><p>
VX/PASCAL produces ANSI C++ for the GNU and Clang toolchains on Linux and Windows and
preserves the OpenVMS behaviours applications depend on. The matrix summarises what is
supported, and the subsections cover the diagnostics and continuous-migration model the tool
provides along the way.
</p><div class="table-wrapper"><table><thead><tr><th>Capability</th><th>Support</th></tr></thead><tbody><tr><td>VAX and OpenVMS Pascal to ANSI C++ translation</td><td><span class="ac-live">live</span></td></tr><tr><td>Target toolchain: GNU C++ (g++) and Clang</td><td><span class="ac-live">live</span></td></tr><tr><td>VARYING, fixed, and packed string types</td><td><span class="ac-live">live</span></td></tr><tr><td>VMS string descriptors and by-descriptor arguments</td><td><span class="ac-live">live</span></td></tr><tr><td>VMS stack argument tracking (PRESENT / not present)</td><td><span class="ac-live">live</span></td></tr><tr><td>External and SYS$ service calls via runtime jackets</td><td><span class="ac-live">live</span></td></tr><tr><td>RMS file I/O (indexed, relative, sequential, block) via VX/RMS</td><td><span class="ac-live">live</span></td></tr><tr><td>Condition handlers (ESTABLISH / REVERT)</td><td><span class="ac-live">live</span></td></tr><tr><td>Embedded SQL and CDD record integration</td><td><span class="ac-live">live</span></td></tr><tr><td>Compile-after-translate and error-classification workflow</td><td><span class="ac-live">live</span></td></tr></tbody></table></div><h6>Diagnostics and exit codes</h6><p>
The translator reports every problem it finds with a numbered error code and a severity, so
warnings and notes can be suppressed for a clean build while genuine errors are surfaced. It
returns a specific exit code for each outcome rather than a single pass or fail, so an
automated pipeline can tell a clean translation from a file-not-found, a too-many-errors
stop, a watchdog timeout, or an out-of-memory condition:
</p><div class="table-wrapper"><table><thead><tr><th>Outcome</th><th>Meaning</th></tr></thead><tbody><tr><td>Success</td><td>Translation completed with no errors.</td></tr><tr><td>Translation errors</td><td>Finished, but with diagnostic errors that need attention.</td></tr><tr><td>Too many errors</td><td>Stopped after exceeding the configured error ceiling.</td></tr><tr><td>File not found</td><td>An input or inherited system file could not be located.</td></tr><tr><td>Bad option or setup file</td><td>An unrecognised command-line option or a setup-file parse error.</td></tr></tbody></table></div><p>
An error-classification workflow can accumulate and report diagnostic categories across many
runs, so a large estate can be triaged and tracked release by release rather than fixed one
file at a time.
</p><h6>Continuous migration without a code freeze</h6><p>
Because conversion is fully automated, a team keeps developing in OpenVMS Pascal while each
release is migrated to Linux and Windows on demand. Source stays synchronised between the
Pascal and C++ versions and builds carry across through converted MMS and command files, so
there is no code freeze during a long project and the estate keeps shipping.
</p><h6>How this differs from a generic Pascal compiler</h6><p>
A generic Pascal compiler does not understand VMS extensions, CDD records, RMS file handling,
or VMS stack operations, so it rejects or misinterprets the constructs a real OpenVMS estate
is built on. VX/PASCAL is a compile-time translator that recognises those constructs and
emits readable, maintainable ANSI C++ that the GNU or Clang toolchain builds and developers
can read, debug, and maintain with mainstream tools. Every change is applied against a typed
model, and any construct the translator cannot represent is named with a diagnostic rather
than dropped, so the migration is auditable rather than a black box.
</p><h2 id="quick-reference">Quick reference</h2><h6>Pipeline</h6><p><code>lex → parse → resolve types → transpile → emit</code>, then build the emitted ANSI C++ with the GNU or Clang toolchain and link the Pascal runtime.</p>
<pre><span class="cmt"># 1. Translate: OpenVMS Pascal in, ANSI C++ out</span>
account.pas -> account.cxx
<span class="cmt"># 2. Translate and compile in one step</span>
vxpascal --g account.pas <span class="cmt"># emits account.cxx, then builds it</span>
<span class="cmt"># 3. Write the C++ to a named file</span>
vxpascal --output=account.cxx account.pas</pre>
<h6>Signature transforms to remember</h6><div class="table-wrapper"><table><thead><tr><th>OpenVMS Pascal</th><th>ANSI C++</th></tr></thead><tbody><tr><td><code>VARYING [n] OF CHAR</code></td><td><code>_VaryingString<n></code></td></tr><tr><td><code>PACKED ARRAY [lo..hi] OF CHAR</code></td><td><code>_FixedString<lo, hi></code></td></tr><tr><td><code>[CLASS_S]</code> descriptor argument</td><td><code>DXX *</code> runtime descriptor</td></tr><tr><td><code>RECORD ... CASE ... OF</code></td><td><code>struct</code> + anonymous <code>union</code></td></tr><tr><td><code>[EXTERNAL(SYS$...)]</code> service</td><td><code>extern "C"</code> jacket prototype</td></tr><tr><td><code>:= %IMMED 0</code> default</td><td>C++ default argument</td></tr><tr><td><code>PROGRAM ... BEGIN ... END.</code></td><td><code>int main(...)</code> + <code>vxrt_exit</code></td></tr><tr><td><code>SUBSTR</code> / <code>INDEX</code> / <code>PAD</code></td><td>Runtime string entry points</td></tr></tbody></table></div><h6>What migrates</h6><div class="kw-grid"><div>VMS Pascal extensions</div><div>VARYING / fixed strings</div><div>VMS descriptors</div><div>Stack argument tracking</div><div>RMS / ISAM I/O</div><div>SYS$ services</div><div>CDD records</div><div>Condition handlers</div><div>STR$ string routines</div><div>MMS / command-file builds</div></div></div>