Toy scripting language with a syntax similar to Rust.

Dust - toy scripting language

Toy scripting language with a syntax similar to Rust.

logo

  • 👍 Syntax similar to Rust
  • 👍 Loose JSON parsing
  • 👍 Calling host functions
  • 👎 Loose and poor type system
  • 👎 Super very poor performance

🚧 ToDo

  • Traditional switch expression
    (switch expr {expr => expr, ..., _ => expr})
  • Casting to complex type
    (a as [u8], a as {x: u8, y: f64}, a as |u8|->u8)
  • Keeping complex type information in the expression
  • If-let not-null conditional expression
    (if let Some(z) = some_nullable_value {...} else {...})
  • Defining and Instantiating Structs
  • Calling function from host
  • Deep marshalling in host function calls
  • Embedding user defined parsers (macro)
    (e.g. sql![select * from user], heredoc!{`foo` x `baz`}, x!(a,b,c))
  • Tail call optimization
  • Const expr checking
  • Maps with non-string keys
  • Closure
  • Improve error messages
  • More testing!

🐞 Bugs

  • The break and return statements do not cast the value.

🚀 Getting started

Execute once

import (
    "fmt"
    "log"
    scripting "github.com/shellyln/dust-lang/scripting"
)

func main() {
    defer func() {
        if err := recover(); err != nil {
            // Catch runtime panic. (e.g. Div0)
            log.Fatal(err)
        }
    }()

    xctx := scripting.NewExecutionContext()

    result, err := xctx.Execute(xctx, "3 + 5")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("%v", result)
}

Compile and execute

import (
    "fmt"
    "log"
    scripting "github.com/shellyln/dust-lang/scripting"
)

func main() {
    defer func() {
        if err := recover(); err != nil {
            // Catch runtime panic. (e.g. Div0)
            log.Fatal(err)
        }
    }()

    xctx := scripting.NewExecutionContext()

    ast, err := xctx.Compile(xctx, "3 + 5")
    if err != nil {
        log.Fatal(err)
    }

    result, err := ExecuteAst(xctx, ast)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("%v", result)
}

Unmarshal

import (
    "fmt"
    "log"
    scripting "github.com/shellyln/dust-lang/scripting"
)

type Foobar struct {
    XFoo string `json:"foo"`
    Bar  string
}

func main() {
    defer func() {
        if err := recover(); err != nil {
            // Catch runtime panic. (e.g. Div0)
            log.Fatal(err)
        }
    }()

    var out []Foobar

    xctx := scripting.NewExecutionContext()

    err := scripting.Unmarshal(xctx, &out, `[{foo:'qwe',Bar:'rty'}]`)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("%v", out)
}

Set host variables and functions

import (
    "fmt"
    "log"
    parser "github.com/shellyln/takenoco/base"
    scripting "github.com/shellyln/dust-lang/scripting"
    xtor "github.com/shellyln/dust-lang/scripting/executor"
)

func main() {
    defer func() {
        if err := recover(); err != nil {
            // Catch runtime panic. (e.g. Div0)
            log.Fatal(err)
        }
    }()

    xctx := scripting.NewExecutionContext(scripting.VariableInfoMap{
        "a": {
            Flags: mnem.ReturnInt | mnem.Lvalue,
            Value: int64(1),
        },
        "sum": {
            Flags: mnem.ReturnFloat | mnem.Callable,
            Value: func(x ...float64) (float64, error) {
                v := 0.0
                for _, w := range x {
                    v += w
                }
                // If an error is set, the script will terminate abnormally.
                return v, nil
            },
        },
    })

    result, err := xctx.Execute(xctx, "sum(a, 3, 5)")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("%v", result)
}

📚 Syntax

Comments

# hash line comment (Allow only on the first line)

/*
 * block comment
 */

// line comment

Identifiers

let a0_foo_bar = 0;

let 0a = 0; // syntax error!

// raw identifier
let r#try = 0;

