30个版本
0.18.2 | 2023年9月19日 |
---|---|
0.18.0 | 2021年10月3日 |
0.17.2 | 2020年10月25日 |
0.16.1 | 2020年7月5日 |
0.6.2 | 2017年10月18日 |
#233 在 编程语言 中
每月下载量43
用于 4 crates
390KB
11K SLoC
gluon
Gluon是一种小巧的、静态类型的、函数式编程语言,旨在用于应用内嵌。I
特性
-
静态类型 - 静态类型使得编写 gluon 和宿主应用之间的安全且高效的接口变得更容易。
-
类型推断 - 类型推断确保类型很少需要显式编写,从而在无需编写类型的情况下获得静态类型的所有好处。
-
简单内嵌 - 将值序列化和反序列化为 gluon 几乎无需任何样板代码,允许在 Rust 中定义的函数 Rust 直接传递给 gluon。
-
默认UTF-8 - Gluon 默认支持 Unicode,使用 utf-8 编码的字符串和 Unicode 代码点作为字符。
-
独立的堆 - Gluon 是一种垃圾回收语言,但为每个执行的 gluon 线程使用独立的堆。这使每个堆保持小巧,减少了垃圾回收器的开销。
-
线程安全 - Gluon 是用 Rust 编写的,这保证了线程安全。Gluon 保持相同的保证,允许多个 gluon 程序并行运行 (示例)*
* Gluon 程序的并行执行是最近添加的功能,可能还存在死锁等问题。
示例
你好世界
let io = import! std.io
io.print "Hello world!"
阶乘
let factorial n : Int -> Int =
if n < 2
then 1
else n * factorial (n - 1)
factorial 10
24
// # 24
//
// From http://rosettacode.org/wiki/24_game
//
// Write a program that randomly chooses and displays four digits, each from 1 ──► 9 (inclusive) with repetitions allowed.
//
// The program should prompt for the player to enter an arithmetic expression using just those, and all of those four digits, used exactly once each. The program should check then evaluate the expression.
//
// The goal is for the player to enter an expression that (numerically) evaluates to 24.
//
// * Only the following operators/functions are allowed: multiplication, division, addition, subtraction
// * Division should use floating point or rational arithmetic, etc, to preserve remainders.
// * Brackets are allowed, if using an infix expression evaluator.
// * Forming multiple digit numbers from the supplied digits is disallowed. (So an answer of 12+12 when given 1, 2, 2, and 1 is wrong).
// * The order of the digits when given does not have to be preserved.
//
//
// ## Notes
//
// The type of expression evaluator used is not mandated. An RPN evaluator is equally acceptable for example.
// The task is not for the program to generate the expression, or test whether an expression is even possible.
// The `import!` macro are used to load and refer to other modules.
// It gets replaced by the value returned by evaluating that module (cached of course, so that
// multiple `import!`s to the same module only evaluates the module once)
let io @ { ? } = import! std.io
let prelude = import! std.prelude
let { Result } = import! std.result
let array @ { ? } = import! std.array
let int = import! std.int
let string = import! std.string
let list @ { List, ? } = import! std.list
let random = import! std.random
let string = import! std.string
// Since imports in gluon returns regular values we can load specific parts of a module using pattern matches.
let char @ { ? } = import! std.char
let { (<>) } = import! std.semigroup
let { flat_map } = import! std.monad
let { (*>), (<*), wrap } = import! std.applicative
let { for } = import! std.traversable
type Op = | Add | Sub | Div | Mul
type Expr = | Int Int | Binop Expr Op Expr
let parse : String -> Result String Expr =
// Gluon has a small parser combinator library which makes it easy to define an expression parser
let parser @ {
between,
satisfy,
satisfy_map,
spaces,
token,
digit,
skip_many1,
recognize,
lazy_parser,
chainl1,
(<?>),
? } = import! std.parser
let { (<|>) } = import! std.alternative
let lex x = x <* spaces
let integer =
// `do` expression provide a way to write monads in a way similiar to procedural code
do i = lex (recognize (skip_many1 digit))
match int.parse i with
| Ok x -> wrap x
| Err _ -> parser.fail "Unable to parse integer"
let operator =
satisfy_map (\c ->
match c with
| '*' -> Some Mul
| '+' -> Some Add
| '-' -> Some Sub
| '/' -> Some Div
| _ -> None)
<?> "operator"
rec
let atom _ =
parser.functor.map Int integer
<|> between (lex (token '(')) (lex (token ')')) (lazy_parser expr)
let binop _ =
let op_parser =
do op = lex operator
wrap (\l r -> Binop l op r)
chainl1 (atom ()) op_parser
let expr _ = binop ()
in
// Gluon makes it possible to partially apply functions which we use here to scope all parser functions
// inside the `let parse` binding above.
let parse : String -> Result String Expr = parser.parse (expr () <* spaces)
parse
/// Validates that `expr` contains exactly the same integers as `digits`
let validate digits expr : Array Int -> Expr -> Bool =
let integers xs expr : List Int -> Expr -> List Int =
match expr with
| Int i -> Cons i xs
| Binop l _ r -> integers (integers xs l) r
let ints = integers Nil expr
list.sort (list.of digits) == list.sort ints
let eval expr : Expr -> Int =
match expr with
| Int i -> i
| Binop l op r ->
let f =
// Operators are just functions and can be referred to like any other identifier
// by wrapping them in parentheses
match op with
| Add -> (+)
| Sub -> (-)
| Div -> (/)
| Mul -> (*)
f (eval l) (eval r)
do digits =
let gen_digit = random.thread_rng.gen_int_range 1 10
do a = gen_digit
do b = gen_digit
do c = gen_digit
do d = gen_digit
wrap [a, b, c, d]
let print_digits = for digits (\d ->
seq io.print " "
io.print (show d))
seq io.print "Four digits:" *> print_digits *> io.println ""
let guess_loop _ =
do line = io.read_line
// Exit the program if the line is just whitespace
if string.is_empty (string.trim line) then
wrap ()
else
match parse line with
| Err err -> io.println err *> guess_loop ()
| Ok expr ->
if validate digits expr then
let result = eval expr
if result == 24
then io.println "Correct!"
else io.println ("Incorrect, " <> int.show.show result <> " != 24") *> guess_loop ()
else
io.println
"Expression is not valid, you must use each of the four numbers exactly once!"
*> guess_loop ()
guess_loop ()
入门指南
在线尝试
您可以在浏览器中尝试 gluon,请访问 https://gluon-lang.org/try/。 (GitHub)
安装
您可以通过使用 Github 上的预构建可执行文件来安装 Gluon,或者您可以使用 Cargo 来安装 gluon_repl
crate
cargo install gluon_repl
REPL
Glueon有一个小的可执行文件,可以直接运行Glueon程序或在一个小的REPL中运行。可以通过向构建的REPL可执行文件传递-i
标志来启动REPL,该可执行文件可以通过以下命令运行:cargo run -p gluon_repl -- -i
。
REPL功能
-
评估表达式(类型为IO的表达式将在IO上下文中评估)。
-
通过编写
let <pattern> <identifier>* = <expr>
(省略正常let绑定中的in <expr>
)来绑定变量。示例let f x = x + 1 let { x, y = z } = { x = 1, y = 2 } f z
-
使用
:h
打印有关可用命令的帮助信息。 -
使用
:l path_to_file
加载文件,加载文件中表达式的评估结果将存储在以文件名(不带扩展名)命名的变量中。 -
使用
:t expression
检查表达式的类型。 -
使用
:i name
打印有关名称的信息。
示例:i std.prelude.List type std.prelude.List a = | Nil | Cons a (std.prelude.List a) /// A linked list type
-
标识符和记录字段的自动完成(请参见图片 )
-
通过写入
:q
退出REPL。
工具
语言服务器
Glueon有一个语言服务器,它提供代码补全和格式化支持。安装使用以下命令:cargo install gluon_language-server
。
Visual Studio Code扩展
Visual Studio Code的gluon扩展提供语法高亮和补全。要安装它,在扩展中搜索gluon
。(Github)
Vim插件
vim-gluon提供语法高亮和缩进。
Glueon语言服务器已测试与https://github.com/autozimu/LanguageClient-neovim和https://github.com/prabirshrestha/vim-lsp一起工作。
示例配置(autozimu/LanguageClient-neovim)
let g:LanguageClient_serverCommands = {
\ 'gluon': ['gluon_language-server'],
\ }
" Automatically start language servers.
let g:LanguageClient_autoStart = 1
nnoremap <silent> K :call LanguageClient_textDocument_hover()<CR>
nnoremap <silent> gd :call LanguageClient_textDocument_definition()<CR>
文档
用法
Rust
Glueon需要最新的Rust编译器来构建(1.9.0或更高版本),并在crates.io上提供。它可以通过添加以下行轻松地包含在Cargo项目中。
[dependencies]
gluon = "0.18.2"
其他语言
目前,与Glueon虚拟机交互的最简单方法是使用Rust,但存在一个基本的C API,将来将扩展以使其更接近Rust API。
贡献
有几种方式可以为Glueon做出贡献。最简单的方法是打开问题或处理标记为入门的问题。有关贡献的更多信息,您可以查看CONTRIBUTING.md。贡献还包括有关为Glueon运行/入门测试的详细信息。
目标
这些目标可能会随着时间的推移而改变或细化,因为我正在尝试语言可能实现的功能。
-
可嵌入 - 类似于 Lua - 它旨在嵌入到另一个程序中,该程序可能使用虚拟机来扩展其功能。
-
静态类型 - 语言使用基于 Hindley-Milner 的类型系统,并进行了某些扩展,允许简单的通用类型推断。
-
小巧 - 由于小巧,语言易于学习,并且实现占用空间小。
-
严格 - 严格的语言通常更容易推理,特别是考虑到这是大多数人习惯的。对于需要惰性的情况,提供了显式的类型。
-
模块化 - 库被分为解析器、类型检查器和虚拟机+编译器。这些组件可以独立于彼此使用,允许应用程序选择它们需要的确切内容。
灵感
lib.rs
:
AST上的原始自动完成和类型查询
依赖关系
~4–12MB
~125K SLoC