[wip] Add Rockstar

This commit is contained in:
Nilay Majorwar 2022-02-02 21:09:00 +05:30
parent 69553b7087
commit 0befc7369a
12 changed files with 537 additions and 2 deletions

View File

@ -0,0 +1,7 @@
# Rockstar
## References
- Official docs: https://codewithrockstar.com/docs
## Implementation details

400
languages/rockstar/SPEC.md Normal file
View File

@ -0,0 +1,400 @@
# The Rockstar Language Spec
This file contains an **unofficial** version of the Rockstar Language Spec. While the official docs are obviously the source of the ultimate truth, they are also a slight bit unorganized in terms of order of content. This file is just a more condensed version, used as reference while building the parser and interpreter.
## Comments
`(your comment)`
- Only single-line comments supported, can appear anywhere
## Variables
Three kinds of variables supported. There is no functional difference in the three whatsoever.
All variable names are **case-insensitive** in usage (apart from proper variables below).
1. **Simple variables**: Single word, only letters, no lang keywords.
```
Variable is 1
Tommy is a rockstar
X is 2
Y is 3
Put x plus y into result
```
2. **Common variables**: Variable name consists of two parts:
1. Keyword: `a, an, the, my, your, our`. This is part of the variable identifier.
2. Name: lowercase letters, no spaces.
```
My variable is 5
Your variable is 4
Put my variable plus your variable into the total
Shout the total
```
3. **Proper variables**: Multi-word, each word starting with capital letter, no lang keywords.
- Proper variales are also case-insensitive except that each word MUST start with capital letter in every use.
`Doctor Feelgood, Mister Crowley, Tom Sawyer, Billie Jean, Distance In KM`
### Case-sensitivity in variable names
- Rockstar keywords and variable names are all case-insensitive...
- ...except proper variables, in which each word MUST start with a capital letters
```
TIME, time, tIMe, TIMe (Simple variables, equivalent)
MY HEART, my heart, My Heart (Common variables, equivalent)
Tom Sawyer, TOM SAWYER, TOm SAWyer (Proper variables, equivalent)
(note that the "S" above must be capital)
DOCTOR feelgood (NOT a valid variable name)
```
### Scope rules
- All variables declared in global scope (outside of any function) are accessible and modifyable everywhere **below** their first initialization.
- Variables defined inside functions are available until end of function.
### Referring to last named variable
`it, he, she, him, her, they, them, ze, hir, zie, zir, xe, xem, ve, ver`
Above keywords can be used to refer to the last named variable, **in parsing order**. This means the variable last used in the lines just above the line being parsed.
### Types
- **Undefined**: `mysterious`, assigned to variables that don't have a value yet. Falsy.
- **Null**: `null, nothing, nowhere, nobody, gone`. Falsy.
- **Boolean**:
- True: `true, right, yes, ok`. Truthy.
- False: `false, wrong, no, lies`. Falsy.
- **Number**: IEEE 754 floating-point numbers. Falsy only if zero.
- **String**: UTF-16 encoded strings. Empty string falsy, else truthy.
- Aliases for empty string: `empty, silent, silence`
### Literals
- Strings use double quotes: `"Hello San Francisco"`
- Numeric literals: `123`, `3.1415`
### Value Assignment
```
Put <expression> into <variable>
Put <expression> in <variable>
Let <variable> be <expression>
```
> Note on the use of single quotes:
>
> - `'s` and `'re` appearing at end of word are considered as `... is` and `... are`.
> - All other uses of single quotes are completely ignored.
### Poetic literals
#### Poetic constant literals
- For assigning constant values like bools, null, undefined.
- Assignment syntax: `<variable> is/are/was/were <value>`
#### Poetic string literals
- For assigning strings without using double quotes.
- String literal continues all the way to `\n`, may contain keywords.
- Assignment syntax: `<variables> say/says/said <string>`
#### Poetic number literals
- For assigning numbers using words.
- Assignment syntax: `<variable> is/are/was/were <words>`
- `<words>` continues all the way until `\n`.
- Each word denotes a digit, which is `word-length % 10`.
- `-` is counted as a letter. All other non-alphabetical chars are ignored.
- `<words>` may contain one period in between, which acts as decimal point.
- `<words>` MUST not start with a constant literal (`nothing`, etc).
```
Tommy was a lovestruck ladykiller (100)
My dreams were ice. A life unfulfilled (3.141)
Tommy was without (7)
Her fire was all-consuming (13)
```
## Operations
### Arithmetic
- Usage: `<expression> <op> <expression>`
- Addition: `plus, with`
- Subtraction: `minus, without`
- Multiplication: `times, of`
- Division: `over, between`
### Compound assignment operators
- Usage: `Let <variable> be <op> <expression>`
- Equivalent to `(variable) (op)= (expression)` (`x += 5`)
### Incrementing and decrementing
- Increment: `Build <variable> up`
- Decrement: `Knock <variable> down`
- Multiple `up`s or `down`s adjust step value
```
Build my world up (my world += 1)
Knock the walls down, down (the walls -= 2)
```
### Rounding numbers
- `turn up`: round up, `turn down`: round down
- `turn round/around`: round to the nearest integer
- Syntax: `Turn <type> <variable>` (acts in-place)
### List arithmetic
- Operators support argument list on right side
- Operator is then applied iteratively on each arg (like `reduce`)
- Can also be used in compound assignment operators
- Only allowed where result type supports further ops
```
Let X be 1 with 2, 3, 4 (X = 1+2+3+4)
Let X be "foo" with "bar", and "baz" (X = "foobarbaz")
Let the wolf be without fear, fury (the wolf -= fear + fury)
Let X be "foo" with 2, 2, 2 (X = "foo"*8 = "foofoo...")
Let X be 2 times "foo", "bar" (unsupported op, X = mysterious)
```
### Converting between string and number
Keyword: `cast, burn`
- This is a mutation op (described in Arrays section)
- Parses strings into numbers (optional arg: base number (default 10))
- Converts numbers into corresponding Unicode characters (no optional arg)
```
Let X be "123.45"
Cast X (X = numeric value 123.45)
Let X be "ff"
Cast X with 16 (X = 255 = OxFF)
Cast "12345" into result (result = 12345)
Cast "aa" into result with 16 (result = 170 = 0xAA)
Cast 65 into result (result = "A" - ASCII code 65)
Cast 1046 into result (result = "Ж" - Unicode 1046)
```
### Comparison
- Syntax: `<expr> <op> <expr>`
- Above syntax only valid in comparison context (eg. `if` blocks)
- Equality comparison: `is/are/was/were` (`If <expr> is <expr>`)
- Not-equal comparison: `ain't, aren't, wasn't, weren't`
- Comparison keywords:
- Greater than: `is higher/greater/bigger/stronger than`
- Less than: `is lower/less/smaller/weaker than`
- Greater or equal: `is as high/great/big/strong as`
- Less than: `is as low/little/small/weak as`
### Logical operators
- `A and/or/nor B`: AND, OR, NOT-OR
- `not A`: negation (NOT)
- All logical ops are **short-circuiting**.
### Input-output
- `Listen to <variable>`: Read one line of STDIN and store in variable.
- `Say/Shout/Whisper/Scream <expression>`: Print expr to STDOUT.
### Operator precedence
1. Function calls (greedy arguments)
2. Logical NOT (right-associative)
3. Multiplication and Division (left-associative)
4. Addition and Subtraction (left-associative)
5. Comparison operators (left-associative)
6. AND, OR, NOR (left-associative)
```
A taking B times C plus not D times E and F
= ((A(B) * C) + (!D * E)) && F
```
### Implicit conversions
For comparison operators:
- `mysterious OP mysterious` => Equal
- `<non-myst> OP mysterious` => Non-equal
- `<str> OP <num>` => Convert str to num (base 10). If fail, non-equal.
- `<str> OP <bool>` => Empty str is false, else str is true.
- `<str> OP null` => Non-equal
- `<num> OP <bool>` => Convert num to bool by truthiness.
- `<num> OP null` => Convert null to 0.
- `<bool> OP null` => Convert null to false.
For increment-decrement operators:
- `OP mysterious/<str>` => Error
- `OP <bool>` => invert bool
- `OP null` => coerce to zero, then apply op
For binary operators (non-mentioned cases are error):
- `<str> PLUS <num>`: convert num to base-10 str. Unnecessary zeros removed. Numbers with no whole part (eg 0.75) have one leading zero when serialized.
- `<str> PLUS <bool>`: convert bool to "true" or "false"
- `<str> PLUS null`: convert null to "null"
- `<str> PLUS mysterious`: convert mysterious to "mysterious"
- `<str> TIMES <num>`: str gets repeated num times
## Arrays
- Rockstar arrays support for numeric and non-numeric keys.
- Numeric keys are zero-indexed.
- Arrays are dynamically allocated when values are assigned.
- Returning array as scalar returns array's length instead.
```
Let my array at 255 be "some value"
Let my array at "some_key" be "some_value"
Shout my array at 255 (will print "some_value")
Shout my array (will print the value 256)
Shout my array at "some_key" (will print "some_value")
```
Read (but not write) characters from a string with array syntax:
```
Let my string be "abcdefg"
Shout my string at 0 (will print "a")
Shout my string at 1 (will print "b")
Let the character be my string at 2
```
### Array comparison
For two arrays to be equal,
- Must be of the same length
- Corresponding elements must be equal
### Array operations
#### Queueing elements onto array
Keyword: `rock, push`
- Create new empty array: `Rock the array`
- Queue value to end of array: `Rock the array with the element`
- Queue poetic literals: `Rock the array like the poetic literal (= [367])`
- List expressions:
```
Rock the array with 1, 2, 3
Rock the array with the one, the two, the three
Rock the array with the one, the two, and the three
```
> NOTE: `with` has other uses too - it is also alias for the addition operator.
> `Rock ints with 1, 2 with 3, 4, 5 (ints = [1, 5, 4, 5])`
#### Dequeue elements from the array
Keyword: `roll, pop`
- Remove first element from array (and optionally return it)
- Special `roll x into y` syntax for assigning result to variable
```
Rock ints with 1, 2, 3
Roll ints (ints is now [ 2, 3 ])
Let the two be roll ints (the two = 2, ints = [3])
Roll ints into three (three = 3, ints = [])
Roll ints (returns mysterious, ints = [])
```
> Below two operations are **mutation operations**, which all share the following syntax:
>
> ```
> Modify X (acts in place)
> Modify X into Y (put result in Y, X is unmodified)
> Modify X with Z (acts in place, Z is op parameter)
> Modify X into Y with Z (put result in Y, X is unmodified, Z is op param)
> ```
#### Splitting strings
Keyword: `cut, split, shatter`
- Split string into array of its characters
- If separator specified, split on the separator
#### Joining arrays
Keyword: `join, unite`
- Join array of strings into single string
- If separator specified, insert separator between each element
## Flow Control
### Block syntax
- Blocks start with an `If` or `Else` of function declaration...
- ...and end with an empty line - one empty line ends the innermost scope only.
- EOF ends all current scopes.
### If-Else
- Conditional, with optional `else` block.
```
If <condition>
...code...
Else
...code...
(empty line)
```
### Loops
- Only `while` loops are supported. Keywords: `while, until`
- Inner code is executed as long as expression is truthy.
- Breaking the loop: `break` or `Break it down`.
- Continue to next iteration: `continue` or `Take it to the top`
### Functions
Declaring functions:
- Function declaration syntax: `<fn-name> takes/wants <arg list>`
- Argument list is separated by `,`, `and`, `&`, `n`.
- Functions always have return value. Keyword: `return, give, give, send`.
- Return syntax: `<keyword> <var> [back]` (`back` is optional)
```
(This function adds 9 to its input and returns the result)
Polly wants a cracker
Cheese is delicious
Put a cracker with cheese into your mouth
Give it back
```
Calling functions:
- Functions are called with `taking` keyword and min 1 argument
- Argument list is separated by `,`, `and`, `&`, `n`
- Arguments can be expressions (literals, arithmetic ops, fn calls)
```
Multiply taking 3, 5 (is an expression, returns 15)
Search taking "hands", "my hands are"
Put Multiply taking 3, 5, and 9 into Large (Large = 3*5*9)
```

