Add files via upload

This commit is contained in:
nora 2021-05-27 16:59:47 +02:00 committed by GitHub
parent 99b5e92e1c
commit 8a4ccb8b2b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 718 additions and 0 deletions

View 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);
}
}

View 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;
}
}

View 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());
}
}
}

View 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();
}
}
}

View 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