📝 Document language (renamed to minbasic), add vscode extension for syntax highlighting

main
Shad Amethyst 8 months ago
parent 4576609b1e
commit 3867ef4991

@ -0,0 +1,160 @@
# MinBasic guide
## Keywords and case sensitivity
Most things in MinBasic are described with keywords.
These keywords are *not* case-sensitive, meaning that `if x > 0 then` is equivalent to `IF x > 0 THEN`.
User-defined things, like functions and variables, *are case-sensitive*. So `myVariable`, `MYVARIABLE` and `myvariable` all refer to different things.
We recommend to prefer uppercase for keywords and lowercase for variable names.
## Comments
Comments are prefixed with the `REM` keyword.
## Variables and assignments
<!-- TODO: prevent usage of LET in incorrect places -->
To assign a value to a variable, use the `variable = value` syntax, where `value` can be any expression.
Optionally, you can prefix the assignment with the `LET` keyword.
To use a variable, simply put its name in an expression.
*Note: multi-line expressions are not yet supported.*
Variables don't need to be declared, and values can be assigned to them at any point in time.
Using a variable before it was assigned any value will yield `null` instead.
### Examples
```basic
REM Sets the variable "answer" to 42:
answer = 42
REM Also sets the variable "answer" to 42:
LET answer = 42
REM Sets the variable "x" to 21:
x = answer / 2
REM Increments "x" by one:
x = x + 1
```
## Expressions
The following binary operators are supported:
- Addition: `a + b`
- Subtraction: `a - b`
- Multiplication: `a * b`
- Division: `a / b`
- Modulo: `a % b`
- Less than: `a < b`
- Greater than: `a > b`
- Less than or equal: `a <= b`
- Greater than or equal: `a >= b`
- Equal: `a == b`
- Not equal: `a != b`
Multiplication and division have a greater precedence than addition and subtraction.
Comparisons have the lowest precedence.
You can wrap sub-expressions in parentheses to override precedence.
Some additional operators are only available by calling builtin functions, which are case-insensitive:
- Maximum: `MAX(a, b)`
- Minimum: `MIN(a, b)`
- Square root: `SQRT(a)`
- Floor: `FLOOR(a)`
- Ceil: `CEIL(a)`
- Round: `ROUND(a)`
- Rand: `RAND(a)`, generates a random number between `0` and `a`
### Examples
```basic
REM Picks a random integer between 0 and 63
n = FLOOR(RAND(64))
REM Sets x to the remainder of n by 8, and y by the integer part of n / 8
x = n % 8
y = FLOOR(n / 8)
REM Sets dist to the euclidean distance between (0, 0) and (x, y)
dist = SQRT(x * x + y * y)
```
## Jumps and labels
Jumping allows you to interrupt the regular flow of instruction to go to another point in the program.
To perform a jump, you will first need to define where you want to jump to.
You have two options: prefixing a line with a number, or writing a named label.
Then, use the `GOTO` statement to jump to either a line number, or a label.
### Using line numbers
```basic
REM The following lines have been numbered. The numbers chosen are arbitrary, but they are commonly increasing multiples of 10,
REM which allows you to squeeze in debugging statements when needed.
10 PRINT "Hello, world"
20 PRINT "This is line 20"
REM We then jump back to line 20, which will cause an infinite loop printing "This is line 20"
30 GOTO 20
```
### Using labels
```basic
REM We define here the "start" label
start:
PRINT "Hello, world"
REM We then jump to the "start" label, causing an infinite loop printing "Hello, world"
GOTO start
```
## Conditions
The `IF` keyword allows you to execute different parts of the code depending on whether a condition is met or not.
The syntax for `IF` is as follows:
```basic
IF condition THEN
REM Code to be executed if "condition" is true
ELSE
REM Code to be executed if "condition" is false
END IF
```
If you do not need to execute code when the condition is false, then you can omit the `ELSE` keyword.
### Example
```basic
REM This is a condition without an ELSE block:
IF age < 0 THEN
PRINT "It seems like you weren't born yet..."
END IF
IF age < 18 THEN
PRINT "You are underaged"
ELSE
REM We can nest conditions within other conditions:
IF age == 18 THEN
PRINT "You just turned 18!"
ELSE
PRINT "You're over 18"
END IF
END IF
```
## Loops
<!-- TODO: finish implementing WHILE..WEND and DO..LOOP -->

