Verified scope: Linux x86-64, local source projects and prebuilt BASIL libraries. Tooling requirements also describe future remote resolution/locking. Package format and ABI specify this release.
Standard-library declaration catalog
: basic api works without
a manifest or native SDK and supplies IDE discovery from parsed declarations.
Build and run
build/selfhost/stage3 --help
build/selfhost/stage3 help build
build/selfhost/stage3 api
build/selfhost/stage3 build hello.bas -o build/hello
build/selfhost/stage3 run --manifest examples/space-invaders/basic.toml -- --help
build without a source reads basic.toml in the working directory. --manifest
selects another manifest; relative paths inside it resolve from its own directory.
Default outputs are <project>/build/<name> or <name>.blib for libraries.
An explicit -o/--output resolves from the invoking directory. run accepts only
executables, inherits terminal input/output, waits and returns the child’s exit
status. Arguments after -- go to the program, including --help.
Help is generated from the same public command declaration as parsing and works
before any manifest/source access. Usage errors print relevant usage to stderr
and exit 2; build/data errors exit 1. Help/version go to stdout. The compiler
version is 0.1.0; package, manifest and ABI versions are separate.
Compatibility remains: [--emit-object|--emit-library|--debug|--debug-lines] source.bas > binary, and no arguments compiles stdin to stdout. A legacy
.bas invocation emits bytes; build writes the output file and prints its path.
The frozen compact checkpoint builds the maintained core; that core builds the
public front end. Stage 2 and stage 3 compare the same public front end.
Manifest
manifest-version = 1
[project]
name = "my-app"
version = "0.1.0"
[toolchain]
compiler = ">=0.1.0, <0.2.0"
[target]
kind = "executable" # or "library"
root = "src/main.bas"
[dependencies]
Raylib = {path = "../raylib/build/raylib.blib", version = "0.1.0"}
IMPORT Raylib resolves the declaring project’s dependency alias. Alternatively,
path names a source project directory containing its own basic.toml.
Quoted source imports retain their existing relative-file behavior. Source
projects compile from current sources each build; no stale binary cache replaces
edits. Dependency aliases are ASCII BASIL identifiers, compared without case.
Unresolved aliases, wrong target kinds, ambiguous aliases and conflicting builds
of the same package name are diagnosed. Optional dependency versions assert an
exact package version. Compiler constraints accept exact MAJOR.MINOR.PATCH or
comma-separated =, <, <=, >, >= comparisons, checked transitively.
The manifest reader implements a project TOML subset, not the planned general TOML library: strings/escapes, integers, Booleans, arrays, inline tables, dotted keys, headers and comments. Arrays of tables, multiline strings, date/time and floating scalar literals are rejected. Unknown schema fields are errors; other platforms’ native tables are ignored. Manifest limit: 2 MiB. Source/import namespace rules remain those of the language.
Library manifests may declare:
[project]
name = "wrapper"
version = "0.1.0"
licenses = ["LICENSE.txt"]
[target]
kind = "library"
root = "src/wrapper.bas"
[native.linux-x86_64]
archives = ["vendor/libwrapper.a", "vendor/libupstream.a"]
system-libraries = ["SDL2", "GL", "m", "dl", "pthread"]
Declared/referenced archives and notices travel inside .blib. Native identity
currently means archive basename; equal hashes merge, unequal bytes under one
identity fail. This deliberately rejects ambiguous builds rather than picking
one. Prebuilt transitive dependencies need no original paths or source projects.
BASIL runtime state/helpers link once; archive members are selected by references.
Generic templates remain checked source input for consumer specialization.
Nongeneric bodies are precompiled, with checked import stubs in their interfaces.
Current ABI v15 retains v14 suspension/cancellation effects, supported resource
cleanup hooks, checked callables and compatible profile contracts. Native resource
owners and owning containers retain source-level borrow/transfer rules across
compiled libraries; see compiled resource interfaces
.
Scope-bound task/channel capabilities retain their source restrictions: channel
owners cannot escape, and task handles have no concrete public signature spelling.
Foreign C interfaces and anonymous CALLBACK/NOSUSPEND adaptation remain separate
restricted boundaries.
The GTK 4 .blib and demo use this path with declared GTK/GLib system dependencies and a bundled static adapter/readiness archive.
Extracted verified interfaces/archive blobs live under the invoking project’s
build/.basic/packages/<id>/. This is build output, not a remote download cache.
Git/registry dependencies, lockfiles and shared incremental compilation caches
remain future work. Put mutually dependent library modules in one project;
cycles between independently packaged libraries are currently rejected. Library
imports must be relative within their project; cross-project imports use the
declared bare alias. Absolute/cross-project quoted imports are rejected when
packaging rather than persisting producer-machine paths.
Public argument API
Command declares Options, Positionals and child Commands. CommandOption
has Name, optional one-character Short, Description, Kind, Required,
optional typed Default, and Global. CommandPositional has name, description,
kind and required status. ArgumentKind is Text/IntegerValue/RealValue/BooleanValue.
Use CommandFlag for a Boolean with default FALSE. Default constructors are
ArgumentText, ArgumentInteger, ArgumentReal, ArgumentBoolean.
ParseArguments(command) reads process arguments and skips the executable.
The explicit ParseArguments(command, arguments) overload remains pure and
expects the executable to have already been excluded. CommandLineParameters()
returns that independent argument list; raw CommandLineArguments() still includes
index zero. Process access declares IoError; explicit parsing does not.
TextOption, IntegerOption, RealOption, BooleanOption supply ordinary typed
defaults without boxing at call sites. For example,
IntegerOption("frames", Default=0, Min=0, Max=360000, Help="Frame limit").
Integer bounds are inclusive and validated for defaults and supplied values.
TextArgument, IntegerArgument, RealArgument, BooleanArgument read the
Options/Positionals dictionaries and raise ArgumentError for missing/mismatched
values; they never silently turn a typo into a default or FALSE.
The parse result is ArgumentResult.Parsed or .Help; invalid input raises
ArgumentError {Message, CommandPath, Usage} (and checked StringError).
ParsedArguments holds CommandPath, Options/Positionals and Forwarded with ordinary independent-value semantics. RenderHelp remains
pure. PrintArgumentHelp(help) prints and returns 0; ReportArgumentError(error)
prints to stderr and returns 2. Neither exits the process; the caller decides
whether to return, continue, or perform additional cleanup.
For ordinary applications, declare a record instead of a command tree:
TYPE Options FORMAT CommandSettings {Name="demo",Description="A typed command"}
Frames AS INTEGER=0 FORMAT OptionSettings {Min=0,Max=360000,Help="Frame limit"}
Hidden AS BOOLEAN=FALSE
Output AS OPTIONAL OF STRING=NONE FORMAT OptionSettings {Name="output",Short="o"}
END TYPE
TRY
SELECT TYPE ParseArguments(Options {})
CASE ArgumentResult.Help AS help
RETURN PrintArgumentHelp(help)
CASE ArgumentResult.Parsed AS parsed
options=ArgumentOptions(parsed) AS Options
PRINT options.Frames
END SELECT
CATCH usage AS ArgumentError
RETURN ReportArgumentError(usage)
END TRYDefaults are ordinary record values; the supplied prototype must be initialized. Field types currently supported: STRING, INTEGER, DOUBLE, BOOLEAN and their OPTIONAL forms. Absent optional flags produce NONE; explicit empty text remains SOME(""). Options default to the lowercase physical field name. OptionSettings supports Name, Short, Help, Required and inclusive integer Min/Max. The TYPE header requires CommandSettings; format options retain ordinary nominal module identity. Shared wire names are respected internally; read/write-excluded fields and unsupported nested/resource fields are rejected. Custom converting serialization contracts are not a supported CLI type-conversion mechanism.
ArgumentOptions(parsed) AS T constructs a complete
independent owner through public checked serialization adapters. Dynamic command
trees may also use it for a selected typed options record (as the compiler does);
missing required/nondefaulted fields and wrong scalar types remain errors.
Help is still a normal result, so no fabricated default record is exposed as a
successful parse. SELECT TYPE checks coverage and binds the selected fresh result
without copying its payload. Subcommands/positionals/forwarding remain the dynamic Command
API; the typed record convenience declares options only.
Measured project-build optimizations preserve full checks on every invocation; persistent incremental/prelude caching remains planned.
Compiler and SDK identity
basic toolchain prints JSON identifying the actual running compiler/hash, target,
BASIL ABI and resolved native SDK root. It works before loading a project and can
help diagnose an incomplete SDK. Incompatible .blib diagnostics show producer and
consumer schema/target/ABI plus the command to rebuild the producer from source.
A .blib file is not a substitute for the source needed by that rebuild command.