Skip to content
A static, type inferred and embeddable language written in Rust.
Rust Shell
Branch: master
Clone or download
bors and Etherian Merge #775
775: feat(repl): add a --no-std option to the standalone interpreter / REPL r=Marwes a=Etherian

## Description
Adds a command-line switch `--no-std` to the standalone interpreter / REPL which causes it to ignore its internal standard library modules when searching for modules to import.

This should make less awkward to use the REPL to test changes to the standard library.

resolves #751 

Co-authored-by: Etherian <etherain@gmail.com>
Latest commit ba66854 Aug 12, 2019
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
base fix(check): Handle aliases better in `do` Aug 5, 2019
benches refactor: Only depend on crossbeam_utils Jul 21, 2019
book feat: Add gc::Mutex Jun 26, 2019
c-api Version 0.12.0 Jul 6, 2019
check fix(check): Handle aliases better in `do` Aug 5, 2019
codegen Update push_new_data call Aug 5, 2019
completion feat: Use line/column numbers in lambda names Jul 21, 2019
doc Update depenendencies Jul 29, 2019
examples fix(vm): Accept trailing comma in record macros Aug 5, 2019
format Update depenendencies Jul 29, 2019
parser Update depenendencies Jul 29, 2019
repl feat(repl): add --no-std option to gluon.exe Aug 12, 2019
scripts Format must be earlier in the publishing Jul 7, 2019
src feat(repl): add --no-std option to gluon.exe Aug 12, 2019
std Export std.stream.uncons Jul 23, 2019
tests feat(codegen): Map Rust's struct enums to records in Gluon. Aug 5, 2019
vm fix(vm): Don't (rust) panic in string.split_at Aug 5, 2019
.clog.toml Version 0.4.0 May 16, 2017
.gitignore Ignore .bak files Oct 9, 2018
.travis.yml test: Error if changes occur after running tests on travis Jul 6, 2019
CHANGELOG.md Version 0.12.0 Jul 6, 2019
CONTRIBUTING.md docs: Update Contributing.md#Testing Nov 4, 2017
Cargo.lock refactor: Switch to ena for union-find Aug 5, 2019
Cargo.toml refactor: vm Aug 3, 2019
Cross.toml chore: Pass the GIT_HASH variable through to cross Feb 3, 2018
LICENSE Added LICENSE and README.md Aug 2, 2015
README.md Update the README correctly Jul 6, 2019
appveyor.yml Update appveyor token Jul 2, 2018
bors.toml chore: Increase bors timeout to 2 hours Apr 22, 2018
build.rs Improve the Cargo.toml files with homepage and badges May 23, 2019
rustfmt.toml feat: Update syn to 0.15 Sep 29, 2018

README.md

gluon

Build Status Gitter crates.io Documentation Book std

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

Features

  • Statically typed - Static typing makes it easier to write safe and efficient interfaces between gluon and the host application.

  • Type inference - Type inference ensures that types rarely have to be written explicitly giving all the benefits of static types with none of the typing.

  • Simple embedding - Marshalling values to and from gluon requires next to no boilerplate, allowing functions defined in Rust to be directly passed to gluon.

  • UTF-8 by default - Gluon supports unicode out of the box with utf-8 encoded strings and unicode codepoints as characters.

  • Separate heaps - Gluon is a garbage-collected language but uses a separate heap for each executing gluon thread. This keeps each heap small, reducing the overhead of the garbage collector.

  • Thread safe - Gluon is written in Rust, which guarantees thread safety. Gluon keeps the same guarantees, allowing multiple gluon programs to run in parallel (example)*

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

Examples

Hello world

let io = import! std.io
io.print "Hello world!"

Factorial

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 ()

Source

Getting started

Try online

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

Install

Gluon can be installed by using one of the prebuilt executables at Github or you can use Cargo in order to install the gluon_repl crate:

cargo install gluon_repl

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
    
  • Printing help about available commands with :h

  • Loading files with :l path_to_file the result of evaluating the expression in the loaded file is stored in a variable named after the filename without an extension.

  • Checking the types of expressions with :t expression

  • Printing information about a name with :i name.
    Example:

    :i std.prelude.List
    type std.prelude.List a = | Nil | Cons a (std.prelude.List a)
    /// A linked list type
    
  • Tab-completion of identifiers and record fields repl completion

  • Exit the REPL by writing :q

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: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>

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.

[dependencies]
gluon = "0.12.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.

  • Embeddable - Similiar to Lua - it is meant to be included in another program which may use the virtual machine to extend its own functionality.

  • Statically typed - The language uses a Hindley-Milner based type system with some extensions, allowing simple and general type inference.

  • Tiny - By being tiny, the language is easy to learn and has a small implementation footprint.

  • Strict - Strict languages are usually easier to reason about, especially considering that it is what most people are accustomed to. For cases where laziness is desired, an explict type is provided.

  • Modular - The library is split into its parser, typechecker, and virtual machine + compiler. Each of these components can be used independently of each other, allowing applications to pick and choose exactly what they need.

Inspiration

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

You can’t perform that action at this time.