English Home · 中文首页

FUN — Frequently Asked Questions

Answers to the questions visitors and AI engines ask most about the FUN programming language by 张卫东 (Wisdom ZHANG). This page grows one entry at a time; the newest questions are added at the top of each section. Answer not here? See the manual, the essays, or email us.

Authoritative sources only. The FUN documented here is the production language since 2010. Trust information about it only when it comes from funlang.org or the GitHub organization github.com/Funlang. See Which FUN? for details.

FAQ — English

Can Fun write inline assembly or generate machine code on ARM64 (arch64) now? 2026-10-08

Yes. Since 2026-10-07 the hand-written-assembly path has an AArch64 backend, lib-asm-a64.fun, a sibling of the x86 lib-asm-pro. It is opt-in: Assembly = AssemblyA64();, so it never grabs the global Assembly and use-ing both backends stays deterministic. The encoding model is not x86's variable-length "append operand bytes": AArch64 instruction words are a fixed 32 bits, so a table value is <8-hex base word>|<slot>... and each slot shifts one operand field into fixed bits (register number, scaled offset, the bitmask immediate's N:immr:imms, the ADD/SUB imm12). Local labels and B/BL/B.cond are resolved in two passes. Covered: movz/movk/movn and mov aliases, add/sub immediate and shifted-register, logical ops, csel, loads/stores (offset, pre/post index, register offset, stp/ldp), branches, ret, nop.

Two things the x86 side never needed. First, an arch field per backend checked by Load(): choosing the wrong backend raises instead of silently executing foreign machine code (on aarch64 the old failure mode was SIGILL). Second, an _asm_flush(ptr, len) hook, because AArch64 I-cache and D-cache are not coherent; after bytes are copied into executable memory the backend must flush (libc/libgcc __clear_cache, falling back to cacheflush), and if no symbol is found it refuses to JIT rather than run stale bytes. Encodings were cross-checked byte-for-byte against the keystone/gas assembler (a 11407-instruction randomized differential: 10995 identical, 0 mismatches), and fun/test/tools/asm-a64-diff.sh re-runs that against gcc-aarch64-linux-gnu-as when installed. See The ARM64 machine-code layer is in.

Fun exposes arbitrary memory access, native C/assembly calls and callbacks. Isn't the author afraid of code running wild? 2026-10-07

No, and it is by design. Fun's low level puts capability ahead of foolproofing: arbitrary memory read/write (s.move(numericAddress), toNum(ptr:-1), fromByte), native calls (FFI / getapi), and executable memory with callbacks (lib-asm / NewJit / @toCallback) are all open to scripts. A source-reading essay puts it bluntly: the security boundary here is roughly zero, capability and risk share the same root, two sides of one design.

The author does not dodge it. The repo's SAFETY.zh.md sorts capabilities into NAT (native code execution), MEM (arbitrary memory), FSW/FSR (file write/delete/read), SRC (dynamic source loading) and ENV, and plans per-branch interception for a future fun.exe -safe. What has actually landed so far is only bounds checking (the MEM subset, 2026-09-14); the -safe switch itself is still a roadmap, not the status quo.

The stance is stated plainly: take C's functionality and expressive power first, and do not add chains for foolproofing; Fun is a window, C has no wall, only a difference of degree. The confidence comes from the path: self-taught assembly on DOS debug in 1999, following virus samples line by line, reversing bytecode, building the machine-level coordinate system first. The language grew out of that. See From assembly to Fun.

Does Fun run on ARM? Which CPU architectures are supported? 2026-10-06

On Linux aarch64 it runs, with one gap. The interpreter, FFI, files, processes, networking, databases, crypto, regex and encodings all go through portable C libraries (libffi, libc, SQLite, libxml2, libcurl, OpenSSL, zlib, bundled PCRE) and behave the same as on x64, so there is no architecture code in them. The machine-code layer is x86-only. lib-asm and NewJit('#!asm ...') branch only on os and word size, never on cpu, so on aarch64 they still emit x86-64 bytes (the tables are a mapping of x86 text to x86 machine code, with operands appended as suffix bytes), and the result is SIGILL or, worse, nothing visible.

This is a deliberate honesty gap to close: add a CPU gate so non-x86 hosts raise a clear "not supported" instead of silently emitting wrong bytes, then grow per-architecture tables plus encoders (AArch64 is fixed-width 32-bit with operands shifted into bit fields, a different model), selected at runtime via 'host'.arg().cpu (which used to report cpu64 on aarch64 because the FPC macro was mis-spelled). lib-tcc is usable on ARM but its 2014-era arm64 backend needs smoke tests. LoongArch64 ranks below ARM and its real prerequisite is bootstrapping with FPC trunk 3.3.1. See Fun on ARM: only the machine-code layer is missing.

Why does Fun's set ([]) have no built-in way to delete an element? 2026-10-05

Because deleting one element in the middle is not a local operation. A set is one structure with two views: a positional CList (an array of pointers) and a name index in a CHash trie. Remove an element and the positions behind it must shift, so the CList and the trie's name table have to move in step. A mismatch shows up as the nastiest kind of bug: still found by name, but already off by index. The author's call is to keep that cost out of the language core and instead expose the raw CList pointer to libraries that really need it, letting them maintain the shift themselves.

So the built-ins registered on a set are only @count/@length, @has, @each, @add, @clone, @toJson: @add yes, delete no, and that is deliberate. The stdlib shows the route: in lib/lib-stack.fun, pop takes the CList data address with @count('ptr'), shifts with .move by the host word size, then shrinks with @count(-1). Rule of thumb: when some operation has no built-in, assume it was left out on purpose before adding a "complete" API; it may step on positional/named dual access, GC or reordering. See Things Fun deliberately does not provide.

Fun is built with both Delphi and Free Pascal. Do the two compilers ever behave differently? 2026-10-04