View File

@ -0,0 +1,49 @@
import { MonacoTokensProvider } from "../../types";
export const sampleProgram = [
`Midnight takes your heart and your soul`,
`While your heart is as high as your soul`,
`Put your heart without your soul into your heart`,
``,
`Give back your heart`,
``,
``,
`Desire is a lovestruck ladykiller`,
`My world is nothing `,
`Fire is ice`,
`Hate is water`,
`Until my world is Desire,`,
`Build my world up`,
`If Midnight taking my world, Fire is nothing and Midnight taking my world, Hate is nothing`,
`Shout "FizzBuzz!"`,
`Take it to the top`,
``,
`If Midnight taking my world, Fire is nothing`,
`Shout "Fizz!"`,
`Take it to the top`,
``,
`If Midnight taking my world, Hate is nothing`,
`Say "Buzz!"`,
`Take it to the top`,
``,
`Whisper my world`,
].join("\n");
/** Syntax highlighting */
export const editorTokensProvider: MonacoTokensProvider = {
ignoreCase: true,
tokenizer: {
root: [
[/\([^\)]*\)/, "comment"],
[/\b(takes|wants|taking)\b/, "red"],
[/\b(mysterious|null|nothing|nowhere|nobody|gone)\b/, "orange"],
[/\b(true|right|yes|ok|false|wrong|no|lies)\b/, "orange"],
[/\b(empty|silent|silence|".+")\b/, "green"],
[/\b(if|else|while|until)/, "violet"],
[/\b(break|break it down|continue|take it to the top)\b/, "violet"],
[/\b(shout|say|whisper|scream)\b/, "blue"],
[/\b(it|he|she|him|her|they|them|ze|hir|zie|zir|xe|xem|ve|ver)\b/, "red"],
],
},
defaultToken: "plain",
};

