23 个版本

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.8.1 2018 年 7 月 1 日

#1061 in 编程语言


用于 gluon_repl

MIT 许可证

305KB
7.5K SLoC

gluon

Build Status Documentation Book std

Gluon 是一种小巧、静态类型、函数式编程语言,旨在用于应用嵌入。

特性

  • 静态类型 - 静态类型使得编写 gluon 与宿主应用之间的安全高效接口变得更加容易。

  • 类型推断 - 类型推断确保类型很少需要显式编写,从而在无需任何类型编写的情况下获得静态类型的所有好处。

  • 简单嵌入 - 将值从 gluon 转换到和从 gluon 转换回需要极少的样板代码,允许在 Rust 中定义的函数直接传递给 gluon。

  • 默认 UTF-8 - Gluon 支持 Unicode,默认使用 utf-8 编码的字符串和 Unicode 代码点作为字符。

  • 单独的堆栈 - Gluon 是一种垃圾收集语言,但为每个执行的 gluon 线程使用单独的堆栈。这使得每个堆栈都很小,从而减少了垃圾收集器的开销。

  • 线程安全 - Gluon 是用 Rust 编写的,这保证了线程安全。Gluon 保持相同的保证,允许多个 gluon 程序并行运行 (示例)*

* Gluon 程序的并行执行是最近添加的,可能仍然存在死锁等问题。

示例

hello world

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)

安装

Gluon 可以通过使用 Github 上的预构建可执行文件安装,或者您可以使用 Cargo 来安装 gluon_repl

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
    
  • 标识符和记录字段的 Tab 自动补全 repl completion

  • 通过编写 :q 退出 REPL。

工具

语言服务器

Glueon 有一个 语言服务器,它提供代码补全和格式化支持。使用 cargo install gluon_language-server 进行安装。

Visual Studio Code 扩展

Visual Studio Code 的 gluon 扩展 提供语法高亮和补全。要安装它,在扩展中搜索 gluon。(Github

example

Vim 插件

vim-gluon 提供语法高亮和缩进。

Glueon 语言服务器已测试可与 https://github.com/autozimu/LanguageClient-neovimhttps://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>

文档

Glueon 书籍

Glueon 标准库 API 参考文档

Rust API 文档

用法

Rust

Glueon 需要 Rust 编译器(1.9.0 或更高版本)来构建,并在 crates.io 上提供。它可以很容易地通过添加以下行包含在 Cargo 项目中。

[dependencies]
gluon = "0.18.2"

其他语言

目前,与 Glueon 虚拟机交互最简单的方式是通过 Rust,但存在一个基本的 C API,将来将扩展以使其更接近 Rust API。

贡献

向 gluon 贡献的方式有很多。最简单的方式有两种:打开问题或处理标记为 初学者 的问题。关于如何贡献的更详细信息,您可以查看 CONTRIBUTING.md。贡献部分还包括关于 运行/测试 gluon 的入门指南 的细节。

目标

随着时间的推移,这些目标可能会发生变化或被细化,因为我会尝试使用语言实现什么。

  • 可嵌入 - 与 Lua 类似 - 它旨在嵌入到另一个程序中,该程序可能使用虚拟机来扩展其功能。

  • 静态类型 - 语言使用基于 Hindley-Milner 类型系统 的类型系统,并有一些扩展,允许简单的通用类型推理。

  • 小巧 - 由于小巧,语言易于学习,并且实现占用空间小。

  • 严格 - 严格的编程语言通常更容易推理,尤其是在考虑到大多数人已经习惯于这一点的情况下。对于需要懒加载的情况,提供了一个显式的类型。

  • 模块化 - 库被分割为其解析器、类型检查器和虚拟机 + 编译器。这些组件可以独立于彼此使用,允许应用程序选择并选择它们所需要的精确组件。

灵感来源

这种语言的主要灵感来自 LuaHaskellOCaml

依赖关系

~18–28MB
~415K SLoC