a small, fast scripting language and bytecode vm written in portable c11.
flint is deliberately small.
no llvm. no cmake. no configure script. no dependency jungle.
just c11, libc, libm, a bytecode vm, and enough language to get things done.
make release
./flint -e 'print(1 + 2)'- portable c11
- stack-based bytecode vm
- 64-bit nan-boxed values
- single-pass pratt parser
- mark-and-sweep garbage collector
- closures with captured locals
- lists, tables, strings and modules
- bytecode verifier
- computed-goto interpreter
- standard library
- human and json diagnostics
- no runtime dependencies beyond libc and libm
the implementation is small enough to read.
that's intentional. large codebases have enough fans already.
to build in place:
git clone https://github.com/programmersd21/flint
cd flint
make releasethe binary is ./flint.
to install, so flint is on your PATH:
make installwhich puts the binary in ~/.local/bin and the standard library in
~/.flint/stdlib. PREFIX, BINDIR, LIBDIR and DESTDIR all override
that, for packaging:
make install PREFIX=/usr/local LIBDIR=/usr/local/share/flint/libthere is nothing to uninstall beyond make uninstall -- no runtime, no shared
libraries, no configuration, no build directory left behind. the whole thing is
one binary and seven .fl files.
flint searches for its standard library in this order:
$FLINT_STDLIB an explicit override, wins over everything
<executable>/lib beside the binary
~/.flint/stdlib where `make install` puts it
the third entry is what make install relies on, because a binary on PATH
has no lib/ beside it. if you move the binary somewhere without lib/ next
to it, either keep ~/.flint/stdlib or set FLINT_STDLIB.
fn counter() {
let n = 0
fn next() {
n += 1
return n
}
return next
}
let c = counter()
print(c())
print(c())
1
2
modules are just files, and each one has its own namespace:
# shapes.fl
let tax = 0.2 # private to this file
export fn taxed(amount) { # public
return amount * (1 + tax)
}
# main.fl
import "shapes.fl"
print(shapes.taxed(10)) # 12
print(shapes.tax) # nil -- a private name is not there
two modules can both have a private tax and neither can see or overwrite
the other's, which was not true before 0.6.0.
| module | purpose |
|---|---|
math |
math functions and constants |
random |
random numbers and shuffling |
time |
clocks and time utilities |
fs |
files and directories |
path |
path manipulation |
collections |
collection helpers |
json |
parsing and serialization |
the library is plain flint code where possible.
no package manager. no registry. no twelve-layer dependency tree to print a number.
source
│
▼
pratt parser
│
▼
bytecode
│
▼
verifier
│
▼
stack vm
the parser emits bytecode directly.
values fit in 64 bits. closures capture locals. memory is managed by a mark-and-sweep collector.
there is no giant intermediate representation sitting around because apparently compilers enjoy paperwork.
the default interpreter uses a portable C11 dispatch loop.
for compilers that support computed goto:
make flint-gotobenchmark both:
make bench
make bench-gotoresults are kept in bench/RESULTS.md.
measurements are preferred over benchmark folklore. the cpu has already suffered enough.
flint --check program.fl
flint --error-format=human program.fl
flint --error-format=json program.fl
flint --dump-bytecode program.fldiagnostics include source locations and stable error codes.
the verifier checks generated bytecode before execution, including opcodes, operands, indices and jump targets.
because executing invalid bytecode and hoping for the best is not a runtime strategy.
make check
make stressmake check performs a clean release build and runs the test suites.
make stress runs with aggressive garbage collection under ASan and UBSan.
other useful targets:
make debug
make test
make unit
make diagnostic-test
make bench
make lint
make fmt
make fmt-checkif make stress finds something, congratulations. the garbage collector has opinions.
docs/language.md| language referencedocs/syntax.md| syntax and expressionsdocs/functions.md| functions and closuresdocs/data.md| strings, lists and tablesdocs/modules.md| modulesdocs/library.md| standard librarydocs/diagnostics.md| diagnosticsdocs/internals.md| vm internalsARCHITECTURE.md| architectureSPEC.md| language specificationexamples/| examples
keep changes focused.
if you find a bug, add a test.
before opening a pull request:
make check
make stresssee CONTRIBUTING.md.
if a patch adds a dependency, explain why c could not do the job.
MIT. see LICENSE.