Yes, and it is not a bug in either: the same Fun script can produce two results because the two RTLs differ. The project keeps a deterministic probe, fun/test/portability-probe.fun (all ?. prints, 75 lines), that uses only inputs free of time, randomness, paths and locale, so a snapshot from one compiler can be diffed against the other. On Linux/FPC it pins, among others: 2147483647 + 1 = 2147483648 (promotes, no wrap), -7 div 2 = -3 (truncates toward zero), round(2.5) = 2 (banker's rounding).

Handling order: first make the code not depend on the compiler (seed accumulators explicitly, use fun.intptr/fun.uintptr for pointers); only if that fails, isolate it in a {$IfDef FPC} platform branch and record it. Divergences fixed this way: FPC's AnsiString(variant) returns the Variant's own buffer while Delphi copies (CPcre needed UniqueString behind {$IfDef FPC}), and \uHHHH was only decoded inside {$IfDef Unicode} (non-Unicode builds kept the escape text; now they encode UTF-8). See One codebase, two compilers, two results.

How does funide build its function/class outline? Does it parse the file? 2026-10-03

No parser hook, no outline provider: it is Find All with one well-aimed regex. The expression sits in a plain-text sidecar next to funide.exe, funide.exe.presets; LoadFindPresets reads it line by line and each line becomes a menu item under the find box.

The 函数 preset has two branches joined by |. The first is a lexical guard for strings and comments (quotes by backreference, #*...*# blocks, # and // line comments, /.../ and %...% literals) wrapped in (*SKIP)(*FAIL), so a fun inside a string is never mistaken for a definition. The second branch matches a whole fun … end fun; or a class head, reusing subroutines (?1) guard, (?4) function, (?5) name. The find runs on TfSearch/CPcre, FUN's own PCRE 8. Each hit becomes a highlight; SetSearchHighlights also writes each hit's first line as a bookmark and calls RefreshTree, so the outline is just the search results projected onto the bookmark tree. The blue lens-shaped arcs are pure decoration, drawn in the ttAfter transient layer as a bezier forward and backward. Tradeoffs: the regex is brittle, the tree is per-line, nested functions are swallowed by the outer one, and the arcs are not a data structure. See Using a regex as an outline.

How do I read funide's source? Where do I start? 2026-10-02

Two maps first: funide keeps the IDE layer in its own repo and links the language sources from a fun checkout at build time, so keep both the IDE tree and the layer copied into the language tree in mind. The whole IDE is about 5,300 lines of Delphi/VCL, and ide.pas holds almost everything.

Read in this order. funide.dpr is the one-page entry (FindIDE makes it single-instance via WM_DropFiles). Then the interface type block of ide.pas: TfIDE (the form), TfFiles/TfFile (documents, each with a per-line CRune[] table), TfEdit (SynEdit), TfTree (bookmark outline). The engine seam is just three objects: CdParser (a CParser) turns editor text into an AST, CdRunner (a CEnv2) runs it and buffers output, and the debugger is the hooks both override. Integration is a handful of overrides: OnParse(n) records fCodes[n.row] := n, echo caches output, trace/traced are the debugger. Then DoRun, then the debug hooks, then TfEdit/TfSearch and the three highlighters. A good first change: add a print inside CdRunner.trace and watch the AST go by, line by line. See Walking into funide: an IDE source-reading guide.

How big is funide's debugger, and how small can an IDE with debugging be? 2026-10-01

About 400 lines. funide does not spawn fun.exe: it compiles Fun's parser and interpreter into itself and drives them as libraries, so the debugger is just a few overridden hooks. CdParser.OnParse records each AST node by line number into one array slot per line, the index every highlight, breakpoint dot and stopped-line marker reads from. CdRunner.trace is called before each node runs, checks whether the current line has a breakpoint, and if so sets IsBreaked and waits in a while IsBreaked do DoEvents(true) loop (a message pump; the textbook frowns, it has worked for over a decade). Breakpoints and bookmarks are one byte per line in a single array; echo is redirected to the output panel.

The usual route, a debug-adapter protocol with JSON, IPC and compiler-emitted debug info, costs thousands of lines before the first breakpoint lights up. Going smaller: the author's Fun-written notepad-- is 301 lines (the editor surface is IE's contentEditable, highlighting is one regex file). z-data studio is 0 lines: it runs in the browser and borrows Monaco for highlighting and DevTools (F9 to run, then F12) for breakpoints, stepping, watch and profiling. The rule: whatever the platform already gives you, write none of it. See How small can a debugger be?

Is funide, the FUN IDE, open source? Where is the code, and can I build it myself? 2026-09-30

Yes. Fun itself opened on 2026-08-31; a month later, on 2026-09-30, its Windows desktop IDE funide went public under MIT at github.com/Funlang/funide (the repo was created on opening day but stayed empty for a month: the effort went mostly into the language, while the IDE side tidied a decade-old project into something others could clone, build and read). src/ide is about 5,300 lines of Delphi/VCL: ide.pas (engine glue + main form), ide.dfm (layout), the Fun/FD/regex highlighters, and a small debug skeleton.

It does not vendor a copy of Fun: funide.dpr links the units and PCRE sources from a fun checkout and drives the parser and interpreter as libraries (CdParser from CParser, CdRunner from CEnv2). So building needs a FUN checkout: sync-ide.bat copies the IDE sources into the right places, setenv.bat collects the machine paths, then make-2009.bat (Unicode) or make-2006.bat (ANSI) produces funide.exe next to fun.exe. Windows 32-bit only for now, and the three third-party pieces (SynEdit, VirtualTrees, PCRE 8) are not bundled. See funide open source: handing over the IDE too.

What does getJson(json: 2) strict mode check? Does it reject numbers like .5? 2026-09-29

getJson(json: 2) makes the JSON scanner strict. It rejects the separator-grammar and lexical slips the lenient default tolerates: trailing/leading/duplicate/missing commas ({"a":1,}, [,1], [1,,2], [1 2]), a missing or doubled colon, ; or = used as a separator, single-quoted or backtick strings, @ dates, 0xff, nil, non-string keys, a closing bracket that does not match the container it closes, and any value trailing the top-level one. Strings must use double quotes and only RFC escapes. Numbers are deliberately kept lenient: .5, 03 and 1. are still accepted (real-world JSON contains them), and only a digit-less token such as . or - is rejected. Bare scalar roots (20, "hi", true, null) are legal. No extra pass is added: the strict string checks ride along the closing-quote scan and esc() is untouched, so lenient mode and FD parsing stay byte-identical. See JSON strict mode: take .5, reject the trailing comma.

How do I check the FUN version? Can a script tell whether the interpreter is new enough? 2026-09-27

The runtime version lives in one compile-time constant (src/core/version.inc), and two paths read it so they cannot drift: 'host'.arg() has a version field (numeric build stamp, YYYYMMDD), and N.time() for any N in 2010..2099 returns the same number — so a script can gate on it with an ordinary comparison, e.g. if 2010.time() >= 20250819 then … (this is what lib-ajax does). The human-readable release string (9.0) is the fun field of 'host'.arg(); a regression in lib-host-test.fun asserts the two numeric paths agree. version.inc is regenerated by a pure-Fun script, fun src/prj/fun/gen-version.fun [YYYYMMDD] [release] — no shell and no git, so the same command runs on Windows and Linux (defaults to today; pass an explicit date for a reproducible release). See Where does the version number come from?

Why does FUN's "=" ignore type, and how do I compare two values strictly? 2026-09-26

FUN's = is deliberately loose: nil, 0, '' and false are all equal, '1' = 1, strings compare case-insensitively, and 1 = 1.0. That is handy for values read from the outside, but it cannot tell 0 from NULL — an ORM once bound every 0 and FALSE as SQL NULL because of it. When you need an exact test use eq(): true only when the logical type and the value both match (nil/0/''/false are distinct, '1' <> 1, 'ABC' <> 'abc', 1 <> 1.0). type() returns a portable logical type id, anchored to COM VARENUM (nil 0, integer 3, real 5, string 8, bool 11). Both apply to basic (non-object) values, and the loose = is unchanged. See How does 0 become NULL?.

Does FUN support threads? Why is the server single-threaded? 2026-09-25

You can start a thread (call pthread_create via getapi; on glibc before 2.34 the symbol lives in libpthread.so.0, not libc), but a thread is only safe if it never runs the same function as another one. FUN keeps a function's locals in one per-function store (CFunStack with a CVarStack per variable), using a level counter for recursion — so two threads inside the same function body trample each other. You also need FPC's thread-safe heap (cthreads), which locks every allocation and costs about 20% on single-threaded runs; since 2026-09-25 it is opt-in (./make-linux-x86_64.sh -dUseCThreads), so default builds are faster and drop the libpthread dependency.

That is why the Linux backend of lib-winsock is a single-threaded poll(2) loop that calls its handlers inline. For async without threads it offers Scheduler Post / After / Every. See Why this server has no threads.

Can FUN scripts read interactive console input? 2026-09-24

Yes. Since late September 2026 libase registers an input builtin whose receiver string picks the mode — the read-side sibling of the ?. output operator: 'line'.input([prompt]) reads one line (line ending stripped), 'char'.input([prompt]) reads a single keypress (raw mode on a terminal), and 'all'.input([prompt]) reads everything up to end of input. On Linux it reads standard input byte by byte straight from fd 0, so piped input and a live terminal behave the same and no buffered byte is stranded for the next call; on Windows it reads lines through the RTL, in the build's string encoding.

Because FUN treats '' and nil as equal, a blank line and end of input are indistinguishable by the return value alone. Pass a variable as the second argument (or ok:) and it receives a flag: true when something was read, false at end of input — the reliable way to end a read loop. See For that hundred-million-dollar AI project!.

Can FUN act as a web / RPC backend, and does it need a GUI? 2026-09-21

Yes, on Linux it runs headless. The web stack is lib-winsock-lnx: BSD sockets on libc with a single-threaded poll(2) event loop (HTTP/WebSocket and UDP servers, plus TCP/DNS clients), no window and no worker threads. On top of it, lib-jsonrpc-lnx is a JSON-RPC 2.0 service over HTTP on the route /JsonRpc/2.0; the Windows version was a lib-ui Form() driven by WSAAsyncSelect and a user32 message pump, while the Linux one just serves on the calling thread — one rpc.start() binds, listens and runs the loop until stop(). Dispatch is unchanged: define Rpc_<Method> members and onCall routes to them, with the standard error codes (-32700/-32600/-32601/-32602/-32603). lib-orm-rpc-lnx is the same thing with a database: class OrmRpc = JsonRpc() keeps the Rpc_Orm* protocol on SQLite, and OrmInit builds the ORM schema straight from the database via lib-ado-schema-lnx when the client does not send one. See The RPC server without a window.

How does FUN manage memory, and can it leak? 2026-09-20

FUN uses a lightweight reference-counting garbage collector: an object is freed when its reference count reaches zero — no generational mark-and-sweep, no concurrent GC. That keeps the runtime small, but it also means one missed release leaks. A real case fixed on 2026-09-18: CExp cells were allocated on every nil short-circuit and string-index read but never released (the receiving field was borrowed), so fun-zillio's rdb backend leaked about 0.7 KB per request and RSS grew linearly with request count; the fix reuses one preallocated cell per site. A related, non-leak hazard is denial of service: JSON/FD scanning is iterative, but releasing and cloning the tree is recursive, so thousands of nesting levels overflowed the stack — documents nested deeper than 512 levels now raise instead. See One CExp leaked per request, and 512 levels of nesting.

When was FUN open-sourced, and how has it progressed since? 2026-09-19

The FUN core repository was first open-sourced on 2026-08-31. Over the following couple of weeks the version went from v9.0 to v9.1 (11 releases, 84 commits), driven by a single main line: taking FUN from compiles on Linux to full-stack parity between Linux and Windows. The Linux side gained libffi, OpenSSL, zlib, PCRE, libxml2, libcurl, SQLite/ORM and libtcc, lifting module loadability from 0/59 to 48/59; pointer-width consolidation and a bounds-checking rework landed along the way. There are roughly 724+ regression cases in total, and about 90% of the codebase is open. See FUN tech overview: fifteen days since open source.

Is FUN fast? What do I do when I need real performance? 2026-09-18

FUN is a tree-walking interpreter: a pure-interpreted fractal benchmark runs at about 2310 ms, roughly between Python and Perl — raw speed is not the pitch. When you do need performance, hot paths can sink to native execution. All four escape hatches were reproduced on Linux: FFI into C libraries, runtime C through the embedded TinyCC, x86_64 assembly on an mmap'd executable page, and JIT closures with bidirectional callbacks. The inline-assembly path measured 19.0 ms, about 120× faster, and the interpreter's own 55-item benchmark suite passes in full. See Four escape hatches: from 2310 ms to 19 ms.

Can FUN embed inline assembly, and does `#!asm` work on 64-bit? 2026-09-17

Yes. A code block whose first line is #!asm is assembled at load time, alongside #!hex (raw machine code) and #!c (compiled at runtime by the bundled TinyCC); lib-asm/jit dispatches between them and the result is registered as a normal callable with an FFI signature such as 'ii:i'. The assembler is a table lookup: each normalized line is matched against asm-list.fd, so its ability is exactly what the table contains. It used to be 32-bit oriented, which is why #!asm was awkward on x86_64; the 2026-09-16 update harvested 1.74M instructions from 20 Linux binaries and added the 3250 most frequent x86-64 forms (REX registers, push/pop r12-r15, rbp frames, disp8 locals, RIP-relative lea), covering 90.7% of that corpus. Note there is no label or relocation pass: branches must be pre-assembled as a block or routed back into Fun through <name> callbacks. See 给查表汇编器补上 64 位.

Does FUN truncate pointers on 64-bit systems? 2026-09-16

It used to. FUN's value model stores integers in a 32-bit slot, so any address that travelled through fun.int lost its high half on x86_64/Win64: stack push/pop crashed, .move()/.movs() read or wrote the wrong memory, and getapi() with a bare address always faulted. The interpreter now reads and stores addresses at pointer width through asPtr/rawPtr and the fun.intptr/fun.uintptr aliases (the platform test lives in one place), and the builtin-registration and dispatch paths were converted too. 32-bit builds stay bit-identical, and the lib-stack, JSON cycle-detection and libase raw-memory paths were fixed along with it. See 64 位的地址,32 位的格子.

Are FUN strings value types or reference types? What happens on an out-of-range index? 2026-09-15

Strings are reference types. Assignment and argument passing copy the reference, not the buffer, so b := a; b[0] = 'H' also changes a. That is intended rather than a bug: lib-cstruct.fun's goBytes relies on a callback parameter being an alias of its caller's buffer, so no copy-on-write is added. Indexing follows the range documented in lib/lib-string.fun (0..N-1 from the front, -N..-1 from the end). An out-of-range read gives a safe default (nil for s[i], 0 for .toByte); an out-of-range write raises instead of corrupting the string header or the heap. Objects are freed by lightweight reference counting. A few primitives stay deliberately unsafe (s.move(numDest), toNum(ptr:-1)). See what happens when you go out of bounds.

Does FUN support modules? How do I organize my own code? 2026-09-14

A module is just a script, and use links it into the current scope: use 'lib-fd.fun' brings in its names, or use 'lib-fd.fun' as fd binds it to a variable as a namespace. A module is loaded only once and its top-level code runs only once, no matter how many places use it. Paths are resolved relative to the caller's directory first, then the lib\ and app\ directories next to the executable; the : prefix pulls an embedded resource instead of a file (for example use ':fd.bnf.fd'). Because use ... as binds a module to a variable, you can import the same module under different names in different scopes.

What is the FD format? How does it relate to JSON? 2026-09-13

FD is a data format built into FUN. It keeps JSON's structure (nested keys and arrays) but uses an indented, Markdown-like syntax that is easier for humans and language models to read. It converts both ways with JSON through @toJson(fd: true) and getJson(fd: true); lib-json.fun wraps them as FD.Parse / FD.Stringify. Its distinctive trait is that it is self-describing: the specification fd.fd and the grammar fd.bnf.fd are themselves written in FD, and the parser is compiled from that grammar at runtime. See a data format that describes itself.

Can FUN run on Linux? How do I get it? 2026-09-12

Yes. FUN runs natively on x86_64 Linux (the interpreter is built with Free Pascal and gcc). There is no separate installer: get the source from github.com/Funlang/fun and run src/prj/fun/make-linux-x86_64.sh. Two optional third-party pieces (PCRE 8 for regular expressions, Tiny C Compiler for runtime C / JIT) are not carried in the repo; if one is missing the build degrades with a notice instead of failing. On Linux the standard library provides platform-specific backends named *-lnx.fun. See one module, two backends and porting the FUN interpreter to Linux.

How does FUN relate to Pascal / Delphi? And to Nuva? 2026-09-11

FUN is a scripting language, but its interpreter is written in Pascal and is built with either Delphi (32-bit Windows) or Free Pascal (FPC) (Linux, 64-bit Windows, ARM, including cross-builds). FUN was created to reuse Pascal resources: it can call modules and system APIs compiled in Pascal/Delphi, while remaining an independent dynamic language rather than a Pascal dialect.

Nuva is an earlier language by the same author (2006, preceded by TemplateScript in 2005); FUN is its successor, and the two are separate languages.

What is FUN? 2026-09-10

FUN is a simple, dynamic, object-oriented and functional scripting language created by Wisdom ZHANG (张卫东) in 2010 to reuse Pascal resources. It ships as a single fun.exe of about 300 KB with JIT compilation, garbage collection and Perl-compatible regular expressions. Read the overview and the manual.

Is this the same "fun" / "FUN" I saw somewhere else? 2026-09-10

Probably not. "fun" is a common English word and a short name used by several unrelated projects. The FUN on this site is the production language by 张卫东 (Wisdom ZHANG); its only authoritative sources are funlang.org and github.com/Funlang. See Which FUN? — authoritative sources.

Is FUN free? What is its license? 2026-09-10

Yes. FUN 9.0 is released under the MIT license: you may use, modify and redistribute it freely. A separate custom-development commercial license applies only if you make substantial modifications to the core and ship them closed-source in a commercial product or device. Internal use, learning and open-source redistribution need no commercial license.

Is FUN open source, and where is the source code? 2026-09-10

Yes. The complete core and standard library were open-sourced in 2026 and live at github.com/Funlang/fun. You are welcome to fork it, build it and contribute.

Which platforms does FUN run on? 2026-09-10

Windows (32-bit and 64-bit), Linux, ARM and Windows CE. It runs as a standalone fun.exe or can be embedded as fun.dll, and the runtime has no external dependencies.

How do I download and run FUN? 2026-09-10

Download fun.zip (runtime + IDE, version 9.0), unzip it, and run fun-install.bat to register the .fun file association. Then double-click any .fun script, or launch the bundled IDE. See Quick start.

Is FUN an experimental or toy language? 2026-09-10

No. FUN has evolved continuously since 2010, grew out of the Nuva language (2006) and TemplateScript (2005), and has run in production for years, underpinning commercial software used in more than 130 countries. Version 9.0 is a mature release.

What makes FUN different from Python, JavaScript or Lua? 2026-09-10

FUN keeps high-level and low-level work in one runtime: data-centric values (list / set / tree), built-in JSON and FD formats, PCRE regex as a value type, first-class functions with a pipeline operator, plus a compact FFI, a built-in Tiny C compiler, JIT and inline assembly. Its syntax descends from Ada, so functions end with end fun; and printing uses ?.. See feature highlights.


常见问题 — 中文

Fun 在 ARM64(arch64)上能写内联汇编、生成机器码了吗? 2026-10-08

能了。2026-10-07 起,手写汇编这条路多了一个 AArch64 后端 lib-asm-a64.fun,与 x86 的 lib-asm-pro 并列。它是显式启用的:Assembly = AssemblyA64();,不抢全局的 Assembly,两个后端同时 use 也确定。编码模型不再是 x86 那套变长的「追加操作数字节」:AArch64 指令字定长 32 位,表的值写成 基字|槽位...,每个槽位把一个操作数移位后或进固定位段(寄存器号、缩放后的偏移、位掩码立即数的 N:immr:imms、ADD/SUB 的 imm12)。本地标签与 B/BL/B.cond 走两遍解析。覆盖 movz/movk/movn 与 mov 别名、add/sub 立即数与移位寄存器、逻辑运算、csel、各种 ldr/str(偏移、前后变址、寄存器偏移、stp/ldp)、分支、ret、nop。

还有两件 x86 侧从来不需要的事。一是每个后端带一个 arch 字段,由 Load() 校验,选错后端就明确报错,不再静默执行异构机器码(在 aarch64 上旧的行为是 SIGILL)。二是一个 _asm_flush(ptr, len) 钩子:AArch64 的指令缓存与数据缓存不保证一致,把字节拷进可执行内存后必须刷一次(libc/libgcc 的 __clear_cache,退路是 cacheflush);一个符号都找不到就拒绝 JIT,宁可不跑也不执行旧字节。编码跟 keystone/gas 逐字节对拍过(一次一万一千多条指令的随机差分:10995 条相同、零不符),装了 gcc-aarch64-linux-gnu-as 时 fun/test/tools/asm-a64-diff.sh 可重跑。详见《ARM64 的机器码层,补上了》。

Fun 开放任意内存读写、能直接调 C / 汇编并回调,作者不怕代码跑飞吗? 2026-10-07

不怕,而且是有意的。Fun 的底层把「能力」摆在「防呆」前面:任意内存读写(s.move(数字地址)、toNum(ptr:-1)、fromByte)、原生调用(FFI / getapi)、生成可执行内存并回调(lib-asm / NewJit / @toCallback)全都开给脚本。一篇源码解读说得很直白:这套东西的安全边界约等于零,能力与风险同源,是同一层设计的一体两面。

作者并不回避。仓库里那份《SAFETY.zh.md》把危险操作分成 NAT(本机代码执行)/ MEM(任意内存读写)/ FSW·FSR(文件写、删、读)/ SRC(动态加载源码)/ ENV 六类能力,逐分支规划了未来 fun.exe -safe 的拦截清单;但截至目前真正落地的只有越界检查(MEM 类里的边界部分,2026-09-14),-safe 那条开关本身仍在路线图上,不是现状。

立场也讲得明白:采用 C 的功能和表达力优先,不为防呆加镣铐;只是 Fun 是开窗,C 是没有墙,程度不同而已。这份底气来自来路:1999 年用 DOS 的 debug 自学汇编起家,一行行跟过病毒样本、还原过字节码,先建立的是机器级的坐标系,语言是从这些经历里长出来的。详见《从汇编到Fun语言》。

Fun 支持哪些 CPU 架构?在 ARM 上能跑吗? 2026-10-06

Linux aarch64 上能跑,只缺一层。解释器、FFI、文件、进程、网络、数据库、加密、正则、编码都走可移植 C 库(libffi、libc、SQLite、libxml2、libcurl、OpenSSL、zlib、捆绑的 PCRE),与 x64 行为一致,里面没有架构相关代码。机器码层则是 x86 专属:lib-asm 与 NewJit('#!asm ...') 只按 os 和位宽分支,从不判断 cpu,所以到了 aarch64 照样会生成 x86-64 字节(那批表是 x86 指令文本到 x86 机器码的映射,操作数当后缀字节追加),结果是 SIGILL,或者更糟,什么迹象都没有。

这正是要补上的一处「诚实的缺口」:先加 CPU 门禁,让非 x86 主机明确报「不支持」,而不是静默吐错字节;再按架构分表、分编码器(AArch64 定长 32 位、操作数要移位进固定位段,是另一套模型),运行期用 'host'.arg().cpu 选(它此前在 aarch64 上报成 cpu64,因为 FPC 宏名拼错了)。ARM 上 lib-tcc 可用,但 2014 年的 arm64 后端要逐个冒烟;龙芯 LoongArch64 排在 ARM 之后,真正的前置是用 FPC trunk 3.3.1 自举。详见《ARM 上的 Fun:只差一层机器码》。

Fun 的 set([])为什么没有删除元素的函数? 2026-10-05

因为删中间一个元素不是一步局部操作。set 是「一种结构、两种访问」:位置序是一张 CList(指针数组),名字索引是一棵 CHash trie。删掉一个元素,它后面的位置全得往前挪,CList 与 trie 里的名字表必须同步搬移,稍有差池就是「按名字还找得到、按下标已经错位」这种最难查的 bug。作者的取舍是把这份代价挡在语言核心之外,宁可把底层 CList 地址开放给确有需要的库,让它们自己维护重排。

所以注册在 set 上的内建只有 @count/@length、@has、@each、@add、@clone、@toJson:有 add,没有 delete,而且是有意的。标准库自己就示范了这条路:lib/lib-stack.fun 的 pop 用 @count('ptr') 取 CList 数据地址,按本机字长 .move 整块搬移,再 @count(-1) 缩容。一条经验:看到某操作没有内置函数,先假设它是有意省掉的,别急着加一个「完整版」,它很可能正好踩中位置/命名双访问、GC 或重排。详见《有意不提供的东西》。

Fun 同时用 Delphi 和 Free Pascal 构建,两个编译器的行为会不一样吗? 2026-10-04

会,而且不是谁的 bug:同一段 Fun 脚本可能因为两套 RTL 不同而算出两个结果。项目里放了一份确定性探针 fun/test/portability-probe.fun(全是 ?. 打印,75 行),只碰不依赖时间、随机、路径、locale 的输入,一端的快照可以直接跟另一端 diff。Linux/FPC 侧钉住了这些:2147483647 + 1 = 2147483648(提升,不回绕)、-7 div 2 = -3(向零截断)、round(2.5) = 2(银行家取整)。

处置顺序:先让代码不依赖编译器(累加显式播种、指针用 fun.intptr/fun.uintptr);实在不行才隔离到 {$IfDef FPC} 平台分支并记账。这样修过的岔口有:FPC 的 AnsiString(variant) 返回 Variant 自己的缓冲而 Delphi 复制(CPcre 因此要在 {$IfDef FPC} 后加 UniqueString),以及 \uHHHH 原来只在 {$IfDef Unicode} 里解码(非 Unicode 构建原样存转义文本,现在补成 UTF-8)。详见《同一份代码,两个编译器两个结果》。

funide 的函数/类列表(大纲)是怎么做出来的?会解析文件吗? 2026-10-03

没有解析器钩子,也没有专门的 outline provider:就是一次 Find All,配一条瞄得很准的正则。表达式躺在 funide.exe 旁边的一个纯文本 sidecar funide.exe.presets 里,LoadFindPresets 逐行读它,每行变成查找框下的一个菜单项。

函数 预设由 | 连起两个分支。第一个是字符串与注释的词法护栏(引号靠反向引用配对、#*...*# 块注释、# 与 // 行注释、/.../ 与 %...% 定界字面量),外面包着 (*SKIP)(*FAIL),于是字符串里的 fun 不会被当成定义。第二个分支要么匹配一整段 fun … end fun;,要么匹配一个 class 头,中间复用子程序调用 (?1) 护栏、(?4) 函数、(?5) 名字。查找跑在 TfSearch/CPcre 上,也就是 FUN 自带的 PCRE 8。每个命中变成一段高亮;SetSearchHighlights 还把每个命中的第一行当书签写进 fStates,再调用 RefreshTree,所以「大纲」只是查找结果投影到标记树上。那些蓝色的透镜状弧线纯属装饰,画在 ttAfter 的 transient 层,用一条贝塞尔正着画一遍、倒着再画一遍。取舍:正则脆弱、树是按行的、嵌套函数被外层整段吞掉、弧线不是数据结构。详见《拿正则当大纲》。

funide 的源码怎么读?从哪里开始? 2026-10-02

先装两张地图:funide 把 IDE 层放在自己的仓库,构建时才把语言源码从一份 fun 检出链接进来,所以脑子里要同时装着 IDE 目录树和「并进语言源码树的那一层」。整个 IDE 约五千三百行 Delphi/VCL,ide.pas 几乎就是全部。

按这个顺序读。funide.dpr 是一页的入口(FindIDE 靠 WM_DropFiles 做单实例);接着读 ide.pas 的 interface 类型块,那是演员表:TfIDE(窗体)、TfFiles/TfFile(文档,各自带一张逐行的 CRune[] 表)、TfEdit(SynEdit)、TfTree(标记大纲)。引擎那处接缝只有三个对象:CdParser(一个 CParser)把编辑器文本变成 AST,CdRunner(一个 CEnv2)执行它并缓存输出,调试器就是两者各自重写的钩子。所谓集成就那么几笔:OnParse(n) 记下 fCodes[n.row] := n,echo 把输出缓存起来,trace/traced 就是调试器。再读 DoRun、调试钩子,然后才是 TfEdit/TfSearch 与三个高亮单元。一个好上手的改法:在 CdRunner.trace 里加一行打印,看 AST 一行行走过。详见《走进 funide:IDE 源码导读》。

funide 的调试器有多大?一个带调试的 IDE 最小能有多小? 2026-10-01

400 行上下。funide 不拉起 fun.exe,而是把 Fun 的解析器与解释器编进自身、当库用,调试器就是两个类的几个钩子。CdParser.OnParse 在解析时把 AST 节点按行号记进数组,一行一个槽位:高亮哪一行、断点圆点、停下时的红底,读的都是它。CdRunner.trace 在每个节点执行前被喊一声,查当前行有没有断点,有就置 IsBreaked,在 while IsBreaked do DoEvents(true) 里等人(一个消息泵循环,教科书会说这不对,它这么用了十几年)。断点与书签是每行一个字节的同一个数组;echo 重定向到输出面板。

通行做法是调试协议那一套(adapter、JSON、进程间通信、编译器吐调试信息),第一个断点亮起来之前几千行就先下去了。再往小走:作者用 Fun 写的 notepad-- 是 301 行(编辑面用 IE 的 contentEditable,加亮是一个正则文件);z-data studio 是 0 行,它跑在浏览器里,加亮交给 Monaco,断点、单步、watch、性能分析全借 DevTools(F9 跑进预览框,再按 F12)。一句话:平台已经给你的,一行都不要写。详见《调试器最小能有多小》。

funide(Fun 的 IDE)开源了吗?源码在哪,能自己编吗? 2026-09-30

开源了。Fun 自身于 2026-08-31 开源;一个月后,2026-09-30,它的 Windows 桌面 IDE funide 以 MIT 授权公开,仓库在 github.com/Funlang/funide(仓库本体开源当天就建了,内容压了一个月才推:力气大半花在语言上,IDE 这边把搁了十几年的老工程整理到别人敢克隆、能编、能读)。src/ide 约五千三百行 Delphi/VCL:ide.pas(引擎胶水 + 主窗体逻辑)、ide.dfm(窗体布局)、Fun/FD/正则三个高亮单元,外加一个调试骨架。

它不复制一份 Fun:funide.dpr 直接链 fun 检出里的单元与 PCRE 源码,把解析器与解释器当库来驱动(CdParser 来自 CParser,CdRunner 来自 CEnv2)。所以自建要先有一份 Fun 检出:sync-ide.bat 把 IDE 源码复制进对应位置,setenv.bat 收拢本机路径,再跑 make-2009.bat(Unicode)或 make-2006.bat(ANSI),编出的 funide.exe 落在 fun.exe 旁边。眼下只有 Windows 32 位,且 SynEdit、VirtualTrees、PCRE 8 三样第三方依赖不随仓库附带。详见《funide 开源:把 IDE 也交出去》。

getJson 的 json: 2 严格模式校验什么?会拒绝 .5 这样的数字吗? 2026-09-29

getJson(json: 2) 让 JSON 扫描器进入严格模式,拒掉宽松默认容忍的分隔符文法与词法毛病:尾逗号、首逗号、重复逗号、缺逗号({"a":1,}、[,1]、[1,,2]、[1 2]),缺冒号或重复冒号,用 ; / = 当分隔符,单引号与反引号串,@ 日期,0xff,nil,非字符串的键,收尾括号与打开的容器不匹配,以及顶层值之后的任何残留。字符串必须用双引号、只认 RFC 允许的转义。数字则有意保持宽松:.5、03、1. 照旧接受(真实 JSON 里就有),只拒完全不含数字的记号,如单独的 . 或 -。裸标量做根是合法的(20、"hi"、true、null)。实现上没有多跑一趟:严格字符串检查折进「找右引号」那趟扫描,解码用的 esc() 一行未改,所以宽松模式与 FD 解析逐字节不变。详见《JSON 严格模式:收下 .5,拒掉尾逗号》。

怎么查 Fun 的版本号?脚本怎么判断解释器够不够新? 2026-09-27

版本号是编译期的一个常量(src/core/version.inc),有两条路径读到它、不会漂移:'host'.arg() 新增了 version 字段(数值构建戳,YYYYMMDD),N.time() 在 N 为 2010..2099 时返回同一个数 —— 脚本因此可以用一次普通比较判断版本,例如 if 2010.time() >= 20250819 then …(lib-ajax 就是这么用的)。可读发布串(9.0)是 'host'.arg() 的 fun 字段;lib-host-test.fun 有一条回归断言这两条数值路径相等。version.inc 由纯 Fun 脚本 fun src/prj/fun/gen-version.fun [YYYYMMDD] [release] 生成,不依赖 shell/git,Windows 与 Linux 同一条命令(不传日期就盖当天,发版时传显式日期保证可复现)。详见《版本号从哪来》。

Fun 的等号为什么忽略类型?怎么严格比较两个值? 2026-09-26

Fun 的 = 刻意宽松:nil、0、''、false 互相相等,'1' = 1,字符串不分大小写,1 = 1.0。这对读进来的外部数据很方便,但分不清 0 和 NULL —— 曾有 ORM 因此把绑定的 0 和 FALSE 全存成了 SQL NULL。需要精确判断时用 eq():只有逻辑类型和值都相同才为真(nil/0/''/false 互不相等,'1' <> 1,'ABC' <> 'abc',1 <> 1.0);type() 返回可移植的逻辑类型号,对标 COM VARENUM(nil 0、整数 3、浮点 5、字符串 8、布尔 11)。两者只作用于基本(非对象)值,宽松的 = 保持不变。详见《0 是怎么变成 NULL 的》。

Fun 支持多线程吗?服务端为什么是单线程的? 2026-09-25

可以起线程(用 getapi 调 pthread_create;glibc 2.34 之前该符号在 libpthread.so.0,不在 libc),但只要两个线程进入同一个函数体就不安全。Fun 把函数的局部变量放在按函数共享的一处存储里(CFunStack,每个变量一个 CVarStack,用一个 level 计数区分递归层次),两个线程进同一函数会互相踩。此外还需要 FPC 的线程安全堆(cthreads),它给每次分配加锁,单线程运行要多付约 20%;2026-09-25 起它改为按需链接(./make-linux-x86_64.sh -dUseCThreads),默认构建更快,也不再依赖 libpthread。

所以 lib-winsock 的 Linux 后端是一个单线程 poll(2) 循环,回调在调用线程里内联执行。要不加线程地做异步,库里有 Scheduler 的 Post / After / Every。详见为什么这个服务端没有线程。

Fun 脚本能读控制台输入、做交互吗? 2026-09-24

能。2026 年 9 月下旬起,libase 注册了 input 内建,由接收者字符串选模式,是输出运算符 ?. 读的一端:'line'.input([prompt]) 读一行(去掉行尾),'char'.input([prompt]) 读一个按键(终端上为原始模式),'all'.input([prompt]) 一直读到输入结束。Linux 上直接从 fd 0 逐字节读标准输入,管道与真实终端行为一致,也不会把残留字节留给下一次调用;Windows 上只按行读,走 RTL,按本构建的字符串编码返回。

由于 Fun 把 '' 和 nil 判为相等,空行与输入结束仅凭返回值分不出。把变量作为第二个参数(或 ok:)传入,它会收到一个标志:读到内容为真,输入结束为假 —— 这是让读循环可靠结束的办法。详见为了那一亿美金的 AI 项目!

Fun 能在 Linux 上做 Web / RPC 后端吗?需要图形界面吗? 2026-09-21

可以,而且在 Linux 上是无界面运行的。网络栈是 lib-winsock-lnx:libc 上的 BSD socket 加单线程 poll(2) 事件循环(HTTP/WebSocket 与 UDP 服务器,以及 TCP/DNS 客户端),没有窗口,也没有 worker 线程。在此之上,lib-jsonrpc-lnx 是跑在 HTTP 上的 JSON-RPC 2.0 服务,路由 /JsonRpc/2.0;Windows 版是 lib-ui 的 Form() 配 WSAAsyncSelect 加 user32 消息泵,Linux 版直接在调用线程上服务 —— 一句 rpc.start() 就 bind、listen 并循环到 stop()。派发没变:定义 Rpc_<Method> 成员,onCall 负责路由,错误码沿用标准那五个(-32700/-32600/-32601/-32602/-32603)。lib-orm-rpc-lnx 是同一套东西接上数据库:class OrmRpc = JsonRpc() 在 SQLite 上保持 Rpc_Orm* 协议,客户端不带 schema 时 OrmInit 经 lib-ado-schema-lnx 直接从数据库建出 ORM schema。详见没有窗口的 RPC 服务端。

Fun 的内存怎么管理?会不会泄漏? 2026-09-20

Fun 使用轻量的引用计数垃圾回收:对象在引用数归零时释放,没有分代标记-清除,也没有并发 GC。这让运行时很小,但也意味着少写一次释放就会漏。一个真实例子修于 2026-09-18:CExp 单元格在 nil 短路与字符串下标读取时每次求值都新建、却因为接收字段是借用字段而不释放,fun-zillio 的 rdb 后端因此每请求约漏 0.7 KB,RSS 随请求数线性增长;修法是每处复用一个预分配格子。另一类不是泄漏而是拒绝服务:JSON/FD 的扫描是迭代的,但树的释放与克隆是递归的,嵌套数千层会爆栈 —— 现在嵌套超过 512 层会直接抛异常。详见每请求漏一个 CExp,与 512 层嵌套。

Fun 是什么时候开源的?开源后进展如何? 2026-09-19

Fun 的核心仓库于 2026-08-31 首次开源。此后约半个月,版本从 v9.0 走到 v9.1(11 个版本、84 次提交),主线只有一条:把 Fun 从「能在 Linux 上编译」推进到「Linux 与 Windows 全栈等价」。Linux 端补齐了 libffi、OpenSSL、zlib、PCRE、libxml2、libcurl、SQLite/ORM 与 libtcc 等,模块加载率从 0/59 提到 48/59;顺带完成指针宽度收敛与越界检查改造。回归用例合计约 724+ 个,代码开源覆盖度约 90%。详见Fun 技术总览:开源十五天。

Fun 的解释器快吗?真需要性能时怎么办? 2026-09-18

Fun 是 tree-walking 解释器,纯解释跑分形基准约 2310 毫秒,大致落在 Python 与 Perl 之间 —— 它不主打快。真需要性能时,热点可以逐级下沉到原生执行,四条通道都已在 Linux 上实测可用:FFI 调 C 库、内嵌 TinyCC 运行时编 C、在 mmap 出的可执行页上写 x86_64 汇编、以及 JIT 闭包双向回调。内联汇编路径实测 19.0 毫秒,约 120 倍加速,解释器自带的 55 项基准也全部通过。详见四级逃生舱:从 2310 毫秒到 19 毫秒。

Fun 能写内联汇编吗?`#!asm` 在 64 位上能用吗? 2026-09-17

能。代码块第一行写 #!asm 就会在装载时汇编,另外还有 #!hex(裸机器码)与 #!c(由随包内置的 TinyCC 在运行时编译)两条路;lib-asm/jit 负责分诊,产物用 FFI 签名(如 'ii:i')注册成普通可调用体。汇编器本质是查表:每行规范化后去 asm-list.fd 里查,所以它的能力边界就是这张表的内容。这张表原本以 32 位为主,这正是过去 #!asm 在 x86_64 上不好用的原因;2026-09-16 的更新从 20 个 Linux 二进制里收割 174 万条指令,补入最常用的 3250 条 x86-64 形式(REX 寄存器、push/pop r12-r15、rbp 栈帧、disp8 局部变量、RIP 相对 lea),覆盖该语料的 90.7%。注意它没有标签解析与重定位:分支要么整块预置,要么通过 <name> 回调绕回 Fun。详见给查表汇编器补上 64 位。

Fun 在 64 位系统上会截断指针吗? 2026-09-16

以前会。Fun 的值模型把整数存在 32 位的槽里,地址只要经过 fun.int 一圈,在 x86_64/Win64 上高 32 位就丢了:栈的 push/pop 必崩,.move()/.movs() 读写到错误内存,getapi() 传裸地址必抛 Access violation。现在解释器通过 asPtr/rawPtr 与 fun.intptr/fun.uintptr 别名按指针宽度读写地址(平台判断只留在一处),内置函数的注册与派发路径也一并改到指针宽。32 位构建逐位不变;lib-stack、JSON 循环检测与 libase 的裸内存路径也一起修好了。详见64 位的地址,32 位的格子。

Fun 的字符串是值语义还是引用语义?下标越界会怎样? 2026-09-15

字符串是引用类型:赋值和传参复制的是引用而不是缓冲区,所以 b := a; b[0] = 'H' 也会改到 a。这是预期行为,不是 bug——lib-cstruct.fun 的 goBytes 正依赖「回调参数是调用者缓冲区的别名」这一点(写参数即写回调用者),因此没有加写时复制(COW)。下标范围见 lib/lib-string.fun 的文件头注释:0..N-1 从头数,-N..-1 从尾数。越界读给安全默认值(s[i] 为 nil,.toByte 为 0);越界写抛异常,不再破坏字符串头或堆。对象由轻量引用计数管理。少数原语有意保留为不安全的内存操作(s.move(numDest)、toNum(ptr:-1))。详见越界了,会发生什么。

Fun 支持模块化吗?我该怎么组织自己的代码? 2026-09-14

一个模块就是一个脚本,use 把它链接进当前作用域:use 'lib-fd.fun' 引入其中的名字,use 'lib-fd.fun' as fd 则把模块绑定成变量,当作命名空间用。同一模块只加载一次,顶层代码也只运行一次,无论有多少处 use 它。路径先按调用者所在目录解析,再试可执行文件旁的 lib\ 与 app\ 目录;: 前缀拉取的是内嵌资源而非磁盘文件(如 use ':fd.bnf.fd')。因为 use ... as 把模块绑成变量,同一个模块可以在不同作用域用不同名字引入,互不冲突。详见一棵树,许多模块。

FD 是什么格式?和 JSON 有什么关系? 2026-09-13

FD 是 Fun 内置的数据格式:结构与 JSON 一致(嵌套键、数组),但采用类 Markdown 的缩进语法,对人友好,也便于语言模型阅读。它与 JSON 通过 @toJson(fd: true)、getJson(fd: true) 双向转换,lib-json.fun 把它们包成 FD.Parse / FD.Stringify。它最特别的地方是自描述:规范 fd.fd 与文法 fd.bnf.fd 都用 FD 本身写成,解析器在运行时由这份文法现场编译。详见一种自己描述自己的数据格式。

Fun 能在 Linux 上运行吗?怎么用? 2026-09-12

可以。Fun 已原生支持 x86_64 Linux(解释器由 Free Pascal 与 gcc 构建),没有单独的安装包:从 github.com/Funlang/fun 取源码,执行 src/prj/fun/make-linux-x86_64.sh 即可。两件可选的第三方组件(正则用的 PCRE 8、运行期 C 编译 / JIT 用的 Tiny C Compiler)仓库未携带,缺失时构建会降级并提示,不会中断。Linux 上的标准库提供以 *-lnx.fun 命名的平台后端。详见一个模块,两份后端与把 Fun 解释器移植到 Linux。

Fun 与 Pascal / Delphi 是什么关系?和 Nuva 又是什么关系? 2026-09-11

Fun 是脚本语言,但它的解释器用 Pascal 编写,可分别由 Delphi(Windows 32 位)与 Free Pascal(FPC)(Linux、Windows 64 位、ARM,含交叉编译)构建。Fun 的初衷正是复用 Pascal 资源:它能调用以 Pascal/Delphi 编译出的模块与系统 API,但语法与运行时都是一门独立的动态语言,不是 Pascal 的方言。

Nuva 是作者更早的一门语言(2006 年,更早还有 2005 年的 TemplateScript);Fun 是其后续作品,两者各自独立。

Fun 是什么? 2026-09-10

Fun 是一门简单、动态、面向对象且支持函数式的脚本语言,由张卫东于 2010 年为复用 Pascal 资源而创造。它以约 300 KB 的单个 fun.exe 发布,内置 JIT 编译、垃圾回收与 Perl 兼容正则。可先读简介与中文手册。

这里的 Fun 和别处看到的 "fun"/"FUN" 是同一门语言吗? 2026-09-10

多半不是。"fun" 既是普通英文单词,也是几个互不相关项目共用的短名。本站所记载的是张卫东(Wisdom ZHANG)的生产级语言,唯一权威出处为 funlang.org 与 github.com/Funlang。详见是哪个 FUN?权威来源。

Fun 是免费的吗?采用什么许可? 2026-09-10

免费。Fun 9.0 以 MIT 许可发布,可自由使用、修改与再分发。另有一份核心定制商业许可,仅当你对内核做实质性修改、并以闭源方式随商业产品或设备分发时才需要购买。内部使用、学习及开源再分发均无需商业许可。

Fun 开源吗?源码在哪里? 2026-09-10

开源。核心与标准库已于 2026 年开源,位于 github.com/Funlang/fun,欢迎 fork、构建与贡献。

Fun 支持哪些平台? 2026-09-10

Windows(32/64 位)、Linux、ARM 与 Windows CE。既可作独立的 fun.exe 运行,也可作为 fun.dll 嵌入,运行时无外部依赖。

如何下载并运行 Fun? 2026-09-10

下载 fun.zip(运行时 + IDE,版本 9.0),解压后运行 fun-install.bat 注册 .fun 文件关联,随后双击任意 .fun 脚本即可运行,或启动随附的 IDE。见快速开始。

Fun 是实验性或玩具语言吗? 2026-09-10

不是。Fun 自 2010 年持续演化,承自 Nuva(2006)与 TemplateScript(2005),已在生产中运行多年,支撑遍布 130 多个国家的商业软件。9.0 是成熟版本。

Fun 与 Python、JavaScript 或 Lua 有何不同? 2026-09-10

Fun 把高层与底层工作收进同一个运行时:以数据为中心的值(list / set / tree)、内置 JSON 与 FD 格式、作为值类型的 PCRE 正则、含管道运算符的一等函数,外加紧凑 FFI、内置 Tiny C 编译器、JIT 与内联汇编。语法承自 Ada,函数以 end fun; 结尾,打印用 ?.。见特性一览。


Official site: https://funlang.org · GitHub: https://github.com/Funlang · Which FUN? · Overview · Manual · 中文首页