mirror of
https://github.com/Noratrieb/GRSBPL.git
synced 2026-03-15 15:06:03 +01:00
Add files via upload
This commit is contained in:
parent
99b5e92e1c
commit
8a4ccb8b2b
7 changed files with 718 additions and 0 deletions
63
StackBasedLanguage/src/main/java/IntStack.java
Normal file
63
StackBasedLanguage/src/main/java/IntStack.java
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
295
StackBasedLanguage/src/main/java/Interpreter.java
Normal file
295
StackBasedLanguage/src/main/java/Interpreter.java
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
41
StackBasedLanguage/src/test/java/IntStackTest.java
Normal file
41
StackBasedLanguage/src/test/java/IntStackTest.java
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
178
StackBasedLanguage/src/test/java/InterpreterTest.java
Normal file
178
StackBasedLanguage/src/test/java/InterpreterTest.java
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
19
StackBasedLanguage/src/test/resources/fizzbuzz.grsbpl
Normal file
19
StackBasedLanguage/src/test/resources/fizzbuzz.grsbpl
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
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