Created
April 27, 2015 18:30
-
-
Save Cxarli/55cd6fe60fe92bd837a1 to your computer and use it in GitHub Desktop.
Java ><> compiler
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package me.comexpert.fish; | |
import java.io.File; | |
import java.io.FileInputStream; | |
import java.io.FileNotFoundException; | |
import java.io.FileOutputStream; | |
import java.io.IOException; | |
import java.util.Random; | |
/** | |
* This is the main class from the Fish.java interpreter, | |
* created by Charlie Bouthoorn. For fun. | |
* | |
* It handles the main function, the arguments, | |
* the parsing and the control of the stack. | |
* | |
* Have fun with it ! | |
* | |
* This class and all function in it use the Creative Commons BY-SA 4.0 license below. | |
* | |
* You are free to: | |
* Share — copy and redistribute the material in any medium or format | |
* Adapt — remix, transform, and build upon the material for any purpose, even commercially. | |
* | |
* Gotten from http://creativecommons.org/licenses/by-sa/4.0/ | |
* | |
* @author Charlie Bouthoorn | |
*/ | |
public class Fish { | |
// All variables | |
int sid = 0; // The current StackID | |
int wait = 0000; // Time to wait between commands in milliseconds | |
int stacksize = 64*1024; // The size of the stack | |
char quote = 0; // The current quote, ' or " | |
int[] pos = {-1, 0}; // The current position | |
double register = 0.0; // The value in the register | |
boolean reg = false; // Boolean whether the register is set or not | |
boolean run = true; // Run while this is true | |
boolean skip = false; // When a command has to be skipped | |
boolean tick = false; // When I have to tick even on NOP | |
boolean debug = false; // Debugging mode | |
boolean string = false; // When string is turned on | |
boolean credit = false; // Show some credit after running the program | |
Stack[] stacks = new Stack[1024]; // The array of stacks | |
char[][] grid = null; // The grid from the file | |
Directions dir = Directions.RIGHT; // The current direction | |
StringBuilder sb = new StringBuilder(); // The StringBuilder for the string | |
/** | |
* Main constructor | |
* | |
* @param args The arguments from main | |
*/ | |
public Fish(String[] args) { | |
// Variables | |
String filename = args.length>=1 ? args[0] : null; | |
String[] data = null; | |
stacks[sid] = new Stack(stacksize, this); | |
boolean isFile = true; | |
for(int i=0; i<args.length; i++) { | |
switch(args[i]) { | |
case "-h": | |
showHelp(); | |
return; | |
case "-c": | |
isFile=false; | |
data=args[++i].split("\n"); | |
break; | |
case "-s": | |
case "--string": | |
String s=args[++i]; | |
for(char c:s.toCharArray()) { | |
stacks[sid].push(c); | |
} | |
break; | |
case "-v": | |
case "--value": | |
i++; | |
while(i<args.length && isNumber(args[i])) { | |
stacks[sid].push(Double.parseDouble(args[i])); | |
i++; | |
} | |
i--; | |
break; | |
case "-t": | |
case "--time": | |
if(isNumber(args[++i])) { | |
// Wait has to be integer | |
wait=(int)Math.round( Double.parseDouble(args[i]) * 1000 ); | |
} | |
break; | |
case "-a": | |
case "--always-tick": | |
tick=true; | |
break; | |
case "--credit": | |
credit=true; | |
break; | |
case "-X": | |
debug=true; | |
break; | |
default: | |
System.out.println("Ignore argument "+args[i]+" !"); | |
break; | |
} | |
} | |
if(isFile) { | |
// If the filename isn't given, you need help | |
if(filename == null) { | |
showHelp(); | |
System.exit(-1); | |
return; | |
} | |
FileIO fio = new FileIO(filename); | |
try { | |
data=fio.readData(); | |
} | |
catch (NullPointerException | FileNotFoundException e) { | |
System.out.println("File \""+filename+"\" not found in currrent directory!"); | |
e.printStackTrace(System.out); | |
return; | |
} catch (IOException ioe) { | |
System.out.println("An unexpected error occured:"); | |
ioe.printStackTrace(System.out); | |
return; | |
} | |
} | |
if(data==null) { | |
System.out.println("No code provided!"); | |
System.exit(-1); | |
return; | |
} | |
if(debug) { | |
for(String s:data) { | |
System.out.println(s); | |
} | |
} | |
// Creating grid | |
grid=new char[data.length][]; | |
// Convert String[] to char[][] | |
int i=0; | |
for(String line:data) { | |
grid[i++]=line.toCharArray(); | |
} | |
// Get maximum x | |
int top=grid[0].length; | |
for(char[] line:grid) { | |
if(line.length > top) { | |
top=line.length; | |
} | |
} | |
// Make sure every line is 'top' long | |
for(int a=0; a<grid.length; a++) { | |
char[] line=grid[a]; | |
char[] newline=new char[top]; | |
int b=0; | |
for(char c:line) { | |
newline[b++]=c; | |
} | |
grid[a]=newline; | |
} | |
// Grid is prepared from here on | |
try { | |
while(run) { | |
// Move the position according to the direction | |
pos=dir.move(pos); | |
// Check for out-of-bounds | |
check(); | |
if(debug) | |
stacks[sid].print(); | |
if(skip) { | |
skip=false; | |
continue; | |
} | |
// Parse current character | |
parse(); | |
// Check for NOP, and whether to wait or not | |
char c=grid[pos[1]][pos[0]]; | |
boolean isNOP=(c==' ' || c==0); | |
if(tick || !isNOP) { | |
sleep(wait); | |
} | |
} | |
// Done running | |
} catch(IOException ioe) { | |
System.out.println("An unexpected error happened: "); | |
ioe.printStackTrace(System.out); | |
return; | |
} | |
// After all code is done, optionally do some more things, then exit | |
if(credit) | |
System.out.print("\nThis code was run with Charlie's Fish.java compiler :)\n"); | |
System.exit(0); | |
return; | |
} | |
/** | |
* Show the help mimiced from the official fish.py interpreter | |
*/ | |
public void showHelp() { | |
System.out.println("usage: java Fish [-h] (<script file> | -c <code>) [<options>]"); | |
System.out.println(""); | |
System.out.println(" Execute a ><> script."); | |
System.out.println(""); | |
System.out.println(" Executing a script is as easy as:"); | |
System.out.println(" java Fish <script file>"); | |
System.out.println(""); | |
System.out.println(" You can also execute code directly using the -c/--code flag:"); | |
System.out.println(" java Fish -c '1n23nn;'"); | |
System.out.println(" > 132"); | |
System.out.println(""); | |
System.out.println(" The -v and -s flags can be used to prepopulate the stack:"); | |
System.out.println(" java Fish echo.fish -s \"hello, world\" -v 32 49 50 51 -s \"456\""); | |
System.out.println(" > hello, world 123456"); | |
System.out.println(""); | |
System.out.println("optional arguments:"); | |
System.out.println(" -h, --help show this help message and exit"); | |
System.out.println(""); | |
System.out.println("code:"); | |
System.out.println(" script .fish file to execute"); | |
System.out.println(" -c <code>, --code <code>"); | |
System.out.println(" string of instructions to execute"); | |
System.out.println(""); | |
System.out.println("options:"); | |
System.out.println(" -s <string>, --string <string>"); | |
System.out.println(" -v <number> [<number> ...], --value <number> [<number> ...]"); | |
System.out.println(" push numbers or strings onto the stack before"); | |
System.out.println(" execution starts"); | |
System.out.println(" -t <seconds>, --tick <seconds>"); | |
System.out.println(" define a tick time, or a delay between the execution"); | |
System.out.println(" of each instruction"); | |
System.out.println(" -a, --always-tick make every instruction cause a tick (delay), even"); | |
System.out.println(" whitespace and skipped instructions"); | |
} | |
/** | |
* Sleep <code>time</code> milliseconds | |
* @param time | |
*/ | |
public void sleep(int time) { | |
try { | |
Thread.sleep(time); | |
} catch(Exception e) { /**/ } | |
} | |
/** | |
* Check for out-of-bounds positioning, then loop back | |
*/ | |
public void check() { | |
int y=pos[1], maxy=grid.length-1; | |
if(y<0) { | |
y=maxy; | |
} | |
if(y>maxy) { | |
y=0; | |
} | |
int x=pos[0], maxx=grid[y].length-1; | |
if(x<0) { | |
x=maxx; | |
} | |
if(x>maxx) { | |
x=0; | |
} | |
pos=new int[]{x, y}; | |
} | |
/** | |
* Parses the current character | |
* Note: I'm <code>return</code>'ing for fun! I know it's useless because I'm using <code>else if</code> | |
* | |
* @throws IOException When System.in failed to read() | |
*/ | |
public void parse() throws IOException { | |
char c=grid[pos[1]][pos[0]]; | |
if(debug) | |
System.out.println(c); | |
// ^C | |
if(c==3) { | |
System.out.println("^C"); | |
System.exit(0); | |
return; | |
} | |
// String | |
else if(c=='"' || c=='\'') { | |
if(string) { | |
if(quote==c) { | |
// Get the built string | |
String s=sb.toString(); | |
// Reset the builder | |
sb=new StringBuilder(); | |
if(debug) | |
System.out.println("STRING "+quote+s+quote+""); | |
// Push every character from the string to the current stack | |
for(char x:s.toCharArray()) { | |
stacks[sid].push(x); | |
} | |
// Reset values | |
string=false; | |
quote=0; | |
} else { | |
// Append quote to the builder | |
sb.append(c); | |
} | |
} else { | |
// Enable string'ing | |
string=true; | |
quote=c; | |
} | |
return; | |
} | |
// Append character to the builder when string'ing is turned on | |
else if(string) { | |
sb.append(c); | |
return; | |
} | |
// NOP | |
else if(c==0 || c==' ') { | |
return; | |
} | |
// Movement | |
else if(c=='>' || c=='<' || c=='^' || c=='v' || c=='/' || c=='\\' || c=='|' || c=='_' || c=='x' || c=='#') { | |
// Let Directions handle this | |
dir=dir.get(c); | |
return; | |
} | |
// Trampoline | |
else if(c=='!') { | |
if(debug) | |
System.out.println("SKIP"); | |
skip=true; | |
} | |
// Conditional trampoline | |
else if(c=='?') { | |
if(debug) | |
System.out.println("COND SKIP"); | |
double x=stacks[sid].pop(); | |
skip=(x==0); | |
} | |
// Jump | |
else if(c=='.') { | |
if(debug) | |
System.out.println("JUMP"); | |
double y=stacks[sid].pop(); | |
double x=stacks[sid].pop(); | |
if(!isInteger(y) || !isInteger(x)) { // Integer check | |
throw new NumberFormatException("X or Y is no integer!"); | |
} | |
// Safe to cast | |
pos=new int[]{(int)x, (int)y}; | |
} | |
// Numbers | |
else if((c>='0' && c<='9') || (c>='a' && c<='f')) { | |
if(debug) | |
System.out.println("NUMBER"); | |
// Parse in hexadecimal (base 16) | |
int i=Integer.parseInt(c+"", 16); | |
stacks[sid].push(i); | |
return; | |
} | |
// Expressions | |
else if(c=='+' || c=='-' || c=='*' || c==',' || c=='%' || c=='=' || c=='(' || c==')') { | |
double y=stacks[sid].pop(); | |
double x=stacks[sid].pop(); | |
double z=0; | |
if(debug) | |
System.out.println("EXPR "+c); | |
// Calculate | |
switch(c) { | |
case '+': | |
z=x+y; | |
break; | |
case '-': | |
z=x-y; | |
break; | |
case '*': | |
z=x*y; | |
break; | |
case ',': | |
z=x/y; | |
break; | |
case '%': | |
z=x%y; | |
break; | |
case '=': | |
z=(x==y)?1:0; | |
break; | |
case '(': | |
z=(y>x)?1:0; | |
break; | |
case ')': | |
z=(y<x)?1:0; | |
break; | |
default: | |
break; | |
} | |
stacks[sid].push(z); | |
} | |
// Stack manipulation | |
else if(c==':' || c=='~' || c=='$' || c=='@' || c=='}' || c=='{' || c=='r' || c=='l') { | |
// Let the stack handle this | |
stacks[sid].action(c); | |
} | |
// New stacks | |
else if(c=='[' || c==']') { | |
if(c=='[') { | |
if(debug) System.out.println("NEW STACK"); | |
double x=stacks[sid].pop(); | |
if(Math.floor(x)!=Math.ceil(x)) { | |
throw new NumberFormatException("X is no Integer!"); | |
} | |
sid++; | |
stacks[sid]=new Stack(stacksize, this); | |
for(int a=0; a<x; a++) { | |
double item=stacks[sid-1].pop(); | |
stacks[sid].push(item); | |
} | |
stacks[sid].reverse(); | |
} | |
else if(c==']') { | |
sid--; | |
stacks[sid]=merge(stacks[sid], stacks[sid+1]); | |
} | |
else { | |
// This is impossible | |
} | |
} | |
// Numberical output | |
else if(c=='n') { | |
double i=stacks[sid].pop(); | |
// I hate 10.0 as output, while it should be 10 | |
if(isInteger(i)) { | |
System.out.print((int)i); | |
} | |
// But 5.5 should stay 5.5 | |
else if(i>=Double.MIN_VALUE && i<=Double.MAX_VALUE) { | |
System.out.print(i); | |
} | |
// And Infinity is ugly | |
else { | |
// I know no better Exception than NPE here :/ | |
throw new NullPointerException("Number too large"); | |
} | |
return; | |
} | |
// Characterictic output | |
else if(c=='o') { | |
double i=stacks[sid].pop(); | |
System.out.print((char)i); | |
return; | |
} | |
// Input | |
else if(c=='i') { | |
int i=System.in.read(); | |
stacks[sid].push(i); // No need to cast to (char) here | |
return; | |
} | |
// Register | |
else if(c=='&') { | |
if(debug) | |
System.out.println("REGISTER"); | |
if(reg) stacks[sid].push(register); | |
else register=stacks[sid].pop(); | |
reg=!reg; | |
return; | |
} | |
// Get character | |
else if(c=='g') { | |
if(debug) | |
System.out.println("GET"); | |
double y=stacks[sid].pop(); | |
double x=stacks[sid].pop(); | |
if(!isInteger(y) || !isInteger(x)) { | |
throw new NumberFormatException("X or Y is no integer!"); | |
} | |
char v=grid[ (int)y ][ (int)x ]; | |
stacks[sid].push(v); | |
return; | |
} | |
// Change character | |
else if(c=='p') { | |
if(debug) | |
System.out.println("PLACE"); | |
double y=stacks[sid].pop(); | |
double x=stacks[sid].pop(); | |
double v=stacks[sid].pop(); | |
if(!isInteger(y) || !isInteger(x) || !isInteger(v)) { | |
throw new NumberFormatException("X, Y or V is no integer!"); | |
} | |
grid[ (int)x ][ (int)y ]=(char) v; | |
return; | |
} | |
// Terminater | |
else if (c==';') { | |
run=false; | |
return; | |
} | |
// Unknown | |
else { | |
System.out.println("What the f*** should '"+c+"' do ?!"); | |
return; | |
} | |
} | |
/** | |
* Check whether the given String is a number | |
* by trying to parse it by Integer.parseInt() and Double.parseDouble() | |
* If one of them fails, return false, else true | |
* | |
* @param s The string to check | |
* @return Whether the string is a valid Integer or Double | |
*/ | |
public boolean isNumber(String s) { | |
try { | |
Integer.parseInt(s); | |
return true; | |
} catch(Exception e) { /**/ } | |
try { | |
Double.parseDouble(s); | |
return true; | |
} catch(Exception e) { /**/ } | |
return false; | |
} | |
/** | |
* Check if the given Double is a valid Integer | |
* by comparing <code>Math.floor()</code> and <code>Math.ceil()</code> | |
* and by checking if it's higher than or equal to <code>Integer.MIN_VALUE</code> | |
* and higher than or equal to <code>Integer.MAX_VALUE</code> | |
* | |
* @param x The double to check | |
* @return True if the given double passes the tests, false otherwise | |
*/ | |
public boolean isInteger(double x) { | |
return Math.floor(x)==Math.ceil(x) && x>Integer.MIN_VALUE && x<Integer.MAX_VALUE; | |
} | |
/** | |
* Merge two stacks to create a new stack. | |
* The second stack (<code>y</code>) is placed AFTER the first stack (<code>x</code>) | |
* Note: Both the input stacks are being cleared ! | |
* | |
* @param x The first stack | |
* @param y The second stack | |
* @return The merged stack | |
*/ | |
public Stack merge(Stack x, Stack y) { | |
if(debug) { | |
System.out.println("MERGE"); | |
System.out.println("~ X ~"); | |
x.forcePrint(); | |
System.out.println("~ Y ~"); | |
y.forcePrint(); | |
} | |
Stack z=new Stack(stacksize, this); | |
while(y.length()>0) { | |
z.push(y.pop()); | |
} | |
while(x.length()>0) { | |
z.push(x.pop()); | |
} | |
z.reverse(); | |
if(debug) { | |
System.out.println("~ Z ~"); | |
z.forcePrint(); | |
} | |
return z; | |
} | |
/** | |
* The required-by-Java method main() | |
* <code>public static Fish fish</code> is for FUN! | |
*/ | |
public static Fish fish; | |
public static void main(String[] args) { | |
fish=new Fish(args); | |
} | |
} | |
/** | |
* The enum for all possible directions in the 2D world; RIGHT, LEFT, DOWN and UP | |
* | |
* This class and all function in it use the Creative Commons BY-SA 4.0 license below. | |
* | |
* You are free to: | |
* Share — copy and redistribute the material in any medium or format | |
* Adapt — remix, transform, and build upon the material for any purpose, even commercially. | |
* | |
* Gotten from http://creativecommons.org/licenses/by-sa/4.0/ | |
* | |
* But seriously, who uses this class ? :P | |
* | |
* @author Charlie Bouthoorn | |
* | |
*/ | |
enum Directions { | |
RIGHT, LEFT, DOWN, UP; | |
/** | |
* Move the given position according to the current direction | |
* | |
* @param pos The old position | |
* @return The new position | |
*/ | |
public int[] move(int[] pos) { | |
switch(this) { | |
case RIGHT: | |
pos[0]+=1; | |
break; | |
case LEFT: | |
pos[0]-=1; | |
break; | |
case DOWN: | |
pos[1]+=1; | |
break; | |
case UP: | |
pos[1]-=1; | |
break; | |
default: | |
break; | |
} | |
return pos; | |
} | |
/** | |
* Get the new direction corresponding to the given character and the current direction | |
* | |
* @param c | |
* @return | |
*/ | |
public Directions get(char c) { | |
switch(c) { | |
case '>': | |
return RIGHT; | |
case '<': | |
return LEFT; | |
case '^': | |
return UP; | |
case 'v': | |
return DOWN; | |
case '/': | |
switch(this) { | |
case RIGHT: | |
return UP; | |
case LEFT: | |
return DOWN; | |
case DOWN: | |
return LEFT; | |
case UP: | |
return RIGHT; | |
default: | |
return null; | |
} | |
case '\\': | |
switch(this) { | |
case RIGHT: | |
return DOWN; | |
case LEFT: | |
return UP; | |
case DOWN: | |
return RIGHT; | |
case UP: | |
return LEFT; | |
default: | |
return null; | |
} | |
case '|': | |
if(this==UP || this==DOWN) return this; | |
else if(this==RIGHT) return LEFT; | |
else if(this==LEFT) return RIGHT; | |
else return null; | |
case '_': | |
if(this==RIGHT || this==LEFT) return this; | |
else if(this==UP) return DOWN; | |
else if(this==DOWN) return UP; | |
else return null; | |
case 'x': | |
int min=1, max=4; | |
int ran=new Random().nextInt(max-min)+min; | |
switch(ran) { | |
case 1: | |
return RIGHT; | |
case 2: | |
return LEFT; | |
case 3: | |
return UP; | |
case 4: | |
return DOWN; | |
default: | |
// Impossible | |
System.err.println("Something impossible happened in the Random function :S"); | |
return null; | |
} | |
case '#': | |
if(this==RIGHT) return LEFT; | |
else if(this==LEFT) return RIGHT; | |
else if(this==UP) return DOWN; | |
else if(this==DOWN) return UP; | |
else return null; | |
default: | |
return null; | |
} | |
} | |
} | |
/** | |
* A stack used by Fish | |
* | |
* This class and all function in it use the Creative Commons BY-SA 4.0 license below, | |
* except the <code>reverse()</code> function, that license is given in his JavaDoc | |
* | |
* You are free to: | |
* Share — copy and redistribute the material in any medium or format | |
* Adapt — remix, transform, and build upon the material for any purpose, even commercially. | |
* | |
* Gotten from http://creativecommons.org/licenses/by-sa/4.0/ | |
* | |
* @author Charlie Bouthoorn | |
*/ | |
class Stack { | |
Fish fish; | |
double[] stack; // The current stack | |
double[] temp=null; // A temporary stack | |
int i=-1; // The current index | |
/** | |
* Main constructor | |
* | |
* @param size The stacksize | |
* @param fish The fish it belongs to (Fish are mighty kings <code>:D</code>!) | |
*/ | |
public Stack(int size, Fish fish) { | |
this.stack=new double[size]; | |
this.fish=fish; | |
} | |
/** | |
* Push a new value to the stack | |
* | |
* @param x The value to push | |
*/ | |
public void push(double x) { | |
if(fish.debug) | |
System.out.println("PUSH "+x+" TO "+(i+1)); | |
if(i>=stack.length-1) { | |
throw new ArrayIndexOutOfBoundsException("Can't push to full stack!"); | |
} | |
stack[++i]=x; | |
} | |
/** | |
* Pop a value from the stack | |
* | |
* @return The top value | |
*/ | |
public double pop() { | |
if(fish.debug) | |
System.out.println("POP "+stack[i]+" FROM "+i); | |
if(i<=-1) { | |
throw new ArrayIndexOutOfBoundsException("Can't pop from empty stack!"); | |
} | |
double x=stack[i]; | |
stack[i]=0; | |
i--; | |
return x; | |
} | |
/** | |
* Reverse the stack | |
* | |
* This piece of code uses the Creative Commons BY-SA 2.5 license, | |
* which can be found here: http://creativecommons.org/licenses/by-sa/2.5/ | |
* | |
* Gotten from "Bill the Lizard" (<code>http://stackoverflow.com/users/1288/bill-the-lizard</code>) | |
* at StackOverflow at the following link: <code>http://stackoverflow.com/a/3523066/4141219</code> | |
*/ | |
public void reverse() { | |
for (int left = 0, right = i; left < right; left++, right--) { | |
double x = stack[left]; | |
stack[left] = stack[right]; | |
stack[right] = x; | |
} | |
} | |
/** | |
* Print the values of the stack, if it didn't change since the previous print | |
* | |
* @see #forcePrint() | |
*/ | |
public void print() { | |
if(!eq(temp,stack)) { | |
//System.out.println("LENGTH: "+length()+"; i: "+i); | |
if(length()==-1) { | |
System.out.println("-- EMPTY --"); | |
return; | |
} | |
System.out.println("-----"); | |
int x=0; | |
for(double d:stack) { | |
if (x>=length()) | |
break; | |
System.out.println(x+" "+d); | |
x++; | |
} | |
System.out.println("-----"); | |
temp=stack.clone(); | |
} | |
} | |
/** | |
* Print the values of the stack, no matter what | |
*/ | |
public void forcePrint() { | |
temp=null; | |
print(); | |
} | |
/** | |
* Check if the given arrays are having the same values | |
* | |
* <code>x.equals(y)</code> doesn't work for me, don't know why ;-; | |
* | |
* @param x The first array | |
* @param y The second array | |
* @return Whether the arrays have the same values | |
*/ | |
public boolean eq(double[] x, double[] y) { | |
if(x==null || y==null ) return x==null && y==null; | |
if(x.length != y.length) return false; | |
for(int a=0; a<x.length; a++) { | |
if(x[a] != y[a]) | |
return false; | |
} | |
return true; | |
} | |
/** | |
* @return The length of the stack (The current index + 1) | |
*/ | |
public int length() { | |
return i+1; | |
} | |
/** | |
* Do the action given by c | |
* | |
* @param c The action, one of <code>:!$@}{rl</code> (No, I'm not swearing :|) | |
*/ | |
public void action(char c) { | |
double x,y,z; | |
switch(c) { | |
case ':': | |
if(fish.debug) | |
System.out.println("COPY"); | |
x=pop(); | |
push(x); | |
push(x); | |
break; | |
case '~': | |
if(fish.debug) | |
System.out.println("REMOVE"); | |
x=pop(); | |
break; | |
case '$': | |
if(fish.debug) | |
System.out.println("SWAP 2"); | |
x=pop(); | |
y=pop(); | |
push(x); | |
push(y); | |
break; | |
case '@': | |
if(fish.debug) | |
System.out.println("SWAP 3"); | |
x=pop(); | |
y=pop(); | |
z=pop(); | |
push(x); | |
push(z); | |
push(y); | |
break; | |
case '}': | |
if(fish.debug) | |
System.out.println("MOVE RIGHT"); | |
x=stack[i]; | |
for(int a=i; a>0; a--) { | |
stack[a]=stack[a-1]; | |
} | |
stack[0]=x; | |
break; | |
case '{': | |
if(fish.debug) | |
System.out.println("MOVE LEFT"); | |
x=stack[0]; | |
for(int a=0; a<i; a++) { | |
stack[a]=stack[a+1]; | |
} | |
stack[i]=x; | |
break; | |
case 'r': | |
if(fish.debug) | |
System.out.println("REVERSE"); | |
reverse(); | |
break; | |
case 'l': | |
if(fish.debug) | |
System.out.println("LENGTH"); | |
push(length()); | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
/** | |
* This class is able to read and write to files. | |
* | |
* This class and all function in it use the Creative Commons BY-SA 4.0 license below. | |
* | |
* You are free to: | |
* Share — copy and redistribute the material in any medium or format | |
* Adapt — remix, transform, and build upon the material for any purpose, even commercially. | |
* | |
* Gotten from http://creativecommons.org/licenses/by-sa/4.0/ | |
* | |
* @author Charlie Bouthoorn | |
*/ | |
class FileIO { | |
File file; | |
int bufSize=64*1024; | |
char newLine='\n'; | |
/** | |
* Construct a FileIO for the file with filename <code>filename</code> | |
* | |
* @param filename The filename for the file to use | |
*/ | |
public FileIO(String filename) { | |
this(new File(filename)); | |
} | |
/** | |
* Construct a FileIO for the file <code>file</code> | |
* | |
* @param file The file to use | |
*/ | |
public FileIO(File file) { | |
this.file=file; | |
} | |
/** | |
* Get all data from <code>file</code> in an array, splitted at a new-line character (<code>\n</code>) | |
* | |
* @see #writeData(String[]) | |
* @return An array of all the lines in <code>file</code> | |
* @throws NullPointerException When <code>file</code> does not exist | |
* @throws FileNotFoundException When <code>file</code> is not found | |
* @throws IOException When I failed to read <code>file</code> | |
* | |
*/ | |
public String[] readData() throws NullPointerException, FileNotFoundException, IOException { | |
synchronized(this.file) { | |
if(!this.file.exists()) { | |
throw new NullPointerException("File \""+this.file+"\" does not exist!"); | |
} | |
if(!this.file.canRead()) { | |
throw new IOException("File \""+this.file+"\" can't be read!"); | |
} | |
try(FileInputStream fis=new FileInputStream(this.file)) { | |
byte[] buf=new byte[this.bufSize]; | |
int bytes=fis.read(buf); | |
String data=bytes<0 ? "":new String(buf, 0, bytes); | |
return data.split(this.newLine+""); | |
} | |
} | |
} | |
/** | |
* Write the data from <code>data</code> to file <code>file</code> | |
* | |
* @see #getData() | |
* @param data The data to save | |
* @throws FileNotFoundException When <code>file</code> is not found | |
* @throws IOException When I failed to write to <code>file</code> | |
*/ | |
public void writeData(String[] data) throws FileNotFoundException, IOException { | |
synchronized(this.file) { | |
StringBuilder sb=new StringBuilder(); | |
for(String line:data) sb.append(line+'\n'); | |
try(FileOutputStream fos=new FileOutputStream(this.file)) { | |
byte[] buf=sb.toString().getBytes(); | |
fos.write(buf); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment