Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
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);
- 62 standard-library modules written in Fun itself ship together as one embeddable `fun.dll`;
- 63 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.**
Expand Down Expand Up @@ -70,7 +70,7 @@ Fun is written in Pascal (Delphi / Free Pascal). The core is a small tree-walkin
- **Keyword aliasing** — a canonical English keyword set with pluggable alias packs (Chinese, French, custom) selected at runtime (see [below](#keyword-aliasing)).
- **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** — 62 modules written in Fun itself, from JSON/YAML/XML to ORM, async, UI, and native bindings.
- **Self-hosted standard library** — 63 modules written in Fun itself, from JSON/YAML/XML to ORM, async, UI, and native bindings.

---

Expand Down Expand Up @@ -371,7 +371,7 @@ fun/
│ ├── utils/ # Utility functions
│ └── prj/fun/ # Projects & build scripts (funcmd.dpr, Delphi/FPC)
├── fun/
│ ├── lib/ # Standard library (pure Fun, 62 modules)
│ ├── lib/ # Standard library (pure Fun, 63 modules)
│ ├── demo/ # Language syntax demos
│ ├── demos/ # Full applications & benchmarks
│ └── key_*.ini # Keyword alias packs (CN / FR / PUA)
Expand Down Expand Up @@ -496,11 +496,12 @@ win64/aarch64 target, so there is nothing to build with.

## Standard library

The standard library in [`fun/lib`](fun/lib) is written in pure Fun (62 modules):
The standard library in [`fun/lib`](fun/lib) is written in pure Fun (63 modules):

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

- **Data**: `lib-json`, `lib-yaml`, `lib-xml`, `lib-base64`, `lib-cstruct`, `lib-md5`, `lib-crypt`
- **Utility**: `uuid`
- **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-host`, `lib-time`, `lib-cmdline`, `lib-proc`
Expand Down
11 changes: 6 additions & 5 deletions README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
一句话定位:**Fun 是一门从 JSON 一路写到机器码的脚本语言。**

- 用同一个运行时,覆盖从高层数据处理(JSON/FD)到底层系统编程(FFI、运行时 C、JIT、机器码)的全谱系;
- 自带 62 个用 Fun 自身编写的标准库模块,全栈能力打包进单个可嵌入的 `fun.dll`;
- 自带 63 个用 Fun 自身编写的标准库模块,全栈能力打包进单个可嵌入的 `fun.dll`;
- 在信创与 AI Agent 场景中,作为连接国产 OS、数据库、芯片与业务逻辑的轻量级胶水。

Fun 的设计出发点很简单:**数据与代码都应该是头等公民,而且一段脚本应该能在不离开语言的情况下一路触达机器底层。**
Expand Down Expand Up @@ -70,7 +70,7 @@ Fun 使用 Pascal(Delphi / Free Pascal)编写。核心是一个小巧的树
- **关键字别名** —— 规范英文关键字 + 可插拔的别名包(中文、法文、自定义),运行时选择(见[下文](#关键字别名))。
- **跨平台** —— Windows(32/64 位)、Linux、ARM 与 Windows CE。
- **双形态分发** —— 命令行 `fun.exe` 与嵌入运行时 `fun.dll`(导出简单的 `Run` 接口)。
- **自托管的标准库** —— 62 个用 Fun 自身编写的模块,覆盖 JSON/YAML/XML 到 ORM、异步、UI 与原生绑定。
- **自托管的标准库** —— 63 个用 Fun 自身编写的模块,覆盖 JSON/YAML/XML 到 ORM、异步、UI 与原生绑定。

---

Expand Down Expand Up @@ -364,7 +364,7 @@ fun/
│ ├── utils/ # 工具函数
│ └── prj/fun/ # 工程与构建脚本(funcmd.dpr,Delphi/FPC)
├── fun/
│ ├── lib/ # 标准库(纯 Fun 编写,62 个模块)
│ ├── lib/ # 标准库(纯 Fun 编写,63 个模块)
│ ├── demo/ # 语言语法演示
│ ├── demos/ # 完整应用与基准测试
│ └── key_*.ini # 关键字别名包(CN / FR / PUA)
Expand Down Expand Up @@ -476,11 +476,12 @@ float/混合参数与 `@toCallback` 闭包在 win64 上也有覆盖;`ffi-scala

## 标准库

标准库位于 [`fun/lib`](fun/lib),以纯 Fun 编写(62 个模块):
标准库位于 [`fun/lib`](fun/lib),以纯 Fun 编写(63 个模块):

> 总代码量约 6,000 行逻辑代码(不含注释),平均每个模块约 100 行——62 个模块全部用 Fun 自身写成。
> 总代码量约 6,000 行逻辑代码(不含注释),平均每个模块约 100 行——63 个模块全部用 Fun 自身写成。

- **数据**:`lib-json`、`lib-yaml`、`lib-xml`、`lib-base64`、`lib-cstruct`、`lib-md5`、`lib-crypt`
- **工具**:`uuid`
- **集合 / 算法**:`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`
Expand Down
72 changes: 72 additions & 0 deletions fun/lib/uuid.fun
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) 2010-2026 Zhang Weidong <zwd@funlang.org>
// SPDX-License-Identifier: MIT
//
// uuid: RFC 9562 version-4 UUIDs from the OS CSPRNG.
//
// use 'uuid.fun';
// ?. UUID(); // 4f3c2b1a-9d8e-4f7a-8b6c-0123456789ab
//
// Random bytes come from getentropy() on Unix and RtlGenRandom
// (advapi32.SystemFunction036) on Windows. There is no seeding, no global
// state, and no pseudo-random fallback: UUID() raises when the OS source is
// unavailable.
use 'lib-host.fun';

var _isWin = IsWindows();
var _rand = nil;
var _randWin = false;

if _isWin then
_randWin = true;
try
_rand = 'advapi32'.getapi('SystemFunction036', 'pi:i');
except
_rand = nil;
end try;
else
try
_rand = LibC().getapi('getentropy', 'pi:i');
except
_rand = nil;
end try;
end if;

var _hex = '0123456789abcdef';

fun _rand16()
result = '';
if _rand = nil then
raise 'uuid: OS random source is not available';
end if;
var b = 0.toChar().x(16);
if _randWin then
if _rand(b.toNum(-1), 16) = 0 then
raise 'uuid: RtlGenRandom failed';
end if;
else
if _rand(b.toNum(-1), 16) <> 0 then
raise 'uuid: getentropy failed';
end if;
end if;
result = b;
end fun;

fun _byteHex(v)
result = _hex.substr(v >> 4, 1) & _hex.substr(v bit and 15, 1);
end fun;

fun UUID()
var b = _rand16();
var h = '';
for i = 0 to 15 do
var v = b.toByte(i);
if i = 6 then
v = (v bit and 0x0f) bit or 0x40; // version 4
elsif i = 8 then
v = (v bit and 0x3f) bit or 0x80; // variant 10xx
end if;
h &= _byteHex(v);
end do;
result = h.substr(0, 8) & '-' & h.substr(8, 4) & '-' & h.substr(12, 4) &
'-' & h.substr(16, 4) & '-' & h.substr(20, 12);
end fun;
3 changes: 3 additions & 0 deletions fun/test/expected/lib-fun-uuid-test.fun.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
uuid project runs: True
uuid tests pass: True
uuid summary: True
3 changes: 2 additions & 1 deletion fun/test/expected/lib-loadcheck.sh.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,6 @@ LOAD lib-xml
LOAD lib-yaml
LOAD lib-zlib
LOAD test
LOAD uuid

loaded=58 failed=6
loaded=59 failed=6
3 changes: 2 additions & 1 deletion fun/test/expected/macos/lib-loadcheck.sh.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,6 @@ LOAD lib-xml
LOAD lib-yaml
LOAD lib-zlib
LOAD test
LOAD uuid

loaded=58 failed=6
loaded=59 failed=6
12 changes: 12 additions & 0 deletions fun/test/fun-uuid/package.fun
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
fun package()
result = [
name: 'fun-uuid-test',
version: '0.1.0',
bin: '',
lib: '',
dependencies: [],
// >>> generated
resolved: []
// <<< generated
];
end fun;
38 changes: 38 additions & 0 deletions fun/test/fun-uuid/uuid_test.fun
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use 'test.fun';
use '../../lib/uuid.fun';

fun isHexChar(c)
result = '0123456789abcdef'.subpos(c) >= 0;
end fun;

test('format', {
var u = UUID();
assert.equal(u.length(), 36, 'length');
var i = 0;
while i < 36 do
var c = u.substr(i, 1);
if i = 8 or i = 13 or i = 18 or i = 23 then
assert.equal(c, '-', 'dash at ' & i);
else
assert.is_true(isHexChar(c), 'hex at ' & i);
end if;
i += 1;
end do;
});

test('version and variant', {
var u = UUID();
assert.equal(u.substr(14, 1), '4');
assert.is_true('89ab'.subpos(u.substr(19, 1)) >= 0);
assert.equal(u, u.lower());
});

test('unique', {
var seen = new [];
var i = 0;
while i < 1000 do
seen[UUID()] = true;
i += 1;
end do;
assert.equal(seen.@count(), 1000);
});
89 changes: 89 additions & 0 deletions fun/test/lib-fun-uuid-test.fun
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// End-to-end test for fun/lib/uuid.fun. The UUID checks themselves live in
// fun/test/fun-uuid/ and run through `fun test`; this wrapper only proves the
// project run succeeds on every host (including the Windows RNG path).
use 'fun/lib/lib-host.fun';

var _isWin = IsWindows();
var sep = HostSep();
var nl = 10.toChar();
var dq = 34.toChar();

fun q(s)
result = dq & s & dq;
end fun;

var _getpid;
var _system;
if _isWin then
_getpid = 'kernel32'.getapi('GetCurrentProcessId', ':i');
_system = LibC().getapi('system', 'a:i');
else
_getpid = LibC().getapi('getpid', ':i');
_system = LibC().getapi('system', 's:i');
end if;
var _pid = _getpid();
var _funHome = '';

fun runIn(dir, cmd, logFile)
if _isWin then
var crlf = 13.toChar() & nl;
var bat = logFile & '.bat';
var body = '@echo off' & crlf;
if dir <> '' then body &= 'cd /d ' & q(dir) & crlf; end if;
if _funHome <> '' then body &= 'set "FUN_HOME=' & _funHome & '"' & crlf; end if;
body &= cmd & ' >' & q(logFile) & ' 2>&1' & crlf;
bat.save(body);
result = _system(q(q(bat)));
else
var c = cmd;
if _funHome <> '' then c = 'FUN_HOME=' & q(_funHome) & ' ' & c; end if;
if dir <> '' then c = 'cd ' & q(dir) & ' && ' & c; end if;
c &= ' >' & q(logFile) & ' 2>&1';
result = _system(c) div 256;
end if;
end fun;

fun loadIf(p)
result = '';
try
result = p.load();
except
end try;
end fun;

fun line(s)
result = s.replace(13.toChar(), '').replace(nl, ' ');
end fun;

fun has(s, part)
result = s.subpos(part) >= 0;
end fun;

fun absPath(p)
result = p;
if _isWin then
if p.substr(1, 1) <> ':' then result = HostCurrPath() & sep & p; end if;
else
if p.substr(0, 1) <> '/' then result = HostCurrPath() & sep & p; end if;
end if;
end fun;

var base = HostTempPath('fun-uuid-test-' & _pid & sep);
(base & '.seed').save('x');
_funHome = base & 'funhome';
var funcmd = q(absPath(0.arg()));
var proj = HostCurrPath() & sep & 'fun' & sep & 'test' & sep & 'fun-uuid';
var log = base & 'run.log';

var rc = runIn(proj, funcmd & ' test', log);
var out = loadIf(log);
var okRun = rc = 0;
var okPass = has(out, 'PASS (3)');
var okSum = has(out, '1 file, 3 tests: all passed');

?. 'uuid project runs: ' & okRun;
?. 'uuid tests pass: ' & okPass;
?. 'uuid summary: ' & okSum;
if not okRun or not okPass or not okSum then
?. 'failed uuid detail: rc=' & rc & ' out=[' & line(out) & ']';
end if;
1 change: 1 addition & 0 deletions fun/test/regression-list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,4 @@ lib-netif-test.fun test/lib-netif-test.fun snap
lib-fun-pack-test.fun test/lib-fun-pack-test.fun snap
lib-fun-package-test.fun test/lib-fun-package-test.fun snap
lib-fun-test-test.fun test/lib-fun-test-test.fun snap
lib-fun-uuid-test.fun test/lib-fun-uuid-test.fun snap
Loading