Project / C11
gBASIC
BASIC that grew up — money, dates and durations as real value kinds, a spreadsheet engine checked against 15,871 real Excel workbooks, and a whole business platform in one binary.
Download · Linux, x86-64 · 2.1 MBWindows, x64 · 4.3 MBNo install step on Linux — extract it and run it. Other files and checksums.

gBASIC is a BASIC-family language for people who like BASIC's readability and
want real power underneath it. One self-contained interpreter in portable C11
— no runtime to install, no VM, no package manager, and nothing required to
build it but a C compiler, make and bison.
The syntax is the easy part to explain and the least interesting thing about it. What makes gBASIC worth a look is what it carries: money as an exact value kind, and on top of it double-entry accounting, loan servicing, deposits and credit analytics; a spreadsheet engine that edits real Excel files and has been measured against 15,871 of them; business-calendar scheduling — "third Thursday", payroll rolled off holidays, SLA clocks, timezones — as part of the standard vocabulary; a statistics library that runs from descriptive stats to survival models and causal inference; a declarative web server; and a stack for calling a model and letting it call back. Sixty-seven libraries written in gBASIC itself, and one binary.
- 97.38%of spreadsheet formula cells agree with Excel's own cached results, across 15,871 real workbooks — with no disagreement at all in 91.1% of them
- 0required dependencies. Every optional library degrades to a clean runtime error, never a build failure
- 145test suites — every one discovered by glob, so a gate cannot silently shrink
A taste
Assignment can carry a modifier — a small piece of behaviour attached at the point a value is set. It is how gBASIC gets money, dates and text transforms without ceremony:
program main(args)
price {USD} = 19.95
invoiced {date} = "2026-03-01 09:00:00"
name {uppered} = "ada lovelace"
print "price: " + price
print "with tax: " + (price * 1.08)
print "due: " + (invoiced + 45 days)
print "customer: " + name
end program
Prints 19.95, 21.55, 2026-04-15 09:00:00, ADA LOVELACE. Money, dates and durations are real value kinds — not floats and strings you have to remember the meaning of.
Money is a value kind, and business is the library
Money in gBASIC is an exact integer of minor units with four guard digits below them — never a float — and it carries its currency. That one decision is what the rest of the business platform is built on: a ledger that balances stays balanced, because there is no rounding error to sweep anywhere.
price {USD} = 19.95
print price * 1.08 ' 21.55
' Three people split a hundred dollars. The remainder goes out a cent at a
' time, so the parts add back up to exactly what was split.
bill {USD} = 100.00
print money.allocate(bill, 3) ' 33.34 33.33 33.33
' 250,000 at 6% a year, monthly, over 30 years.
loan {USD} = 250000.00
print finance.pmt(0.06 / 12, 360, loan) ' -1498.88
eur {EUR} = 10.00
print price + eur ' refused, by name
All 178 ISO 4217 currencies, each with its own minor unit — JPY has none,
KWD has three — from a table committed to the repository rather than read
from the build machine. Adding two currencies raises; comparing them
for equality answers false. Splitting three ways never loses a
cent nor invents one, and the payment figure is the one Excel and LibreOffice
give.
On that sit five more libraries, each with a cookbook the test suite
enforces: accounting (double-entry, where the accounting
equation is asserted after every posting rather than enforced
anywhere — it falls out of correct account typing, which is what makes it a
good test), lending (servicing as an auditable fold: replay the
events and the balance is explained, rather than stored and unexplainable),
deposits, credit (roll rates, vintage triangles,
charge-offs) and scoring (WOE binning, AUC, point scales).
These libraries are written the way they are because every defect they prevent is an ordinary-looking number. A ledger with the right amounts on the wrong side still balances. A roll rate computed over the loans still visible instead of the whole starting bucket is a perfectly plausible percentage. So the cookbooks put the wrong answer beside the right one, and the suites assert arithmetic rather than transcripts.
Spreadsheets it can actually be trusted with
Most spreadsheet libraries generate new files. gBASIC edits existing ones, which is a much harder guarantee: the reader keeps every part of a workbook, including the parts it does not understand, so saving never quietly destroys what it could not model.
It also does something unusual. An .xlsx stores the value Excel
computed for every formula the last time it calculated — which makes the file
its own test oracle. xlsx.check evaluates each formula and
compares:
program main(args)
wb = xlsx.open("quarterly.xlsx")
r = xlsx.check(wb, "Ledger")
print r.agree + " agree, " + r.disagree + " disagree, " + r.unsupported + " unsupported"
end program
You can ask, before trusting a single number, whether this engine and Excel agree on your workbook. Across a corpus of 15,871 real spreadsheets, 97.38% of judged formula cells agree and 91.1% of workbooks show no disagreement at all. What the engine cannot evaluate it refuses by name (an array formula, a reference into a workbook that isn't there), never answering with a plausible wrong number — which is why the rate is quoted over cells it actually judged rather than over all of them, since counting a refusal as a pass would flatter it.
On top of that sit three libraries that turn a messy sheet into something a
database can answer questions about: grid finds the real table
inside a report full of titles, subtotals and notes (and tells you how
confident it is), consolidate merges differently-shaped sources
onto one schema, and dbframe loads the result into SQLite with
column types inferred from every value rather than the first.
Calendars are a language feature
No mainstream language treats business-calendar work — the third Thursday, payroll that rolls off holidays, "respond within 4 business hours" — as a first-class part of its standard vocabulary. gBASIC does, and it starts with arithmetic that respects the calendar:
jan31 {date} = "2026-01-31"
print jan31 + 1 month ' 2026-02-28 — clamped, the accountant's rule
cal = dates.calendar({ holidays: [xmas], hours: { open: "9:00", close: "17:00" } })
' the board meets every third Thursday at 14:00
board = { every: "month", when: { nth: 3, weekday: "thursday" }, at: "14:00" }
meetings = dates.series(board, { from: jan1, through: dec31 }, cal)
' an SLA clock: nights, weekends and holidays excluded
due = dates.add_business_hours(ticket, 4 hours, cal)
' stored as civil time + zone, rendered anywhere — DST handled per occurrence
print to_zone(from_zone(m, "America/Chicago"), "Europe/Berlin")
A Friday-16:00 ticket with a 4-business-hour SLA is due Monday 12:00. A rule is a record — store it in a database, print it, send it to another process. Rules that no day satisfies answer unknown; questions with no honest answer (1 month > 30 days? a typo'd timezone?) are refused rather than guessed.
Calendars are data, so merging two people's calendars is a union of
constraints and "a day that works for everyone" is one call. On top sit
schedule.slots (appointment grids) and
schedule.layout (sessions packed into working days around an
immovable lunch — anything that fits nowhere is reported by name, never
silently dropped). The whole surface is a
12-recipe cookbook
whose every example the test suite runs.
Charts are strings
A chart in gBASIC is a string of SVG — no image library, no canvas object, and byte-for-byte deterministic, so charts are tested against goldens like everything else. Lines, scatter, area, bars, histograms, pies, correlation heatmaps and sparklines, straight off the frames the spreadsheet and statistics layers produce. Missing data draws as a gap, never a silent zero; a chart that would mislead — a stacked bar with negatives, a pie with an unknown share — is refused with an error that names the problem.
q = { quarter: [1, 2, 3, 4, 5, 6, 7, 8],
revenue: [310, 345, unknown, 420, 462, 501, 548, 610],
costs: [270, 280, 285, 300, 315, 330, 355, 370] }
s = chart.spec("line", q)
s = chart.x(s, "quarter")
s = chart.y(s, ["revenue", "costs"])
s = chart.title(s, "Drawn by gBASIC - the code above, verbatim")
s = chart.size(s, 560, 260)
s = chart.options(s, { markers: true })
print(chart.render(s))
The picture above is that program's output, pasted verbatim — note
the gap where the third quarter's revenue is unknown. The
11-recipe cookbook
is enforced by the test suite like the other two.
Ideas most BASICs don't have
Reactive watchers. Attach behaviour to values and it re-runs when they change — reactive programming with no framework and no wiring.
subtotal = 100.00
tax_rate = 0.085
watch pricing(subtotal, tax_rate)
total = subtotal * (1 + tax_rate)
end watch
print total ' 108.5
subtotal = 250.00
print total ' 271.25
unwatch pricing ' a named watcher is a first-class value with an off-switch
Naming a watcher binds a handle you can store, pass, and turn off —
re-declaring the same name replaces the old watcher instead of
stacking a second one, so setup code is safe to re-run, and
watchers() recovers any handle you lost. Nothing can fire with
its off-switch out of reach.
Policy-Based Inheritance. Compose objects by declaring what each field does on derivation — copied, shared, recomputed, or dropped — instead of building rigid class hierarchies.
account = {
owner (copy): "unassigned",
bank (link): "First National",
id (reset next_id): 0,
notes (exclude): "internal"
}
a = new account ' owner is its own; bank is shared;
b = new account ' id is recomputed; notes never appears
Shared-nothing actors. Real OS processes over
fork, with message passing, selective receive and death
monitoring. No shared memory means no data races.
function doubler(parent)
job = receive()
send(parent, job * 2)
end function
program main(args)
me = self()
w = spawn doubler(me)
send(w, 21)
print "worker replied: " + receive() ' 42
end program
Failure has a shape
A language earns trust by how it behaves when something goes wrong, and gBASIC's rule is that a failure is never silent. Errors are frame-scoped: a function decides its own error policy, and that decision cannot leak into the caller or be leaked into by it.
function parse_price(text)
on error goto next
n = number(text)
if error then
return unknown
end if
return n
end function
Prints 19.95, then unknown, then keeps running. on error goto next and on error goto LABEL are the whole vocabulary — there is no separate resume keyword to remember.
Beside it runs a second, quieter channel. A warning reports
something suspicious without stopping anything, and — unlike an error — its
volume is the caller's choice, not the callee's:
on warning print, ignore, stop, or
goto next. That suppression is what makes an aggressive warning
affordable, which is why gBASIC can afford to tell you that a return value was
discarded, or that a name you just assigned quietly shadows an outer one.
The same principle runs through the library surface. Operations that must
report failure as a value have non-raising twins —
try_decode for JSON you did not write,
xlsx.try_open so one bad workbook cannot end a batch of
thousands, process.which so asking "is this tool installed?"
never kills the program that asked.
A website is a block
The server is not a library you wire together — it is a declarative block, and it adds exactly one reserved word to the language:
server demo( port: 8080 )
get "/hello/{name}"( req )
return { body: "hello, " + req.params.name }
end get
end server
Route patterns are checked when the file loads, not when a request arrives: two routes that could never be told apart, a duplicate, a capture name used twice, a greedy segment that is not last — each is refused by name before the program runs. And because the block is data, the routes can be read and a request answered with no socket open at all, which is what makes a routing table testable:
print "routes declared: " + string(count(web.routes(demo)))
r = web.dispatch(demo, { id: 1, method: "GET", path: "/hello/ada", headers: {} })
print "answered with no socket open: " + r.body
Prints routes declared: 1 and answered with no socket open: hello, ada. Underneath sit TLS with SNI, streaming responses, a worker pool that reloads one worker at a time and never retires a healthy one on faith, and refusals for the request framings that carry smuggling attacks.
Calling a model, and letting it call back
tools is one declaration of what a model may
call, and both the schema the model is shown and the validation of what comes
back are derived from it — so they cannot disagree. The older shape had two
hand-written things: a JSON-Schema record and a function, with nothing
checking that the schema's id was the customer_id
the function read.
A tool that raises comes back as a result the model can read rather than ending the run, and tool bodies execute in a pre-spawned worker pool rather than on the event loop, where one slow call would freeze every other client.
agent is the piece that makes an approval gate possible: a
conversation in progress is a value, and the loop is a pure
step that performs no I/O and returns the actions its caller must perform.
That is what lets a run be stored between HTTP requests and an approval
arrive ten minutes later on a different one — a naive loop cannot wait for a
person on a single-threaded server. mcp publishes a toolset to
any Model Context Protocol client over stdio or HTTP through one dispatcher,
and consumes one the same way.
What a database estate says about itself
discovery reads the declared facts of a database estate — tables,
columns, keys, and the SQL of views and procedures — across several databases
at once, and traces where a column came from across hops.
Two reports both show a number called total_volume, they
disagree, and both are correct; what is wanted is where the derivations
parted. It infers nothing, deliberately: an inferred relationship is
the result of a search, and a search over a 500-table estate is some fifty
million candidate pairs where coincidences are a certainty.
nlq answers a question in English over such an estate, and is
built around the same finding. Hand a model twenty tables chosen badly and it
writes flawless SQL about the wrong ones — and the SQL runs, because
the tables it was given are real. Measured end to end, one question came back
36% short of its true answer by summing a warehouse table an ETL had already
filtered: valid SQL, real table, wrong question, nothing in the output saying
so. So a grounding carries its own search width, its near misses, and the
words it could not match, and it refuses rather than guesses when the catalog
cannot say which of two objects was meant.
Scored by an independently written answer key over nineteen questions — including deliberately ambiguous ones, and unanswerable ones where correctly declining is the right answer, with partial never folded into right — it scores 18 of 19 over a 127-object estate and 16 of 19 over a 517-object one. Grounding needs neither a model nor a database, which is exactly why that can be a gate rather than a demo.
An IDE, written in itself
gBASIC Studio is a desktop IDE for gBASIC — written in gBASIC, on GTK 4 through the GObject-Introspection bridge. It is the project's hardest dogfooding: an editor, a file browser, a run pipeline that executes a section in a child interpreter and keeps every result, a variable inspector, and state-only branches that replay the same source under different bindings.
Much of the platform on this page exists because Studio needed it and found it missing — the live child-process control, the source outliner, the atomic file replacement, the diagnostics stream. A language whose IDE is written in it cannot hide its own friction. Studio now lives in its own repository and consumes gBASIC the way any other application would, which is the more honest arrangement: it gets no privileged access to the platform it helped shape.
Batteries, optional by design
Every native module is detected at build time and compiles out cleanly when absent — a missing library turns one feature into a clear runtime error, never a broken build.
- Data — SQLite, PostgreSQL, ODBC (one connection string for SQL Server, MySQL, Oracle, DB2 and the rest), the xlsx engine, XML with a constant-memory streaming reader, JSON with a decode that reports failure as a value instead of raising.
- Network — an HTTP/HTTPS client, and a real HTTP server:
TLS with SNI, routing with typed captures, streaming responses, a process
worker pool with rolling reload, and request-smuggling defences. A whole
site is a
serverblock. - Time — precision-carrying datetimes, duration algebra that never blurs months into days, business calendars, date expressions, recurring schedules, SLA arithmetic, and timezone conversion over the system IANA database.
- Statistics — pure gBASIC: distributions, matrices, OLS and the GLM suite, clustering and PCA, time series through ARIMA and GARCH, survival analysis and Cox regression, meta-analysis, factor analysis, event studies and causal inference, plus data frames.
- Text — regular expressions as a first-class value kind, binary-safe Unicode strings, and a report-parsing spec language.
- Charts — pure-gBASIC SVG rendering: line, scatter, area, bar, histogram, pie, heatmap, sparklines; deterministic output, honest gaps, colorblind-safe palette.
- Desktop — a generic GObject-Introspection bridge, so any GObject library (GTK 4 first among them) is reachable without binding code, with constructors, a virtualizing table and a declarative reconciler layered over it. GUI code is testable without a display: widgets are built and interrogated, never shown.
- Shipping — an application becomes a
.debcarrying its own interpreter and standard library, built lean: a server build drops the desktop toolkit and takes the interpreter from 48 shared libraries to 10, while using the system's OpenSSL and SQLite rather than vendoring them. - Crypto and identity — hashing, HMAC, AES-GCM, Ed25519, PBKDF2 and scrypt key derivation, JWT and signed cookies over libcrypto; LDAP bind and search against a directory, where a directory that is down and a password that is wrong are deliberately different answers; and one-time passwords as a second factor, whose replay guard is structural rather than advisory.
- Mail — composing a message is pure gBASIC and needs no network, so the whole of it is testable without one; the native module owns only the wire.
Evidence, not adjectives
Output is compared byte-for-byte against committed goldens, and the claims on this page are the ones the test suite actually asserts. The spreadsheet engine is scored against Excel's own cached results rather than against itself. The build is exercised in clean containers with every optional dependency present, with none present, and installed to a prefix and run from there — because a conditional-compilation guard is invisible to the compiler you build with, and that is exactly how three defects once survived unnoticed.
Fifteen cookbooks are enforced. Every code block and every output block on those pages is owned by a file the test suite runs and compares byte for byte, so a page cannot drift from the product without a test going red. Spreadsheets, charts, dates & scheduling, money, finance, accounting, lending, deposits, credit, ODBC, and database discovery among them.
That harness pays for itself in a way worth naming. Writing the money
cookbook — an ordinary amortization schedule, not a stress case — surfaced a
silently wrong answer in money × scalar: a double's shortest
decimal can need more fractional places than a power of ten fits in an
int64, and every payment in a twelve-month schedule came out zero while a
360-month one worked. The unit tests had all used short scalars. Only
realistic worked examples produce a long one.
The full suite runs in CI on x86-64 Linux. riscv64 is a
supported target, and the interpreter is also brought up on big-endian
s390x — strings are byte-oriented and endian-independent,
and a big-endian machine is how you find out whether that is actually true.
Windows is supported as of 0.7.0 — a signed installer, a
statically linked gbasic.exe that needs nothing beside it, and a
suite of its own that runs on every platform so a Windows-only assumption
shows up on Linux too. It is built and tested on Windows by hand rather than
in CI, which is a weaker guarantee than the Linux figure above and is said
plainly for that reason. A few things are deliberately absent there and say so
when called: POSIX signals, the prompt's line editor, and the GTK bindings.
macOS is not supported and no support for it is claimed.
Status
gBASIC 0.9.0 — an early release, and the version number is honest about that.
0.9.0 fixes a crash you could be hit with from outside. The
JSON parser's nesting limit was a fixed 10,000 levels, chosen against the
8 MB stack a desktop Linux gives you. A nesting limit is really a
stack limit wearing a count — about 240 bytes a level — so where
ulimit -s is nearer 2 MB, as it often is in a container or
on a hardened host, the crash came before the limit could refuse.
And a request body is decoded as the request is read, whether or not the
program ever looks at it, so a 20 KB POST of nested
brackets killed a gBASIC server outright. The limit is derived from the
actual stack now; nothing changes at the 8 MB default.
Exactness now reaches past the operators.
mod(9007199254740993, 2) answered 0 — not a lost
digit but the wrong answer, since the odd value became an even double before
mod saw it, and a parity test is the commonest thing anyone asks
it. min, max, sum,
median, abs, floor, ceil,
round and pow all dropped a digit above
253; sort could not order two exact integers at all;
and decode(encode(x)) lost the value, so a number could be
written to a file and not read back from it. All fixed. Below
253 nothing moves, so an ordinary program sees no difference.
One thing changed shape, which is why this is 0.9.0 and not
0.8.1: the gtkui widget reconciler takes a backend argument now,
because GTK moved behind a seam. The reconciler no longer needs GTK at all —
it loads on the plain download, which has no GUI libraries in it — and a
second backend becomes a small job rather than a port.
0.8.0 was about the downloads rather than the language. 0.7.0
shipped a standard library the binary beside it could not run: of 65
libraries, 13 would not load on the -full download
and 21 on the plain one. Those numbers are 6 and 14 now, and
spreadsheets, XML, SQLite, compression and password hashing are in both —
for about 1.4 MB, with the plain download still asking the machine for
nothing but libc and libm.
Three things in it change answers a program was already getting, and all
three were wrong before. has_builtin — the call a program makes
to find out what the interpreter it is running on can do — affirmed
sha256, password_hash and compress on
downloads that cannot run any of them, so a program that checked first and
then called crashed on the branch it had checked for; it answers for the
build it is in now. number("…") on integer text is exact, so
number("9007199254740992") + 1 no longer comes back one short.
And ldap tells a TLS failure from an outage by asking the
network rather than by reading a message whose wording belongs to whichever
TLS library the distribution happened to build against. One
diagnostic changed its text: on a build without zlib,
compress said unknown function and now says
compression requires zlib.
0.7.0 brought Windows, and with it one fix that changes
answers a correct program was already getting: the difference of two
datetimes used to consult the machine's time zone, so a day count, an age or
a deadline worked out anywhere but UTC could come out a day wrong. In
gBASIC's own credit library that put a sixty-days-past-due loan one bucket
too healthy, silently, and differently in different offices. A
datetime here is civil and carries no zone — which is
why epoch(dt, zone) takes one — so the subtraction had no
business asking about a zone at all. If you have code quoting an
interval, re-check it; code that ran in UTC was always right, which
is exactly why this went unnoticed.
0.6.0 was about working with data in bulk, and it was
planned by counting what the loops in this tree actually do rather than by
taste — 813 of them, classified by the shape of their body. The answer was
that the missing piece was smaller than map or
filter: every summing loop was adding up a field, and
there was no way to name a field across a list. So
sum(rows.amount) now works, and five aggregates reached
business data for nothing. Beside it: sort on records by one
field or several, a excluding b and
a intersecting b, exact arithmetic on whole numbers past
253, and mode answering every tied value instead of
always handing back the first element.
The 0.5 series before them was a language release. Dates gained a layout notation that
reads the way it prints — date parts uppercase and time parts
lowercase, so MM is the month and mm the minutes
— used for both rendering and parsing, where before there was no output
formatting at all. Modifiers chain with ;, any of them now
works as a comparison lens, and every shape works inline in an expression
rather than only as an assignment clause. It also refuses a date that
does not exist: 2026-02-30 used to be accepted, and adding
a day to it answered 2026-03-03 — so a date nobody could have
meant became a different real date two days later, with nothing raised.
0.5.1 was diagnostics only: three things a reader arriving from QBasic
types first now say what to write instead: <> names
!=, and let and rem are refused by
name — both used to parse as the start of a call, so the error asked for a
parenthesis, which is the one change that cannot help. Neither word is
reserved, so let is still a legal name for your own function.
The release-candidate run was less about adding features than about closing the ways a program could fail quietly: a frame-scoped error model, a warning channel whose volume is the caller's choice, and a run of fixes for failures that reported nothing at all — a top-level file that stopped halfway and exited 0, a serializer that wrote what its own reader refused, child processes that outlived the interpreter that started them, two compound values that compared equal because the comparison had no branch for them. Since then the work has been the business platform on this page, and the AI stack under it.
It is still early and the language surface may still change; each
deliberate break is listed in the changelog with its migration. The known
limits are written down and executable — a test suite runs one
probe per documented limitation and goes red when one stops being true,
naming the entry to strike. That exists because five of fourteen entries
were once found to be false, each fixed by a shipped phase in the same
tree, and no amount of re-reading had caught it. Live today: no closures,
repeated string concatenation is quadratic, and timezone data comes from
the host system. (Scientific notation was on that list —
1e20 lexed as a duration, answered 0 seconds and
exited 0. It is a literal as of 0.3.0, and the duration path that swallowed
it now refuses.) See the
changelog,
and
DOGFOOD.md
for the friction log kept while building real software in it.
Licence
gBASIC is Apache-2.0 — all of it. Every file declares that in its own header.
Write gBASIC programs, embed the interpreter, build a product on any of it and ship it closed-source. Nothing here restricts you, and that includes the whole spreadsheet engine, the spreadsheet-to-database pipeline and the SEC/EDGAR analysis suite.
Ten libraries were AGPL until 0.3.0, with a commercial licence offered beside
them. That split is retired rather than adjusted: a gBASIC library is
source, so load combined it into the caller's own program with no
linking exception — which pointed the obligation at the one direction only the
caller can see. Contributions are open now too, inbound=outbound, with no
contributor agreement to sign.
Full map and the argument in LICENSING.md.
The gBASIC book series
Every listing was run. Every printed release stays available.
These are books built with the same discipline as the language: each code listing is a real file, each transcript records actual output, and each edition names the exact gBASIC release used to produce it.
For new programmers
gBASIC: Learning Programming
Programming from nothing, at a prompt, for somebody who has never written a program. One animal-guessing game grows from three lines in Chapter 1 to a database by Chapter 9. Fourteen chapters.
Paperback · 454 pages · ISBN 9798177530765 · targets gBASIC 0.3.0
Find on Amazon ↗ Companion examples ↓ Pinned 0.3.0 release ↓
For experienced programmers
gBASIC: The Core Language
The whole language in one place: values, records, exact money, calendar-aware time, files, schedules, watchers, and actors. Eighteen chapters of short, runnable examples for building dependable tools.
Paperback · 339 pages · ISBN 9798161798768 · targets gBASIC 0.8.0
Why the older downloads remain: the checksums are printed on paper and cannot be corrected after publication. The 0.3.0 and 0.8.0 builds therefore remain first-class releases for readers, while new users should take the latest version offered at the top of this page.
Measured against the current set of 67 standard libraries, the 0.3.0
plain build loads 44 of the 67 and its -full
build loads 52 of the 67. For 0.8.0 those figures are
52 of the 67 and 60 of the 67, respectively.
Volume 1 uses the 0.3.0 full build; Volume 2 targets 0.8.0.
Errata
None reported yet. That is not a claim that there are none; it is a claim that nobody has told us. If you find something the book got wrong and it is not listed here, please say so — being told is the only way the list gets made, and a reader stuck because a page is wrong has no way of knowing it is the page.
Getting started
Download it and run it. There is no install step, no runtime to fetch, and nothing to set:
tar xzf gbasic-0.9.0-linux-x86_64.tar.gz
gbasic-0.9.0-linux-x86_64/bin/gbasic program.bas
Linux, x86-64, 2.1 MB. It is built against glibc 2.34 in a container on the oldest supported toolchain, so it runs on RHEL, Rocky and Alma 9, Ubuntu 22.04 LTS and later, Debian 12 and later, and anything newer — and the build refuses to publish an artifact whose floor has risen. It finds its own standard library wherever you unpack it.
That archive needs nothing at all, which is why it is the one to take first.
It carries the language and 53 of the 67 standard libraries —
dates, money, finance, accounting, lending, statistics, charts, frames and the
rest, and as of 0.8.0 a real SQL database, spreadsheets, XML, compression and
password hashing as well: SQLite, libxml2 and zlib are linked into the
binary, and password_hash uses a vendored yescrypt. It still
cannot fetch a URL.
If you want a database or the internet
Take gbasic-0.9.0-linux-x86_64-full.tar.gz instead — same
language, same libraries, plus HTTP, the web client, email and the crypto
builtins all working. 61 of the 67 standard libraries load in
it, and the eight it adds over the first download all want the same one thing:
libcurl.
It expects libcurl and OpenSSL to be on the machine — present by default on ordinary desktop and server installs, and their sonames are the same across every distribution listed above, which is measured rather than assumed. If one is missing the binary will not start, so on something minimal take the first download. It asks for two fewer libraries than 0.7.0 did, SQLite and zlib having moved inside the binary.
Spreadsheets (xlsx), XML and password_hash used to
be in neither download, because libxml2's and libxcrypt's sonames are
spelled differently on different distributions and a download linking them
would fail to start somewhere. That argument was about dynamic linking
and was never re-asked for the static case: as of 0.8.0 both downloads carry
all three, libxml2 and zlib linked statically from pinned sources and password
hashing from a vendored yescrypt, at no change to the libraries either binary
asks the machine for. Each archive ships
share/doc/gbasic/THIRD-PARTY-NOTICES.txt, since code compiled in
is code redistributed.
Which libraries need which download
A library is shipped whether or not the binary beside it can run it, and
load says so at once rather than failing at the first call —
XML support is not available in this build. The
reference
has the table, derived from the libraries' own load lines so
it cannot drift out of date. The short version:
-
Either download — 52 libraries. Everything pure gBASIC (dates, money,
finance, accounting, lending, deposits, credit, scoring, statistics, charts,
frames, notation, fake data, OTP, business rules and the rest), plus
everything that needs only what is now compiled in:
ownershipand the fourfinioreaders for XML, anddbframeandscreenerfor SQLite. -
The
-fulldownload only — 8, and every one of them for the same reason, libcurl:llm,agent,mcp,market,ari_advisor,edgar,insidersandmdna. -
Neither; build from source — 6. Four need GObject-Introspection
(
gtk,gtkui,datagrid,sourceeditor), one needs unixODBC (discovery) and one libpq (retrieval). Those four are a deployment problem rather than a packaging one — GTK needs typelibs, icon themes and a display that no binary can carry — which is why a distribution package, not a tarball, is the route to them.
The dependency is transitive, which is why a couple are surprising:
mdna reads XML filings and still wants libcurl, because it loads
llm; insiders likewise, through edgar.
Two load anywhere and then fail, which is worth knowing because the
failure comes at the call rather than at the load:
crypto and otp need OpenSSL. On the first download
they import cleanly and every function of either answers HMAC requires
OpenSSL (libcrypto); rebuild with it installed. They work in the
-full download. gpdf was a third until 0.8.0, needing
zlib for a compressed stream; zlib is compiled in now, so it works in both.
On Windows
Take gbasic-0.8.0-x64.msix — a signed installer, 4.3 MB,
also published as a
GitHub
release. Note the version: Windows is still on 0.8.0 while Linux
above is on 0.9.0. The Windows installer is built and signed by hand
on a Windows machine rather than in the Linux release container, so it follows
a release rather than arriving with it. Everything 0.9.0 fixed is therefore
still outstanding on Windows — including the nesting crash described under
Status, which matters most to a server and least to a desktop.
gbasic.exe is linked statically, so once installed it needs
nothing beside it: no runtime, no redistributable, nothing to put on the PATH
by hand.
Windows arrived in 0.7.0 and is a newer surface than the Linux build above. It is tested by its own suite — which runs on every platform, so a Windows-only assumption shows up on Linux too — but that testing is done by hand rather than in CI, so it is a weaker guarantee and worth knowing. Three things are deliberately absent and say so when you call them: POSIX signals, the prompt's line editor (arrow keys and history), and the GTK bindings. The port's status document records what was measured, including the places Windows and Linux genuinely differ.
From source
A C11 compiler, make and bison is the whole list:
git clone https://github.com/solifugus/gbasic.git
cd gbasic
./tools/check-deps.sh # says what is optional and how to install it
make
./gbasic program.bas
Install it so gbasic is on your PATH and finds its standard library with no environment setup:
sudo make install # or: make install PREFIX=$HOME/.local
Optional features are auto-detected via pkg-config; whatever is
missing simply turns that feature into a clean runtime error. Start with the
tutorial,
or the
spreadsheet cookbook,
whose every example is a real file the test suite runs.
Downloads & installation
- gbasic-0.3.0-linux-x86_64-full.tar.gzsha256 24c9912147edb8dbac23a778fdb87cf0aca0146e21cdb5b90106260af294b383
- gbasic-0.3.0-linux-x86_64.tar.gzsha256 4a5bdf1ee22e7a019fa1065bee024af1c6ba1dc33a3565eab07a63dd1183a3d6
- gbasic-0.8.0-linux-x86_64-full.tar.gzsha256 0c988c466663313eefbc9d0ee8da8e77ac87ec7dad16b490baa0bc85fe8102b9
- gbasic-0.8.0-linux-x86_64.tar.gzsha256 fd10ab8bff4bd674ae2f799aa6182d9e1ddfc466f48d8a6e5cd4ce799abdbed3
- gbasic-0.8.0-x64.msixsha256 a1b4133f5c19901506841b34b1a48bd83fd84f730c4f7de01f406cf1803854c2
- gbasic-0.9.0-linux-x86_64-full.tar.gzsha256 f84eba8b8872a6e6a6ba53bf2b7c8d97dcd0f013670964b91cf3826f27286420
- gbasic-0.9.0-linux-x86_64.tar.gzsha256 0bfe5f51ec31bfc2590ef21ba989701e35707e9e3e182fcc8cd8a1b6faaa3da9
- gbasic-learning-programming-examples.tar.gzsha256 e94d9f20bd36c032de08cf1eb66c8d2ffc22cececba22f26afa1d368745dd20c