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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in new issue