Sage ProvideX for Visual Studio CodeLanguage support for Sage ProvideX (the Business BASIC behind Sage 100 / MAS 90 / MAS 200), built as a VS Code extension plus a language server, with optional integration with an installed ProvideX to decompile, compile, syntax-check and run programs.
Features
Requirements
The home folder (the folder containing Readable view of compiled programsCompiled programs open in a readable form (
ProvideX/TS (
|
| ProvideX | ProvideX/TS |
|---|---|
obj'Method()'Prop, _OBJ'x, %Global |
obj.Method().Prop, this.x, global.Global |
rec.Field$ (dotted names) |
rec::Field$ |
= (comparison), <>, AND, OR, NOT(x), ^ |
==, !=, &&, ||, !x, ** |
! comments |
// comments |
"a""b", $0D0A$, 'CS' (mnemonic) |
"a\"b", "\x0D\x0A", m`CS` |
IF c THEN { } ELSE { }, WHILE/WEND, REPEAT/UNTIL, SELECT/NEXT RECORD, TRY/CATCH/FINALLY/END_TRY |
if (c) { } else { }, while (c) { }, repeat { } until (c), select ... { }, try { } catch { } finally { } |
FOR I=1 TO N / STEP 2 / STEP -1 |
for (I = 1; I <= N; I++) / I += 2 / I >= 1; I-- |
SWITCH X / CASE 1,2 / DEFAULT |
switch (X) { case 1: case 2: default: } |
READ (1,KEY=K$,ERR=*NEXT)A$, CLOSE (F), DROP OBJECT X,ERR=*NEXT |
read({ ch: 1, key: K$, err: *NEXT }, A$), close({ ch: F }), dropObject(X, { err: *NEXT }) |
X'Open(A$,ERR=*NEXT), NEW("Cls",A) |
X.Open(A$, { err: *NEXT }), new Cls(A) |
DEF FNX(A)=expr |
const FNX = (A) => expr; |
DEF CLASS "X" CREATE REQUIRED / LIKE "A" / END DEF |
@create(required) class X extends A { } |
PROPERTY P, PROPERTY P GET G SET ERR, LOCAL x |
public P;, @get(G) readonly P;, private x; |
FUNCTION M(a$)M_LABEL + M_LABEL: ENTER a$ ... RETURN |
M(a$) { ... } inside the class (the label is the method name in UPPER_SNAKE, or @label(X)) |
FUNCTION LOCAL M(), FUNCTION PERFORM M(), FUNCTION M(x)=expr, overloads |
private M(), @perform M(), M(x) { return expr; }, M(); M(a$); M(a$?) { } |
ON_CREATE: / ON_DELETE: / property GET/SET labels |
constructor() / destructor() / get P() / set P(v) |
| Unbalanced or GOTO-entered loops, unknown syntax | pvx`...` (verbatim) |
Classes keep their layout. A ProvideX class is a header (DEF CLASS … END DEF) followed by LABEL: sections. In ProvideX/TS the methods sit inside the class, so the header and the sections are regenerated when you save. When you save over an existing program, declarations and comments that only moved go back to where the program had them, and so do equivalent spellings such as IF (x) THEN vs IF x THEN. Saving an unchanged file therefore leaves the program unchanged. A class keeps the ProvideX layout when its methods are implemented outside its region.
Functions with local scope. ProvideX DEF FN parameters are global (calling FNX(5) overwrites a caller's X), and RETURN is not allowed inside a loop. function lowers to a pattern that was verified against ProvideX 9.62. Parameters are passed under mangled names (px_name.arg) and copied into LOCAL variables, which are saved per call, so recursion works. Every let becomes a LOCAL. A return inside loops sets a flag and BREAKs out level by level. The generated lines carry ! @pxts tags, so they fold back into the function form. Limits: LOCAL arrays are not allowed, and DEF FN recursion is capped at roughly 100 levels by ProvideX.
Commands: Convert to ProvideX/TS (.pxts), Convert ProvideX/TS to ProvideX, Open Compiled Program as ProvideX/TS (or set providex.compiledView to typescript). Compile and Run work on .pxts files directly.
Verification: every program in the ~2,000-program Sage corpus was translated readable → .pxts → ProvideX and compiled. ProvideX's own listing has to be identical to the readable form's, apart from equivalent spellings such as ""/$$. Runtime tests confirm the function semantics (recursion, locals, return from nested loops and from switch).
ProvideX licences
Decompiling, compiling and ProvideX syntax checking all need ProvideX to list and edit programs. ProvideX only allows that with a development activation. An install activated as runtime-only (the end-user licence that ships with a typical Sage 100 install) refuses with Error #61 "Authorization failure". The licence is checked by ProvideX itself, against the activation in its Lib\Keys folder (set up with pvxwactv.exe). The extension cannot and does not change that.
At startup the extension tries every ProvideX install it finds and uses one with a development licence. It prefers providex.home, then the install nearest the workspace. Which ProvideX does the work doesn't depend on where the program lives, so a development install can open another install's programs. If the preferred install is runtime-only, you're told which one is used instead. If none has a development licence, the status bar shows ProvideX: runtime licence, and every feature that doesn't need ProvideX keeps working. Click the status bar item (or run ProvideX: Choose ProvideX Installation) to see what each install reported and to pick another.
File types
- Text sources:
.pvx,.pvt,.pvxsrc,.bbx,.pxs(and.txtfiles that look like ProvideX). Statement numbers are optional. Numbered (0010 PRINT …) and lineless (label-based) code are both supported. Lineless sources are numbered automatically when compiled. - Compiled programs (
[Pvxprg]header) open through thepvxprg:file system, decompiled with ProvideX's ownLIST.
Commands
ProvideX: Open Compiled Program as Source, Decompile Program to Text File, Compile Current File, Check Syntax with ProvideX, Run Program, Evaluate Expression, Renumber Statements, Toggle Statement Numbers, Show Status, Restart Language Server.
Settings
| Setting | Default | |
|---|---|---|
providex.home |
auto | ProvideX home folder |
providex.executable |
<home>\pvxwin32.exe |
Interpreter used by Run Program |
providex.runArguments |
[] |
Extra arguments before the program name |
providex.bridge.enabled |
true |
Use the installed ProvideX for checking/compiling/decompiling |
providex.diagnostics.useProvideX |
onSave |
off / onSave / onType |
providex.diagnostics.undefinedLabels / unusedLabels / blockStructure |
true / false / true |
Analyzer checks |
providex.format.keywordCase |
upper |
upper / lower / preserve |
providex.format.indentBlocks, indentSize |
false, 2 |
Indent block bodies (lineless sources) |
providex.compile.outputExtension, compile.onSave |
.pvc, false |
Compiling text sources |
providex.decompile.lineNumbers |
true |
Keep statement numbers when decompiling |
providex.classPath |
[] |
Extra folders searched for compiled classes |
How the language data was produced
The lexicon (data/lexicon.json: 195 directives, 126 functions, 46 system variables, mnemonics, I/O options, operators and aliases) was extracted from the ProvideX 9.62 lexer table Lib/_lexdef.en. The installed ProvideX read it via its COM server. Error texts (data/errors.json) come from MSG(n), and system parameters (data/params.json) from Lib/_prmdef.en.
REA (with Rizin) was used to inspect pvxwin32.dll. It showed that the interpreter carries no keyword strings of its own, which means the external lexer table is authoritative. That finding is why the tables above are taken from _lexdef.en. Descriptions and signatures (data/docs/*.json) were written separately, and their arities and return types were checked against the real ProvideX compiler (see data/docs/VALIDATION.md).
To regenerate the tables from your own ProvideX version:
set PVX_HOME=C:\Sage\Sage 100\MAS90\Home
npm run extract
Development
npm install
npm run build # bundle client + server into dist/
npm test # unit, grammar and language-server tests (set PVX_HOME to include ProvideX bridge tests)
npm run test:vscode # integration tests in an isolated VS Code (set VSCODE_EXECUTABLE to use an installed Code.exe)
npm run test:corpus -- <folder of .pvx files> # analyzer false-positive report over a corpus
npm run package # build a .vsix
scripts/build-corpus.mjs decompiles a folder of compiled programs into a local corpus for test:corpus. Never commit decompiled Sage code.
Limitations
- ProvideX reports syntax errors per statement (Error #20), with no column. The whole statement is marked.
- There is no debugger integration: ProvideX has no debug-adapter interface. Use Run Program together with ProvideX's own
SETTRACE/ESCAPEtooling. - The COM bridge is Windows-only. On other platforms every language feature works except the ProvideX-backed ones.