Skip to content

Repository files navigation

Fun Language

A lightweight, embeddable, data-centric scripting language whose reach extends from JSON/FD data all the way down to runtime-compiled C and raw machine code.

English (this file) · 简体中文

Version 9.0: this is not a toy or a prototype — a mature language refined and production-validated for over 15 years (2010–present), with a lineage that reaches back through Nuva (2006) and TemplateScript (2005).


Table of contents


Why Fun

In one sentence: Fun is a scripting language that goes all the way from JSON to machine code.

  • One runtime spans the whole spectrum, from high-level data work (JSON/FD) down to low-level system programming (FFI, C at runtime, JIT, machine code);
  • 58 standard-library modules written in Fun itself ship together as one embeddable fun.dll;
  • in AI-agent and domestic-IT scenarios, it is the lightweight glue connecting OSes, databases, chips, and business logic.

Fun is designed around a simple premise: data and code should be equally first-class, and a script should be able to reach down to the machine without leaving the language.

Most scripting languages either stay high-level (easy, but you hit a wall at the FFI) or force you to write C for performance-critical parts. Fun collapses that ladder into one runtime:

  • Manipulate JSON and FD data with native list / set / tree collections;
  • call arbitrary native code through a compact FFI signature;
  • compile C at runtime with a bundled Tiny C Compiler;
  • drop to inline assembly / JIT for the tightest loops;

all from pure script, in a single embeddable binary.

Fun is written in Pascal (Delphi / Free Pascal). The core is a small tree-walking interpreter with a dynamic value model, an object system, closures, and modules — deliberately compact so it can be embedded as fun.dll or run as a standalone fun.exe.


Core features

  • Dynamic, data-centric value model — numbers, strings, time, regex, and first-class collections: list (array), set, and tree, built on a variant-based runtime.
  • First-class functions — anonymous functions, function references, currying, default and variadic parameters, and -> pipelines.
  • Object systemclass, this / base, methods, and property get/set dispatch (methods, getters and setters are dispatched uniformly through the . operator).
  • Modulesuse 'file.fun' (optionally as alias) pulls in other scripts; the runtime tracks module state and dependencies.
  • Regex as a value type — bundled PCRE engine with inline regex literals (/.../) and match operators (=~, !~).
  • Built-in data formats — JSON and FD, a JSON-compatible, Markdown-like format friendly to humans and to AI (see below).
  • Runtime C & machine code — bundled Tiny C Compiler, JIT, and inline assembly (see the capability ladder).
  • Compact FFI — call DLL/shared-library functions with a terse signature string (see FFI).
  • Keyword aliasing — a canonical English keyword set with pluggable alias packs (Chinese, French, custom) selected at runtime (see below).
  • Cross-platform — Windows (32/64-bit), Linux, ARM, and Windows CE.
  • Dual distribution — command-line fun.exe and embeddable runtime fun.dll exporting a simple Run interface.
  • Self-hosted standard library — 58 modules written in Fun itself, from JSON/YAML/XML to ORM, async, UI, and native bindings.

Quick start

Run a script:

fun.exe <file.fun>            # run a script (.fun / .foo / .fxx)
fun.exe -v <file.fun>         # show compile & run timing
fun.exe -log[:log.txt] -gui   # log output / run in GUI mode
fun.exe -key:key_cn.ini       # load a keyword alias pack

Hello world (canonical keywords):

fun foo()
  ?. 'Hello, fun!';
end fun;

foo();

fun max(a, b)
  if a > b then
    result = a;
  else
    result = b;
  end if;
end fun;

?. max(1, 9);
?. max(9, 1);

Run:

> fun.exe fun/demo/fun.fun
Hello, fun!
9
9

A taste of Fun

Two quick, self-contained snippets that show both ends of the language (the full files live in fun/demos/benchmarks/).

Custom operators on classes, first-class functions, composition, and pipelines:

# a class with a custom operator
class Number(me)
  this['#'] = you -> (me + you) * me * you;
end class;
var $ = Number(2);
?. $ .# 3;            # (2 + 3) * 2 * 3 = 30

# first-class functions, composition & pipelines
var f = a -> a * 2;
var g = a -> a + 3;
?. g(f(1 + 2) + 3);   # 12
?. 1 + 2 | f + 3 | g; # 12 (same, pipelined)

A data pipeline that turns a query string into a map using regex and collection combinators:

