{"id":17124933,"url":"https://github.com/shellyln/dust-lang","last_synced_at":"2025-07-16T23:32:25.027Z","repository":{"id":57645535,"uuid":"440517913","full_name":"shellyln/dust-lang","owner":"shellyln","description":"Toy scripting language with a syntax similar to Rust.","archived":false,"fork":false,"pushed_at":"2023-03-14T03:20:22.000Z","size":143,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-05T03:27:38.463Z","etag":null,"topics":["go","golang","interpreter","json-parser","scripting-language","toy-language"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shellyln.png","metadata":{"files":{"readme":"README.md","changelog":"Changelog.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-21T12:59:27.000Z","updated_at":"2024-02-24T06:07:44.000Z","dependencies_parsed_at":"2024-06-20T04:26:29.381Z","dependency_job_id":"7c46383d-62b4-4c11-9957-709910963d3f","html_url":"https://github.com/shellyln/dust-lang","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/shellyln/dust-lang","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shellyln%2Fdust-lang","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shellyln%2Fdust-lang/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shellyln%2Fdust-lang/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shellyln%2Fdust-lang/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shellyln","download_url":"https://codeload.github.com/shellyln/dust-lang/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shellyln%2Fdust-lang/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265550338,"owners_count":23786545,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golang","interpreter","json-parser","scripting-language","toy-language"],"created_at":"2024-10-14T18:43:43.470Z","updated_at":"2025-07-16T23:32:24.609Z","avatar_url":"https://github.com/shellyln.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Dust - 🧸 toy scripting language 🪅\nToy scripting language with a syntax similar to Rust.\n\n[![Test](https://github.com/shellyln/dust-lang/actions/workflows/test.yml/badge.svg)](https://github.com/shellyln/dust-lang/actions/workflows/test.yml)\n[![release](https://img.shields.io/github/v/release/shellyln/dust-lang)](https://github.com/shellyln/dust-lang/releases)\n[![Go version](https://img.shields.io/github/go-mod/go-version/shellyln/dust-lang)](https://github.com/shellyln/dust-lang)\n\n\u003cimg src=\"https://raw.githubusercontent.com/shellyln/dust-lang/master/_assets/dust-lang-logo.svg\" alt=\"logo\" style=\"width:250px;\" width=\"250\"\u003e\n\n\n* 👍 Syntax similar to Rust\n* 👍 Loose JSON parsing\n* 👍 Calling host functions\n\n\u003c!--  --\u003e\n\n* 👎 Loose and poor type system\n* 👎 Super very poor performance\n\n\n## 🚧 ToDo\n\n* Traditional switch expression  \n  (`switch expr {expr =\u003e expr, ..., _ =\u003e expr}`)\n* Casting to complex type  \n  (`a as [u8]`, `a as {x: u8, y: f64}`, `a as |u8|-\u003eu8`)\n* Keeping complex type information in the expression\n* If-let not-null conditional expression  \n  (`if let Some(z) = some_nullable_value {...} else {...}`)\n* Defining and Instantiating Structs\n* Calling function from host\n* Deep marshalling in host function calls\n* Embedding user defined parsers (macro)  \n  (e.g. `sql![select * from user]`, \u003ccode\u003eheredoc!{\u0026#x60;foo\u0026#x60; x \u0026#x60;baz\u0026#x60;}\u003c/code\u003e, `x!(a,b,c)`)\n* Tail call optimization\n* Const expr checking\n* Maps with non-string keys\n* Closure\n* Improve error messages\n* More testing!\n\n## 🐞 Bugs\n\n* The `break` and `return` statements do not cast the value.\n\n\n## 👋 Examples\n\n* [Hello, World!](https://github.com/shellyln/dust-lang/blob/master/_examples/hello-world.rs)\n* [Mandelbrot set](https://github.com/shellyln/dust-lang/blob/master/_examples/mandelbrot.rs)\n* [Tarai function](https://github.com/shellyln/dust-lang/blob/master/_examples/tarai.rs)\n\n\n## 🚀 Getting started\n\n### Execute once\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"log\"\n    \"github.com/shellyln/dust-lang/scripting\"\n)\n\nfunc main() {\n    defer func() {\n        if err := recover(); err != nil {\n            // Catch runtime panic. (e.g. Div0)\n            log.Fatal(err)\n        }\n    }()\n\n    xctx := scripting.NewExecutionContext()\n\n    result, err := scripting.Execute(xctx, \"3 + 5\")\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    fmt.Println(result)\n}\n```\n\n### Compile and execute\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"log\"\n    \"github.com/shellyln/dust-lang/scripting\"\n)\n\nfunc main() {\n    defer func() {\n        if err := recover(); err != nil {\n            // Catch runtime panic. (e.g. Div0)\n            log.Fatal(err)\n        }\n    }()\n\n    xctx := scripting.NewExecutionContext()\n\n    ast, err := scripting.Compile(xctx, \"3 + 5\")\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    result, err := scripting.ExecuteAst(xctx, ast)\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    fmt.Println(result)\n}\n```\n\n### Unmarshal\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"log\"\n    \"github.com/shellyln/dust-lang/scripting\"\n)\n\ntype Foobar struct {\n    XFoo string `json:\"foo\"`\n    Bar  string\n}\n\nfunc main() {\n    defer func() {\n        if err := recover(); err != nil {\n            // Catch runtime panic. (e.g. Div0)\n            log.Fatal(err)\n        }\n    }()\n\n    var out []Foobar\n\n    xctx := scripting.NewExecutionContext()\n\n    err := scripting.Unmarshal(xctx, \u0026out, `[{foo:'qwe',Bar:'rty'}]`)\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    fmt.Println(out)\n}\n```\n\n### Set host variables and functions\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"log\"\n    \"github.com/shellyln/dust-lang/scripting\"\n    mnem \"github.com/shellyln/dust-lang/scripting/executor/opcode\"\n)\n\nfunc main() {\n    defer func() {\n        if err := recover(); err != nil {\n            // Catch runtime panic. (e.g. Div0)\n            log.Fatal(err)\n        }\n    }()\n\n    xctx := scripting.NewExecutionContext(scripting.VariableInfoMap{\n        \"a\": {\n            Flags: mnem.ReturnInt | mnem.Lvalue,\n            Value: int64(1),\n        },\n        \"sum\": {\n            Flags: mnem.ReturnFloat | mnem.Callable,\n            Value: func(x ...float64) (float64, error) {\n                v := 0.0\n                for _, w := range x {\n                    v += w\n                }\n                // If an error is set, the script will terminate abnormally.\n                return v, nil\n            },\n        },\n    })\n\n    result, err := scripting.Execute(xctx, \"sum(a, 3, 5)\")\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    fmt.Println(result)\n}\n```\n\n\n## 📚 Syntax\n\n### Comments\n\n```rust\n# hash line comment (Allow only on the first line)\n\n/*\n * block comment\n */\n\n// line comment\n```\n\n### Identifiers\n\n```rust\nlet a0_foo_bar = 0;\n\nlet 0a = 0; // syntax error!\n\n// raw identifier\nlet r#try = 0;\n\n// A single `_` is not an identifier.\n// The right-hand side is evaluated without being assigned.\nlet _ = 0;\n```\n\n### Variable and constant definitions\n\n```rust\n// immutable variable\nlet a: i64 = 3;\n// immutable variable (type inference)\nlet b = 3_i64;\n\n// mutable variable\nlet mut c: i64 = 3;\n// mutable variable (type inference)\nlet mut d = 3_i64;\n\n// constant\nconst E: i64 = 3;\n// constant (type inference)\nconst F = 3_i64;\n```\n\n### Types\n\n```rust\n// signed integer\nlet a: int = 3;\nlet a: i64 = 3;\nlet a: i32 = 3;\nlet a: i16 = 3;\nlet a: i8 = 3;\n\nlet b = 3_i64;\nlet b = 3i64;\nlet b = 3_i32;\nlet b = 3_i16;\nlet b = 3_i8;\n\n// unsigned integer\nlet c: uint = 3;\nlet c: u64 = 3;\nlet c: u32 = 3;\nlet c: u16 = 3;\nlet c: u8 = 3;\n\nlet d = 3_u64;\nlet d = 3_u32;\nlet d = 3_u16;\nlet d = 3_u8;\n\n// float\nlet e: float = 3;\nlet e: f64 = 3;\nlet e: f32 = 3;\n\nlet f = 3.0;\nlet f = 3_f64;\nlet f = 3_f32;\n\n// bool\nlet g: bool = true;\n\nlet h = true;\n\n// string\nlet i: String = \"\";\nlet i: string = \"\";\nlet i: String = '';\nlet i: String = ``;\n\nlet j = \"\";\nlet j = '';\nlet j = ``;\n\n// array\nlet k = [1, 2, 3i32];\nlet k = [1i32;3]; // equivalent to `[1i32, 1i32, 1i32]`\nlet k = vec![1, 2, 3];\nlet k = i64![1, 2, 3];\nlet k = i32![1, 2, 3];\nlet k = i16![1, 2, 3];\nlet k = i8![1, 2, 3];\nlet k = u64![1, 2, 3];\nlet k = u32![1, 2, 3];\nlet k = u16![1, 2, 3];\nlet k = u8![1, 2, 3];\nlet k = u8![1u8;3]; // equivalent to `u8![1u8, 2u8, 3u8]`\nlet k = f64![1, 2, 3];\nlet k = f32![1, 2, 3];\nlet k = bool![true, false, true];\nlet k = str![\"1\", '2', `3`];\n\n// object\nlet l = {\"a\": 1, 'b': 2, `c`: 3, [\"d\" + \"\"]: 4, e: 5};\nlet l = hashmap!{\"a\" =\u003e 1, \"b\" =\u003e 2, \"c\" =\u003e 3, \"d\" =\u003e 4, \"e\" =\u003e 5};\nlet l = map!{\"a\" =\u003e 1, \"b\" =\u003e 2, \"c\" =\u003e 3, \"d\" =\u003e 4, \"e\" =\u003e 5};\nlet l = collection!{\"a\" =\u003e 1, \"b\" =\u003e 2, \"c\" =\u003e 3, \"d\" =\u003e 4, \"e\" =\u003e 5};\n\n// null\nlet m: any = None;\nlet m: any = null;\n\n// unit value\nlet n: any = ();\nlet n: any = undefined;\n```\n\n### Scope\n\n```rust\n{\n    let x = 3;\n    // x is defined\n}\n// x is not defined\n\n// scope as an expression\nlet a = {let b = 3; b + 5};\n```\n\n\n### Control statements / expressions\n\n#### If-else if-else\n\n```rust\nlet q = doSomething();\n\n// statement\nlet x = 11, y = 13;\nif q \u003c x {\n    11\n} else if q \u003c y {\n    13\n} else {\n    q / 2\n}\n\n// statement with variable definition\nif let x = 11; q \u003c x {\n    11\n} else if let y = 13; q \u003c y {\n    13\n} else {\n    q / 2\n}\n\n// expression with variable definition\nlet z = if let x = 11; q \u003c x {\n    11\n} else if let y = 13; q \u003c y {\n    13\n} else {\n    q / 2\n}\n```\n\n\n#### Infinite loop\n\n```rust\n// statement\nlet mut x = 0, y = 100;\nloop {\n    y++;\n    x = ++x + y\n    if x \u003c y {\n        break;\n    }\n}\n\n// statement with variable definition\nloop let mut x = 0, y = 100; {\n    y++;\n    x = ++x + y\n    if x \u003c y {\n        break;\n    }\n}\n\n// expression with variable definition\nlet z = loop let mut x = 0, y = 100; {\n    y++;\n    x = ++x + y\n    if x \u003c y {\n        break;\n    }\n};\n```\n\n\n#### While loop\n\n```rust\n// statement\nlet mut x = 0, y = 100;\nwhile x \u003c y {\n    y++;\n    x = ++x + y\n}\n\n// statement with variable definition\nwhile let mut x = 0, y = 100; x \u003c y {\n    y++;\n    x = ++x + y\n}\n\n// expression with variable definition\nlet z = while let mut x = 0, y = 100; x \u003c y {\n    y++;\n    x = ++x + y\n};\n```\n\n\n#### Do-while loop\n\n```rust\n// statement\nlet mut x = 0, y = 100;\ndo {\n    y++;\n    x = ++x + y\n} while x \u003c y; // \";\" is required if the statement follows.\n\n// statement with variable definition\ndo let mut x = 0, y = 100 {\n    y++;\n    x = ++x + y\n} while x \u003c y;\n\n// expression with variable definition\nlet z = do let mut x = 0, y = 100 {\n    y++;\n    x = ++x + y\n} while x \u003c y;\n```\n\n\n### For-in loop\n\n```rust\n// statement\nlet mut sum = 0;\nfor n in 0..99 {\n    sum = sum + n;\n}\n\nlet mut sum = 0;\nfor n in [3 ,5, 7, 11] {\n    sum = sum + n;\n}\n\n// statement with variable definition\nfor let mut sum = 0; n in 0..99 {\n    sum = sum + n;\n}\n\nfor let mut sum = 0; n in [3 ,5, 7, 11] {\n    sum = sum + n;\n}\n\n// expression with variable definition\nlet z = for let mut sum = 0; n in 0..99 {\n    sum = sum + n;\n};\n\nlet z = for let mut sum = 0; n in [3 ,5, 7, 11] {\n    sum = sum + n;\n};\n```\n\n\n### Break and continue\n\n```rust\nloop {\n    break returning 3;\n}\n\n'mylabel: loop {\n    break 'mylabel returning 3;\n}\n\nfor x in 0..99 {\n    if x == 3 {\n        continue;\n    }\n}\n\n'mylabel: for x in 0..99 {\n    if x == 3 {\n        continue 'mylabel;\n    }\n}\n```\n\n\n\n### Traditional for loop\n\n```rust\n// statement\nfor let mut i = 0, j = 0; i \u003c 10; i++ {\n    j++\n}\n\n// expression\nlet z = for let mut i = 0, j = 0; i \u003c 10; i++ {\n    j++\n};\n```\n\n\n### Function\n\n```rust\nfn tarai(x: i64, y: i64, z: i64) -\u003e i64 {\n    if x \u003c= y {\n        y\n    } else {\n        tarai(\n            tarai(x - 1, y, z),\n            tarai(y - 1, z, x),\n            tarai(z - 1, x, y),\n        )\n    } as i64\n}\n\ntarai(8, 6, 0);\n```\n\n### Lambda\n\n```rust\nlet tarai = |x: i64, y: i64, z: i64| -\u003e i64 {\n    if x \u003c= y {\n        y\n    } else {\n        recurse(\n            recurse(x - 1, y, z),\n            recurse(y - 1, z, x),\n            recurse(z - 1, x, y),\n        )\n    } as i64\n}\n\ntarai(8, 6, 0);\n\n|a: i64, b: i64| -\u003e i64 {a + b}(3, 5)\n```\n\n\n### Operators\n\n```rust\n// Member Access `op1 . op2`\nhashmap!{abc =\u003e 11}.abc;\n\n// Computed Member Access `op1[op2]`\nhashmap!{abc =\u003e 11}[\"ab\" + \"c\"];\n[1, 3, 5, 7][0];\n\"abcd\"[0]; // u8\n\n// Slicing `op1[start..end]`\n[1, 3, 5, 7][0..4];\n[1, 3, 5, 7][..4];\n[1, 3, 5, 7][0..];\n\"abcd\"[0..4]; // String\n\n// Type cast `op1 as T`\n7 as f64;\n\n// Function Call `op1(args)`\nmy_func1();\nmy_func2(1, 2, 3);\n\n//-------------------------------\n\n// Postfix Increment `op1 ++`\nlet mut i = 0;\ni--;\n\n// Postfix Decrement `op1 --`\ni++;\n\n//-------------------------------\n\n// Logical NOT `! op1`\nlet mut a = 0;\na = !a;\n\n// Bitwise NOT `~ op1`\na = ~a;\n\n// Unary plus `+ op1`\na = +a;\n\n// Unary negation `- op1`\na = -a;\n\n// Prefix Increment `++ op1`\nlet mut i = 0;\n++i;\n\n// Prefix Decrement `-- op1`\n--i;\n\n//-------------------------------\n\n// Exponentiation `op1 ** op2`\nlet mut a = 3.0, b = 5.0;\na = a ** b;\n\n\n//-------------------------------\n\n// Multiplication `op1 * op2`\nlet mut a = 3, b = 1;\n\n// Division `op1 / op2`\na = a / b;\n\n// Remainder `op1 % op2`\na = a % b;\n\n//-------------------------------\n\n// Addition `op1 + op2`\nlet mut a = 3, b = 5;\na = a + b;\n\n// Subtraction `op1 - op2`\na = a - b;\n\n//-------------------------------\n\n// Bitwise Left Shift `op1 \u003c\u003c op2`\nlet mut a = 3, b = 5;\na = a \u003c\u003c 5;\n\n// Bitwise Right Shift `op1 \u003e\u003e op2`\na = a \u003e\u003e 5;\n\n// Bitwise Unsigned Right Shift `op1 \u003e\u003e\u003e op2`\na = a \u003e\u003e\u003e 5;\n\n//-------------------------------\n\n// Less Than `op1 \u003c op2`\nlet mut a = 3, b = 5;\na \u003c b;\n\n// Less Than Or Equal `op1 \u003c= op2`\na \u003c= b;\n\n// Greater Than `op1 \u003e op2`\na \u003e b;\n\n// Greater Than Or Equal `op1 \u003e= op2`\na \u003e= b;\n\n//-------------------------------\n\n// Equality `op1 == op2`\nlet mut a = 3, b = 5;\na == b;\n\n// Inequality `op1 != op2`\na != b;\n\n// Strict Equality `op1 === op2`\na === b;\n\n// Strict Inequality `op1 !== op2`\na !== b;\n\n//-------------------------------\n\n// Bitwise AND `op1 \u0026 op2`\nlet mut a = 3, b = 5;\na \u0026 b;\n\n//-------------------------------\n\n// Bitwise XOR `op1 ^ op2`\nlet mut a = 3, b = 5;\na ^ b;\n\n//-------------------------------\n\n// Bitwise OR `op1 | op2`\nlet mut a = 3, b = 5;\na | b;\n\n//-------------------------------\n\n// Logical AND `op1 \u0026\u0026 op2`\nlet mut a = true, b = false;\na \u0026\u0026 b;\n\n//-------------------------------\n\n// Logical OR `op1 || op2`\nlet mut a = true, b = false;\na || b;\n\n//-------------------------------\n\n// Conditional (ternary) operator `op1 ? op2 : op3`\nlet mut a = true;\na ? 3 : 5;\n\n//-------------------------------\n\n// Pipeline Function Call `op1 |\u003e op2(args)`\nfn add(a: f64, b: f64) -\u003e f64 {\n    a + b\n}\n\nlet p = add(1, 2) |\u003e add(3) |\u003e add(5); // equivalent to `add(add(add(1,2),3),5)`\n\n//-------------------------------\n\n// Assignment `op1 = op2`\nlet mut a = 0;\na = 3;\n\n//-------------------------------\n\n// Comma / Sequence `op1, op2`\nlet mut a = 0;\na = 1 + 2, 3 + 4, 5 + 6;\n\n++a, ++a, ++a;\n\n```\n\n\n## ⚖️ License\n\nMIT  \nCopyright (c) 2021 Shellyl_N and Authors.\n\n\n---\nPowered by [takenoco](https://github.com/shellyln/takenoco) Parser Combinator Library\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshellyln%2Fdust-lang","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshellyln%2Fdust-lang","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshellyln%2Fdust-lang/lists"}