{"id":22912910,"url":"https://github.com/demindiro/ballscript","last_synced_at":"2025-04-01T11:25:32.910Z","repository":{"id":137321076,"uuid":"358710700","full_name":"Demindiro/ballscript","owner":"Demindiro","description":null,"archived":false,"fork":false,"pushed_at":"2021-05-07T22:56:28.000Z","size":430,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-07T06:26:59.395Z","etag":null,"topics":["embed","rust","sandbox","scripting-language"],"latest_commit_sha":null,"homepage":"","language":"Rust","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/Demindiro.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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-04-16T20:20:30.000Z","updated_at":"2021-05-07T22:56:31.000Z","dependencies_parsed_at":null,"dependency_job_id":"2369bb72-d96a-4509-8537-7a25aade664a","html_url":"https://github.com/Demindiro/ballscript","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Demindiro%2Fballscript","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Demindiro%2Fballscript/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Demindiro%2Fballscript/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Demindiro%2Fballscript/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Demindiro","download_url":"https://codeload.github.com/Demindiro/ballscript/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246629790,"owners_count":20808388,"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":["embed","rust","sandbox","scripting-language"],"created_at":"2024-12-14T04:30:06.668Z","updated_at":"2025-04-01T11:25:32.876Z","avatar_url":"https://github.com/Demindiro.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ballscript - embeddable \u0026 sandbox-able scripting language for Rust\n\n[Quickstart guide](#quickstart-guide)\n\n[Integrating into existing projects](#integrating-into-existing-projects)\n\n## What is Ballscript?\n\nWhile Rust is great for creating stable and fast programs, the extensive\ncompile time checks hinder efficient prototyping or cases where you just\nneed to \"get it done\". Ballscript is intended to plug this gap by being\neasy to integrate and having a simple, short syntax.\n\n## Quickstart guide\n\nThis section briefly describes the Ballscript syntax and how to integrate\nit in an existing project. For full details, check the documentation (TODO).\nIt is also recommended to check out the `examples/` directory.\n\n| All indentation **must be tabs**. Using spaces will result in a parser error! |\n| --- |\n\n### \"Hello, world!\"\n\nAs every other language guide, we'll start with a \"hello-world\" example.\n\nCreate a file named `hello.bs` with the following contents:\n\n```bs\nfn main()\n\tprint(\"Hello, world!\")\n```\n\nYou can then run it with `bs hello.bs`, which should then print out\n`Hello, world!`.\n\n### Declaring functions\n\nCode can only be executed inside functions. A function can be declared\nwith the `fn` keyword. It is possible to specify one or more parameters,\nwhich can then be accessed as variables.\n\n```bs\nfn main()\n\tprint(length(0.5, 0.7))\n\nfn length(x, y)\n\treturn (x * x + y * y).sqrt()\n```\n\n### Declaring variables\n\nVariables can declared using the `var` keyword. There are two types of local\nvariables:\n\n#### Block-local variables\n\nThese variables can only be accessed within the same block or sub-blocks they\nare declared in.\n\n```bs\nfn main()\n\tvar x = \"duck\"\n\tif x == \"duck\"\n\t\t# x can be accessed within sub-blocks\n\t\tvar y = \"quack\"\n\t\tprint(x, \" says \", y)\n\t# y is no longer accessible. Uncommenting the statement below will\n\t# cause a parser error\n\t#print(x, \" can't say \", y)\n```\n\n#### Instance-local variables\n\nThese variables are shared between all functions in a script. The values\nof each of these are unique per script instance.\n\n```bs\nvar x\n\nfn main()\n\tquack()\n\tx = \"duck\"\n\tquack()\n\nfn quack()\n\tprint(x, \" says woof\")\n```\n\nIt is not possible to assign an initial value to instance variables. This may\nchange in the future.\n\n#### Variable types\n\nThere are a couple of built-in types with a dedicated syntax:\n\n##### None\n\nThe `none` type is the default value of all variables. Performing an operation\non on it will almost always cause an error. A `none` can be explicitly declared\nusing the `none` keyword.\n\n##### Integer\n\nAn integer is internally represented as an `isize` and can be declared as follows:\n\n```bs\n42\n13_37\n0xbaff\n0b1011\n```\n\nNote that multiplying an integer with a real number will return another real number.\n\n```bs\n# This expression evaluates to 561.54\n13.37 * 42 \n```\n\n##### Real\n\nA real number is internally represented as a `f64` and can be declared as follows:\n\n```bs\n4.2\n13_37.01_101\n0xba.ff\n0b10.11\n```\n\n##### Booleans\n\nA boolean can be created using either `true` or `false`. Comparison operators also\nproduce boolean values.\n\n```bs\ntrue\nfalse\n1 \u003c 2 # true\n2 \u003e 3 # false\n```\n\n##### Strings\n\nStrings can be created using two double quotes (`\"`).\n\n```bs\n\"This is a string\"\n```\n\n##### Arrays\n\nArrays can be created using square brackets (`[]`). They can hold any type.\n\nElements of an array can be accessed using the index operator (also square\nbrackets).\n\n```bs\n[]\n[1, 2, \"beep\"]\narr[1]\n```\n\n##### Dictionaries\n\nDictionaries can be created using curly brackets (`{}`). The values can be\nany type, but keys are currently limited to strings, integers and booleans\nto ensure the key is always valid. This restriction may be lifted in the\nfuture for object types.\n\nElements of a dictionary can be accessed using the index operator.\n\n```bs\n{}\n{1: 2, \"foo\": \"bar\"}\ndict[\"foo\"]\n```\n\nThe keys in a dictionary are **not** guaranteed to be in any particular order.\nThe expressions used when instantiating a dictionary are evaluated in\ndeclaration order however.\n\n### Expressions\n\nIt is possible to do math:\n\n```bs\nfn main()\n\tvar x = 2\n\tvar pi = 3.14\n\t(pi * 2).sqrt()\n```\n\n#### Operator precedence\n\nOperators at the top of the table will be evaluated before operators at the\nbottom.\n\n| Operator             | Description                                    |\n| -------------------- | ---------------------------------------------- |\n| `.`                  | Accesses a named element                       |\n| `[x]`                | Indexes with the value `x`                     |\n| `!`                  | Negates a value                                |\n| `*`, `/`, `%`        | Multiplies, divides or takes the remainder     |\n| `+`, `-`             | Adds or substacts                              |\n| `\u003c\u003c`, `\u003e\u003e`           | Shifts a value to the left or right            |\n| `\u0026`                  | Performs a bitwise `and`                       |\n| `^`                  | Performs a bitwise `xor`                       |\n| `\\|`                 | Performs a bitwise `or`                        |\n| `\u003c`, `\u003e`, `\u003c≃`, `\u003e=` | Checks the relative order of two values        |\n| `==`, `!=`           | Checks if two values are equivalent            |\n| `\u0026\u0026`, `\\|\\|`         | Performs a short-circuit boolean `and` or `or` |\n\n### Control flow\n\nThere are a number of statements to skip or repeat blocks of code.\n\n### `if`, `else`, `elif`\n\nTo execute a block only if a certain condition is met, an `if` statement can\nbe used. This can be chained by `elif` statements which are evaluated if the\nprevious `if`/`elif` expression evaluated to false. An optional `else`\nstatement can be added at the end, which will be evaluated if all previous\nexpressions evaluated to false.\n\n```bs\nif cond\n\tprint(\"cond is true\")\nelif other_cond\n\tprint(\"cond is false, but other_cond is true\")\nelse\n\tprint(\"Both cond and other_cond are false\")\n```\n\nThe expressions must evaluate to a boolean value. Any other value will result\nin an error.\n\n### `while`\n\nA `while` statement is much like an `if` statement, except it repeats the block\nas long as the expression evaluates to `true`. You **cannot** put `elif` or\n`else` statements behind it.\n\n```bs\nwhile cond\n\tprint(\"cond is still true\")\n```\n\nA `while` loop can be prematurely terminated using a `break` statement. When\nnesting loops, it is possible to break out of multiple at once by specifying\nan integer argument\n\n```bs\nwhile a: # 1\n\twhile b: # 0\n\t\tbreak 1 # breaks out of loop 1\n```\n\nIt is possible to skip to the end of a `while` loop with the `continue`\nstatement, which will cause the expression to be evaluated immediately.\nLike with the `break` statement, it is possible to specify an integer\nargument to break out of multiple loops.\n\n### `for`\n\nA `for` statement will evaluate an expression **once**. If the resulting value\ncan be iterated, the block following the `for` will be evaluated for each value\nthe iterator returns.\n\nIntegers can be used as iterators. It will return all values from 0 up to the\ninteger, excluding the integer itself.\n\n```bs\nfor x in 4\n\tprint(x) # 0, 1, 2, 3\n\nfor x in -4\n\tprint(x) # 0, -1, -2, -3\n\nfor c in \"abcde\"\n\tprint(c) # 'a', 'b', 'c', 'd', 'e'\n\nfor v in [1, \"duck\", [3]]\n\tprint(v) # 1, \"duck\", [3]\n\nfor k in {1: 2, \"duck\": \"meow\"}\n\tprint(k) # 1, \"duck\"\n```\n\nThe `break` and `continue` statements can also be used and have the same rules as\nwith the `while` loop.\n\n## Integrating into existing projects\n\nA script can be parsed using `ballscript::parse`. This will return a\n`Class` which can be used to create `Instance`s.\n\nTo call a function, you need to pass a slice with `Variant`s as argument\nand an `Environment`, which defines globally accessible functions such\nas `print`\n\n```rust\nlet source = \"\\\nfn vulkan_lives()\\\n\tprint(\\\"*stomp stomp*\\\")\n\";\n\nlet mut environment = ballscript::Environment::new();\nenvironment\n\t.add_function(\"print\".into(), Box::new(|a: \u0026[_]| println!(\"{:?}\", a)))\n\t.unwrap();\n\nlet class = ballscript::parse(source).unwrap();\n\nlet script = class.instance();\n\nscript.call(\"vulkan_lives\", \u0026[], \u0026environment);\n```\n\n### The `Environment` structure\n\nThe `Environment` structure is the primary way to allow and limit what a script\ncan do. If you need a script to be able to interact with the filesystem, you\ncan expose methods such as `file_read` or `file_write`. Similary, if you do\nnot want the script to be able to access the filesystem, you don't expose any\nmethods for it at all.\n\n| Be careful with passing objects! It is possible to define an object in Rust that accesses anything (e.g. `File` object) |\n| --- |\n\n```rust\nlet mut environment = ballscript::Environment::new();\nenvironment\n\t.add_function(\"print\".into(), Box::new(|a: \u0026[_]| println!(\"{:?}\", a)))\n\t.unwrap();\nenvironment\n\t.add_function(\"explode\".into(), Box::new(|_: \u0026[_]| panic!(\"KABOOM\")))\n\t.unwrap();\n```\n\n### Exposing Rust objects\n\nTo expose a Rust \"object\" to Ballscript, it must implement the `ScriptType`\ntrait. This object must then be wrapped in a `Variant` to pass it to a\nscript.\n\n```rust\nstruct MyStruct;\n\nimpl\u003cV\u003e ballscript::ScriptType\u003cV\u003e for MyStruct\nwhere\n\tV: ballscript::VariantType\n{\n\t...\n}\n```\n\n### Custom `Variant` type\n\nIt may be desireable to \"extend\" the default `Variant` type (e.g. a game engine\nmay want to add `Vector3`, `Quaternion`, ...). To do so, a type that implements\n`VariantType` must be implemented and passed as a generic argument:\n\n```rust\nenum MyVariant {\n\t...\n}\n\nimpl ballscript::VariantType for MyVariant {\n\t...\n}\n\nfn main() {\n\t...\n\tlet class = ballscript::parse::\u003cMyVariant\u003e(source).unwrap();\n\t...\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdemindiro%2Fballscript","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdemindiro%2Fballscript","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdemindiro%2Fballscript/lists"}