// A single `_` is not an identifier.
// The right-hand side is evaluated without being assigned.
let _ = 0;

Variable and constant definitions

// immutable variable
let a: i64 = 3;
// immutable variable (type inference)
let b = 3_i64;

// mutable variable
let mut c: i64 = 3;
// mutable variable (type inference)
let mut d = 3_i64;

// constant
const E: i64 = 3;
// constant (type inference)
const F = 3_i64;

Types

// signed integer
let a: int = 3;
let a: i64 = 3;
let a: i32 = 3;
let a: i16 = 3;
let a: i8 = 3;

let b = 3_i64;
let b = 3i64;
let b = 3_i32;
let b = 3_i16;
let b = 3_i8;

// unsigned integer
let c: uint = 3;
let c: u64 = 3;
let c: u32 = 3;
let c: u16 = 3;
let c: u8 = 3;

let d = 3_u64;
let d = 3_u32;
let d = 3_u16;
let d = 3_u8;

// float
let e: float = 3;
let e: f64 = 3;
let e: f32 = 3;

let f = 3.0;
let f = 3_f64;
let f = 3_f32;

// bool
let g: bool = true;

let h = true;

// string
let i: String = "";
let i: string = "";
let i: String = '';
let i: String = ``;

let j = "";
let j = '';
let j = ``;

// array
let k = [1, 2, 3i32];
let k = [1i32;3]; // equivalent to `[1i32, 1i32, 1i32]`
let k = vec![1, 2, 3];
let k = i64![1, 2, 3];
let k = i32![1, 2, 3];
let k = i16![1, 2, 3];
let k = i8![1, 2, 3];
let k = u64![1, 2, 3];
let k = u32![1, 2, 3];
let k = u16![1, 2, 3];
let k = u8![1, 2, 3];
let k = u8![1u8;3]; // equivalent to `u8![1u8, 2u8, 3u8]`
let k = f64![1, 2, 3];
let k = f32![1, 2, 3];
let k = bool![true, false, true];
let k = str!["1", '2', `3`];