@ -0,0 +1,19 @@
Copyright (c) 2023 Emilie BURGUN
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -1,6 +1,37 @@
# BASIC to Mindustry logic
This is a small transpiler from the [BASIC](https://en.wikipedia.org/wiki/BASIC) language to [Mindustry](https://github.com/Anuken/Mindustry/)'s [logic system](https://www.reddit.com/r/Mindustry/comments/kfea1e/an_overly_indepth_logic_guide/) (also known as `mlog`).
This is a small transpiler from a dialect of the [BASIC](https://en.wikipedia.org/wiki/BASIC) language, "MinBasic" (also known as `mbas`), to [Mindustry](https://github.com/Anuken/Mindustry/)'s [logic system](https://www.reddit.com/r/Mindustry/comments/kfea1e/an_overly_indepth_logic_guide/) (also known as `mlog`).
Basic is chosen as the source language as it already contains jumps (which mindustry heavily relies on), while allowing for some higher-order constructs like conditions, loops and functions.
For now this is a heavily work-in-progress project.
## Installation and running
To use this project, start by cloning this git repository:
```sh
git clone https://git.shadamethyst.xyz/amethyst/basic-to-mindustry/
cd basic-to-mindustry
```
You will then need an installation of the Rust compiler, which you can quickly get from [rustup.rs](https://rustup.rs/).
```sh
# To build the source code:
cargo build
# To run the binary:
./target/debug/basic-to-mindustry examples/prime.mbas
# You can do both of these with the following command (note the --):
cargo run -- examples/prime.mbas
```
<!-- TODO: add options -->
## VSCode syntax highlighting
Any language support extension for QuickBasic (the dialect MinBasic is based on) will work,
but if you would like an extension that was tailored to support MinBasic, you can have a look at [the one bundled with this project](./minbasic-vscode/README.md).
## Language features
The [GUIDE.md](./GUIDE.md) file describes how to write programs in MinBasic.

@ -0,0 +1,5 @@
node_modules
*.vsix
.vscode
yarn.lock
target

@ -0,0 +1,7 @@
.gitattributes
.gitignore
yarn.lock
node_modules
.vscode
*.vsix
target

@ -0,0 +1,19 @@
Copyright (c) 2023 Emilie BURGUN
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,22 @@
# Language support for the MinBASIC language
This small vscode extension provides language support for the MinBASIC language.
To build it, run the following:
```sh
npm i -g @vscode/vsce
vsce package
# Alternatively,
npx @vscode/vsce package
```
Then, install it by running:
```sh
vscodium --install-extension ./minbasic-vscode-*.vsix
# If you're using the proprietary builds of VSCode:
vscode --install-extension ./minbasic-vscode-*.vsix
```

@ -0,0 +1,30 @@
{
"comments": {
// symbol used for single line comment. Remove this entry if your language does not support line comments
"lineComment": "//",
// symbols used for start and end a block comment. Remove this entry if your language does not support block comments
"blockComment": [ "/*", "*/" ]
},
// symbols used as brackets
"brackets": [
["{", "}"],
["[", "]"],
["(", ")"]
],
// symbols that are auto closed when typing
"autoClosingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"]
],
// symbols that can be used to surround a selection
"surroundingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"]
]
}

@ -0,0 +1,31 @@
{
"name": "minbasic-vscode",
"displayName": "MinBasic Language Support",
"description": "Language support for the MinBasic language, which is an extension of QuickBasic for compilation to Mindustry Logic",
"version": "0.0.1",
"engines": {
"vscode": "^1.79.0"
},
"categories": [
"Programming Languages"
],
"repository": {
"type": "git",
"url": "https://git.shadamethyst.xyz/amethyst/basic-to-mindustry"
},
"author": "Shad Amethyst",
"license": "MIT",
"contributes": {
"languages": [{
"id": "minbasic",
"aliases": ["MinBasic", "minbasic"],
"extensions": ["mbas"],
"configuration": "./language-configuration.json"
}],
"grammars": [{
"language": "minbasic",
"scopeName": "source.mbas",
"path": "./syntaxes/minbasic.tmLanguage.json"
}]
}
}

