Skip to content

Compiler: $EXTERN pragma for calling C functions from BASIC (Level 2 FFI) - #1

Merged
evvaletov merged 1 commit into
mainfrom
feat/extern-ffi
Jun 13, 2026
Merged

Compiler: $EXTERN pragma for calling C functions from BASIC (Level 2 FFI)#1
evvaletov merged 1 commit into
mainfrom
feat/extern-ffi

Conversation

@evvaletov

@evvaletov evvaletov commented Jun 13, 2026

Copy link
Copy Markdown
Owner

Summary

Adds Level 2 cross-language linking: a '$EXTERN NAME(ARGTYPES) AS RET pragma lets compiled BASIC call C functions directly, the natural follow-up to Level 1 (--emit-obj / --main-name). The pragma is an apostrophe comment, so the interpreter ignores it while the compiler registers it.

10 '$EXTERN Cmul(DOUBLE, DOUBLE) AS DOUBLE
20 '$EXTERN Greet(STRING) AS STRING
30 PRINT Cmul(2.5, 4)
40 PRINT Greet("World")

INTEGER/SINGLE/DOUBLE/STRING map to int16_t/float/double/const char * at the boundary. The call name is matched case-insensitively but emitted as the C symbol with the case written in the pragma, and is recognized before parse_var() truncates identifiers to two significant characters (so multi-character C names work). The host project supplies the symbols at link time; from Fortran, declare the routine bind(c).

Implementation

Compiler-only (src/analysis.c, src/codegen.c); the interpreter is untouched.

  • Analysis pre-pass registers $EXTERN declarations and keeps their names out of the variable census.
  • peek_full_ident() recognizes full extern names before truncation; emit_extern_call() emits a statement-expression with BASIC↔C coercion; prototypes are emitted in the C prologue.
  • Hooks in emit_atom / emit_str_atom / peek_expr_type / emit_print route extern calls correctly.

Review-driven hardening

A codex + agy review caught three bugs the smoke test missed, all fixed here:

  • A string return that aliases a char * argument is now copied into the pool before the argument temporaries are freed (avoids a use-after-free).
  • Over-supplied arguments are consumed without desyncing the token stream, with an arity-mismatch warning.
  • Over-long pragma names / type words no longer leave the parser mid-token.

Remaining edge cases (INSTR/WRITE dispatch sites, suffix/name validation) are tracked in git-bug 8329647.

Tests

tests/run_ffi_test.sh (new, wired into CI) builds a BASIC program that calls C functions, links against a companion C object, and checks the output, including a regression case where the callee returns its own string argument. Existing suites stay green: 63/63 compiler, 72/72 interpreter, 68/68 compat.

Docs

docs/getting-started.md gains a "Foreign Functions from BASIC" section. docs/roadmap.md is pruned to point at git-bug as the source of truth for planned work.

Summary by CodeRabbit

  • New Features

    • Cross-language FFI support now complete—BASIC code can call C/Fortran functions via $EXTERN pragma with automatic type mapping between BASIC and C types
  • Documentation

    • New getting-started guide for foreign function integration with practical examples and usage constraints
    • Roadmap updated reflecting completed Level 1 & 2 cross-language linking capabilities
  • Tests

    • FFI test suite and CI validation pipeline added to verify cross-language functionality

…FFI)

Add a '$EXTERN NAME(ARGTYPES) AS RET pragma so compiled BASIC can call C
functions directly, the natural follow-up to Level 1 (--emit-obj /
--main-name). The pragma is an apostrophe comment, so the interpreter
ignores it while the compiler registers it.

Map INTEGER/SINGLE/DOUBLE/STRING to int16_t/float/double/const char* at the
boundary: a string argument crosses as a temporary C copy that is freed
after the call, and a string return is copied into the pool. The call name
is matched case-insensitively but emitted as the C symbol with the case
written in the pragma. Names are recognized before parse_var() truncates
identifiers to two significant characters, so multi-character C function
names work.

A string return that aliases a char* argument is copied before the argument
temporaries are freed, which avoids a use-after-free. Over-supplied
arguments are consumed without desyncing the token stream and warn on arity
mismatch.

Docs: getting-started.md "Foreign Functions from BASIC". Test:
tests/run_ffi_test.sh, wired into CI. 63/63 compiler, 72/72 interpreter,
68/68 compat still pass.

Also refile the roadmap "Next Up" backlog as git-bug issues and prune
docs/roadmap.md to point at git-bug as the source of truth for planned work.
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR implements Level 2 cross-language FFI, enabling BASIC programs to call C/Fortran functions via '$EXTERN pragmas. The compiler now parses extern declarations from comments, tracks them during analysis, generates type-coerced C function calls during code generation, and includes end-to-end test coverage with documentation.

Changes

Cross-Language FFI (Level 2)

Layer / File(s) Summary
FFI type definitions and analysis contracts
include/analysis.h
Introduces extern_func_t descriptor (name, return type, argument types, count) and extends analysis_t with extern storage and analysis_find_extern() lookup.
Pragma parsing and extern registration in analyzer
src/analysis.c
Parses '$EXTERN ... AS ... pragmas from REM comments, recognizes full-width BASIC identifiers for extern matching, and adds early Pass 0b to register externs before variable analysis.
Code generation for extern calls and type coercion
src/codegen.c
Emits C function calls with argument/return type coercion, handles string pool integration, supports both numeric and string expression contexts, updates expression-type detection for extern returns, and generates C prototypes.
FFI test suite with demo and runner
tests/ffi/extern_demo.bas, tests/ffi/extern_lib.c, tests/ffi/expected.txt, tests/run_ffi_test.sh
BASIC demo with multiple extern declarations and calls, C helper library with arithmetic and string functions, expected golden output, and bash test runner that compiles, links, executes, and validates output.
Documentation and CI integration
docs/getting-started.md, docs/roadmap.md, .github/workflows/ci.yml
Adds FFI section to getting-started with type mappings and examples, marks Level 2 as completed in roadmap, and integrates FFI test into CI workflow.

🐰 A rabbit hops through code,
From BASIC leaps to C calls,
Type coercions flow,
Strings dance in pools,
FFI magic takes control! 🎉


🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately and clearly summarizes the main feature added: a $EXTERN pragma enabling BASIC to call C functions, and identifies it as Level 2 FFI support.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/extern-ffi

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@evvaletov
evvaletov merged commit 89fe0fb into main Jun 13, 2026
2 of 3 checks passed
@evvaletov
evvaletov deleted the feat/extern-ffi branch June 13, 2026 12:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant