gluon

Build Status Gitter crates.io Documentation Book std

Gluon is a small, statically-typed, functional programming language designed for application embedding.

Features

* Parallel execution of gluon programs is a recent addition and may still have issues such as deadlocks.

Examples

Hello world

f#,rust let io = import! std.io io.print "Hello world!"

Factorial

```f#,rust let factorial n : Int -> Int = if n < 2 then 1 else n * factorial (n - 1)

factorial 10 ```

24

```f#,rust,ignore // # 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, satisfymap, spaces, token, digit, skipmany1, recognize, lazy_parser, chainl1, (), ? } = import! std.parser let { (<|>) } = import! std.applicative

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 ->
        if c == '*'
        then Some Mul
        else if c == '+'
        then Some Add
        else if c == '-'
        then Some Sub
        else if c == '/'
        then Some Div
        else None)
        <?> "operator"

let atom _ =
    parser.functor.map Int integer
        <|> between (lex (token '(')) (lex (token ')')) (lazy_parser expr)

and binop _ =
    let op_parser =
        do op = lex operator
        wrap (\l r -> Binop l op r)
    chainl1 (atom ()) op_parser

and expr _ = binop ()

// 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 gendigit = random.threadrng.genintrange 1 10 do a = gendigit do b = gendigit do c = gendigit do d = gendigit wrap [a, b, c, d]

let printdigits = for io.applicative digits (\d -> do _ = io.print " " io.print (show d)) do _ = io.print "Four digits:" *> printdigits *> io.println ""

let guessloop _ = do line = io.readline // Exit the program if the line is just whitespace if string.isempty (string.trim line) then wrap () else match parse line with | Err err -> io.println err *> guessloop () | 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") *> guessloop () else io.println "Expression is not valid, you must use each of the four numbers exactly once!" *> guessloop ()

guess_loop () ```

Source

Getting started

Try online

You can try gluon in your browser at http://gluon-lang.org/try. (Github)

REPL

Gluon has a small executable which can be used to run gluon programs directly or in a small REPL. The REPL can be started by passing the -i flag to the built repl executable which can be run with cargo run -p gluon_repl -- -i.

REPL features: * Evaluating expressions (expressions of type IO will be evaluated in the IO context). * Bind variables by writing let <pattern> <identifier>* = <expr> (omitting in <expr> from a normal let binding) Example:

     let f x = x + 1
     let { x, y = z } = { x = 1, y = 2 }
     f z

Tools

Language server

Gluon has a language server which provides code completion and formatting support. Installation is done with cargo install gluon_language-server.

Visual Studio Code Extension

The gluon extension for Visual Studio Code provides syntax highlighting and completion. To install it, search for gluon among the extensions. (Github)

example

Vim plugin

vim-gluon provides syntax highlighting and indentation.

The gluon language server has been tested to work with https://github.com/autozimu/LanguageClient-neovim and https://github.com/prabirshrestha/vim-lsp.

Example configuration (autozimu/LanguageClient-neovim)

``` let g:LanguageClientserverCommands = { \ 'gluon': ['gluonlanguage-server'], \ }

" Automatically start language servers. let g:LanguageClient_autoStart = 1

nnoremap K :call LanguageClienttextDocumenthover() nnoremap gd :call LanguageClienttextDocumentdefinition() ```

Documentation

The Gluon Book

Gluon Standard Library API Reference

Rust API Docs

Usage

Rust

Gluon requires a recent Rust compiler to build (1.9.0 or later) and is available at crates.io. It can easily be included in a Cargo project by adding the lines below.

toml [dependencies] gluon = "0.8.0"

Other languages

Currently the easiest way to interact with the gluon virtual machine is through Rust but a rudimentary C api exists which will be extended in the future to bring it closer to the Rust api.

Contributing

There are many ways to contribute to gluon. The two simplest ways are opening issues or working on issues marked as beginner. For more extensive information about contributing, you can look at CONTRIBUTING.md. Contributing also has details on running/getting-started-with tests for gluon.

Goals

These goals may change or be refined over time as I experiment with what is possible with the language.

Inspiration

This language takes its primary inspiration from Lua, Haskell and OCaml.