@ -0,0 +1,140 @@
{
"scopeName": "source.mbas",
"fileTypes": [
"mbas",
"minbasic"
],
"name": "MinBasic",
"patterns": [
{
"captures": {
"1": {
"name": "punctuation.definition.comment.minbasic"
}
},
"comment": "Comment",
"match": "^ *(REM\\b|').*",
"name": "comment.line.minbasic"
},
{
"comment": "Delimiter",
"match": "[,:;]",
"name": "meta.delimiter.object.minbasic"
},
{
"comment": "Keyword",
"match": "(?i)(\\b((END ?)?IF|(END )?SELECT|(RESUME )?NEXT|CASE|CLOSE|DO|ELSE|FOR|GOSUB|GOTO|LOOP|ON|OPEN|RETURN|THEN|TO|UNTIL|WHILE)\\b)",
"name": "keyword.control.minbasic"
},
{
"comment": "Function",
"match": "(?i)(\\b(PRINT)\\b)",
"name": "support.function.minbasic"
},
{
"comment": "Operator",
"match": "(?i)((\\+|=|<|>|<>|AND|OR))",
"name": "keyword.operator.minbasic"
},
{
"comment": "Numeric",
"match": "\\b(\\d(\\.\\d)?)+",
"name": "constant.numeric.minbasic"
},
{
"comment": "Mindustry builtins",
"match": "(@(?:time|tick|unit|counter|this[xy]?|ipt|links|map[wh]))\\b",
"name": "constant.global.minbasic"
},
{
"captures": {
"1": {
"name": "entity.name.function.minbasic"
}
},
"comment": "SUB",
"match": "(?i)(^ *(\\w+):)",
"name": "meta.function.minbasic"
},
{
"name": "meta.assignment.minbasic",
"match": "^ *((?i)LET +)([a-zA-Z_@#][a-zA-Z0-9_@#]*) *(=)",
"captures": {
"1": {
"name": "keyword.other.minbasic"
},
"2": {
"name": "variable.other.minbasic"
},
"3": {
"name": "keyword.operator.assignment.minbasic"
}
}
},
{
"comment": "Brace, round",
"match": "[\\(\\)]",
"name": "meta.brace.round.minbasic"
},
{
"comment": "Brace, curly",
"match": "[\\{\\}]",
"name": "meta.brace.curly.minbasic"
},
{
"begin": "(\\w+)(\\()",
"beginCaptures": {
"1": {
"name": "entity.name.function.minbasic"
},
"2": {
"name": "meta.brace.round.minbasic"
}
},
"comment": "Function call",
"end": "(\\))",
"endCaptures": {
"1": {
"name": "meta.brace.round.minbasic"
}
},
"name": "meta.function.call.minbasic",
"patterns": [
{
"include": "$self"
}
]
},
{
"begin": "(\")",
"beginCaptures": {
"1": {
"name": "punctuation.definition.string.begin.minbasic"
}
},
"comment": "String, double-quoted",
"end": "(\")",
"endCaptures": {
"1": {
"name": "punctuation.definition.string.end.minbasic"
}
},
"name": "string.quoted.double.minbasic",
"patterns": [
{
"comment": "Escaped double-quote inside double-quoted string",
"match": "(\\\")",
"name": "constant.character.escape.minbasic"
},
{
"comment": "Single quote inside double-quoted string",
"match": "(')",
"name": "other.minbasic"
},
{
"include": "$self"
}
]
}
]
}

@ -19,7 +19,7 @@ fn read_basic_examples() -> impl Iterator<Item = (String, String)> {
if entry
.file_name()
.into_string()
.map(|name| name.ends_with(".basic"))
.map(|name| name.ends_with(".mbas"))
.unwrap_or(false)
{
let file_name = entry.file_name().into_string().unwrap();

Loading…
Cancel
Save