View File

@ -0,0 +1,4 @@
/** Type of props passed to renderer */
export type RS = {
value: number;
};

View File

@ -0,0 +1,4 @@
import { setupWorker } from "../setup-worker";
import RockstarLanguageEngine from "./runtime";
setupWorker(new RockstarLanguageEngine());

View File

@ -0,0 +1,12 @@
import { Renderer } from "./renderer";
import { LanguageProvider } from "../types";
import { RS } from "./common/types";
import { sampleProgram, editorTokensProvider } from "./common/misc";
const provider: LanguageProvider<RS> = {
Renderer,
sampleProgram,
editorTokensProvider,
};
export default provider;

View File

@ -0,0 +1 @@
export {};

View File

@ -0,0 +1,6 @@
import { RendererProps } from "../types";
import { RS } from "./common";
export const Renderer = ({ state }: RendererProps<RS>) => {
return state == null ? null : <p>state.value</p>;
};

View File

@ -0,0 +1,21 @@
import { LanguageEngine, StepExecutionResult } from "../../types";
import { RS } from "../common/types";
export default class XYZLanguageEngine implements LanguageEngine<RS> {
resetState() {
// TODO: Unimplemented
}
validateCode(code: string) {
// TODO: Unimplemented
}
prepare(code: string, input: string) {
// TODO: Unimplemented
}
executeStep(): StepExecutionResult<RS> {
// TODO: Unimplemented
return { rendererState: { value: 0 }, nextStepLocation: { line: 0 } };
}
}

27
pages/ide/rockstar.tsx Normal file
View File

@ -0,0 +1,27 @@
// @ts-nocheck
import React from "react";
import { NextPage } from "next";
import Head from "next/head";
import { Mainframe } from "../../ui/Mainframe";
import { Header } from "../../ui/header";
import LangProvider from "../../languages/rockstar";
const LANG_ID = "$LANG_ID";
const LANG_NAME = "Rockstar";
const IDE: NextPage = () => {
return (
<>
<Head>
<title>{LANG_NAME} | Esolang Park</title>
</Head>
<div style={{ height: "100%", display: "flex", flexDirection: "column" }}>
<Header langId={LANG_ID} langName={LANG_NAME} />
<div style={{ flexGrow: 1 }}>
<Mainframe langName={LANG_ID} provider={LangProvider} />
</div>
</div>
</>
);
};
export default IDE;

View File

@ -14,5 +14,9 @@
{
"display": "Deadfish",
"id": "deadfish"
},
{
"display": "Rockstar",
"id": "rockstar"
}
]
]

View File

@ -4,7 +4,7 @@ import { NextPage } from "next";
import Head from "next/head";
import { Mainframe } from "../../ui/Mainframe";
import { Header } from "../../ui/header";
import LangProvider from "../../engines/$LANG_ID";
import LangProvider from "../../languages/$LANG_ID";
const LANG_ID = "$LANG_ID";
const LANG_NAME = "$LANG_NAME";