tokiwa-software/fuzion

GitHub: tokiwa-software/fuzion

Fuzion 是一门通过统一的 feature 机制、契约式设计和代数效应系统来实现简洁、安全、正确编程的现代语言实现。

Stars: 60 | Forks: 13

# fuzion logo Fuzion [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/tokiwa-software/fuzion/badge)](https://api.securityscorecards.dev/projects/github.com/tokiwa-software/fuzion) [![run tests on linux](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/294cf690e8190249.svg)](https://github.com/tokiwa-software/fuzion/actions/workflows/linux.yml) [![run tests on macOS](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/8416e4881e190250.svg)](https://github.com/tokiwa-software/fuzion/actions/workflows/apple.yml) [![run tests on windows](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/ef3e661082190252.svg)](https://github.com/tokiwa-software/fuzion/actions/workflows/windows.yml) [![interpreter](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/1f67fbf1d8190253.svg)](https://github.com/tokiwa-software/fuzion/actions/workflows/interpreter.yml) ## 一门专注于简单性、安全性和正确性的语言。 * [示例](#examples) * [文档](#documentation) * [克隆](#clone) * [依赖包](#required-packages) * [Linux](#linux) * [MacOS](#macos) * [Windows](#windows) * [构建](#build) * [运行](#run) * [测试](#tests) * [运行所有测试](#running-all-tests) * [运行单个测试](#run-a-single-test) * [记录测试](#record-a-test) * [软依赖](#soft-dependencies) * [安装预编译版本](#install-prebuilt) * [语言服务器](#language-server) * [安装](#install) * [Vim](#vim) * [Emacs](#emacs) * [Eglot](#eglot) * [LSP-Mode](#lsp-mode) * [独立运行](#run-standalone) * [socket](#transport-socket) * [stdio](#transport-stdio) ## 示例 ``` hello_world is # first we define a custom mutate effect. # we will need this for buffered reading from stdin # lm : mutate is # calling `lm` creates an instance of our mutate effect, # `instate_self` is then used to instate this instance and # run code in the context of the instated effect. # lm ! ()-> # read someone's name from standard input # get_name => (io.stdin.reader lm) ! ()-> (io.buffered lm).read_line ? str String => str | io.end_of_file => "" # greet someone with the name given # greet(name String) is say "Hello, {name}!" # greet the user # x := greet get_name # you can access any feature - even argument features of other features # from outside # say "How are you, {x.name}?" ``` 这个 `hello_world` 示例很好地展示了 Fuzion 中一个非常重要的概念:一切都是 *feature*。在其他编程语言中,*类*、*方法*、*接口* 以及各种其他概念常常会造成混乱,而 *feature* 正是 Fuzion 解决这种混乱的方案。既然一切都是 feature,程序员就不必再关心这些区别,编译器会完成这项工作。正如你所看到的,甚至可以从外部访问某个 feature 的参数 feature。 ``` ex_gcd is # return common divisors of a and b # common_divisors_of(a, b i32) => max := max a.abs b.abs (1..max).flat_map i-> if (a % i = 0) && (b % i = 0) [-i, i] else [] # find the greatest common divisor of a and b # gcd(a, b i32) pre safety: (a != 0 || b != 0) post safety: a % result = 0 safety: b % result = 0 pedantic: (common_divisors_of a b).reduce bool true (acc,cur -> acc && (result % cur = 0)) => if b = 0 then a else gcd b (a % b) say <| gcd 8 12 say <| gcd -8 12 say <| gcd 28 0 ``` 这个示例实现了一个简单版本的算法,用于找出两个数的最大公约数。然而,它也演示了 Fuzion 的一个显著特性:契约式设计。通过为 feature 指定前置条件和后置条件,正确性检查成为可能。 ``` generator_effect is # define a generator effect with a yield operation # gen(T type, yield T->unit # yield is called by code to yield values ) : effect is # traverse a list and yield the elements # list.traverse unit => match list.this c Cons => (generator_effect.gen A).env.yield c.head; c.tail.traverse nil => # bind the yield operation dynamically # (gen i32 (i -> say "yielded $i")) ! ()-> [0,8,15].as_list.traverse ``` Fuzion 的另一个主要概念是 *[代数效应](https://en.wikipedia.org/wiki/Effect_system)* - 这是一种以安全方式封装带有副作用代码的新方法。 在上面的示例中,使用了一个自定义的 *effect* 来实现一个带有 `yield` 操作的生成器。在其他一些语言中,这需要语言本身提供一个 `yield` 关键字,但在 Fuzion 中,无需语言层面的特殊支持即可实现这一点。 如果你想亲自体验一下 Fuzion,请尝试 [交互式教程](https://fuzion-lang.dev/tutorial/index)。 ## 文档 有关语言和实现设计,请访问 [fuzion-lang.dev](https://fuzion-lang.dev)。 ## 克隆 ``` git clone https://github.com/tokiwa-software/fuzion ``` ## 依赖包 ### Linux - OpenJDK 25[^1], (需要包含 jmods) - clang LLVM C 编译器 - GNU make - libgc ### MacOS - OpenJDK 25[^1], (需要包含 jmods) - clang LLVM C 编译器 - GNU make - libgc ### Windows 1. 安装 chocolatey: [chocolatey.org](https://chocolatey.org/install) 2. 在 Powershell 中: 1. choco install git openjdk make msys2 diffutils 2. [Environment]::SetEnvironmentVariable("Path","c:\tools\msys64\ucrt64\bin;" + $env:Path , "User") 3. 在文件 C:\tools\msys64\msys2_shell.cmd 中修改这一行:'rem set MSYS2_PATH_TYPE=inherit' 为 'set MSYS2_PATH_TYPE=inherit' 4. 在 msys2 shell 中(执行 C:\tools\msys64\msys2_shell.cmd): 1. pacman -S mingw-w64-x86_64-clang 2. make 5. 执行 ./bin/windows_install_boehm_gc.sh ## 构建 ``` cd fuzion make ``` 你现在应该有一个名为 **build** 的文件夹了。 ## 运行 ``` cd build export PATH=$PWD/bin:$PATH cd tests/rosettacode_factors_of_an_integer fz factors ``` 要编译同一个示例(需要 clang C 编译器): ``` fz -c factors ./factors ``` 祝你玩得开心! ## 测试 ### 运行所有测试 在源代码文件夹中,要运行所有后端的所有测试,请使用以下命令: ``` make run_tests ``` 当测试完成后,你将看到一份包含成功、失败和跳过的测试的摘要。 如果只想运行特定后端的测试,请使用: ``` # 针对 jvm backend make run_tests_jvm # 针对 c backend make run_tests_c # 针对 interpreter backend make run_tests_int ``` ### 运行单个测试 在源代码文件夹中运行: ``` make _BACKEND_ -C _BUILD_DIR_/tests/_TEST_/ ``` 其中 `_BACKEND_` 可以是以下之一: - jvm - c - int - effect - 留空以在所有后端上运行测试 除非你指定了自定义的构建目录,否则你需要将 `_BUILD_DIR_` 替换为 `build`。 最后,将 `_TEST_` 替换为测试的名称。 完整示例: ``` make jvm -C build/tests/hello ``` ### 记录测试 这与运行测试的操作相同,只需指定不同的 make target 即可。 - record - record_jvm - record_c - record_int - record_effect 完整示例: ``` make record_jvm -C ./build/tests/hello ``` 这将会记录标准输出和标准错误,并将它们保存在测试目录中的文件里。(HelloWorld.fz.expected_out, HelloWorld.fz.expected_err) ## 软依赖 没有这些依赖也可以编译和使用编译器。 但以下工具/依赖会用于例如生成文档或运行测试套件: - antlr,用于 ebnf 语法 - asciidoctor,asciidoctor-pdf - sed,用于规范化测试输出 - wget,用于下载 jar 依赖 - org.eclipse.lsp4j 及其他,用于语言服务器 ## 安装预编译版本 [![Packaging status](https://repology.org/badge/vertical-allrepos/fuzion.svg)](https://repology.org/project/fuzion/versions) ## 语言服务器 ### 安装 |客户端|仓库| |---|---| |vscode|https://github.com/tokiwa-software/vscode-fuzion| |vim|请参阅以下说明| |emacs|请参阅以下说明| |eclipse (theia)|https://github.com/tokiwa-software/vscode-fuzion| #### Vim 0. 注意:fuzion_language_server (来自 ./bin/) 需要包含在 $PATH 中 1. .vimrc 示例: :filetype on call plug#begin('~/.vim/plugged') Plug 'neoclide/coc.nvim', {'branch': 'release'} call plug#end() 2. 在 vim 中 1. `:PlugInstall` 2. `:CocConfig` { "languageserver": { "fuzion": { "command": "fuzion_language_server", "args" : ["-stdio"], "filetypes": [ "fz", "fuzion" ] } } } 3. 添加文件类型检测文件 ~/.vim/ftdetect/fz.vim au BufRead,BufNewFile *.fz set filetype=fz #### Emacs 对于 emacs,有 eglot 或 lsp-mode 两种选择。 ##### Eglot - M-x package-install RET eglot RET (仅 emacs 版本 <29 时需要) - 将以下代码添加到 ~/.emacs.d/fuzion-lsp.el 以启用 [eglot](https://github.com/joaotavora/eglot) ``` (require 'package) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) (add-to-list 'package-archives '("elpa" . "https://elpa.gnu.org/packages/")) ;; Comment/uncomment this line to enable MELPA Stable if desired. See `package-archive-priorities` ;; and `package-pinned-packages`. Most users will not need or want to do this. ;;(add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/") t) (package-initialize) (custom-set-variables ;; custom-set-variables was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful. ;; Your init file should contain only one such instance. ;; If there is more than one, they won't work right. '(inhibit-startup-screen t) '(package-selected-packages '(eglot ##))) (custom-set-faces ;; custom-set-faces was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful. ;; Your init file should contain only one such instance. ;; If there is more than one, they won't work right. ) (define-derived-mode fuzion-mode fundamental-mode "Fuzion" "Major mode for Fuzion.") (add-to-list 'auto-mode-alist '("\\.fz\\'" . fuzion-mode)) (require 'eglot) (add-to-list 'eglot-server-programs '(fuzion-mode . ("fuzion_language_server" "-stdio"))) (add-hook 'after-init-hook 'global-company-mode) (add-hook 'fuzion-mode-hook 'eglot-ensure) (provide 'init) ;;; init.el ends here ``` - 将以下行添加到 ~/.emacs.d/init.el 或 ~/.emacs 中 (load "~/.emacs.d/fuzion-lsp.el") ##### LSP-Mode - 使用以下命令为 emacs 安装 lsp-mode、flycheck 和 company - M-x package-install RET lsp-mode - M-x package-install RET flycheck - M-x package-install RET company RET - 将以下代码添加到 ~/.emacs.d/fuzion-lsp.el 以启用 [lsp-mode](https://github.com/emacs-lsp/lsp-mode) ``` (require 'package) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) (add-to-list 'package-archives '("elpa" . "https://elpa.gnu.org/packages/")) (package-initialize) (custom-set-variables ;; custom-set-variables was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful. ;; Your init file should contain only one such instance. ;; If there is more than one, they won't work right. '(inhibit-startup-screen t) '(package-selected-packages '(lsp-ui company flycheck lsp-mode ##))) (custom-set-faces ;; custom-set-faces was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful. ;; Your init file should contain only one such instance. ;; If there is more than one, they won't work right. ) (define-derived-mode fuzion-mode fundamental-mode "Fuzion" "Major mode for Fuzion.") (add-to-list 'auto-mode-alist '("\\.fz\\'" . fuzion-mode)) (require 'lsp-mode) (global-flycheck-mode) (add-to-list 'lsp-language-id-configuration '(fuzion-mode . "fuzion")) (defgroup lsp-fuzionlsp nil "LSP support for Fuzion, using fuzionlsp." :group 'lsp-mode :link '(url-link "")) (lsp-register-client (make-lsp-client :new-connection (lsp-stdio-connection (lambda () `(,"fuzion_language_server" "-stdio"))) :major-modes '(fuzion-mode) :priority -1 :server-id 'fuzionls)) (lsp-consistency-check lsp-fuzion) (add-hook 'fuzion-mode-hook #'lsp) (add-hook 'after-init-hook 'global-company-mode) (setq lsp-enable-symbol-highlighting t) ;; (setq lsp-enable-semantic-highlighting t ;; lsp-semantic-tokens-enable t ;; lsp-semantic-tokens-warn-on-missing-face t ;; lsp-semantic-tokens-apply-modifiers nil ;; lsp-semantic-tokens-allow-delta-requests nil ;; lsp-semantic-tokens-allow-ranged-requests nil) ;; (setq lsp-modeline-code-actions-mode t) ;; (setq lsp-modeline-code-actions-segments '(name icon)) ;; (setq lsp-log-io t) (provide 'lsp-fuzion) (provide 'init) ;;; init.el ends here ``` - 将以下行添加到 ~/.emacs.d/init.el 或 ~/.emacs 中 (load "~/.emacs.d/fuzion-lsp.el") ### 独立运行 #### 传输 socket - 运行 `./bin/fuzion_language_server -socket --port=3000` - 将客户端连接到服务器打印到标准输出的(随机)端口。 #### 传输 stdio - 运行 `./bin/fuzion_language_server -stdio`