-
My brainfuck (Java)
So this term I am taking a Java course, and as a simple exercise I implemented my own version of >>>Brainfuck<<<
It can take a brainfuck string as a commandline parameter and interpret and execute it immediately, and it can take file input as well. Additionally it can compile brainfuck into a binary file to be executed without having to be precompiled.
An array of 30,000 short integers [chars] to play around with, currently.
Here're the language specs:
Code:
Commands --
+ -- Increment the current memory cell
- -- Decrement the current memory cell
< -- Decrement the memory position
> -- Increment the memory position
[ -- If the current memory cell is 0, skip to the next matching ']'
] -- If the current memory cell is non zero, skip to the previous matching '['
. -- Print the value of the current memory cell as an integer to standard output
, -- Print the value of the current memory cell as a single character to standard output
^ -- Accept one 16 bit integer from standard input and assign the current memory cell that value
$ -- Accept one character from standard input and assign the current memory cell that value
c -- For simplicity's sake, this takes the following character from a source file and places its value into the current memory cell
w -- This takes the characters proceeding it as a single 16 bit integer and places it into the current memory cell
Comments --
/ -- Start and end a comment block
Macros -- I implemented a simple #macro system as well
#place <location/of/filename>
#define <MyMacro> (param1,param2) wparam1 > wparam2 [-<+>] / Adds param2 to param1 /
#MyMacro (23,12)
Here's an example program --
PHP Code:
#define <PrintInt> (x) wx.
#define <Putstring> (r,s,t,u,v,w,x,y,z) cr,cs,ct,cu,cv,cw,cx,cy,cz,
#define <AddTwo> (f,s) wf >ws [-<+>]< / This adds two numbers together by using the second parameter as a loop counter /
#AddTwo (23,57)
.w10,
#Putstring (D,e,r,p,!, , , , ) / This prints 'Derp!' lolo /
w10,
[-] / Zero out out mems for s's 'n g's /
#Putstring (P,i,c,k, ,a, ,n,u)
#Putstring (m,b,e,r,:, ,\b,\b, )
^
/ Get a number from stdin /
>
#Putstring (P,i,c,k, ,a, ,n,u)
#Putstring (m,b,e,r,:, ,\b,\b, )
^
/ Get another # from stdin /
>
#Putstring (T,h,e, ,s,u,m, ,=)
c ,
<
/ Tell the user what's coming up /
[- < + >]<.
/ Get the sum /
#AddTwo (78,12)
/ This is just proof of concept /
/ This shit down here returns 1 or 0 - the inverse boolean value of the current mem cell. i.e. !(boolean)memory[memcell] /
[
[
-
]
@
]
+
@
Call the program with --
Code:
java -jar brainfuck.jar "brainfuck code to interpret"
java -jar brainfuck.jar -f "name of file to interpret"
java -jar brainfuck.jar -c "name of file to compile" "name of binary file to output"
java -jar brainfuck.jar -e "name of binary file to execute"
java -jar brainfuck.jar -h (Shows help stuff)
and finally, the source code -
Code:
import java.io.*;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.regex.Matcher;
class BrainFKParser{
private Hashtable<String, Integer> symbol_map;
private Hashtable<String, ArrayList<String>> def_macros;
private Hashtable<String, String> exp_macros;
BrainFKParser(){
symbol_map = new Hashtable<String, Integer>();
def_macros = new Hashtable<String, ArrayList<String>>();
exp_macros = new Hashtable<String, String>();
}
public boolean AddSymbol(String name, int opcode){
if(!symbol_map.containsKey(name)){
symbol_map.put(name, opcode);
return true;
}
return false;
}
public ArrayList<Integer> ParseString(String fn){
if(!MatchBraces(fn)){
return null;
}
ArrayList<Integer> program = new ArrayList();
String temp = "";
for(int i=0;i<fn.length();++i){
temp += fn.charAt(i);
if("c".equals(temp) && (i+1<fn.length())){
if(fn.charAt(i+1) != '\\'){
//System.out.println("Found character: " + fn.charAt(i) + " " + fn.charAt(i+1));
program.add(symbol_map.get(temp) | fn.charAt(i+1));
}else if(i+2<fn.length()){
//System.out.println("Found control character: " + fn.charAt(i+1) + " " + fn.charAt(i+2));
switch(fn.charAt(i+2)){
case 'n':
program.add(symbol_map.get(temp) | '\n');
break;
case 'b':
program.add(symbol_map.get(temp) | '\b');
break;
case 'r':
program.add(symbol_map.get(temp) | '\r');
break;
case '\\':
program.add(symbol_map.get(temp) | '\\');
break;
case '0':
program.add(symbol_map.get(temp) | '\0');
break;
}
++i;
}
++i;
}
else if("w".equals(temp)){
String number = "", temp2 = "";
for(++i;i<fn.length();++i){
temp2 += fn.charAt(i);
if(!symbol_map.containsKey(temp2) && Character.isDigit(fn.charAt(i))){
number += temp2;
}
else if(symbol_map.containsKey(temp2)){
--i;
break;
}
temp2 = "";
}
if(number.length() > 0){
program.add(symbol_map.get(temp) | Short.valueOf(number));
}
}
else if(symbol_map.containsKey(temp)){
program.add(symbol_map.get(temp));
}
temp = "";
}
return program;
}
public String PreParse(String fn) throws FileNotFoundException, IOException{
String brainFKCode = "", temp = "";
for(int i=0;i<fn.length();++i){
boolean ischar = false;
temp += fn.charAt(i);
if(i > 0){
if(fn.charAt(i-1) == 'c'){
ischar = true;
}
}
if("#".equals(temp) && !ischar){
String macroname = "", filename = "", params = "", expression = "", temp2 = "";
for(++i;i<fn.length();++i){
temp2 += fn.charAt(i);
if(!Character.isWhitespace(fn.charAt(i)) && !(filename.length() > 0) && !(params.length() > 0) && !(expression.length() > 0) && !"\n".equals(temp2) && !"\r".equals(temp2) && !"<".equals(temp2) && !"(".equals(temp2)){
macroname += temp2;
}
else if("<".equals(temp2) && !(expression.length() > 0)){
for(;i<fn.length();++i){
if(fn.charAt(i) != '>' && fn.charAt(i) != '<'){
filename += fn.charAt(i);
}
else if(fn.charAt(i) == '>'){
break;
}
}
}
else if("(".equals(temp2)){
for(++i;i<fn.length();++i){
if(fn.charAt(i) != ')' && fn.charAt(i) != '('){
params += fn.charAt(i);
}
else{
break;
}
}
}
else if(!Character.isWhitespace(fn.charAt(i)) && filename.length() > 0 && params.length() > 0 && !"\n".equals(temp2) && !"\r".equals(temp2)){
expression += temp2;
}
else if("\n".equals(temp2) || "\r".equals(temp2)){
--i;
break;
}
temp2 = "";
}
if(macroname.length() > 0 ){
brainFKCode += FillMacro(macroname, filename, params, expression);
}
}
else if("/".equals(temp) && !ischar && i < fn.length()){
for(++i;i < fn.length();++i){
temp += fn.charAt(i);
if (fn.charAt(i) == '/'){
break;
}
}
//System.out.println("Found a comment: " + temp);
temp = "";
}
else{
brainFKCode += temp;
}
temp = "";
}
return brainFKCode;
}
public boolean MatchBraces(String fn){
int braces = 0;
for(int i=0;i<fn.length();++i){
switch(fn.charAt(i)){
case '[': //Beginning of brace/brace nest
{
braces = 1;
for(++i;braces != 0 && i<fn.length();++i){
switch(fn.charAt(i)){
case '[':
{
++braces;
}break;
case ']':
{
--braces;
}break;
}
}
}break;
case ']': //Out of place brace
{
System.out.println("Fatal error:\n\t\tMismatched braces");
return false;
}
}
}
return braces == 0 ? true : false;
}
private String FillMacro(String macroname, String filename, String params, String expression) throws FileNotFoundException, IOException{
String expansion = "";
//macroname = macroname.toLowerCase();
//System.out.println(params);
if("place".equals(macroname) && filename.length() > 0){
BufferedReader inputStream = new BufferedReader(new FileReader(filename));
while(inputStream.ready()){
expansion += String.valueOf((char)inputStream.read());
}
}
else if("sysplace".equals(macroname) && filename.length() > 0){
filename = "/usr/include/brainfk/" + filename;
BufferedReader inputStream = new BufferedReader(new FileReader(filename));
while(inputStream.ready()){
expansion += String.valueOf((char)inputStream.read());
}
}
else if("define".equals(macroname) && filename.length() > 0 && params.length() > 0 && expression.length() > 0){
//System.out.println("Defining a macro... " + macroname + " " + filename + " " + params + " " + expression);
String temp = "";
ArrayList<String> parameters = new ArrayList<String>();
for(int i=0;i<params.length();++i){
if(params.charAt(i) != ','){
temp += params.charAt(i);
//System.out.println(temp);
}
else{
parameters.add(temp);
temp ="";
}
}
if(temp.length() > 0){
parameters.add(temp);
}
def_macros.put(filename, parameters);
exp_macros.put(filename, PreParse(expression));
//System.out.println("Macro defined");
}
else if(def_macros.containsKey(macroname) && exp_macros.containsKey(macroname)){
//System.out.println("Found def'd macro");
String temp = "";
ArrayList<String> parameters = new ArrayList<String>();
for(int i=0;i<params.length();++i){
if(params.charAt(i) != ','){
temp += params.charAt(i);
}
else if(params.charAt(i) == ',' || (i+1) == params.length()){
parameters.add(temp);
temp ="";
}
}
if(temp.length() > 0){
parameters.add(temp);
temp = "";
}
//System.out.println(parameters.size() + " " + def_macros.get(macroname).size());
temp = exp_macros.get(macroname);
//System.out.println(temp);
if(parameters. size() == def_macros.get(macroname).size()){
for(int i=0;i<parameters.size();++i){
//System.out.println(def_macros.get(macroname).get(i) + " " + parameters.get(i));
temp = temp.replace(def_macros.get(macroname).get(i), parameters.get(i));
//System.out.println(temp);
}
}
expansion += temp;
//System.out.println(expansion);
}
return PreParse(expansion);
}
}
class BrainFKEngine{
private ArrayList<Integer> code;
private char[] memory;
private int step, mem_ptr, memory_size;
private boolean exit = false;
BrainFKEngine(ArrayList<Integer> program, int memsize){
code = program;
memory_size = memsize;
mem_ptr = 0;
step = 0;
memory = new char[memory_size];
for(int i = 0;i<memory_size;++i){
memory[i] = 0;
}
}
public Integer Execute() throws IOException{
while(!exit && step<code.size()){
switch(code.get(step) >> 16){
case 0x01: // '+'
++memory[mem_ptr];
break;
case 0x02: // '-'
--memory[mem_ptr];
break;
case 0x03: // '>'
{
if(mem_ptr < memory_size-1){
++mem_ptr;
}
else{
mem_ptr = 0;
}
}
break;
case 0x04: // '<'
{
if(mem_ptr > 0){
--mem_ptr;
}
else{
mem_ptr = memory_size-1;
}
}
break;
case 0x05: // '['
{
if(memory[mem_ptr] == 0){
int brackets = 1;
while(step < code.size() && brackets != 0){
++step;
if(code.get(step) >> 16 == 0x05){
++brackets;
}
if(code.get(step) >> 16 == 0x06){
--brackets;
}
}
}
}
break;
case 0x06: // ']'
{
if(memory[mem_ptr] != 0){
int brackets = 1;
while(brackets != 0 && step > 0){
--step;
if(code.get(step) >> 16 == 0x05){
--brackets;
}
if(code.get(step) >> 16 == 0x06){
++brackets;
}
}
}
}
break;
case 0x07: // '.'
System.out.print((int)memory[mem_ptr]);
break;
case 0x08: // ','
System.out.print(String.valueOf(memory[mem_ptr]));
break;
case 0x09: // '$'
memory[mem_ptr] = (char)System.in.read();
break;
case 0x0A: // '^'
{
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String number = in.readLine();
memory[mem_ptr] = (char)Integer.valueOf(number).intValue();
}
break;
case 0x0B: // '@'
exit = true;
break;
case 0x0C: // 'c' + char
memory[mem_ptr] = (char)(code.get(step) ^ 0x0C0000);
break;
case 0x0D: // 'w' + short integer
memory[mem_ptr] = (char)(code.get(step) ^ 0x0D0000);
break;
}
++step;
}
return (int)memory[mem_ptr];
}
}
public class BrainFKInterpreter {
public static void main(String[] args) throws IOException {
BrainFKParser Compiler = new BrainFKParser();
Compiler.AddSymbol("+", 0x010000); //Incrument Current Memory Cell
Compiler.AddSymbol("-", 0x020000); //Decrument Current Memory Cell
Compiler.AddSymbol(">", 0x030000); //Incrument Memory Pointer
Compiler.AddSymbol("<", 0x040000); //Decrument Memory Pointer
Compiler.AddSymbol("[", 0x050000); //Jump to next matching ] if zero
Compiler.AddSymbol("]", 0x060000); //Jump to previous matching [ if not zero
Compiler.AddSymbol(".", 0x070000); //Print current memory cell as an integer
Compiler.AddSymbol(",", 0x080000); //Print current memory cell as a character
Compiler.AddSymbol("$", 0x090000); //Read one character from standard input
Compiler.AddSymbol("^", 0x0A0000); //Read integer from standard input
Compiler.AddSymbol("@", 0x0B0000); //Halt Program Execution
Compiler.AddSymbol("c", 0x0C0000); //Take next tape token as character and set the current memory cell to that value
Compiler.AddSymbol("w", 0x0D0000); //Take next tape tokens as decimal digits until another command is found and set current memory cell to the short integer value
BrainFKEngine Executioner;
if(args.length>2){ // 3 commandline args or more
if("-c".equals(args[0].toLowerCase()) && args[1].length() > 0 && args[2].length() > 0){
BufferedReader inputStream = new BufferedReader(new FileReader(args[1]));
String brainfksource = "";
while(inputStream.ready()){
brainfksource += String.valueOf((char)inputStream.read());
}
ArrayList<Integer> brainfkopcodes = Compiler.ParseString(Compiler.PreParse(brainfksource));
if(brainfkopcodes == null){
return;
}
BufferedWriter outputStream = new BufferedWriter(new FileWriter(args[2]));
for(int i=0;i<brainfkopcodes.size();++i){
char[] buffer = new char[2];
buffer[0] = (char)(brainfkopcodes.get(i) >> 16);
buffer[1] = (char)((brainfkopcodes.get(i) << 16) >> 16);
outputStream.write(buffer);
//System.out.println(buffer);
}
outputStream.close();
System.out.println("Compiled file \'" + args[1] + "\' into binary file \'" + args[2] + "\' - execute with command brainfk -e " + args[2]);
}
}
else if(args.length>1){ // 2 commandline args or more
if("-f".equals(args[0].toLowerCase()) && args[1].length() > 0){
BufferedReader inputStream = new BufferedReader(new FileReader(args[1]));
String brainfksource = "";
while(inputStream.ready()){
brainfksource += String.valueOf((char)inputStream.read());
}
inputStream.close();
Executioner = new BrainFKEngine(Compiler.ParseString(Compiler.PreParse(brainfksource)), 30000);
if(Executioner == null){
return;
}
System.out.println("\nReturned Status: " + Executioner.Execute());
}else if("-e".equals(args[0].toLowerCase()) && args[1].length() > 0){
BufferedReader inputStream = new BufferedReader(new FileReader(args[1]));
ArrayList<Integer> bfkbinary = new ArrayList<Integer>();
while(inputStream.ready()){
char[] buffer = new char[2];
inputStream.read(buffer);
int opcode = buffer[0];
opcode <<= 16;
opcode += buffer[1];
bfkbinary.add(opcode);
//System.out.println(opcode);
}
inputStream.close();
Executioner = new BrainFKEngine(bfkbinary, 30000);
System.out.println("\nReturned Status: " + Executioner.Execute());
}
}
else if(args.length>0 && !"-h".equals(args[0])){ // Just one commandline arg
Executioner = new BrainFKEngine(Compiler.ParseString(Compiler.PreParse(args[0])), 30000);
if(Executioner == null){
return;
}
System.out.println("\nReturned Status: " + Executioner.Execute());
}else{ // No commandline arguments
System.out.println("brainfk - \n\tUseage: brainfk \"brainfk source code to interpret\"\t\t\tParse and execute enquoted code\n\t\t[-f][brainfk source filename]\t\t\t\t\tParse and execute brainfk source file\n\t\t[-c][brainfk source filename]"
+ "[compiled output filename]\t\tCompile input filename and output to output filename as brainfk binary\n\t\t[-e][compiled brainfk filename]\t\t\t\t\tExecute brainfk binary code\n\t\t[-h/--help]\t\t\t\t\t\t\tShow this help message");
}
}
}
-
What a waste of time. Write a java compiler next time.
-