use 'lib-regex.fun';
use 'lib-set.fun';
var q = 'a=1&b=2&c=';
var parseQuery = s -> (s | split(/&/) | map(sp_eq) | fromPairs);
?. parseQuery(q).@toJson(1);  # {"a":"1","b":"2","c":""}

See test-high-level-language.fun for the complete version.


The capability ladder

What makes Fun unusual is how far a script can go without a separate toolchain:

Layer Facility Backing
High-level JSON / FD data, list / set / tree native collections and parsers
Native calls dll.getapi(name, 'signature') FFI with a compact signature convention
C at runtime ccompile(code, ...) bundled Tiny C Compiler (libtcc.dll)
JIT NewJit(...) dispatch between C (TCC) and assembly
Machine code Assembly(code, ...).Load() inline assembly, executable memory allocation

For example, lib-tcc loads libtcc.dll and exposes tcc_new, tcc_compile_string, tcc_get_symbol, ... so you can compile C source from a string and call the resulting symbol immediately. lib-asm allocates executable memory, writes machine code, and invokes it; lib-jit picks the right path based on the source (#!c → TCC, otherwise assembly).

A single script can mix both: NewJit compiles C and inline assembly at runtime, and each can call straight back into a Fun function (@toCallback). The snippet below sums 1..n — once via assembly, once via compiled C — and hands the result to a Fun callback both times (full version: test-jit-cb.fun):

use 'lib-jit.fun';
fun cb(a, b)          # a Fun callback called from JIT'd code
  result = a * 2^32 + b;
end fun;

# assembly path
var asm = `#!asm i:i
    mov ecx, dword ptr [esp+04]
    @sum1ton
    push eax
    push edx
    mov  eax, <test>
    call eax
`;
var jit = NewJit(asm, names: [test: cb.@toCallback(nil, 'ii:i', true)]);
?. jit.Run(100000);

# C path (compiled at runtime with bundled TCC)
var c = `#!C ii:i
  int sum(int n, int (*test)(int, int)) {
    long long s = 0;
    for (int i = 1; i <= n; i++) s += i;
    test((int)(s >> 32), (int)(s & 0xFFFFFFFF));
    return 1;
  }
`;
jit = NewJit(c);
?. jit.call(100000, cb.@toCallback(nil, 'ii:C', true));

Platform note: the #!asm block above is 32-bit x86 (Windows) assembly (esp-relative args, ecx). The #!C path is portable, but the assembly snippet is architecture-specific — on Linux/ARM use the C form or rewrite the assembly for the target ABI.


The FD data format

FD is a data format designed to be readable by humans and by AI, and interoperable with JSON:

  • JSON-compatible structure (nested keys, arrays);
  • indentation-based, Markdown-like syntax;
  • optional SSE compression;
  • bidirectional conversion with JSON (@toJson, getJson).
# FD format (Markdown-like: indentation + space-separated keys)
person
  name Zhang Weidong
  age 46
  skills
    - pascal
    - c
    - fun

# equivalent JSON
{"person": {"name": "Zhang Weidong", "age": 46, "skills": ["pascal", "c", "fun"]}}

Its defining trait is that FD is self-describing: the parser in lib-fd.fun is generated from a BNF grammar written in FD itself (fd.bnf.fd). The grammar file src/parse/bnf/fun.ebnf documents the language grammar in the same spirit.


Foreign function interface

Fun exposes native functions through a compact signature-string convention. Examples drawn from the standard library:

'kernel32'.getapi('VirtualAlloc', 'iiii:i');  // 4 int args -> int
'tcc'.getapi('tcc_new', 'v:i');               // void arg -> int

The signature encodes each argument's type, then :, then the return type (i = int, s = string, v = void, d = double, etc.). The same mechanism drives COM (lib-winole), the Windows API (lib-winapi), and the C runtime.


Keyword aliasing

The interpreter's canonical keywords are English (fun, if, then, loop, class, ...). Because keywords are resolved through an alias layer (ParseAlias), you can supply pluggable alias packs at startup:

fun.exe -key:key_cn.ini   # Chinese keywords (函数=fun, 如果=if, ...)
fun.exe -key:key_fr.ini   # French keywords

Included packs: key_cn.ini (简体中文), key_fr.ini (français), and key_pua.ini (custom/private-use). The language itself is keyword-neutral — aliases are a localization/accessibility feature, not a different language.


Embedding with fun.dll

fun.dll is the embeddable runtime for integrating Fun scripts into third-party applications:

Run(PChar('script.fun'));  // compile the script
Run(scriptContent);        // execute it
Run(-1);                   // release
  • Free use (MIT): personal study, internal tools, and products released under an open-source license.
  • Commercial use: embedding fun.dll in a closed-source commercial product (exe/dmg/apk or a SaaS backend) requires a commercial license — see LICENSE.dll.

Repository layout

fun/
├── src/
│   ├── core/          # Interpreter core (value model, object system, control flow, IO)
│   ├── parse/         # Lexer/parser
│   │   └── bnf/       #   Grammar: fun.ebnf, yacc.y, lex.l (generates .inc/.cod)
│   ├── lib/           # Built-in runtime library (winapi, winole, UI, host, etc.)
│   ├── regex/pcre/    # Bundled PCRE regex engine
│   ├── 3rd/           # Third-party components (KOL)
│   ├── utils/         # Utility functions
│   └── prj/fun/       # Projects & build scripts (funcmd.dpr, Delphi/FPC)
├── fun/
│   ├── lib/           # Standard library (pure Fun, 58 modules)
│   ├── demo/          # Language syntax demos
│   ├── demos/         # Full applications & benchmarks
│   └── key_*.ini      # Keyword alias packs (CN / FR / PUA)
└── LICENSE*           # License files

Building

The Fun core is Pascal; the source entry point is src/prj/fun/funcmd.dpr.

Toolchain locations are centralized in one file: src/prj/fun/setenv.bat. Edit it once to point at your installed Delphi / Free Pascal, or set the FPC, DELPHI2006, DELPHI2009 environment variables beforehand — every make-*.bat script calls it and picks up the paths. Supported toolchains:

Target Script Toolchain
Windows 32-bit src/prj/fun/make-2006.bat / -2009.bat Delphi 2006 / 2009
Linux (i386) make-linux.bat FPC 2.4.0
Linux / ARM make-arm-linux.bat FPC cross
Windows 64-bit make-win64.bat FPC cross
Windows CE / ARM make-wince.bat FPC cross

Building with -DFunDll produces the embeddable runtime fun.dll, which exports the Run interface. Build artifacts are covered by .gitignore and are not versioned.

The full grammar is defined in src/parse/bnf/fun.ebnf.


Standard library

The standard library in fun/lib is written in pure Fun (58 modules):

In total roughly 6,000 lines of logical code (excluding comments), about 100 lines per module — all 58 modules written in Fun itself.

  • Data: lib-json, lib-yaml, lib-xml, lib-base64, lib-cstruct, lib-md5, lib-crypt
  • Collections / algorithms: lib-set, lib-tree, lib-stack, lib-dyns, lib-math
  • Text: lib-string, lib-regex, lib-match, lib-unicode
  • System / IO: lib-file, lib-os, lib-time, lib-cmdline, lib-proc
  • Network: lib-winsock, lib-ajax, lib-jsonrpc
  • Databases: lib-orm, lib-orm-pro, lib-orm-gen, lib-orm-rpc, lib-orm-cte, lib-ado, lib-ado-schema
  • Native / low-level: lib-winapi, lib-winole, lib-tcc (C compile), lib-jit, lib-asm, lib-asm-pro
  • UI: lib-ui, lib-ui-base, lib-dialog, lib-trayicon
  • Concurrency / async: lib-async, lib-jsasync, lib-bind, lib-message

Examples

  • fun/demo — syntax demos: functions, closures, currying, classes, objects, sets, regex, and exception handling.
  • fun/demos — full applications and benchmarks: the notepad-- editor, odbc-search, and performance/assembly/JIT benchmarks.

License

Fun is distributed under a dual-license model:

  1. Open-source (MIT) — see LICENSE. You are free to use, modify, and redistribute the source, provided the copyright notice is retained.
  2. Commercial license (additional terms that apply only to these closed-source redistribution cases):
    • Substantially modifying the kernel and commercially distributing the result closed-source — see LICENSE.custom;
    • Embedding fun.dll in a closed-source commercial product or SaaS backend — see LICENSE.dll.

In short: internal use, study/research, and releasing modified versions under an open-source license (MIT/GPL etc.) are all free. A commercial license is needed only when you sell a modified kernel or fun.dll as a closed-source commercial product. Commercial licenses are one-time, priced per project/company — contact <zwd@funlang.org>.


© 2010-2026 Zhang Weidong <zwd@funlang.org>

About

A lightweight, embeddable, data-centric scripting language whose reach extends from JSON/FD data all the way down to runtime-compiled C and raw machine code.

Topics

Resources

Contributing

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages