Remove Rockstar-related files

Rockstar got slightly tricky to implement, so for the sake of completing
the project, Rockstar is being kept for later in a separate branch.
This commit is contained in:
Nilay Majorwar 2022-02-13 19:34:11 +05:30
parent a7b7879fb3
commit 10221b0767
16 changed files with 1 additions and 7611 deletions

View File

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

View File

@ -1,400 +0,0 @@
# 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 or equal: `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, 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

@ -1,116 +0,0 @@
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`,
``,
`(♬ FizzBuzz riff plays ♬)`, // :)
`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");
// prettier-ignore
const MAYBE_EQ_COMPARE_WORDS = ["high", "great", "big", "strong", "low", "little", "small", "weak"]
const MAYBE_EQ_COMPARE_REGEX = MAYBE_EQ_COMPARE_WORDS.join("|");
// prettier-ignore
const NON_EQ_COMPARE_WORDS = ["higher", "greater", "bigger", "stronger", "lower", "less", "smaller", "weaker"];
const NON_EQ_COMPARE_REGEX = NON_EQ_COMPARE_WORDS.join("|");
/** Syntax highlighting */
export const editorTokensProvider: MonacoTokensProvider = {
ignoreCase: true,
// prettier-ignore
varPronouns: ["it", "he", "she", "him", "her", "they", "them",
"ze", "hir", "zie", "zir", "xe", "xem", "ve", "ver"],
// prettier-ignore
operators: ["plus", "with", "minus", "without", "times", "of", "over", "between",
"build", "knock", "up", "down", "is", "are", "was", "were", "ain't", "aren't",
"wasn't", "weren't", "and", "or", "nor", "not", "like"],
// prettier-ignore
inbuiltFns: ["say", "shout", "whisper", "scream", "cast", "burn", "rock", "push",
"roll", "pop", "cut", "split", "shatter", "join", "unite"],
// prettier-ignore
keywords: ["if", "else", "while", "until", "break", "continue", "put", "let", "be",
"into", "give", "return", "back"],
// prettier-ignore
constants: ["mysterious", "null", "nothing", "nowhere", "nobody", "gone", "true",
"right", "yes", "ok", "false", "wrong", "no", "lies", "empty", "silent", "silence"],
tokenizer: {
root: [
[/\(/, { token: "comment", next: "@comment" }],
[/"/, { token: "string", next: "@string" }],
[/,|\./, ""],
// Function usage
[
// Allowing multi-word fn names here leads to too greedy rule,
// so for syntax highlighting, we use only one-word functions
/([a-zA-Z]+)( )(takes|wants|taking)( )/,
["variable.function", "", "keyword", ""],
],
// Comparator clauses
[
/(is as )([a-zA-Z]+)( as)/,
{ cases: { [`$2~${MAYBE_EQ_COMPARE_REGEX}`]: "operators" } },
],
[
/(is )([a-zA-Z]+)( than)/,
{ cases: { [`$2~${NON_EQ_COMPARE_REGEX}`]: "operators" } },
],
// Poetic string literals
[/(says )(.+$)/, ["keyword", "string"]],
// Multi-word keywords (can't be captured by keyword catchall)
[/turn (up|down|round|around)\b/, "operators"],
[/(break it down|take it to the top)/, "keyword"],
[/listen to/, "constant.function"],
// Catchall for keywords
[
/[^\s]+\b/,
{
cases: {
"@constants": "constant",
"@keywords": "keyword",
"@varPronouns": "variable",
"@operators": "operators",
"@inbuiltFns": "constant.function",
},
},
],
],
comment: [
[/\)/, { token: "comment", next: "@pop" }],
[/[^\)]*/, "comment"],
],
string: [
[/"/, { token: "string", next: "@pop" }],
[/[^"]*/, "string"],
],
},
defaultToken: "variable",
};

View File

@ -1,13 +0,0 @@
import { DocumentRange } from "../../types";
import { Line } from "../parser/grammar-types";
/** Type of props passed to renderer */
export type RS = {
value: number;
};
/** A single step of the Rockstar AST */
export type ASTStep = {
op: Line;
range: DocumentRange;
};

View File

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

View File

@ -1,12 +0,0 @@
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

@ -1,83 +0,0 @@
import { ParseError } from "../../worker-errors";
import { ASTStep } from "../common/types";
import * as D from "./grammar-types";
/** Single item of the scope block stack */
type BlockStackItem =
| { type: "if"; line: number }
| { type: "loop"; line: number }
| { type: "function"; line: number };
/** Stack to track the current scopes */
type BlockStack = BlockStackItem[];
/**
* Check that the given line is valid to be the next line of
* an inline if-statement. The next line of an inline if-statement
* can only be an empty line or an else-clause.
*
* @param lineNum Line number of the line in code
* @param line The line in parsed form
*/
const validateInlineIfNextLine = (lineNum: number, line: D.Line) => {
if (line.type !== "blank" && line.type !== "else") {
throw new ParseError(
"Expected else clause or blank line after inline if-statement",
{ line: lineNum }
);
}
};
/**
* Build the executable AST from the parsed program, by filling in
* the jump addresses of control flow statements and validating blocks.
*
* @param program Program in parsed form
* @returns AST of the program
*/
export const buildAST = (program: D.Program): ASTStep[] => {
const lines = program.list;
const stack: BlockStack = [];
const ast: ASTStep[] = [];
for (let i = 0; i < lines.length; ++i) {
const line = lines[i];
// Function declaration or loop start: push block onto stack
if (line.type === "function_decl" || line.type === "loop") {
stack.push({ type: "function", line: i });
}
if (line.type === "if") {
stack.push({ type: "if", line: i });
// Check if the next line is valid to follow an inline if-statement
if (line.statement && i + 1 < lines.length)
validateInlineIfNextLine(i + 1, lines[i + 1]);
}
if (line.type === "else") {
// Pop if-item from block stack
const ifItem = stack.pop();
if (!ifItem || ifItem.type !== "if")
throw new ParseError("Unexpected else clause", { line: i });
// Fill in the jump address for the corresponding if-statement
const ifLine = ast[ifItem.line];
if (!ifLine || ifLine.op.type !== "if") throw new Error("Bad stack item");
ifLine.op.jump = i;
}
if (line.type === "blank") {
// Pop block from stack
const blockItem = stack.pop();
if (blockItem) {
// Fill in the jump address for the corresponding block
const blockLine = ast[blockItem.line];
if (!blockLine || blockLine.op.type !== blockItem.type)
throw new Error("Bad stack item");
// TODO: Check block type and act accordingly
}
}
}
return ast;
};

View File

@ -1,16 +0,0 @@
// @ts-check
const fs = require("fs");
const path = require("path");
const peggy = require("peggy");
const grammarPath = path.resolve(__dirname, "grammar.peg");
const grammar = fs.readFileSync(grammarPath).toString();
const parser = peggy.generate(grammar, {
output: "source",
format: "commonjs",
allowedStartRules: ["program"],
});
const outputPath = path.resolve(__dirname, "parser.out.js");
fs.writeFileSync(outputPath, parser);

View File

@ -1,245 +0,0 @@
/** AST of the entire program */
export type Program = { list: Line[] };
/** A single line of the program */
export type Line = Statement | { type: "blank" };
/** A single (non-blank-line) statement */
export type Statement =
| Break
| Continue
| FunctionDecl
| FunctionCall
| FunctionReturn
| Loop
| If
| Else
| Operation
| Expression;
/*********************************
FLOW CONTROL
*********************************/
/** If-statement, optionally containing inline statement */
export type If = {
type: "if";
condition: Expression;
statement?: Statement | null;
/** Filled by AST builder: address to block ender */
jump?: number;
};
/** Else-statement, optionally containing inline statement */
export type Else = {
type: "else";
statement: Statement | null;
};
/** Loop-starting statement */
export type Loop = {
type: "loop";
condition: Expression;
};
/** Loop-breaking statement */
export type Break = { type: "break" };
/** Loop-continuing statement */
export type Continue = { type: "continue" };
/** Function declaration statement */
export type FunctionDecl = {
type: "function_decl";
name: Variable;
args: VariableList;
};
/** Function return statement */
export type FunctionReturn = {
type: "return";
expression: Expression;
};
/*********************************
VARIABLES AND ASSIGNMENT
*********************************/
/** Pronoun used to refer to last assigned variable */
export type Pronoun = { pronoun: string };
/** Identifier for a variable */
export type Variable = string;
/** List of variable identifiers */
export type VariableList = Variable[];
/** Target for an assignment statement */
export type Assignable = {
variable: Variable;
index: Expression;
};
/** Assignment statement */
export type Assignment =
| { type: "assign"; target: Assignable; expression: Literal | PoeticNumber }
| { type: "assign"; target: Assignable; expression: PoeticString }
| { type: "assign"; target: Assignable; expression: Expression }
// Return types of branch 4 & 5 are a subset of branch 3
| { type: "enlist"; variable: Variable; expression: Expression }
| { type: "enlist"; variable: Variable; expression: Literal | PoeticNumber }
// Return type of branch 8 is subset of branch 6
| { type: "enlist"; variable: Variable }
| { type: "assign"; target: Assignable; expression: Delist };
/*********************************
EXPRESSION TREE
*********************************/
/** List of atomic expressions */
export type ExpressionList = SimpleExpression[];
/** Root of an expression tree */
export type Expression = Nor;
/** NOR expression clause */
export type Nor = { type: "binary"; op: "nor"; lhs: Or; rhs: Nor } | Or;
/** OR expression clause */
export type Or = { type: "binary"; op: "or"; lhs: And; rhs: Or } | And;
/** AND expression clause */
export type And =
| { type: "binary"; op: "and"; lhs: EqualityCheck; rhs: And }
| EqualityCheck;
/** Equality/inequality check clause */
export type EqualityCheck =
| {
type: "comparison";
comparator: "eq" | "ne";
lhs: Not;
rhs: EqualityCheck;
}
| Not;
/** NOT expression clause */
export type Not = { type: "not"; expression: Not } | Comparison;
/** Comparison clause */
export type Comparison = {
type: "comparison";
comparator: "gt" | "lt" | "ge" | "le";
lhs: Arithmetic;
rhs: Comparison;
};
/** Add/Subtract arithmetic clause */
export type Arithmetic =
| { type: "binary"; op: "+" | "-"; lhs: Arithmetic; rhs: Product }
| Product;
/** Utility type for main branch of "product" grammar rule */
type _Product = {
type: "binary";
op: "*" | "/";
lhs: _Product | SimpleExpression;
rhs: ExpressionList;
};
/** Multiply/Divide arithmetic clause */
export type Product = _Product | ExpressionList | SimpleExpression;
/** Leaf of an expression tree */
export type SimpleExpression =
| FunctionCall
| Constant
| Lookup
| Literal
| Pronoun;
/** Expression for a function call */
export type FunctionCall = {
type: "call";
name: Variable;
args: ExpressionList;
};
/** Constant literal */
export type Literal = Constant | Number | String;
/** Unit constant literal */
export type Constant = { constant: null | boolean | "" } | "__MYSTERIOUS__";
/** Constant numeric literal */
export type Number = { number: number };
/** Constant string literal */
export type String = { string: string };
/*********************************
OPERATION STATEMENTS
*********************************/
/** Single-operation statements */
export type Operation =
| Readline
| Output
| Crement
| Mutation
| Assignment
| Rounding;
/** STDIN statement */
export type Readline = { type: "stdin"; target?: Assignable };
/** STDOUT statement */
export type Output = { type: "stdout"; output: Expression };
/** Increment/decrement statements */
export type Crement = {
type: "increment" | "decrement";
variable: Variable;
multiple: number;
};
/** Types of mutation operations */
export type Mutator = "split" | "cast" | "join";
/** Mutation operation statement */
export type Mutation = {
type: "mutation";
target: Assignable;
expression: {
mutation: {
type: Mutator;
modifier: Expression;
source: Expression | { lookup: Assignable };
};
};
};
/** Rounding operation statement */
export type Rounding = {
type: "rounding";
direction: "up" | "down" | "nearest";
variable: Variable;
};
/*********************************
KITCHEN SINK
*********************************/
/** Clause representing dequeueing of an array */
export type Delist = { type: "delist"; variable: Variable };
/** Clause for variable lookup at leaf of expression tree */
export type Lookup =
| { type: "lookup"; variable: Variable; index?: Expression }
| Delist;
/** Poetic numeric literal */
export type PoeticNumber = { number: number };
/** Poetic string literal */
export type PoeticString = { string: string };

View File

@ -1,460 +0,0 @@
/*
This is a slightly modified version of the grammar defined in Satriani,
the official reference implementation of Rockstar. Modifications made:
- Add very short comment for each grammar rule
- Adapt grammar to parse the program line-by-line
- Organize the rules into broad categories
- Adjust action returns to be easier to use in TypeScript
Apart from line-by-line parsing, all changes are cosmetic and the
actual grammar should be identical to Satriani. The rule names should not
be changed too much, so that matching any upstream updates is easy.
Satriani grammar: https://github.com/RockstarLang/rockstar/blob/main/satriani/rockstar.peg
After updating, run `node generate-parser.js` in this directory.
*/
{
/* initialiser code - this is JS that runs before the parser is generated */
const keywords = new Set([
// common variable prefixes
'a', 'an', 'the', 'my', 'your', 'our',
// pronouns
'it', 'he', 'she', 'him', 'her', 'they', 'them', 'ze', 'hir', 'zie', 'zir', 'xe', 'xem', 've', 'ver',
// literal values
'mysterious',
'null', 'nothing', 'nowhere', 'nobody', 'gone',
'true', 'right', 'yes', 'ok',
'false', 'wrong', 'no', 'lies',
'maybe', 'definitely', // reserved for future use
'empty', 'silent', 'silence',
// assignment
'let', 'be', 'put', 'into', 'in', // expression
'is', 'are', 'was', 'were', 'say', 'says', 'said', // poetic
// operations
'at', 'rock', 'with', 'roll', 'into', 'push', 'pop', 'like', // arrays
'cut', 'split', 'shatter', 'join', 'unite', 'cast', 'burn', // strings
'build', 'up', 'knock', 'down', // increment/decrement
'plus', 'with', 'minus', 'without', 'times', 'of', 'over', 'between', // arithmetic
'and', // list arithmetic
'turn', 'up', 'down', 'round', 'around', // rounding
'and', 'or', 'nor', 'not', // logical
// comparison
'is', "isn't", 'isnt', "ain't", 'aint',
'arent', "aren't", 'wasnt', "wasn't", 'werent', "weren't",
'not',
'than',
'higher', 'greater', 'bigger', 'stronger',
'lower', 'less', 'smaller', 'weaker',
'as',
'high', 'great', 'big', 'strong',
'low', 'little', 'small', 'weak',
// input/output
'listen', 'to',
'say', 'shout', 'whisper', 'scream',
// control flow
'if', 'else',
'while', 'until',
'break', 'continue',
'break', 'it', 'down',
'take', 'it', 'to', 'the', 'top',
'take',
// functions
'takes', 'wants',
'give', 'return', 'send', 'back',
'taking',
])
function isKeyword(string) {
return keywords.has(string.toLowerCase());
}
}
/* Rule for the entire program */
program = p:line * { return { list: p } }
/* Rule for a single block of the program */
line = _* s:statement _* (EOL / EOF) { return s }
/ _* EOL { return { type: 'blank' } }
/ _+ EOF {return { type: 'blank' } }
/* Utility types for whitespace and comments */
whitespace = [ \t]
comment = '(' [^)]* ')'
_ = (whitespace / comment)+
noise = (_ / [;,?!&.])
EOL "end of line" = noise* '\r'? '\n'
EOF = !.
ignore_rest_of_line = (_[^\n]*)?
/* Rule for a single statement */
statement = _* s:(break / continue / function_decl / function_call
/ function_return / loop / if / else / operation / expression) { return s }
/*********************************
FLOW CONTROL
*********************************/
/* Rule for an if-statement */
// To run inline statements in a separate step, we need location information
inline_statement = s:statement { return {s: s, start: location().start.offset }}
if = 'if'i _ e:expression s:inline_statement?
{ return {
type: 'if',
condition: e,
statement: s && s.s,
split: s && s.start,
} }
/* Rule for an else-statement */
else = _* 'else'i _ a:statement { return { type: 'else', statement: a } }
/ _* 'else'i _* {return { type: 'else', statement: null } }
/* Rule for starting a while-loop */
loop_keyword = ('while'i / 'until'i)
loop = loop_keyword _ e:expression { return { type: 'loop', condition: e } }
/* Rule for the loop break statement */
break = 'break'i ignore_rest_of_line { return { type: 'break' } }
/* Rule for the loop continue statement */
continue = ('continue'i ignore_rest_of_line / 'take it to the top'i)
{ return { type: 'continue' } }
/* Rule for function declaration statement */
takes = ('takes'i / 'wants'i)
function_decl = name:variable _ takes _ args:variable_list
{ return { type: 'function_decl', name: name, args: args } }
/* Rule for function return statement */
return = 'return'i / 'give back'i / 'send'i / 'give'i
function_return = return _ e:expression (_ 'back'i)?
{ return { type: 'return', expression: e } }
/*********************************
VARIABLES AND ASSIGNMENT
*********************************/
/* Keywords used to refer to last assigned variable */
pronoun "pronoun" = pronoun:(
'they'i / 'them'i
/ 'she'i / 'him'i / 'her'i / 'hir'i / 'zie'i / 'zir'i / 'xem'i / 'ver'i
/ 'ze'i / 've'i / 'xe'i / 'it'i / 'he'i
) &(is / _ / EOL / EOF)
{ return { pronoun: pronoun.toLowerCase() } }
/* Prefix for defining common variables */
common_prefix "common variable prefix" = ( 'an'i / 'a'i / 'the'i / 'my'i / 'your'i / 'our'i)
/* Set of recognized uppercase letters */
uppercase_letter "uppercase letter" = [A-ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮİIJĴĶĸĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŸŹŻŽ]
/* Set of recognized lowercase letters */
lowercase_letter "lowercase letter" = [a-zàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþāăąćĉċčďđēĕėęěĝğġģĥħĩīĭįıijĵķĸĺļľŀłńņňŋōŏőœŕŗřśŝşšţťŧũūŭůűųŵŷÿźżžʼnß]
/* Set of recognized letters */
letter "letter" = uppercase_letter / lowercase_letter
/* Rule for variable identifiers */
variable "variable" = common_variable / proper_variable / pronoun / simple_variable
/* Name of a common variable */
common_variable = prefix:common_prefix _ name:$(letter+)
{ return (prefix + '_' + name).toLowerCase() };
/* Rule for name of a simple variable */
simple_variable = name:$(letter letter*) !{ return isKeyword(name) } { return name.toLowerCase() }
/* Rule for a single word in proper variable */
proper_noun = noun:$(uppercase_letter letter*) !{ return isKeyword(noun) } { return noun }
/* Rule for name of a proper variable */
proper_variable = head:$(proper_noun (' ' $proper_noun)*)
{ return head.replace(/ /g, '_').toLowerCase() }
/* Rule for a list of variables */
variable_list_separator "separator" = expression_list_separator / _ 'and'i _
variable_list "variable list" = head:variable variable_list_separator tail:variable_list
{ return [head].concat(tail) }
/ arg:variable { return [arg] }
/* Rule for part of array access clause */
indexer = _ 'at'i _ i:expression { return i };
/* Rule for possible target of a value assignment */
assignable "assignable variable"
= v:variable i:indexer?
{ return { variable: v, index: i }; }
/* Operators allowed in compound assignment */
compoundable_operator "operator" = add / subtract / multiply / divide
/* Rule for assignment statements */
assignment "assignment statement" = target:assignable is _* e:(literal / poetic_number)
{ return { type: "assign", target: target, expression: e} }
/ target:assignable _ ('says 'i / 'say 'i / 'said 'i) e:poetic_string
{ return { type: "assign", target: target, expression: e} }
/ 'put'i _ e:expression into target:assignable
{ return { type: "assign", target: target, expression: e} }
/ 'let'i _ target:assignable _ 'be'i o:compoundable_operator e:expression {
return {
type: "assign",
target: target,
expression: { binary: { op: o, lhs: { lookup: target }, rhs: e } }
}
}
/ 'let'i _ t:assignable _ 'be'i _ e:expression
{ return { type: "assign", target: t, expression: e} }
/ push _ e:expression into v:variable
{ return { type: "enlist", variable: v, expression: e } }
/ push _ v:variable _ 'like'i _ e:(literal / poetic_number)
{ return { type: "enlist", variable: v, expression: e } }
/ push _ v:variable (_ 'with'i)? _ e:expression
{ return { type: "enlist", variable: v, expression: e } }
/ push _ v:variable
{ return { type: "enlist", variable: v } }
/ e:delist into target:assignable
{ return { type: "assign", target: target, expression: e } }
/*********************************
EXPRESSION TREE
*********************************/
/* Rule for a list of expressions */
expression_list_separator "separator" = (_? ', and'i _ / _?('&' / ',' / "'n'"i)_?)
expression_list "expression list" = head:simple_expression expression_list_separator tail:expression_list
{ return [head].concat(tail) }
/ arg:simple_expression { return [arg] }
/* Root rule for expression tree */
expression "expression" = boolean
/* Rule for a boolean operation clause */
boolean = nor
/* Rule for NOR operation */
nor = lhs:or _ 'nor'i _ rhs:nor {
return { type: "binary", op: 'nor', lhs: lhs, rhs: rhs } }
/ or
/* Rule for OR operation */
or = lhs:and _ 'or'i _ rhs:or {
return { type: "binary", op: 'or', lhs: lhs, rhs: rhs } }
/ and
/* Rule for AND operation */
and = lhs:equality_check _ 'and'i _ rhs:and {
return { type: "binary", op: 'and', lhs: lhs, rhs: rhs } }
/ equality_check
/* Keywords for equality/inequality check */
is = ("'s"i / "'re"i / _ ('=' / 'is'i / 'was'i / 'are'i / 'were'i)) _
isnt = _ (
'isnt'i / "isn't"i /
'aint'i / "ain't"i /
'arent'i / "aren't"i /
'wasnt'i / "wasn't"i /
'werent'i / "weren't"i
) _
/* Rule for equality/inequality check */
eq = isnt { return 'ne' } / is { return 'eq' }
equality_check = lhs:not c:eq rhs:equality_check
{ return { type: "comparison", comparator: c, lhs: lhs, rhs: rhs } }
/ not
/* Rule for NOT operation */
not = 'not'i _ e:not { return { type: "not", expression: e } }
/ comparison
/* Keywords for comparison operators */
greater = ('higher'i / 'greater'i / 'bigger'i / 'stronger'i)
smaller = ('lower'i / 'less'i / 'smaller'i / 'weaker'i)
great = ('high'i / 'great'i / 'big'i / 'strong'i)
small = ('low'i / 'little'i / 'small'i / 'weak'i)
comparator = is greater _ 'than'i _ { return 'gt' }
/ is smaller _ 'than'i _ { return 'lt' }
/ is 'as'i _ great _ 'as'i _ { return 'ge' }
/ is 'as'i _ small _ 'as'i _ { return 'le' }
/* Rule for comparison clause */
comparison = lhs:arithmetic c:comparator rhs:comparison
{ return { type: "comparison", comparator: c, lhs: lhs, rhs: rhs } }
/ arithmetic
/* Rule for plus/minus arithmetic clause */
arithmetic = first:product rest:((add / subtract) product)+
{ return rest.reduce(function(memo, curr) {
return { type: "binary", op: curr[0], lhs: memo, rhs: curr[1] }
}, first); }
/ product
/* Rule for multiply/divide arithmetic clause */
product = first:simple_expression rest:((multiply / divide) expression_list)+
{ return rest.reduce(function(memo, curr) {
return { binary: { op: curr[0], lhs: memo, rhs: curr[1] } };
}, first); }
/ expression_list
/ simple_expression
/* Rule for the leaf of an expression tree */
simple_expression = function_call / constant / lookup / literal / pronoun
/* Rule for function call expression */
function_call "function call" = name:variable _ 'taking'i _ args:expression_list
{ return { type: "call", name: name, args: Array.isArray(args) ? args : [args] } }
/* Rule for a constant literal */
literal "literal" = constant / number / string
/* Rule for keyword-based constant literals */
constant "constant" = null / true / false / empty_string / mysterious
null = ('null'i / 'nothing'i / 'nowhere'i / 'nobody'i / 'gone'i) { return { constant: null } }
true = ('true'i / 'ok'i / 'right'i / 'yes'i) !letter { return { constant: true } }
false = ('false'i / 'lies'i / 'wrong'i / 'no'i) !letter { return { constant: false } }
empty_string = ('empty'i / 'silent'i / 'silence'i) { return { constant: "" } }
mysterious = 'mysterious'i { return '__MYSTERIOUS__' }
/* Rule for a numeric literal */
number "number" = n:$('-'?[0-9]+ ('.' [0-9]+)?) '.'?
{ return {number: parseFloat(n)} }
/ n:$('.' [0-9]+)
{ return {number: parseFloat(n) } }
/* Rule for a string literal */
string "string" = '"' s:$[^"]* '"' { return {string: s }; }
/* 'TODO remove */
/*********************************
OPERATION STATEMENTS
*********************************/
/* Rule for single-operation statements */
operation "operation statement" = readline / output / crement / mutation / assignment / rounding
/* Rule for STDIN operation statement */
readline "stdin statement" = 'listen to'i _ target:assignable
{ return { type: "stdin", target: target } }
/ 'listen'i { return { type: "stdin" } }
/* Rule for STDOUT statement */
output "stdout statement" = ('say'i/'shout'i/'whisper'i/'scream'i) _ e:expression
{ return { type: "stdout", output: e } }
/* Rule for increment/decrement statements */
crement "increment/decrement statement" = increment / decrement
/* Rule for increment statement */
increment = 'build'i _ v:variable _ t:('up'i noise*)+
{ return { type: "increment", variable: v, multiple: t.length } }
/* Rule for decrement statement */
decrement = 'knock'i _ v:variable _ t:('down'i noise*)+
{ return { type: "decrement", variable: v, multiple: t.length } }
split = ('cut'i / 'split'i / 'shatter'i) { return 'split' }
cast = ('cast'i / 'burn'i) { return 'cast' }
join = ('join'i / 'unite'i) { return 'join' }
/* Rule for mutation operation statements */
mutator "mutation keyword" = split / cast / join
modifier = _ ('with'i / 'using'i) _ m:expression { return m }
mutation "mutation statement" = op:mutator _ s:expression into t:assignable m:modifier?
{ return { assign: { target: t, expression: { mutation: { type: op, source: s, modifier: m } } } } ; }
/ op:mutator _ s:assignable m:modifier?
{ return { assign: { target: s, expression: { mutation: { type: op, source: { lookup: s }, modifier: m } } } } ; }
/* Rule for rounding operation statements */
rounding "rounding statement" = floor / ceil / math_round
floor = 'turn'i _ v:variable _ 'down'i
{ return { type: "rounding", variable: v, direction: 'down' } }
/ 'turn'i _ 'down'i _ v:variable
{ return { type: "rounding", variable: v, direction: 'down' } }
ceil = 'turn'i _ v:variable _ 'up'i
{ return { type: "rounding", variable: v, direction: 'up' } }
/ 'turn'i _ 'up'i _ v:variable
{ return { type: "rounding", variable: v, direction: 'up' } }
math_round = 'turn'i _ v:variable _ ('round'i/'around'i)
{ return { type: "rounding", variable: v, direction: 'nearest' } }
/ 'turn'i _ ('round'i/'around'i) _ v:variable
{ return { type: "rounding", variable: v, direction: 'nearest' } }
/*********************************
KITCHEN SINK
*********************************/
/* Keywords for arithmetic operators */
// Note that operator aliases explicitly include a trailing space,
// otherwise 'with' is a prefix code for 'without' and confuses the parser.
add = _* ('+' / 'plus 'i / 'with 'i) _* { return '+' }
subtract = _* ('-' / 'minus 'i / 'without 'i) _* { return '-' }
multiply = _* ('*' / 'times 'i / 'of 'i) _* { return '*' }
divide = _* ('/' / 'over 'i / 'between 'i) _* { return '/' }
push = ('rock'i / 'push'i )
pop = ('roll'i / 'pop'i)
into = _ ('into'i / 'in'i) _
/* Rule representing array dequeue clause */
delist "array roll" = pop _ v:variable
{ return { type: "delist", variable: v } }
/* Rule for variable in expression tree leaf */
lookup "variable or array element" = d:delist { return d; }
/ v:variable _ 'at'i _ i:expression
{ return { type: "lookup", variable: v, index: i } }
/ v:variable
{ return { type: "lookup", variable: v } }
/* Rule for poetic string literal */
poetic_string "poetic string" = s:$[^\r\n]* { return { string: s } }
/* Rule for poetic number literal */
poetic_number "poetic number" = poetic_digit_separator* n:poetic_digits poetic_digit_separator* d:poetic_decimal? poetic_digit_separator*
{ return { number: parseFloat(d?n+'.'+d:n)}}
/* Rule for poetic decimal literal */
poetic_decimal = '.' poetic_decimal_digit_separator* d:poetic_decimal_digits poetic_decimal_digit_separator* {return d}
/ '.' poetic_decimal_digit_separator*
/* Separator used in poetic literals */
poetic_digit_separator = ( _ / [0-9\',;:?!+_/] )
poetic_digits = poetic_digit_separator* head:poetic_digit poetic_digit_separator+ tail:poetic_digits
{ return head + tail }
/ d: poetic_digit
{ return d }
poetic_decimal_digit_separator = ( _ / poetic_digit_separator / '.')
poetic_decimal_digits = poetic_decimal_digit_separator* head:poetic_digit poetic_decimal_digit_separator+ tail:poetic_decimal_digits
{ return head + tail }
/ d: poetic_digit
{ return d }
poetic_digit = t:[A-Za-z\-']+
{ return (t.filter(c => /[A-Za-z\-]/.test(c)).length%10).toString() }

View File

@ -1,29 +0,0 @@
import { ParseError } from "../../worker-errors";
import * as parser from "./parser.out.js";
import * as D from "./grammar-types";
import { ASTStep } from "../common/types";
import { buildAST } from "./ast-builder";
/** Run the program source code through the Peg parser */
export const pegParseProgram = (program: string): D.Program => {
try {
return parser.parse(program);
} catch (err) {
if (err instanceof parser.SyntaxError) {
const error = err as any;
const message = error.message;
const line = error.location.start.line - 1;
const charRange = {
start: error.location.start.offset,
end: error.location.end.offset,
};
throw new ParseError(message, { line, charRange });
} else throw err;
}
};
export const parseProgram = (program: string): ASTStep[] => {
const lines = pegParseProgram(program);
const ast = buildAST(lines);
return ast;
};

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -1,24 +0,0 @@
import React from "react";
import { NextPage } from "next";
import Head from "next/head";
import { Mainframe } from "../../ui/Mainframe";
import LangProvider from "../../languages/rockstar";
const LANG_ID = "rockstar";
const LANG_NAME = "Rockstar";
const IDE: NextPage = () => {
return (
<>
<Head>
<title>{LANG_NAME} | Esolang Park</title>
</Head>
<Mainframe
langId={LANG_ID}
langName={LANG_NAME}
provider={LangProvider}
/>
</>
);
};
export default IDE;

View File

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