# fun **Repository Path**: funlang/fun ## Basic Information - **Project Name**: fun - **Description**: 一门轻量、可嵌入、以数据为中心的脚本语言,能力覆盖从 JSON/FD 数据处理一直到运行时编译 C 与直接编写机器码。 - **Primary Language**: Pascal - **License**: MIT - **Default Branch**: main - **Homepage**: https://www.funlang.org - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2026-08-31 - **Last Updated**: 2026-10-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Fun 语言 > 一门轻量、可嵌入、**以数据为中心**的脚本语言,能力覆盖从 JSON/FD 数据处理一直到运行时编译 C 与直接编写机器码。 **English**: [README.md](README.md) · **简体中文**(本文件) - 版本:**9.0** - 官网: - 作者:张卫东 <zwd@funlang.org> - 授权:MIT(另有商业授权条款,见下文「许可证」) > **版本 9.0**:这不是玩具或原型——一门经历了超过 15 年(2010 至今)持续演化与生产级验证的成熟语言,其前身可追溯至 Nuva(2006)与 TemplateScript(2005)。 --- ## 目录 - [为什么是 Fun](#为什么是-fun) - [核心特性](#核心特性) - [快速开始](#快速开始) - [体验 Fun 语言](#体验-fun-语言) - [能力阶梯](#能力阶梯) - [FD 数据格式](#fd-数据格式) - [外部函数接口(FFI)](#外部函数接口ffi) - [关键字别名](#关键字别名) - [嵌入运行时 fun.dll](#嵌入运行时-fundll) - [目录结构](#目录结构) - [构建](#构建) - [标准库](#标准库) - [示例](#示例) - [许可证](#许可证) --- ## 为什么是 Fun 一句话定位:**Fun 是一门从 JSON 一路写到机器码的脚本语言。** - 用同一个运行时,覆盖从高层数据处理(JSON/FD)到底层系统编程(FFI、运行时 C、JIT、机器码)的全谱系; - 自带 59 个用 Fun 自身编写的标准库模块,全栈能力打包进单个可嵌入的 `fun.dll`; - 在信创与 AI Agent 场景中,作为连接国产 OS、数据库、芯片与业务逻辑的轻量级胶水。 Fun 的设计出发点很简单:**数据与代码都应该是头等公民,而且一段脚本应该能在不离开语言的情况下一路触达机器底层。** 多数脚本语言要么停留在高层(好用,但在 FFI 处撞墙),要么要求你在性能关键处去写 C。Fun 把这条能力阶梯收敛进同一个运行时: - 用原生的 list / set / tree 集合处理 **JSON 与 FD** 数据; - 通过 **紧凑的 FFI 签名**调用任意原生代码; - 用内置的 Tiny C 编译器 **在运行时编译 C**; - 需要最紧的循环时,直接 **内联汇编 / JIT**; 这一切都可以从纯脚本出发,并打包进单个可嵌入的二进制。 Fun 使用 Pascal(Delphi / Free Pascal)编写。核心是一个小巧的树遍历解释器,带有动态值模型、对象系统、闭包与模块——刻意保持精简,以便既可嵌入为 `fun.dll`,也可作为独立的 `fun.exe` 运行。 --- ## 核心特性 - **动态、以数据为中心的值模型** —— 数字、字符串、时间、正则,以及头等集合:list(数组)、set 与 tree,构建于基于变体(variant)的运行时之上。 - **函数一等公民** —— 匿名函数、函数引用、柯里化、默认参数与可变参数,以及 `->` 管道。 - **对象系统** —— `class`、`this` / `base`、方法,以及属性的 get/set 分发(方法、getter、setter 统一通过 `.` 运算符分发)。 - **模块** —— `use 'file.fun'`(可 `as alias`)引入其它脚本;运行时跟踪模块状态与依赖。 - **正则作为值类型** —— 内嵌 PCRE 引擎,支持行内正则字面量(`/.../`)与匹配运算符(`=~`、`!~`)。 - **内置数据格式** —— JSON 与 **FD**,一种兼容 JSON、对人类与 AI 都友好的类 Markdown 格式(见[下文](#fd-数据格式))。 - **运行时 C 与机器码** —— 内置 Tiny C 编译器、JIT 与内联汇编(见[能力阶梯](#能力阶梯))。 - **紧凑的 FFI** —— 用简短的签名串调用 DLL/共享库函数(见 [FFI](#外部函数接口ffi))。 - **关键字别名** —— 规范英文关键字 + 可插拔的别名包(中文、法文、自定义),运行时选择(见[下文](#关键字别名))。 - **跨平台** —— Windows(32/64 位)、Linux、ARM 与 Windows CE。 - **双形态分发** —— 命令行 `fun.exe` 与嵌入运行时 `fun.dll`(导出简单的 `Run` 接口)。 - **自托管的标准库** —— 59 个用 Fun 自身编写的模块,覆盖 JSON/YAML/XML 到 ORM、异步、UI 与原生绑定。 --- ## 快速开始 运行脚本: ```text fun.exe # 运行脚本(.fun / .foo / .fxx) fun.exe -v # 显示编译与运行耗时 fun.exe -log[:log.txt] -gui # 输出到日志 / 以 GUI 模式运行 fun.exe -key:key_cn.ini # 加载关键字别名包 ``` Hello world(规范英文关键字): ```fun 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); ``` 运行: ```text > fun.exe fun/demo/fun.fun Hello, fun! 9 9 ``` --- ## 体验 Fun 语言 两个短小、自成一体的小片段,展示语言的两极(完整文件位于 `fun/demos/benchmarks/`)。 类的自定义运算符、一等函数、函数组合与管道: ```fun # 带自定义运算符的类 class Number(me) this['#'] = you -> (me + you) * me * you; end class; var $ = Number(2); ?. $ .# 3; # (2 + 3) * 2 * 3 = 30 # 一等函数、组合与管道 var f = a -> a * 2; var g = a -> a + 3; ?. g(f(1 + 2) + 3); # 12 ?. 1 + 2 | f + 3 | g; # 12(同样结果,用管道写) ``` 用正则与集合组合子把查询串解析成映射的数据管道: ```fun 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":""} ``` 完整版本见 [`test-high-level-language.fun`](fun/demos/benchmarks/test-high-level-language.fun)。 --- ## 能力阶梯 Fun 的特殊之处在于,一段脚本在无需独立工具链的情况下能走多远: | 层次 | 能力 | 支撑 | | ---------- | --------------------------------- | ------------------------------------------------ | | 高层 | JSON / FD 数据,list / set / tree | 原生集合与解析器 | | 原生调用 | `dll.getapi(name, 'signature')` | 紧凑签名约定的 FFI | | 运行时 C | `ccompile(code, ...)` | 内置 Tiny C 编译器(`libtcc.dll`/`.so`) | | JIT | `NewJit(...)` | 在 C(TCC)与汇编之间分发 | | 机器码 | `Assembly(code, ...).Load()` | 内联汇编、可执行内存分配 | 例如,`lib-tcc` 加载 `libtcc.dll`(Linux 上为 `libtcc.so`)并暴露 `tcc_new`、`tcc_compile_string`、`tcc_get_symbol` 等,从而你可以从字符串编译 C 源码并立即调用所得到的符号。`lib-asm` 分配可执行内存、写入机器码并调用之;`lib-jit` 则根据源码形态自动选择路径(`#!c` → TCC,否则走汇编)。 一段脚本可以同时混合两者:`NewJit` 在运行时编译 C **和**内联汇编,并且两者都能通过 `@toCallback` 直接回调回 Fun 函数。下面的片段对 `1..n` 求和——一次走汇编、一次走编译出的 C,两次都把结果交给 Fun 回调(完整版本见 [`test-jit-cb.fun`](fun/demos/benchmarks/test-jit-cb.fun)): ```fun use 'lib-jit.fun'; fun cb(a, b) # 由 JIT 编译出的代码回调的 Fun 函数 result = a * 2^32 + b; end fun; # 汇编路径 var asm = `#!asm i:i mov ecx, dword ptr [esp+04] @sum1ton push eax push edx mov eax, call eax `; var jit = NewJit(asm, names: [test: cb.@toCallback(nil, 'ii:i', true)]); ?. jit.Run(100000); # C 路径(用内置 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)); ``` > **平台说明**:上面的 `#!asm` 块是 32 位 x86(Windows)汇编(基于 `esp` 相对寻址、`ecx`)。`#!C` 路径可跨平台,但汇编片段与架构相关——在 Linux/ARM 上请改用 C 形式,或按目标 ABI 重写汇编。 --- ## FD 数据格式 FD 是一种 **对人类与 AI 都友好**、且与 JSON 互通的数据格式: - 兼容 JSON 的结构(嵌套键、数组); - 基于缩进、类 Markdown 的语法; - 可选的 SSE 压缩; - 与 JSON 双向转换(`@toJson`、`getJson`)。 ```text # FD 格式(类 Markdown:缩进 + 空格分隔) person name 张卫东 age 46 skills - pascal - c - fun # 等价的 JSON {"person": {"name": "张卫东", "age": 46, "skills": ["pascal", "c", "fun"]}} ``` 它的定义性特点是**自描述**:`lib-fd.fun` 中的解析器是由一份用 FD 本身写成的 BNF 文法(`fd.bnf.fd`)生成的。文法文件 `src/parse/bnf/fun.ebnf` 以同样的精神记录了语言文法。 --- ## 外部函数接口(FFI) Fun 通过**紧凑的签名串**约定暴露原生函数。标准库中的例子: ```fun 'kernel32'.getapi('VirtualAlloc', 'iiii:i'); // 4 个 int 参数 -> int 'tcc'.getapi('tcc_new', 'v:i'); // void 参数 -> int ``` 签名依次编码每个参数类型,然后是 `:`,最后是返回类型(`i`=int、`s`=string、`v`=void、`d`=double 等)。同一机制驱动 COM(`lib-winole`)、Windows API(`lib-winapi`)与 C 运行时。 --- ## 关键字别名 解释器的规范关键字是英文(`fun`、`if`、`then`、`loop`、`class`……)。由于关键字通过别名层(`ParseAlias`)解析,你可以在启动时提供**可插拔的别名包**: ```text fun.exe -key:key_cn.ini # 中文关键字(函数=fun、如果=if……) fun.exe -key:key_fr.ini # 法文关键字 ``` 已附带别名包:`key_cn.ini`(简体中文)、`key_fr.ini`(français)、`key_pua.ini`(自定义/私有区)。语言本身与关键字无关——别名是本地化/无障碍特性,而非另一种语言。 --- ## 嵌入运行时 fun.dll `fun.dll` 是嵌入运行时,用于把 Fun 脚本集成进第三方应用: ```pascal Run(PChar('script.fun')); // 编译脚本 Run(scriptContent); // 执行 Run(-1); // 释放 ``` - **免费使用(MIT)**:个人学习、内部工具,以及以开源许可证发布的产品。 - **商业使用**:将 `fun.dll` 嵌入闭源商业产品(exe/dmg/apk 或 SaaS 后端)需要商业授权,见 [`LICENSE.dll`](LICENSE.dll)。 --- ## 目录结构 ```text fun/ ├── src/ │ ├── core/ # 解释器核心(值模型、对象系统、流程控制、IO) │ ├── parse/ # 词法/语法解析器 │ │ └── bnf/ # 文法定义:fun.ebnf、yacc.y、lex.l(生成 .inc/.cod) │ ├── lib/ # 内建运行时库(winapi、winole、UI、host 等) │ ├── regex/pcre/ # 内嵌 PCRE 正则引擎 │ ├── 3rd/ # 第三方组件(KOL) │ ├── utils/ # 工具函数 │ └── prj/fun/ # 工程与构建脚本(funcmd.dpr,Delphi/FPC) ├── fun/ │ ├── lib/ # 标准库(纯 Fun 编写,59 个模块) │ ├── demo/ # 语言语法演示 │ ├── demos/ # 完整应用与基准测试 │ └── key_*.ini # 关键字别名包(CN / FR / PUA) └── LICENSE* # 授权文件 ``` --- ## 构建 Fun 核心为 Pascal,源码入口为 `src/prj/fun/funcmd.dpr`。 工具链路径集中在一个文件里:[`src/prj/fun/setenv.bat`](src/prj/fun/setenv.bat)。只需修改一次,指向你安装的 Delphi / Free Pascal;或在构建前预先设置 `FPC`、`DELPHI2006`、`DELPHI2009` 环境变量——每个 `make-*.bat` 脚本都会调用它并读取这些路径。支持的工具链: | 目标 | 脚本 | 工具链 | | ------------------- | -------------------------------------- | ------------- | | Windows 32 位 | `src/prj/fun/make-2006.bat` / `-2009.bat` | Delphi 2006 / 2009 | | Linux(i386) | `make-linux.bat` | FPC 2.4.0 | | Linux x86_64(原生) | `src/prj/fun/make-linux-x86_64.sh` | FPC(本机)+ gcc | | Linux / ARM | `make-arm-linux.bat` | FPC 交叉 | | Windows 64 位 | `make-win64.bat` | FPC 交叉 | | Windows CE / ARM | `make-wince.bat` | FPC 交叉 | 以 `-DFunDll` 编译可生成嵌入运行时 `fun.dll`,导出 `Run` 接口。构建产物已由 `.gitignore` 覆盖,不纳入版本控制。 完整文法定义见 [`src/parse/bnf/fun.ebnf`](src/parse/bnf/fun.ebnf)。 ### 第三方依赖(仓库未携带) 为控制体积与许可,仓库**不携带**下列第三方源码/二进制。缺失时构建脚本会**降级并打印提示**,不会中断: | 组件 | 放到哪里 | 从哪里获取 | 由谁构建 | | ---- | -------- | ---------- | -------- | | PCRE 8(8-bit) | `src/3rd/pcre-8/` | (8.x 源码,如 8.45) | Linux:`make-linux-x86_64.sh` 用 gcc 直接编译;Windows(i386):`src/regex/pcre/make.bat`(Borland bcc32 + make,可 `-Dpcresrc=<路径>`) | | Tiny C Compiler 0.9.27 | `src/3rd/tcc/` | savannah/nongnu 的 tinycc 发行包,或 repo.or.cz 的 `release_0_9_27` tag | `make-linux-x86_64.sh`,产出 `libtcc.so`(放在 `funcmd` 旁) | | Windows 运行期 DLL | `fun/libtcc.dll`、`fun/zlib.dll` | 官方发行包或自行构建 | 无需构建;运行时与 `fun.exe` 同目录,供 `ccompile` / `NewJit` 使用 | - 放好目录后,`cd src/prj/fun && ./make-linux-x86_64.sh` 会自动编译 PCRE 与 TCC。 - 也可以显式指定路径:`PCRE_SRC=/path/to/pcre TCC_SRC=/path/to/tcc ./make-linux-x86_64.sh`。 - 缺 PCRE → 构建不带正则(`-dRegex` 关闭);缺 TCC → 无 `libtcc.so`,`lib-tcc` 不可用。两者都不影响核心解释器。 --- ## 标准库 标准库位于 [`fun/lib`](fun/lib),以纯 Fun 编写(59 个模块): > 总代码量约 6,000 行逻辑代码(不含注释),平均每个模块约 100 行——59 个模块全部用 Fun 自身写成。 - **数据**:`lib-json`、`lib-yaml`、`lib-xml`、`lib-base64`、`lib-cstruct`、`lib-md5`、`lib-crypt` - **集合 / 算法**:`lib-set`、`lib-tree`、`lib-stack`、`lib-dyns`、`lib-math` - **文本**:`lib-string`、`lib-regex`、`lib-match`、`lib-unicode` - **系统 / IO**:`lib-file`、`lib-os`、`lib-host`、`lib-time`、`lib-cmdline`、`lib-proc` - **网络**:`lib-winsock`、`lib-ajax`、`lib-jsonrpc` - **数据库**:`lib-orm`、`lib-orm-pro`、`lib-orm-gen`、`lib-orm-rpc`、`lib-orm-cte`、`lib-ado`、`lib-ado-schema` - **原生 / 底层**:`lib-winapi`、`lib-winole`、`lib-tcc`(C 编译)、`lib-jit`、`lib-asm`、`lib-asm-pro` - **UI**:`lib-ui`、`lib-ui-base`、`lib-dialog`、`lib-trayicon` - **并发 / 异步**:`lib-async`、`lib-jsasync`、`lib-bind`、`lib-message` > **平台拆分惯例**:Windows 实现保留原名,Linux 后端为 `-lnx.fun`,两者共用的逻辑放在 > `-base.fun`(不计入上面的模块数)。例如 `lib-proc` / `lib-proc-lnx`, > 以及 `lib-unicode-base` + `lib-unicode` / `lib-unicode-lnx`。 --- ## 示例 - [`fun/demo`](fun/demo) —— 语法演示:函数、闭包、柯里化、类、对象、集合、正则与异常处理。 - [`fun/demos`](fun/demos) —— 完整应用与基准测试:记事本 `notepad--`、`odbc-search`,以及性能 / 汇编 / JIT 基准。 --- ## 许可证 Fun 采用 **双授权** 模式: 1. **开源许可证(MIT)** —— 见 [`LICENSE`](LICENSE)。允许自由使用、修改、分发源码,只要保留版权声明。 2. **商业授权**(额外条款,仅适用于下列闭源分发场景): - 对内核做实质性修改并以闭源形式商业化分发 —— 见 [`LICENSE.custom`](LICENSE.custom); - 将 `fun.dll` 嵌入闭源商业产品或 SaaS 后端 —— 见 [`LICENSE.dll`](LICENSE.dll)。 **简单说明**:内部使用、学习研究,以及以开源方式(MIT/GPL 等)发布修改版均免费。只有把修改过的内核或 `fun.dll` 当作闭源商业产品对外售卖时才需要付费授权。商业授权为一次性授权,按项目/公司计费,请联系 <zwd@funlang.org>。 --- © 2010-2026 张卫东 <zwd@funlang.org>