mirror of
https://github.com/Noratrieb/GRSBPL.git
synced 2026-01-14 19:55:03 +01:00
f
This commit is contained in:
parent
c9ab1114f2
commit
52d7e0cbaf
8 changed files with 0 additions and 757 deletions
|
|
@ -1,92 +0,0 @@
|
||||||
# GRSBPL - Generic Random Stack Based Programming Language
|
|
||||||
|
|
||||||
uses some form of reverse polish notation
|
|
||||||
|
|
||||||
```
|
|
||||||
1 5 * 5 +
|
|
||||||
> 10
|
|
||||||
```
|
|
||||||
|
|
||||||
There is a stack and variables. Operations are done on the stack, and you can store results in variables (a bit like in
|
|
||||||
the JVM). The stack contains integer values. Floating point numbers are not supported.
|
|
||||||
|
|
||||||
When the program finishes (run to the end of the program), the last value on the stack is returned. If the stack is
|
|
||||||
clear, 0 is always returned. If there is an error during execution, -1 is returned along with an error message to
|
|
||||||
stderr.
|
|
||||||
|
|
||||||
## Operators and keywords:
|
|
||||||
|
|
||||||
* any number `n` -> push the numeric value of n
|
|
||||||
* any character `'c'` -> push c as its escaped ascii value
|
|
||||||
* `+` -> add two values on the stack, pops both and pushes the result
|
|
||||||
* `-` -> subtract two values on the stack, pops both and pushes the result
|
|
||||||
* `*` -> multiply two values on the stack, pops both and pushes the result
|
|
||||||
* `/` -> divide, pops both and pushes the result
|
|
||||||
* `%` -> mod, pops both and pushes the result
|
|
||||||
* `not` -> invert stack value (!=0 -> 0, 0 -> 1)
|
|
||||||
* `swap` -> swaps the 2 top stack values
|
|
||||||
* `out` -> pop and output it to the console as ascii
|
|
||||||
* `nount` -> pop and output as a number to the console
|
|
||||||
* `in` -> push input char as ascii to the stack
|
|
||||||
* `# comment #` text between # is ignored
|
|
||||||
* `# comment\n` text after # is ignored
|
|
||||||
* `&word` -> pop and store it in a variable
|
|
||||||
* `@word` -> load variable and push it, does not consume the variable
|
|
||||||
* `:indent` -> define a label
|
|
||||||
* `goto ident` -> goto a label if the value on the stack is !=0, peek
|
|
||||||
|
|
||||||
Identifier: \w
|
|
||||||
|
|
||||||
Character escape sequences:
|
|
||||||
\n, \r, \\, \0, \', \b, \f
|
|
||||||
|
|
||||||
## Examples:
|
|
||||||
|
|
||||||
FizzBuzz
|
|
||||||
|
|
||||||
```grsbpl
|
|
||||||
1 &i # init loop counter
|
|
||||||
:start # set start label
|
|
||||||
@i 100 - not goto exit # if i is 100, exit
|
|
||||||
@i 15 % not goto print_fizz_buzz # fizzbuzz
|
|
||||||
@i 5 % not goto print_buzz # buzz
|
|
||||||
@i 3 % not goto print_fizz # fizz
|
|
||||||
@i nout '\n' out # normal number
|
|
||||||
:end # go back here after printing
|
|
||||||
@i 1 + &i # increment i
|
|
||||||
1 goto start # go back to the start
|
|
||||||
|
|
||||||
:print_fizz_buzz
|
|
||||||
'F' out 'i' out 'z' out 'z' out 'B' out 'u' out 'z' out 'z' out '\n' out
|
|
||||||
goto end
|
|
||||||
:print_fizz
|
|
||||||
'F' out 'i' out 'z' out 'z' out '\n' out
|
|
||||||
goto end
|
|
||||||
:print_buzz
|
|
||||||
'B' out 'u' out 'z' out 'z' out '\n' out
|
|
||||||
goto end
|
|
||||||
|
|
||||||
:exit 0
|
|
||||||
```
|
|
||||||
|
|
||||||
## Some Tips
|
|
||||||
|
|
||||||
* Increment a variable:
|
|
||||||
`@i 1 + &i`
|
|
||||||
* Pop a value from the stack and discard it:
|
|
||||||
`&dev_null` (just use any unused variable)
|
|
||||||
* Goto if equal
|
|
||||||
`@i 100 - not goto finished`
|
|
||||||
* Goto not equal
|
|
||||||
`@i 100 - goto finished`
|
|
||||||
* Exit the program
|
|
||||||
`... goto exit ... :exit 0`
|
|
||||||
* Exit with exit code depending on the branch
|
|
||||||
```grsbpl
|
|
||||||
...
|
|
||||||
69 swap goto exit # push 69 to the 2nd stack position
|
|
||||||
...
|
|
||||||
5 swap goto exit # push 5 to the 2nd stack position
|
|
||||||
...
|
|
||||||
:exit &del # pop the top stack value to expose the pushed value
|
|
||||||
```
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
# All GRSBPL Error Messages
|
|
||||||
|
|
||||||
## Syntax Errors
|
|
||||||
|
|
||||||
- Any uncaught exception occurs during lexing:
|
|
||||||
`Unknown Syntax Error. <exceptionname>: <exceptionmessage>`
|
|
||||||
|
|
||||||
- Invalid character escaped:
|
|
||||||
`Invalid escape sequence <escaped>`
|
|
||||||
|
|
||||||
- Integer parse failed (can only happen because number is too big)
|
|
||||||
`Value not an integer: <number>`
|
|
||||||
|
|
||||||
## Runtime Errors
|
|
||||||
|
|
||||||
- Label not found
|
|
||||||
`Label '<name>' not found`
|
|
||||||
|
|
||||||
- Function not found
|
|
||||||
`Function '<name>' not found`
|
|
||||||
|
|
||||||
- Stack empty on return
|
|
||||||
`Function has to return some value, but no value was found on the stack`
|
|
||||||
|
|
||||||
- Pop called on empty stack
|
|
||||||
`Cannot pop empty stack`
|
|
||||||
|
|
||||||
- No stack frame left after return
|
|
||||||
`Tried to return outside of function, probably forgot to skip a function`
|
|
||||||
|
|
||||||
- Stackoverflow - limit 1 000 000
|
|
||||||
`Stackoverflow. Limit of <STACK_LIMIT> stack frames reached.`
|
|
||||||
|
|
||||||
- Invalid token found
|
|
||||||
`Excepted token '<name>' but found '<name>'`
|
|
||||||
|
|
||||||
- Failed to read input from stdin
|
|
||||||
`[VM] - Error reading input`
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<groupId>com.github.nilstrieb</groupId>
|
|
||||||
<artifactId>GRSBPL</artifactId>
|
|
||||||
<version>1.0-SNAPSHOT</version>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.junit.jupiter</groupId>
|
|
||||||
<artifactId>junit-jupiter-api</artifactId>
|
|
||||||
<version>5.6.1</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.junit.jupiter</groupId>
|
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
|
||||||
<version>5.6.1</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<maven.compiler.source>14</maven.compiler.source>
|
|
||||||
<maven.compiler.target>14</maven.compiler.target>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
import java.util.OptionalInt;
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
|
|
||||||
public class IntStack {
|
|
||||||
private int[] values;
|
|
||||||
private int pointer;
|
|
||||||
|
|
||||||
private static final int INITIAL_CAPACITY = 100;
|
|
||||||
|
|
||||||
public IntStack() {
|
|
||||||
this(INITIAL_CAPACITY);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntStack(int initialCapacity) {
|
|
||||||
values = new int[initialCapacity];
|
|
||||||
pointer = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void push(int value) {
|
|
||||||
checkResize();
|
|
||||||
values[++pointer] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int pop() {
|
|
||||||
if (pointer == -1) {
|
|
||||||
throw new IndexOutOfBoundsException("Cannot pop below zero");
|
|
||||||
}
|
|
||||||
return values[pointer--];
|
|
||||||
}
|
|
||||||
|
|
||||||
public int peek() {
|
|
||||||
return values[pointer];
|
|
||||||
}
|
|
||||||
|
|
||||||
public OptionalInt tryPop() {
|
|
||||||
if (pointer == -1) {
|
|
||||||
return OptionalInt.empty();
|
|
||||||
} else {
|
|
||||||
return OptionalInt.of(pop());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void performOn2(BiFunction<Integer, Integer, Integer> function) {
|
|
||||||
int val2 = pop();
|
|
||||||
int val1 = pop();
|
|
||||||
push(function.apply(val1, val2));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkResize() {
|
|
||||||
if (pointer == values.length - 1) {
|
|
||||||
int[] newValues = new int[values.length * 2];
|
|
||||||
System.arraycopy(values, 0, newValues, 0, values.length);
|
|
||||||
this.values = newValues;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void swap() {
|
|
||||||
int val1 = pop();
|
|
||||||
int val2 = pop();
|
|
||||||
push(val1);
|
|
||||||
push(val2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,295 +0,0 @@
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class Interpreter {
|
|
||||||
|
|
||||||
private final Map<String, Runnable> KEYWORDS = Map.of(
|
|
||||||
"out", this::out,
|
|
||||||
"nout", this::nout,
|
|
||||||
"in", this::in,
|
|
||||||
"goto", this::condGoto,
|
|
||||||
"not", this::not,
|
|
||||||
"swap", this::swap
|
|
||||||
);
|
|
||||||
|
|
||||||
private IntStack stack;
|
|
||||||
private HashMap<String, Integer> variables;
|
|
||||||
private HashMap<String, Integer> labels;
|
|
||||||
private char[] program;
|
|
||||||
private int i;
|
|
||||||
private int lineNumber;
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
if (args.length < 2) {
|
|
||||||
System.err.println("usage: <filename>");
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
String s = Files.readString(Path.of(args[1]));
|
|
||||||
Interpreter interpreter = new Interpreter();
|
|
||||||
int exit = interpreter.run(s.toCharArray());
|
|
||||||
System.exit(exit);
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.err.println("File not found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int run(char[] chars) {
|
|
||||||
program = chars;
|
|
||||||
stack = new IntStack();
|
|
||||||
variables = new HashMap<>();
|
|
||||||
labels = new HashMap<>();
|
|
||||||
i = 0;
|
|
||||||
lineNumber = 1;
|
|
||||||
|
|
||||||
firstPass();
|
|
||||||
i = 0;
|
|
||||||
lineNumber = 1;
|
|
||||||
|
|
||||||
while (hasNext()) {
|
|
||||||
try {
|
|
||||||
runStatement();
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Exception occurred on line: " + lineNumber);
|
|
||||||
e.printStackTrace();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rest();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int rest() {
|
|
||||||
OptionalInt i = stack.tryPop();
|
|
||||||
|
|
||||||
if (i.isEmpty()) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return i.getAsInt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void firstPass() {
|
|
||||||
while (hasNext()) {
|
|
||||||
if (advance() == ':') {
|
|
||||||
label();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runStatement() {
|
|
||||||
switch (advance()) {
|
|
||||||
case '+' -> add();
|
|
||||||
case '-' -> subtract();
|
|
||||||
case '*' -> multiply();
|
|
||||||
case '/' -> divide();
|
|
||||||
case '%' -> modulo();
|
|
||||||
case '\'' -> character();
|
|
||||||
case '\n' -> lineNumber++;
|
|
||||||
case ' ', '\t', '\r' -> {
|
|
||||||
}
|
|
||||||
case '#' -> comment();
|
|
||||||
case '&' -> store();
|
|
||||||
case '@' -> load();
|
|
||||||
case ':' -> ignoreLabel();
|
|
||||||
default -> {
|
|
||||||
if (Character.isDigit(current())) {
|
|
||||||
number();
|
|
||||||
} else {
|
|
||||||
keyword();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void add() {
|
|
||||||
stack.performOn2(Integer::sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void subtract() {
|
|
||||||
stack.performOn2((i1, i2) -> i1 - i2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void multiply() {
|
|
||||||
stack.performOn2((i1, i2) -> i1 * i2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void divide() {
|
|
||||||
stack.performOn2((i1, i2) -> i1 / i2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void modulo() {
|
|
||||||
stack.performOn2((i1, i2) -> i1 % i2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void character() {
|
|
||||||
int value = advance();
|
|
||||||
if (value == '\\') {
|
|
||||||
char escaped = advance();
|
|
||||||
value = switch (escaped) {
|
|
||||||
case 'n' -> '\n';
|
|
||||||
case 'r' -> '\r';
|
|
||||||
case '\\' -> '\\';
|
|
||||||
case '0' -> '\0';
|
|
||||||
case '\'' -> '\'';
|
|
||||||
case 'b' -> '\b';
|
|
||||||
case 'f' -> '\f';
|
|
||||||
default -> {
|
|
||||||
System.err.println("Invalid escape sequence: \\" + escaped + " on line " + lineNumber);
|
|
||||||
System.exit(1);
|
|
||||||
throw new IllegalStateException("system exit failed");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
stack.push(value);
|
|
||||||
consume();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void comment() {
|
|
||||||
while (true) {
|
|
||||||
char next = advance();
|
|
||||||
if (next == '\n') {
|
|
||||||
lineNumber++;
|
|
||||||
break;
|
|
||||||
} else if (next == '#') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void store() {
|
|
||||||
whitespace();
|
|
||||||
String name = ident();
|
|
||||||
variables.put(name, stack.pop());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void load() {
|
|
||||||
whitespace();
|
|
||||||
String name = ident();
|
|
||||||
stack.push(variables.get(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void label() {
|
|
||||||
whitespace();
|
|
||||||
String name = ident();
|
|
||||||
labels.put(name, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// consume but don't use
|
|
||||||
private void ignoreLabel() {
|
|
||||||
whitespace();
|
|
||||||
ident();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void number() {
|
|
||||||
String number = String.valueOf(current());
|
|
||||||
while (Character.isDigit(peek())) {
|
|
||||||
number += advance();
|
|
||||||
}
|
|
||||||
stack.push(Integer.parseInt(number));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String ident() {
|
|
||||||
String word = String.valueOf(current());
|
|
||||||
|
|
||||||
while (Character.isAlphabetic(peek()) || Character.isDigit(peek()) || peek() == '_') {
|
|
||||||
word += advance();
|
|
||||||
}
|
|
||||||
|
|
||||||
return word;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void keyword() {
|
|
||||||
String word = ident();
|
|
||||||
|
|
||||||
Runnable r = KEYWORDS.get(word);
|
|
||||||
|
|
||||||
if (r == null) {
|
|
||||||
throw new RuntimeException("Invalid keyword: " + word);
|
|
||||||
}
|
|
||||||
r.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void whitespace() {
|
|
||||||
while (Character.isWhitespace(advance())) {
|
|
||||||
if (current() == '\n') {
|
|
||||||
lineNumber++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// keywords
|
|
||||||
private void out() {
|
|
||||||
System.out.print((char) stack.pop());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void nout() {
|
|
||||||
System.out.print(stack.pop());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void in() {
|
|
||||||
try {
|
|
||||||
stack.push(System.in.read());
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.err.println("Error reading input on line number " + lineNumber);
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void condGoto() {
|
|
||||||
whitespace();
|
|
||||||
String label = ident();
|
|
||||||
consume();
|
|
||||||
if (stack.peek() != 0) {
|
|
||||||
Integer index = labels.get(label);
|
|
||||||
if (index == null) {
|
|
||||||
System.err.println("Label :" + label + " not found on line number: " + lineNumber);
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
i = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void not() {
|
|
||||||
int value = stack.pop();
|
|
||||||
if (value == 0) {
|
|
||||||
stack.push(1);
|
|
||||||
} else {
|
|
||||||
stack.push(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void swap() {
|
|
||||||
stack.swap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// parsing
|
|
||||||
private char current() {
|
|
||||||
return program[i - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
private char advance() {
|
|
||||||
if (i == program.length) {
|
|
||||||
return '\0';
|
|
||||||
}
|
|
||||||
return program[i++];
|
|
||||||
}
|
|
||||||
|
|
||||||
private char peek() {
|
|
||||||
if (i == program.length) {
|
|
||||||
return '\0';
|
|
||||||
}
|
|
||||||
return program[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
private void consume() {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasNext() {
|
|
||||||
return i < program.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class IntStackTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void pushPop() {
|
|
||||||
IntStack s = new IntStack();
|
|
||||||
|
|
||||||
s.push(100);
|
|
||||||
s.push(50);
|
|
||||||
|
|
||||||
assertEquals(50, s.pop());
|
|
||||||
assertEquals(100, s.pop());
|
|
||||||
|
|
||||||
assertThrows(IndexOutOfBoundsException.class, s::pop);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void applyFunction() {
|
|
||||||
IntStack s = new IntStack();
|
|
||||||
s.push(10);
|
|
||||||
s.push(2);
|
|
||||||
s.performOn2((i1, i2) -> i1 / i2);
|
|
||||||
assertEquals(5, s.pop());
|
|
||||||
assertThrows(IndexOutOfBoundsException.class, s::pop);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void resize() {
|
|
||||||
IntStack s = new IntStack();
|
|
||||||
for (int i = 0; i < 1000; i++) {
|
|
||||||
s.push(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 999; i >= 0; i--) {
|
|
||||||
assertEquals(i, s.pop());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,178 +0,0 @@
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
|
|
||||||
class InterpreterTest {
|
|
||||||
|
|
||||||
static Interpreter interpreter;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void setup() {
|
|
||||||
interpreter = new Interpreter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void arithmeticOperations() {
|
|
||||||
String program = "1 1 * 2 +";
|
|
||||||
int result = 3;
|
|
||||||
assertEquals(result, interpreter.run(program.toCharArray()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void bigNumbers() {
|
|
||||||
String program = "1000 1234 +";
|
|
||||||
int result = 2234;
|
|
||||||
|
|
||||||
assertEquals(result, interpreter.run(program.toCharArray()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void comment() {
|
|
||||||
String program = "1 # sdkfjsaf se9 83 252h43ui\n 2 # test 5 # +";
|
|
||||||
int result = 3;
|
|
||||||
|
|
||||||
assertEquals(result, interpreter.run(program.toCharArray()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void variables() {
|
|
||||||
String program = "1 &one 2 &two 3 &three 8 @two +";
|
|
||||||
int result = 10;
|
|
||||||
assertEquals(result, interpreter.run(program.toCharArray()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void labels() {
|
|
||||||
String program = "1 :first 2 0";
|
|
||||||
int result = 0;
|
|
||||||
assertEquals(result, interpreter.run(program.toCharArray()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void gotoBack() {
|
|
||||||
String program = "10 &i \n" +
|
|
||||||
":start \n" +
|
|
||||||
"@i nout '\n' out \n" +
|
|
||||||
"@i 1 - &i \n" +
|
|
||||||
"@i goto start \n" +
|
|
||||||
" 0";
|
|
||||||
int result = 0;
|
|
||||||
assertEquals(result, interpreter.run(program.toCharArray()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void gotoSkip() {
|
|
||||||
String program = "1 :first 0 goto first 1 goto skip 3754 78349758 :skip";
|
|
||||||
int result = 1;
|
|
||||||
assertEquals(result, interpreter.run(program.toCharArray()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void fizzBuzz() throws IOException, URISyntaxException {
|
|
||||||
String program = Files.readString(Path.of(getClass().getResource("fizzbuzz.grsbpl").toURI()));
|
|
||||||
int result = 0;
|
|
||||||
assertEquals(result, interpreter.run(program.toCharArray()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void stackManipulationTest() {
|
|
||||||
String program = "1 2 swap";
|
|
||||||
int result = 1;
|
|
||||||
assertEquals(result, interpreter.run(program.toCharArray()));
|
|
||||||
|
|
||||||
String program2 = "0 not";
|
|
||||||
int result2 = 1;
|
|
||||||
assertEquals(result2, interpreter.run(program2.toCharArray()));
|
|
||||||
|
|
||||||
String program3 = "1 not";
|
|
||||||
int result3 = 0;
|
|
||||||
assertEquals(result3, interpreter.run(program3.toCharArray()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void outTest() {
|
|
||||||
String program = "'\n' '!' 'd' 'l' 'r' 'o' 'w' ' ' 'o' 'l' 'l' 'e' 'h' out out out out out out out out out out out out out 0";
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
OutStream o = null;
|
|
||||||
try {
|
|
||||||
o = new OutStream();
|
|
||||||
System.setOut(o);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(result, interpreter.run(program.toCharArray()));
|
|
||||||
assertEquals("hello world!\n", o.getOut());
|
|
||||||
}
|
|
||||||
|
|
||||||
static class OutStream extends PrintStream {
|
|
||||||
private final StringBuilder builder = new StringBuilder();
|
|
||||||
|
|
||||||
public OutStream() throws FileNotFoundException {
|
|
||||||
super(new OutputStream() {
|
|
||||||
@Override
|
|
||||||
public void write(int b) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(boolean b) {
|
|
||||||
builder.append(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(char c) {
|
|
||||||
builder.append(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(int i) {
|
|
||||||
builder.append(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(long l) {
|
|
||||||
builder.append(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(float f) {
|
|
||||||
builder.append(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(double d) {
|
|
||||||
builder.append(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(char[] s) {
|
|
||||||
builder.append(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(String s) {
|
|
||||||
builder.append(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(Object obj) {
|
|
||||||
super.print(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getOut() {
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
1 &i # init loop counter
|
|
||||||
:start # set start label
|
|
||||||
@i 100 - not goto finished # if i is 100, finish
|
|
||||||
@i 15 % not goto print_fizz_buzz # fizzbuzz
|
|
||||||
@i 5 % not goto print_buzz # buzz
|
|
||||||
@i 3 % not goto print_fizz # fizz
|
|
||||||
@i nout '\n' out # normal number
|
|
||||||
:end # go back here after printing
|
|
||||||
@i 1 + &i # increment i
|
|
||||||
1 goto start # go back to the start
|
|
||||||
|
|
||||||
:print_fizz_buzz
|
|
||||||
'F' out 'i' out 'z' out 'z' out 'B' out 'u' out 'z' out 'z' out '\n' out goto end
|
|
||||||
:print_fizz
|
|
||||||
'F' out 'i' out 'z' out 'z' out '\n' out goto end
|
|
||||||
:print_buzz
|
|
||||||
'B' out 'u' out 'z' out 'z' out '\n' out goto end
|
|
||||||
|
|
||||||
:finished 0
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue