Lynxer

Documentation

CLI

The Lynxer executable is lynxer. --install installs it under /usr/lib/lynxer and links /usr/bin/lynxer; --uninstall removes both; otherwise run it from where it was built.

make buildLynxer
lynxer/lynxer --help

--help (or -h, or no arguments at all) prints the usage summary and exits 0. The rest of this page is the authoritative description of every flag.

Running a program

lynxer program.lynx
lynxer --no-opt program.lynx    # run without the AST optimizer

Anything the parser does not recognise as a flag is treated as a file path, not an option. lynxer --bogus-flag therefore reports lynxer: file not found: '--bogus-flag' and exits 1.

A .lynxc argument is rejected explicitly: lynxer: bytecode files are no longer supported; compile the .lynx source with --compile instead.

Exit codes

CodeMeaning
0success (also --help, --version, a clean --lint)
1any error: a missing file, a parse error, a runtime error, an unsupported flag
130interrupted (SIGINT)

The AST optimizer

Before execution Lynxer rewrites the parsed program: constant folding, short-circuit simplification of constant and/or, and dead-branch elimination for a constant if, while (false), and iterate (0). The pass is semantics-preserving, so output and exit codes are identical with and without it. --no-opt (or -no-opt) disables it for the run; a compiled executable ignores its command line and always optimizes.

LYNXER_OPT_REPORT=1 prints one line of transformation counts to stderr after the program runs, for example optimizer: constant folds=10, short-circuits=2, dead branches=3.

Checking syntax

lynxer --lint program.lynx

--lint parses the file and runs conservative static checks without executing it. It checks known initializer, assignment, return, call, method, and constructor types; argument counts; unreachable statements; unused local variables and parameters; and conversions that may lose information. Warnings are printed but do not fail lint; static errors and syntax errors exit 1. Values and external calls whose types cannot be known are left to runtime checks. A clean file prints Lint OK: <path> (0 warning(s)) and exits 0. It requires exactly one file; otherwise it prints lynxer: --lint requires exactly one file argument.

Debugging

lynxer --debug program.lynx

The debugger pauses before the first source statement. Commands are step, continue, break [file:]line, delete [file:]line, locals, print name, backtrace, frame n, and detach. Breakpoints may be set at the initial pause before continuing. step enters function calls; frame 0 selects the currently stopped frame, with larger indexes moving toward its callers.

Printing the AST

lynxer --ast program.lynx

--ast lexes and parses the file and prints the resulting tree without running it: the header Lynxer AST / ===========, then a position-free, indented dump of every top-level function and its statements. Source line/column are omitted. A syntax error is reported with its source location and exits 1; the flag requires exactly one file argument.

The tree uses Lynxer's own node and field names, so it is not byte-comparable to an earlier implementation's --ast. Named-type declarations (struct, class, enum) are parse-time metadata held in the type registry rather than part of the returned program, so --ast shows the executable AST only.

Formatting

lynxer --format program.lynx
lynxer --format-oneline program.lynx

Both rewrite the file in place. --format gives canonical spacing and four-space indentation; --format-oneline collapses everything onto a single physical line, turning each // comment into the delimited ///.../// form so it survives next to code. Comments — line and //////// blocks — are preserved verbatim, and formatting is idempotent (running it twice changes nothing). The file is parsed first, so a syntax error is reported with its source location and the file is left untouched. Each flag requires exactly one file argument.

The formatter never changes tokens or reorders them; it only decides spacing and line breaks.

Interpreter self-check

lynxer --validate-executeable

Runs a built-in corpus of small programs that must either run cleanly or raise a specific source-located error, and prints ok <name> per case plus a summary. It exits 1 if any check fails. It needs no external files, so it also works from an installed binary. The alias --validate-executable is accepted.

Compiling to an executable

--compile produces one self-contained executable that embeds the program, every transitively imported .lynx module and .so library, and anything added with --include. It is the interpreter with the program embedded, so it runs on its own — the target machine does not need a separate Lynxer installation. There is no .lynxc bytecode path.

lynxer --compile app.lynx -o app
lynxer --compile app.lynx helpers.lynx vendor/lib.so \
        --include assets/message.txt -o app
FlagAliasesEffect
--compile-c, --c, -compilecompile the input files
--include <file>-iembed an extra module, native library, or data file
-o <name>--output, -name, --namename the output executable

The first .lynx input is the program; any further .lynx or .so input is embedded and importable by name. A --include file that is not .lynx/.so is embedded as data and read from the program with bundledFile(name) / bundledFiles(); see builtins. Without -o, the output is named after the program (app.lynx → app).

The result is a native executable for the host platform — on Linux, a standalone ELF. Inside it, --no-opt has no effect: the payload is always optimized.

Emitting a C ABI library

--emit-library builds a shared library (.so on Linux, .dll on Windows) that exposes a program's top-level exports as C symbols other languages can call. It needs a C++ compiler and the matching embedding runtime (liblynxer.so, or lynxer.dll plus its .dll.a import library on Windows), so Lynxer must be built first (make buildLynxer); the feature is unavailable from an unbuilt tree and fails with build Lynxer first when the runtime is missing. The emitted library embeds the program and needs only that runtime at run time. On Windows, put lynxer.dll beside the host executable or on the DLL search path. See embedding and windows.

lynxer --emit-library app.lynx -o libapp.so
lynxer --emit-library app.lynx --include helpers.lynx -o libapp.so
lynxer --emit-library app.lynx -o app.dll        # Windows
FlagAliasesEffect
--emit-library--shared-library, -emit-librarybuild an exporting shared library
--include <file>-iembed an extra module, native library, or data file
-o <path>--outputname the output library
--runtime <path>—use an explicit embedding runtime (liblynxer.so or lynxer.dll)
--cc <compiler>—C++ compiler used to build the library

See embedding for the export syntax and the C ABI mapping. Alongside the library, --emit-library writes a header with the exported prototypes (libapp.so → libapp.h); on Windows it also writes a .dll.a import library. make sdk stages lynxer.h, ffi_abi.h and the runtime under lynxer/build/sdk/{include,lib,bin}.

Listing the standard library

lynxer --list-stdlibs

Aliases: --stdlibs, -stdlibs, -list-stdlibs. It lists every module in the interpreter's stdlib/ directory together with its //// docstring, and exits 1 with No stdlib directory found. when that directory is missing. See modules for the docstring format.

Version, help, easter egg

FlagAliasesEffect
--version-v, -version, --vprint Lynxer <version>
--help-hprint the usage summary
--easterEgg-easterEgg, --idklmao, -wnwnerbcyunwrbygnubeuyxnqybxunprint the easter egg

The version string comes from the version key in lynxer/lynxer.config (currently 0.1.8.4).

Installing

sudo lynxer --install      # install into /usr/lib/lynxer, link /usr/bin/lynxer
sudo lynxer --uninstall    # remove the installed interpreter

--install copies the running executable (resolved through /proc/self/exe), its stdlib/ directory and lynxer.config into a self-contained tree under /usr/lib/lynxer, then creates the symlink /usr/bin/lynxer to the real binary. The symlink resolves through /proc/self/exe, so the installed interpreter finds its stdlib from any working directory — lynxer app.lynx works with no build tree present. Set LYNXER_PREFIX to use a prefix other than /usr (for example LYNXER_PREFIX=$HOME/.local), and pass the same value to --uninstall. Without write permission it prints the failure and a sudo hint and exits 1. --uninstall removes the symlink and the tree; when neither is present it reports why and exits 1.

On Windows the default prefix is %LOCALAPPDATA%\Programs\Lynxer, the launcher is a copy (bin\lynxer.exe) rather than a symlink, and the installed binary is lynxer.exe. See windows.

Removed with the bytecode backend

These are recognised and exit 1 with lynxer: '<flag>' was removed with the bytecode backend; use lynxer --compile instead:

--view-bytecode (aliases --inspect-bytecode, --disasm), --benchmark-compile (alias --bench-compile), --no-cache.

Environment variables

VariableEffect
LYNXER_OPT_REPORT=1print the optimizer counts to stderr after the run
LYNXER_GAME_HEADLESS=1run the game module without opening a window
LYNXER_GRAPHICS_HEADLESS=1run the graphics module without opening a window

LYNXER_SKIP_DISPLAY=1 is a build/test variable, not a run-time one: it makes make testLynxer skip the display and audio fixtures. See install.

See also