// object
let l = {"a": 1, 'b': 2, `c`: 3, ["d" + ""]: 4, e: 5};
let l = hashmap!{"a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5};
let l = map!{"a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5};
let l = collection!{"a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5};

// null
let m: any = None;
let m: any = null;

// unit value
let n: any = ();
let n: any = undefined;

Scope

{
    let x = 3;
    // x is defined
}
// x is not defined

// scope as an expression
let a = {let b = 3; b + 5};

Control statements / expressions

If-else if-else

let q = doSomething();

// statement
let x = 11, y = 13;
if q < x {
    11
} else if q < y {
    13
} else {
    q / 2
}

// statement with variable definition
if let x = 11; q < x {
    11
} else if let y = 13; q < y {
    13
} else {
    q / 2
}

// expression with variable definition
let z = if let x = 11; q < x {
    11
} else if let y = 13; q < y {
    13
} else {
    q / 2
}

Infinite loop

// statement
let mut x = 0, y = 100;
loop {
    y++;
    x = ++x + y
    if x < y {
        break;
    }
}

// statement with variable definition
loop let mut x = 0, y = 100; {
    y++;
    x = ++x + y
    if x < y {
        break;
    }
}

// expression with variable definition
let z = loop let mut x = 0, y = 100; {
    y++;
    x = ++x + y
    if x < y {
        break;
    }
};

While loop

// statement
let mut x = 0, y = 100;
while x < y {
    y++;
    x = ++x + y
}

// statement with variable definition
while let mut x = 0, y = 100; x < y {
    y++;
    x = ++x + y
}

// expression with variable definition
let z = while let mut x = 0, y = 100; x < y {
    y++;
    x = ++x + y
};

Do-while loop

// statement
let mut x = 0, y = 100;
do {
    y++;
    x = ++x + y
} while x < y; // ";" is required if the statement follows.

// statement with variable definition
do let mut x = 0, y = 100 {
    y++;
    x = ++x + y
} while x < y;

// expression with variable definition
let z = do let mut x = 0, y = 100 {
    y++;
    x = ++x + y
} while x < y;

For-in loop

// statement
let mut sum = 0;
for n in 0..99 {
    sum = sum + n;
}

let mut sum = 0;
for n in [3 ,5, 7, 11] {
    sum = sum + n;
}

// statement with variable definition
for let mut sum = 0; n in 0..99 {
    sum = sum + n;
}

for let mut sum = 0; n in [3 ,5, 7, 11] {
    sum = sum + n;
}

// expression with variable definition
let z = for let mut sum = 0; n in 0..99 {
    sum = sum + n;
};

let z = for let mut sum = 0; n in [3 ,5, 7, 11] {
    sum = sum + n;
};

Break and continue

loop {
    break returning 3;
}

'mylabel: loop {
    break 'mylabel returning 3;
}

for x in 0..99 {
    if x == 3 {
        continue;
    }
}

'mylabel: for x in 0..99 {
    if x == 3 {
        continue 'mylabel;
    }
}

Traditional for loop

// statement
for let mut i = 0, j = 0; i < 10; i++ {
    j++
}

// expression
let z = for let mut i = 0, j = 0; i < 10; i++ {
    j++
};

Function

fn tarai(x: i64, y: i64, z: i64) -> i64 {
    if x <= y {
        y
    } else {
        tarai(
            tarai(x - 1, y, z),
            tarai(y - 1, z, x),
            tarai(z - 1, x, y),
        )
    } as i64
}

tarai(8, 6, 0);

Lambda

let tarai = |x: i64, y: i64, z: i64| -> i64 {
    if x <= y {
        y
    } else {
        recurse(
            recurse(x - 1, y, z),
            recurse(y - 1, z, x),
            recurse(z - 1, x, y),
        )
    } as i64
}

tarai(8, 6, 0);

|a: i64, b: i64| -> i64 {a + b}(3, 5)

Operators

// Member Access `op1 . op2`
hashmap!{abc => 11}.abc;

// Computed Member Access `op1[op2]`
hashmap!{abc => 11}["ab" + "c"];
[1, 3, 5, 7][0];
"abcd"[0]; // u8

// Slicing `op1[start..end]`
[1, 3, 5, 7][0..4];
[1, 3, 5, 7][..4];
[1, 3, 5, 7][0..];
"abcd"[0..4]; // String

// Type cast `op1 as T`
7 as f64;

// Function Call `op1(args)`
my_func1();
my_func2(1, 2, 3);

//-------------------------------

// Postfix Increment `op1 ++`
let mut i = 0;
i--;

// Postfix Decrement `op1 --`
i++;

//-------------------------------

// Logical NOT `! op1`
let mut a = 0;
a = !a;

// Bitwise NOT `~ op1`
a = ~a;

// Unary plus `+ op1`
a = +a;

// Unary negation `- op1`
a = -a;

// Prefix Increment `++ op1`
let mut i = 0;
++i;

// Prefix Decrement `-- op1`
--i;

//-------------------------------

// Exponentiation `op1 ** op2`
let mut a = 3.0, b = 5.0;
a = a ** b;


//-------------------------------

// Multiplication `op1 * op2`
let mut a = 3, b = 1;

// Division `op1 / op2`
a = a / b;

// Remainder `op1 % op2`
a = a % b;

//-------------------------------

// Addition `op1 + op2`
let mut a = 3, b = 5;
a = a + b;

// Subtraction `op1 - op2`
a = a - b;

//-------------------------------

// Bitwise Left Shift `op1 << op2`
let mut a = 3, b = 5;
a = a << 5;

// Bitwise Right Shift `op1 >> op2`
a = a >> 5;

// Bitwise Unsigned Right Shift `op1 >>> op2`
a = a >>> 5;

//-------------------------------

// Less Than `op1 < op2`
let mut a = 3, b = 5;
a < b;

// Less Than Or Equal `op1 <= op2`
a <= b;

// Greater Than `op1 > op2`
a > b;

// Greater Than Or Equal `op1 >= op2`
a >= b;

//-------------------------------

// Equality `op1 == op2`
let mut a = 3, b = 5;
a == b;

// Inequality `op1 != op2`
a != b;

// Strict Equality `op1 === op2`
a === b;

// Strict Inequality `op1 !== op2`
a !== b;

//-------------------------------

// Bitwise AND `op1 & op2`
let mut a = 3, b = 5;
a & b;

//-------------------------------

// Bitwise XOR `op1 ^ op2`
let mut a = 3, b = 5;
a ^ b;

//-------------------------------

// Bitwise OR `op1 | op2`
let mut a = 3, b = 5;
a | b;

//-------------------------------

// Logical AND `op1 && op2`
let mut a = true, b = false;
a && b;

//-------------------------------

// Logical OR `op1 || op2`
let mut a = true, b = false;
a || b;

//-------------------------------

// Conditional (ternary) operator `op1 ? op2 : op3`
let mut a = true;
a ? 3 : 5;

//-------------------------------

// Pipeline Function Call `op1 |> op2(args)`
fn add(a: f64, b: f64) -> f64 {
    a + b
}

let p = add(1, 2) |> add(3) |> add(5); // equivalent to `add(add(add(1,2),3),5)`

//-------------------------------

// Assignment `op1 = op2`
let mut a = 0;
a = 3;

//-------------------------------

// Comma / Sequence `op1, op2`
let mut a = 0;
a = 1 + 2, 3 + 4, 5 + 6;

++a, ++a, ++a;

License

MIT
Copyright (c) 2021 Shellyl_N and Authors.

Owner
shellyln
https://shellyln.github.io/
shellyln
Similar Resources

The Freetype font rasterizer in the Go programming language.

The Freetype font rasterizer in the Go programming language. To download and install from source: $ go get github.com/golang/freetype It is an incom

Dec 25, 2022

Frongo is a Golang package to create HTML/CSS components using only the Go language.

Frongo Frongo is a Go tool to make HTML/CSS document out of Golang code. It was designed with readability and usability in mind, so HTML objects are c

Jul 29, 2021

👄 The most accurate natural language detection library in the Go ecosystem, suitable for long and short text alike

👄 The most accurate natural language detection library in the Go ecosystem, suitable for long and short text alike

👄 The most accurate natural language detection library in the Go ecosystem, suitable for long and short text alike

Dec 25, 2022

A simple programming language with emojis only

MOGEE mogee (pronounced ēˈmōjē) is an extremely simple interpreted programming language that consists solely of emojis. Similar to Brainfuck, mogee is

Nov 15, 2021

Build "Dictionary of the Old Norwegian Language" into easier-to-use data formats

Old Norwegian Dictionary Builder Build "Dictionary of the Old Norwegian Language" into easier-to-use data formats. Available formats: JSON DSL XML Usa

Oct 11, 2022

Interpreted Programming Language built in Go. Lexer, Parser, AST, VM.

Gago | Programming Language Built in Go if you are looking for the docs, go here Gago is a interpreted programming language. It is fully written in Go

May 6, 2022

Go specs implemented as a scripting language in Rust.

Goscript A script language like Python or Lua written in Rust, with exactly the same syntax as Go's. The Goal Runs most pure Go code, probably add som

Jan 8, 2023

Kakoune syntax highlighting for the Godot Engine / Godot Scripting Language gdscript

Kakoune syntax highlighting for the Godot Engine / Godot Scripting Language gdscript

gdscript-kak Kakoune syntax highlighting for the Godot Engine / Godot Scripting Language gdscript. Adds basic syntax highlighting to your .gd files fo

Mar 2, 2021

Translate your Go program into multiple languages with similar fmt.Sprintf format syntax.

Loafer-i18n Loafer-i18n is a Go package and a command that helps you translate Go programs into multiple languages. Supports pluralized strings with =

Dec 22, 2021

Golem is a general purpose, interpreted scripting language.

Golem is a general purpose, interpreted scripting language.

The Golem Programming Language Golem is a general purpose, interpreted scripting language, that brings together ideas from many other languages, inclu

Sep 28, 2022

Scripting language for Go.

Minima Minima is an experimental interpreter written in Go (the language is called the same). We needed a way to massage our JSON data with a scriptin

Feb 11, 2022

Gopherscript is a secure and minimal scripting language written in Go.

Gopherscript is a secure and minimal scripting language written in Go.

Gopherscript Gopherscript is a secure scripting/configuration language written in Go. It features a fined-grain permission system and enforces a stron

Oct 2, 2022

CodePlayground is a playground tool for go and rust language.

CodePlayground CodePlayground is a playground tool for go and rust language. Installation Use homebrews to install code-playground. brew tap trendyol/

Mar 5, 2022

A toy language parser, lexer and interpreter written in Golang

Monkey - A toy programming language Monkey is a toy programming language used to learn how to write a lexer, parser and interpreter. The language is i

Nov 16, 2021

Txt-lsp - A toy project with Language Server Protocol (LSP)

txt-lsp txt-lsp is a toy project where I play around with Language Server Protoc

Jan 22, 2022

xlsxlang is a tiny toy script programming language. xlsxlang is heavily inspired by Lisp

xlsxlang is a tiny toy script programming language. xlsxlang is heavily inspired by Lisp

xlsxlang Table of Contents 1. Usage 1.1. Examples 2. Installation 3. Supported functions 4. LICENSE xlsxlang is a tiny toy script programming language

Feb 11, 2022

Yay - Yet another stack-oriented toy language that transpiles to Go code. Heavily inspired by Tsoding's Porth YouTube series

yay Yet another stack-oriented toy language that transpiles to Go code. Heavily

Feb 12, 2022
A general purpose syntax highlighter in pure Go

Chroma — A general purpose syntax highlighter in pure Go NOTE: As Chroma has just been released, its API is still in flux. That said, the high-level i

Dec 27, 2022
Go package for syntax highlighting of code

syntaxhighlight Package syntaxhighlight provides syntax highlighting for code. It currently uses a language-independent lexer and performs decently on

Nov 18, 2022
Search for Go code using syntax trees

gogrep GO111MODULE=on go get mvdan.cc/gogrep Search for Go code using syntax trees. Work in progress. gogrep -x 'if $x != nil { return $x, $*_ }' In

Dec 9, 2022
Go library for the TOML language

go-toml Go library for the TOML format. This library supports TOML version v1.0.0-rc.3 Features Go-toml provides the following features for using data

Dec 27, 2022
Parses the Graphviz DOT language in golang

Parses the Graphviz DOT language and creates an interface, in golang, with which to easily create new and manipulate existing graphs which can be writ

Dec 25, 2022
Guess the natural language of a text in Go

guesslanguage This is a Go version of python guess-language. guesslanguage provides a simple way to detect the natural language of unicode string and

Dec 26, 2022
String i18n utilities for the Go Programming Language

About polyglot polyglot is a String translation package and tool for Go. Setup Make sure you have a working Go installation. See Getting Started Now r

Dec 22, 2022
Unified text diffing in Go (copy of the internal diffing packages the officlal Go language server uses)

gotextdiff - unified text diffing in Go This is a copy of the Go text diffing packages that the official Go language server gopls uses internally to g

Dec 26, 2022
Simple HCL (HashiCorp Configuration Language) parser for your vars.

HCL to Markdown About To write a good documentation for terraform module, quite often we just need to print all our input variables as a fancy table.

Dec 14, 2021
An anthology of a variety of tools for the Persian language in Golang
An anthology of a variety of tools for the Persian language in Golang

Persian tools An anthology of a variety of tools for the Persian language in Golang Todos Bill calculator Digits Validate Bank card number. Find Bank'

Nov 22, 2022