Level 1 of the cross-language linking roadmap entry: produce an object file with a renamed entry point so a BASIC program can be linked into a larger C or Fortran build. - src/compiler_main.c: --emit-obj runs gcc -c (compile-only, produces prog.o) and skips the runtime link. --main-name NAME (or --main-name=NAME) is plumbed through codegen_opts_t. - src/codegen.c: emit `int <name>(int argc, char **argv)` instead of always emitting `main`. Default unchanged when --main-name isn't specified. - include/codegen.h: add main_name to codegen_opts_t. - docs/getting-started.md: new "Cross-Language Linking" section with C and Fortran (iso_c_binding) driver examples. - docs/roadmap.md: three levels of cross-language linking, with Level 1 marked done, Level 2 (BASIC-side EXTERN declarations) as the next concrete step, Level 3 (BASIC SUBs as C functions) deferred. Also added: FORTRAN-style WRITE / C-style PRINTF formatted I/O extensions, and a NumPy / DataFrame / Matplotlib- style standard library section as a separate sub-project track. Verified end-to-end: a BASIC program compiled with --emit-obj --main-name=run_basic_greet links cleanly with both a C driver (gcc) and a Fortran driver (gfortran with iso_c_binding), and prints the BASIC output before returning to the host. All 72 interpreter / 68 compat / 63 compiler tests still pass.
9.1 KiB
Roadmap
Completed
Ahead-of-Time Compiler (v0.16.0)
gwbasic-compile translates tokenized .bas programs to C source, then
invokes GCC to produce native executables linked against libgwrt.a.
Pipeline: .bas → gw_crunch() → analysis pass → C codegen → gcc → native binary.
63 of 63 eligible tests pass (100%) via tests/run_compiler_tests.sh.
The harness only skips hardware-dependent tests (graphics/sound/timer)
and CHAIN/RUN target files that aren't standalone. The compiler now
accepts unnumbered direct-mode programs by auto-numbering them.
Language coverage:
- All statements: PRINT, LET, IF/THEN/ELSE, GOTO, GOSUB/RETURN, FOR/NEXT, WHILE/WEND, ON GOTO/GOSUB, ON ERROR GOTO, RESUME/RESUME NEXT, DIM, DEF FN, SWAP, READ/DATA/RESTORE, INPUT/LINE INPUT, OPEN/CLOSE/PRINT#/INPUT#/WRITE#, FIELD/LSET/RSET/GET/PUT, BSAVE/BLOAD, SAVE/LOAD, CHAIN/COMMON, SCREEN, PSET/PRESET, COLOR/LOCATE/CLS, CIRCLE/DRAW/PAINT/PLAY, VIEW/WINDOW/PALETTE, POKE/OUT/WAIT, DEF SEG, RANDOMIZE, CLEAR, MID$ assignment, ERROR, KILL/NAME/FILES/SHELL/MKDIR/CHDIR/RMDIR, ENVIRON, LPRINT/LLIST, WIDTH, KEY
- All operators:
+-*/\MOD^ANDORXORNOTEQVIMP><=<=>=<>(including string comparison via strcmp) - All functions: math, string, file, conversion (CVI/CVS/CVD/MKI$/MKS$/MKD$), graphics (POINT/PMAP), system (FRE/ERR/ERL/TIMER/DATE$/TIME$/ENVIRON$/INKEY$)
- Token embedding for complex statements (PRINT USING, DEF FN, graphics, file I/O, MID$ assignment) with selective variable sync
- Division-by-zero detection, RNG matching (gw_rnd), ON ERROR GOTO via setjmp/longjmp
Optimizations:
- Constant folding (compile-time arithmetic on literals)
- Dead code elimination (skip statements after GOTO/END/STOP)
- FOR step=1 elision (var++ instead of step variable, simple comparison)
- Fast-path expression emitter (skip buffering for common case)
- Selective variable sync in delegated statements
- REM-line skip (no runtime check for comment-only lines)
Hardware I/O Simulator (v0.15.0)
Implemented in portio.c / portio.h following the virmem.c dispatch
pattern. Emulates 8253 PIT channel 2 (speaker frequency), PPI port B
(speaker on/off with continuous tone via PulseAudio), CGA mode/color
registers, game port (joystick stub), and COM1 serial (transmitter-ready
stub). Default: reads return 0xFF (floating bus), writes discarded.
Also in v0.15.0: 100% token coverage (all 144 GW-BASIC tokens handled), string space pool with compacting garbage collector, RESET, ENVIRON/ENVIRON$, ERDEV/ERDEV$, IOCTL/IOCTL$, LCOPY, DATE$/TIME$ assignment, CALL, COM.
Jupyter Kernel (v0.15.0)
gwbasickernel/ -- Jupyter notebook kernel using the persistent subprocess
model with sentinel protocol.
- Inline Sixel graphics -- pure-Python Sixel decoder renders SCREEN commands as inline PNG images in the notebook
- INPUT statement support via Jupyter stdin protocol
- Pygments syntax highlighting for code cells
- Tab completion for all GW-BASIC keywords
- Magic commands:
%reset,%timeout,%new
Install: pip install -e . && gwbasickernel-install --user
Compiler Memory Safety (v0.17.0)
--warn, --safe, and --safe=sanitize flags for the ahead-of-time compiler.
--warn-- static analysis: uninitialized variables, GOTO to nonexistent line, unreachable code detection. Zero runtime cost.--safe(implies--warn) -- checked integer arithmetic viagw_int_add/sub/mul/neg(raises Overflow instead of wrapping), enhanced array bounds diagnostics with variable names and line numbers, GOSUB stack overflow diagnostics, ABS/SGN type-preserving codegen, string pool GC pinning infrastructure--safe=sanitize-- above plus-fsanitize=address,undefinedpassed to gcc
DOS / FreeDOS Target (v0.17.0)
Cross-compiles to DOS using OpenWatcom V2. Two targets:
- 16-bit real-mode (
Makefile.dos16): 128KB standalone MZ executable, MEDIUM memory model, far-heap TUI screen buffer, no DOS extender required - 32-bit DOS/4GW (
Makefile.dos): 175KB LE executable, flat memory model, requires DOS4GW.EXE extender
Tested on FreeDOS 1.4 via QEMU.
Next Up
Compiler Optimization Flags
--inline-arrays-- emit direct array indexing for statically-DIMmed arrays instead of runtimegwrt_array_elem()lookup-O0through-O3-- compiler-level optimization tiers mapping to different sets of codegen optimizations (constant folding, dead code elimination, FOR step=1 elision, fast-path expressions)
Cross-Language Linking
Three levels of integration with C and Fortran. Level 1 is implemented; Level 2 is the natural follow-up; Level 3 is deferred unless a concrete use case appears.
-
Level 1 -- Link BASIC objects into a larger C/Fortran project (done) --
gwbasic-compile prog.bas --emit-obj --main-name=run_basicproducesprog.owith the entry point renamed. The host project links it alongside its own objects againstlibgwrt. From Fortran, declare the entry withbind(c). -
Level 2 -- Foreign function declarations from BASIC: extend the language with a
'$EXTERN NAME(ARGS) AS TYPEpragma (or a newEXTERNALstatement) so BASIC code can call C functions directly. Type mapping:INTEGER<->int16_t,SINGLE<->float,DOUBLE<->double,STRING<->char *(NUL-terminated, owned bygw_str_to_cstr) orgw_string_tfor richer interop. Fortran callees must usebind(c); legacy F77/F90 mangling out of scope -- users write a thin C shim instead. -
Level 3 (deferred) -- Embed individual BASIC SUBs/FUNCTIONs as C-callable functions. Compile each labeled SUB or DEF FN to a separate C function with a stable signature; emit a header so C drivers can call them. Useful when BASIC is the configuration / rule-engine language for a larger application. Bigger scope: needs export annotations, header generation, and a way to share state between calls. Defer until a specific use case appears.
IDE Integration
- VS Code extension -- syntax highlighting (TextMate grammar), snippets, run/debug tasks, integrated terminal runner
- JetBrains plugin (IntelliJ/CLion) -- syntax highlighting, code completion,
run configurations, debugger integration (breakpoints via
STOP, variable inspection), structure view (line number outline)
Formatted I/O Extensions
Beyond the existing PRINT / PRINT USING / PRINT#, expose two
formatted-I/O styles familiar from neighbouring languages. Both write
through the existing HAL output path so they work in interactive mode
and in compiled binaries.
- FORTRAN-style
WRITE--WRITE (#unit, "(format-spec)") argswith Fortran format-spec language:I5,F8.3,E12.4,A,X,/(newline), repeat counts, slashes, parenthesized groups. Useful for porting numerical code; Fortran formats are denser than PRINT USING. - C-style
PRINTF--PRINTF format$, arg, arg, ...accepting C's%d/%f/%e/%g/%s/%c/%x/%o/ width / precision / flags. Cheaper to learn for users coming from C / Python. Goes to stdout;FPRINTF #unit, ...for file output.
Both share an underlying formatter (probably a C function in
libgwrt) that the codegen calls directly; the interpreter tokenizes
and dispatches the same way.
Numerical / Data Standard Library
Substantial scope -- treat as a separate sub-project, possibly a companion repo. All three modules build on top of GW-BASIC arrays (or new dynamically-typed buffers via DEF SEG / virtual memory). Likely written partly in BASIC and partly in C for the inner loops.
- NumPy-style array module --
NDARRAYtype with shape, dtype, broadcasting; element-wise ops (+,*,SIN,EXP); reductions (SUM,MIN,MAX,MEAN); slicing; basic linear algebra (MATMUL,INV,EIG). The existing single-typed BASIC arrays are a starting point; the new module needs a proper shape/dtype descriptor. - DataFrame module (pandas-like) -- column-oriented table with named columns and heterogeneous dtypes; CSV / TSV load and save; filter, sort, group-by, aggregate, join. Builds on the array module.
- Plotting module (matplotlib-like) -- high-level wrappers
(
PLOT x, y,SCATTER,BAR,HIST) with axes, labels, legend, title. Backend: existing CGA / Sixel rendering for terminals; PNG output via stb_image_write or libpng for files; SVG as a third backend that needs no library. Output format selectable (SET BACKENDor per-call argument).
Each module wants its own design pass before implementation; the sketches above are the rough shapes.
Known Limitations
- Static caps -- 32-bit / Linux builds: 1024 variables, 256 arrays, 64 FOR nesting, 128 GOSUB nesting, 64 WHILE nesting. 16-bit real-mode DOS keeps the original modest caps (256 / 64 / 16 / 24 / 16) because the MEDIUM model has a single 64KB DGROUP for all static data.
CALL/CALLS(machine code execution) raises Illegal function callDATE$/TIME$assignment shifts the program's view of the clock via a process-local offset; the OS time is unaffected (setting the OS clock would require root)- Device stubs (
ERDEV,IOCTL,COM,LCOPY) return defaults