├── headfuck ├── lists │ ├── CellList.java │ ├── v2 │ │ ├── CellListv2.java │ │ └── RefShortList.java │ └── ShortList.java ├── vars │ ├── JFunc.java │ ├── Func.java │ ├── VarMap.java │ └── Variable.java ├── bf │ └── BrainFuck.java ├── Main.java ├── util │ └── ErrorHelper.java └── HeadFuck.java ├── LICENSE └── README.md /headfuck/lists/CellList.java: -------------------------------------------------------------------------------- 1 | package headfuck.lists; 2 | 3 | public interface CellList { 4 | 5 | public void set(int index, int val); 6 | 7 | public void inc(int index); 8 | 9 | public void dec(int index); 10 | 11 | public boolean cmp(int index, int val); 12 | 13 | public int get(int index); 14 | 15 | public void clear(int index); 16 | 17 | 18 | public int getCapacity(); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /headfuck/lists/v2/CellListv2.java: -------------------------------------------------------------------------------- 1 | package headfuck.lists.v2; 2 | 3 | public interface CellListv2 { 4 | 5 | public void set(int index, int val); 6 | public void set(int index, final Object val); 7 | 8 | public void inc(int index); 9 | 10 | public void dec(int index); 11 | 12 | public boolean cmp(int index, int val); 13 | public boolean cmp(int index, final Object val); 14 | 15 | public short geti(int index); 16 | public Object geto(int index); 17 | 18 | public void clear(int index); 19 | 20 | public int getCapacity(); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /headfuck/vars/JFunc.java: -------------------------------------------------------------------------------- 1 | package headfuck.vars; 2 | 3 | import java.io.InputStream; 4 | import java.io.OutputStream; 5 | 6 | public abstract class JFunc extends Variable { 7 | 8 | public JFunc(String content) { 9 | super(null); 10 | } 11 | 12 | public JFunc() { 13 | super(null); 14 | } 15 | 16 | public void set(String var) { 17 | return; 18 | } 19 | 20 | public short exec(short arg) { 21 | return jexec(arg); 22 | } 23 | 24 | protected abstract short jexec(short arg); 25 | 26 | public InputStream atIn() {return null;} 27 | public OutputStream atOut() {return null;} 28 | 29 | } 30 | -------------------------------------------------------------------------------- /headfuck/vars/Func.java: -------------------------------------------------------------------------------- 1 | package headfuck.vars; 2 | 3 | import java.io.InputStream; 4 | import java.io.OutputStream; 5 | 6 | import headfuck.HeadFuck; 7 | import headfuck.util.ErrorHelper; 8 | import headfuck.util.ErrorHelper.Errors; 9 | 10 | public class Func extends Variable { 11 | 12 | public Func(String content) { 13 | super(content); 14 | } 15 | 16 | public short exec(short arg) { 17 | char[] instr = content.toCharArray(); 18 | HeadFuck ref = VarMap.instance().getRef(); 19 | for (int i = 0; i < instr.length; i++) { 20 | try { 21 | ref.nextCharacter(instr[i]); 22 | } catch (Exception e) { 23 | ErrorHelper.error(e, Errors.OTHER, ref); 24 | } 25 | } 26 | return arg; 27 | } 28 | 29 | public InputStream atIn() { 30 | return null; 31 | } 32 | 33 | public OutputStream atOut() { 34 | return null; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 0x666c 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /headfuck/vars/VarMap.java: -------------------------------------------------------------------------------- 1 | package headfuck.vars; 2 | 3 | import headfuck.HeadFuck; 4 | import headfuck.util.ErrorHelper; 5 | import headfuck.util.ErrorHelper.Errors; 6 | 7 | public class VarMap { 8 | 9 | private static final VarMap INSTANCE = new VarMap(); 10 | 11 | private HeadFuck mainRef; 12 | 13 | public VarMap() {} 14 | 15 | public void setRef(HeadFuck ref) { 16 | mainRef = ref; 17 | } 18 | 19 | public HeadFuck getRef() { 20 | return mainRef; 21 | } 22 | 23 | public static final VarMap instance() { 24 | return INSTANCE; 25 | } 26 | 27 | private Variable[] variables = new Variable['z' - 'a']; 28 | 29 | public int size() { 30 | return variables.length; 31 | } 32 | 33 | public boolean varExists(char letter) { 34 | if(isEnglish(letter)) { 35 | return variables[letter - 'a'] != null; 36 | } 37 | return false; 38 | } 39 | 40 | public Variable get(char letter) { 41 | if(isEnglish(letter)) { 42 | return variables[letter - 'a']; 43 | } else { 44 | ErrorHelper.error(new RuntimeException("character '" + letter + " is invalid"), Errors.OTHER, mainRef); 45 | return null; // error() exits 46 | } 47 | } 48 | 49 | public void put(char letter, Variable value) { 50 | if(isEnglish(letter)) { 51 | variables[letter - 'a'] = value; 52 | } else 53 | ErrorHelper.error(new RuntimeException("character '" + letter + "' is invalid"), Errors.OTHER, mainRef); 54 | } 55 | 56 | 57 | public static final boolean isEnglish(char letter) { 58 | return (letter >= 'a' && letter <= 'z'); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /headfuck/lists/ShortList.java: -------------------------------------------------------------------------------- 1 | package headfuck.lists; 2 | 3 | public class ShortList implements CellList { 4 | 5 | private int capacity; 6 | private short[] positive_zero; 7 | // private short[] negative; TODO: Allow negative cells making the list infinite 8 | 9 | private float loadFactor = 1.5f; 10 | 11 | public ShortList(int initialCapacity) { 12 | capacity = initialCapacity; 13 | 14 | positive_zero = new short[initialCapacity]; 15 | } 16 | 17 | public void set(int index, int val) { 18 | ensureCapacity(index); 19 | 20 | positive_zero[index] = (byte) val; 21 | } 22 | 23 | public void inc(int index) { 24 | ensureCapacity(index); 25 | 26 | positive_zero[index]++; 27 | } 28 | 29 | public void dec(int index) { 30 | ensureCapacity(index); 31 | 32 | positive_zero[index]--; 33 | } 34 | 35 | public boolean cmp(int index, int val) { 36 | ensureCapacity(index); 37 | 38 | return positive_zero[index] == (byte)val; 39 | } 40 | 41 | public int get(int index) { 42 | ensureCapacity(index); 43 | 44 | return positive_zero[index]; 45 | } 46 | 47 | 48 | public void clear(int index) { 49 | ensureCapacity(index); 50 | 51 | positive_zero[index] = 0; 52 | } 53 | 54 | public int getCapacity() { 55 | return capacity; 56 | } 57 | 58 | 59 | private void ensureCapacity(int atLeast) { 60 | if(capacity <= atLeast) { 61 | int times = 1; 62 | for(;(capacity * loadFactor) < atLeast; times++); 63 | 64 | short[] ref = new short[(int)((capacity * loadFactor) * times) + 1]; 65 | System.arraycopy(positive_zero, 0, ref, 0, capacity); 66 | positive_zero = ref; 67 | 68 | capacity = ref.length; 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /headfuck/lists/v2/RefShortList.java: -------------------------------------------------------------------------------- 1 | package headfuck.lists.v2; 2 | 3 | public class RefShortList implements CellListv2 { 4 | 5 | private static final class Cell { 6 | 7 | private short sval = 0; 8 | private Object refval = null; 9 | private boolean isref; 10 | 11 | private Cell(final short value) { 12 | isref = false; 13 | sval = value; 14 | } 15 | private Cell(final Object refValue) { 16 | isref = true; 17 | refval = refValue; 18 | } 19 | 20 | private final void setr(final Object ref) { 21 | isref = true; 22 | sval = 0; 23 | refval = ref; 24 | } 25 | private final void sets(final short s) { 26 | isref = false; 27 | refval = null; 28 | sval = s; 29 | } 30 | } 31 | 32 | 33 | 34 | private final Cell[] cells; 35 | private int capacity; 36 | 37 | public RefShortList(int initialCapacity) { 38 | capacity = initialCapacity; 39 | cells = new Cell[initialCapacity]; 40 | for (int i = 0; i < cells.length; i++) { 41 | cells[i] = new Cell(0); 42 | } 43 | } 44 | 45 | 46 | public void set(int index, int val) { 47 | cells[index].sets((short)val); 48 | } 49 | public void set(int index, final Object val) { 50 | cells[index].setr(val); 51 | } 52 | 53 | public void inc(int index) { 54 | Cell c = cells[index]; 55 | if(!c.isref) { 56 | c.sval++; 57 | } else { 58 | // Exception 59 | } 60 | } 61 | 62 | public void dec(int index) { 63 | } 64 | 65 | public boolean cmp(int index, int val) { 66 | return false; 67 | } 68 | public boolean cmp(int index, final Object val) { 69 | return false; 70 | } 71 | 72 | public short geti(int index) { 73 | return 0; 74 | } 75 | public Object geto(int index) { 76 | return 0; 77 | } 78 | 79 | public void clear(int index) { 80 | 81 | } 82 | 83 | public int getCapacity() { 84 | return 0; 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /headfuck/bf/BrainFuck.java: -------------------------------------------------------------------------------- 1 | package headfuck.bf; 2 | 3 | public class BrainFuck { 4 | 5 | public static final void interpret(String code) { 6 | try { 7 | char[] prog = code.toCharArray(); 8 | int err = 0; 9 | for (int i = 0; i < prog.length; i++) { 10 | char c = prog[i]; 11 | if (c == '[') 12 | err++; 13 | else if (c == ']') 14 | err--; 15 | } 16 | if (err > 0) 17 | System.err.println("error: dangling ["); 18 | else if (err < 0) 19 | System.err.println("error: dangling ]"); 20 | else { 21 | int ptr = 0; 22 | int lptr = 0; 23 | byte[] cells = new byte[10000]; 24 | for (int i = 0; i < prog.length; i++) 25 | { 26 | switch (prog[i]) { 27 | case '+': 28 | cells[ptr]++; 29 | break; 30 | case '-': 31 | cells[ptr]--; 32 | break; 33 | case '>': 34 | ptr++; 35 | break; 36 | case '<': 37 | ptr--; 38 | break; 39 | case '[': 40 | if (cells[ptr] == 0) { 41 | i++; 42 | while (lptr > 0 || prog[i] != ']') { 43 | if (prog[i] == '[') 44 | lptr++; 45 | if (prog[i] == ']') 46 | lptr--; 47 | i++; 48 | } 49 | } 50 | break; 51 | case ']': 52 | if (cells[ptr] != 0) { 53 | i--; 54 | while (lptr > 0 || prog[i] != '[') { 55 | if (prog[i] == ']') 56 | lptr++; 57 | if (prog[i] == '[') 58 | lptr--; 59 | i--; 60 | } 61 | i--; 62 | } 63 | break; 64 | case '.': 65 | System.out.print((char) cells[ptr]); 66 | break; 67 | case ',': 68 | cells[ptr] = (byte) System.in.read(); 69 | break; 70 | 71 | default: 72 | break; 73 | 74 | } 75 | } 76 | } 77 | } catch (Exception e) { 78 | e.printStackTrace(); 79 | } 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HeadFuck 2 | A brainfuck-based esoteric programming language 3 | 4 | Language specs: 5 | 6 | ## Old operators (brainfuck) 7 | 8 | \> - increment pointer 9 | 10 | \< - decrement pointer 11 | 12 | \+ - increment cell value 13 | 14 | \- - decrement cell value 15 | 16 | \* - write byte from attached input into current cell 17 | 18 | \^ - write byte to attached output 19 | 20 | \[ - loop while current cell != 0 open 21 | 22 | \] - loop closing 23 | 24 | ## New operators: \[Done: 9.5/14\] 25 | 26 | \. - reset pointer 27 | 28 | \_ - set cell value to 0 29 | 30 | \! - terminate program with exit code equal to current cell 31 | 32 | \@*or^a-z - attach stream to input/output, if self passed as argument, then default value is assigned 33 | 34 | a\-z - call a function (argument is current cell, return gets placed in the same cell) 35 | 36 | Preallocation: 37 | 38 | \#a-z - put value/reference from preallocated memory into current cell (variables) 39 | 40 | \: - preallocated code section open 41 | 42 | \; - preallocated code section close 43 | 44 | \, - next declaration 45 | 46 | \{ - function scope open 47 | 48 | \} - function scope close 49 | 50 | a-z\{\\} - declare a function 51 | 52 | a-z"\" 53 | 54 | a-z'\' 55 | 56 | `` - comment 57 | 58 | 59 | ## STD functions: 60 | ### (f) 61 | Call: creates a file with either a numeric name if argument is a byte value, or alphabetic name if argument is a string reference. 62 | 63 | @ operator: opens handle to file with specified name and assigns it to chosen stream. If file does not exist, do nothing. 64 | 65 | ### (m) 66 | Call: shows an info message box with either number represented by current byte value or string. 67 | 68 | 69 | ## Sample programs: 70 | ### hello world: 71 | >+++++++++[<++++++++>-]<^>+++++++[<++++>-]<+^+++++++^^+++^[-]>++++++++[<++++>-]<^>+++++++++++[<++++++++>-]<-^--------^+++^------^--------^[-]>++++++++[<++++>-]<+^[-]++++++++++^ 72 | ### Write "hello world" to a file: 73 | f@^f>+++++++++[<++++++++>-]<^>+++++++[<++++>-]<+^+++++++^^+++^[-]>++++++++[<++++>-]<^>+++++++++++[<++++++++>-]<-^--------^+++^------^--------^[-]>++++++++[<++++>-]<+^[-]++++++++++^ 74 | 75 | -------------------------------------------------------------------------------- /headfuck/Main.java: -------------------------------------------------------------------------------- 1 | package headfuck; 2 | 3 | import java.util.stream.LongStream; 4 | import java.util.stream.Stream; 5 | 6 | /* hello world 7 | >+++++++++[<++++++++>-]<^>+++++++[<++++>-]<+^+++++++^^+++^[-] 8 | ">++++++++[<++++>-]<^>+++++++++++[<++++++++>-]<-^--------^+++ 9 | "^------^--------^[-]>++++++++[<++++>-]<+^[-]++++++++++^ 10 | */ 11 | 12 | 13 | // Old (brainfuck) operators: [Done: 8/8] 14 | // > - increment pointer 15 | // < - decrement pointer 16 | // + - increment cell value 17 | // - - decrement cell value 18 | // * - write byte from attached input into current cell 19 | // ^ - write byte to attached output 20 | // [ - loop while current cell != 0 open 21 | // ] - loop closing 22 | 23 | // New operators: [Done: 5/14] 24 | // . - reset pointer 25 | // _ - set cell value to 0 26 | // ! - terminate program with exit code equal to current cell 27 | // @*|^a-z - attach stream to input/output, if self passed as argument, then default value is assigned 28 | // a-z - call a function (argument is current cell, return gets placed in the same cell) 29 | // Preallocation: 30 | // #a-z - put value/reference from preallocated memory into current cell (variables) 31 | // : - preallocated code section open 32 | // ; - preallocated code section close 33 | // , - next declaration 34 | // { - function scope open 35 | // } - function scope close 36 | 37 | // a-z{} - declare a function 38 | // a-z"" 39 | // a-z'' 40 | 41 | // STD functions: 42 | // (f) 43 | // Call: creates a file with either a numeric name if argument is a byte value, or alphabetic name if argument is a string reference. 44 | // @ operator: opens handle to file with specified name and assigns it to chosen stream. If file does not exist, do nothing. 45 | // (m) 46 | // Call: shows an info message box with either number represented by current byte value or string. 47 | 48 | public class Main { 49 | 50 | public static void main(String[] args) throws Exception { 51 | new HeadFuck(false, ">+++++++++[<++++++++>-]<^>+++++++[<++++>-]<+^+++++++^^+++^[-]\">++++++++[<++++>-]<^>+++++++++++[<++++++++>-]<-^--------^+++\"^------^--------^[-]>++++++++[<++++>-]<+^[-]++++++++++^").run(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /headfuck/util/ErrorHelper.java: -------------------------------------------------------------------------------- 1 | package headfuck.util; 2 | 3 | import java.util.stream.Collectors; 4 | import java.util.stream.Stream; 5 | import headfuck.HeadFuck; 6 | 7 | public class ErrorHelper { 8 | 9 | private static final boolean debugMode = true; 10 | 11 | public static enum Errors { 12 | SYNTAX, 13 | IO, 14 | OTHER; 15 | } 16 | 17 | private static final int ERROR_OFFSET_LEFT = 12; 18 | private static final int ERROR_OFFSET_RIGHT = 12; 19 | 20 | public static final void error(Throwable e, Errors err, HeadFuck runtime) { // Exit 21 | System.err.println(generateMessage(e, err, runtime, "~FATAL ERROR~ ", true, true)); 22 | if(debugMode) { 23 | System.err.flush(); 24 | e.printStackTrace(); 25 | } 26 | System.exit(runtime.flowPointer); 27 | } 28 | 29 | public static final void warn(Throwable e, Errors err, HeadFuck runtime) { // Do not exit 30 | if(debugMode) { 31 | System.err.flush(); 32 | e.printStackTrace(); 33 | } 34 | System.err.println(generateMessage(e, err, runtime, "~WARNING~ ", false, false)); 35 | } 36 | 37 | 38 | private static final String generateMessage(Throwable e, Errors err, HeadFuck runtime, final String title, boolean showArrow, boolean doDump) { 39 | final String src = runtime.src; 40 | 41 | String left = src.substring(0, runtime.flowPointer); 42 | String right = src.substring(runtime.flowPointer, src.length()); 43 | 44 | if(left.length() > ERROR_OFFSET_LEFT) { 45 | left = left.substring(left.length() - ERROR_OFFSET_LEFT, left.length()); 46 | } 47 | 48 | if(right.length() > ERROR_OFFSET_RIGHT) { 49 | right = right.substring(0, ERROR_OFFSET_RIGHT); 50 | } 51 | 52 | int errAtChar = left.length(); 53 | final String arrow = Stream.generate(() -> "~").limit(errAtChar).collect(Collectors.joining()) + "^ here"; 54 | 55 | final String msg = 56 | title + err + " error" + (e.getMessage() == null ? "" : ": "+ e.getClass().getSimpleName() + ": " + e.getMessage()) + "\n" 57 | + "character '"+runtime.program[runtime.flowPointer]+"' at "+runtime.flowPointer+".\n\n" 58 | + (showArrow ? left + right + "\n" : "") 59 | + (showArrow ? arrow + "\n\n" : "") 60 | + (doDump ? dump(runtime) : ""); 61 | 62 | return msg; 63 | } 64 | 65 | private static final String dump(HeadFuck runtime) { 66 | final String dump = 67 | 68 | "flowPointer: " + runtime.flowPointer + "\n" 69 | + "cellPointer: " + runtime.cellPointer + "\n" 70 | + "loopPointer: " + runtime.loopPointer + "\n" 71 | + "current cell: " + runtime.cells.get(Math.max(0, runtime.cellPointer)) + "\n" 72 | + "in : " + runtime.attachedInput + "\n" 73 | + "out: " + runtime.attachedOutput + "\n"; 74 | 75 | return dump; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /headfuck/vars/Variable.java: -------------------------------------------------------------------------------- 1 | package headfuck.vars; 2 | 3 | import java.io.File; 4 | import java.io.FileNotFoundException; 5 | import java.io.FileOutputStream; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.io.OutputStream; 9 | import java.io.RandomAccessFile; 10 | import java.nio.channels.Channels; 11 | import java.nio.channels.FileChannel; 12 | import java.nio.file.OpenOption; 13 | import java.nio.file.Path; 14 | import java.nio.file.Paths; 15 | 16 | import javax.swing.JOptionPane; 17 | 18 | import headfuck.HeadFuck; 19 | import headfuck.util.ErrorHelper; 20 | import headfuck.util.ErrorHelper.Errors; 21 | 22 | public abstract class Variable { 23 | 24 | protected String content; 25 | 26 | public Variable(String content) { 27 | set(content); 28 | } 29 | 30 | public void set(String content) { 31 | this.content = content; 32 | } 33 | 34 | public abstract short exec(short arg); 35 | public abstract InputStream atIn(); 36 | public abstract OutputStream atOut(); 37 | 38 | 39 | // STATIC // 40 | 41 | 42 | private static final JFunc m = new JFunc() { 43 | public short jexec(short arg) { 44 | JOptionPane.showMessageDialog(null, arg); 45 | return arg; 46 | } 47 | }; 48 | 49 | private static final JFunc f = new JFunc() { 50 | public short jexec(short arg) { 51 | try { 52 | new File("c:/users/user/desktop/test/" + arg).createNewFile(); 53 | return arg; 54 | } catch (IOException e) { 55 | ErrorHelper.warn(e, Errors.IO, VarMap.instance().getRef()); 56 | return arg; 57 | } 58 | } 59 | public InputStream atIn() { 60 | try { 61 | File f = new File("c:/users/user/desktop/test/" + VarMap.instance().getRef().currentCell()); 62 | if(!f.exists()) 63 | return null; 64 | RandomAccessFile raf = new RandomAccessFile(f, "rw"); 65 | return Channels.newInputStream(raf.getChannel()); 66 | } catch (Exception e) { 67 | ErrorHelper.warn(e, Errors.IO, VarMap.instance().getRef()); 68 | return null; 69 | } 70 | } 71 | public OutputStream atOut() { 72 | try { 73 | File f = new File("c:/users/user/desktop/test/" + VarMap.instance().getRef().currentCell()); 74 | if(!f.exists()) 75 | return null; 76 | RandomAccessFile raf = new RandomAccessFile(f, "rw"); 77 | raf.seek(raf.length()); 78 | OutputStream out = Channels.newOutputStream(raf.getChannel()); 79 | return out; 80 | } catch (Exception e) { 81 | ErrorHelper.warn(e, Errors.IO, VarMap.instance().getRef()); 82 | return null; 83 | } 84 | } 85 | }; 86 | 87 | 88 | public static final boolean isStd(char val) { 89 | switch (val) {case'f':case'm': 90 | return true;} 91 | return false; 92 | } 93 | 94 | public static final Variable getStd(char var) { 95 | switch (var) { 96 | case 'm': 97 | return m; 98 | case 'f': 99 | return f; 100 | } 101 | 102 | return null; 103 | } 104 | } -------------------------------------------------------------------------------- /headfuck/HeadFuck.java: -------------------------------------------------------------------------------- 1 | package headfuck; 2 | 3 | import java.io.InputStream; 4 | import java.io.OutputStream; 5 | 6 | import headfuck.bf.BrainFuck; 7 | import headfuck.lists.CellList; 8 | import headfuck.lists.ShortList; 9 | import headfuck.util.ErrorHelper; 10 | import headfuck.util.ErrorHelper.Errors; 11 | import headfuck.vars.Func; 12 | import headfuck.vars.VarMap; 13 | import headfuck.vars.Variable; 14 | 15 | public class HeadFuck { 16 | 17 | public HeadFuck(boolean bfCompatible, String source) { 18 | src = source; 19 | program = tokenize(src); 20 | bf = bfCompatible; 21 | } 22 | 23 | 24 | public final String src; 25 | public final boolean bf; 26 | 27 | public InputStream attachedInput = System.in; 28 | public OutputStream attachedOutput = System.out; 29 | private InputStream defaultInput = System.in; 30 | private OutputStream defaultOutput = System.out; 31 | 32 | public int cellPointer = 0; 33 | public int flowPointer = 0; 34 | public int loopPointer = 0; 35 | public char[] program; 36 | 37 | // CellListv2 cells = new RefShortList(10000); 38 | public CellList cells = new ShortList(10000); 39 | 40 | VarMap map = VarMap.instance(); {map.setRef(this);} 41 | 42 | public final void nextCharacter(char c) throws Exception { 43 | switch (c) { 44 | case '+': cells.inc(cellPointer); break; 45 | case '-': cells.dec(cellPointer); break; 46 | case '>': cellPointer++; break; 47 | case '<': cellPointer--; break; 48 | case '[': openBracket(); break; 49 | case ']': closingBracket(); break; 50 | case '*': cells.set(cellPointer, getch()); break; 51 | case '^': putch(cells.get(cellPointer)); break; 52 | 53 | case '.': cellPointer = 0; break; 54 | case '_': cells.set(cellPointer, 0); break; 55 | case '!': System.exit(cells.get(cellPointer)); break; 56 | 57 | case '@': at(); break; 58 | 59 | default: 60 | if(VarMap.isEnglish(c)) { 61 | variable(c).exec(currentCell()); 62 | } 63 | } 64 | } 65 | 66 | private final void openBracket() { 67 | if(cells.cmp(cellPointer, 0)) { 68 | flowPointer++; 69 | while(loopPointer > 0 || program[flowPointer] != ']') { 70 | if(program[flowPointer] == '[') loopPointer++; 71 | if(program[flowPointer] == ']') loopPointer--; 72 | flowPointer++; 73 | } 74 | } 75 | } 76 | 77 | private final void closingBracket() { 78 | if(!cells.cmp(cellPointer, 0)) { 79 | flowPointer--; 80 | while(loopPointer > 0 || program[flowPointer] != '[') { 81 | if(program[flowPointer] == ']') loopPointer++; 82 | if(program[flowPointer] == '[') loopPointer--; 83 | flowPointer--; 84 | } 85 | flowPointer--; 86 | } 87 | } 88 | 89 | private final void at() { 90 | char boundTo = program[++flowPointer]; 91 | char boundWhat = program[++flowPointer]; 92 | 93 | if(boundTo == '*') { 94 | if(boundWhat == '*') { 95 | attachedInput = defaultInput; 96 | return; 97 | } else if(VarMap.isEnglish(boundWhat)) { 98 | InputStream o = variable(boundWhat).atIn(); 99 | if(o == null) 100 | return; 101 | attachedInput = o; 102 | return; 103 | } 104 | } else if(boundTo == '^') { 105 | if(boundWhat == '^') { 106 | attachedOutput = defaultOutput; 107 | return; 108 | } else if(VarMap.isEnglish(boundWhat)) { 109 | OutputStream o = variable(boundWhat).atOut(); 110 | if(o == null) 111 | return; 112 | attachedOutput = o; 113 | return; 114 | } 115 | } 116 | ErrorHelper.error(new RuntimeException("Syntax error"), Errors.SYNTAX, this); 117 | } 118 | 119 | private final Variable variable(char var) { 120 | if(Variable.isStd(var)) { 121 | return Variable.getStd(var); 122 | } 123 | return map.get(var); 124 | } 125 | 126 | private final byte getch() { 127 | try { 128 | return (byte) attachedInput.read(); 129 | } catch (Exception e) { 130 | ErrorHelper.error(e, Errors.IO, this); 131 | return 0; // Never reach this line since error() exits 132 | } 133 | } 134 | 135 | private final void putch(int b) { 136 | try { 137 | attachedOutput.write(b); 138 | attachedOutput.flush(); 139 | } catch (Exception e) { 140 | ErrorHelper.error(e, Errors.IO, this); 141 | } 142 | } 143 | 144 | public final short currentCell() { 145 | return (short) cells.get(cellPointer); 146 | } 147 | 148 | 149 | private final char[] tokenize(String src) { 150 | return src.toCharArray(); 151 | } 152 | 153 | private void preallocate() { 154 | int start = src.indexOf(':'); 155 | int end = src.indexOf(';'); 156 | if(start != -1 && end == -1) 157 | ErrorHelper.error(new RuntimeException("Unclosed :"), Errors.SYNTAX, this); 158 | if(start == -1 && end != -1) 159 | ErrorHelper.error(new RuntimeException("Dangling ;"), Errors.SYNTAX, this); 160 | if(start == -1 && end == -1) 161 | return; 162 | else { 163 | flowPointer = end + 1; 164 | 165 | char[] sec = src.substring(start, end).toCharArray(); 166 | for (int i = 0; i < sec.length; i++) { 167 | char c = sec[i]; 168 | 169 | if(c == '{') { 170 | int fstart = i+1; 171 | int fend = -1; 172 | for (int j = fstart; j < sec.length; j++) { 173 | if(sec[j] == '}') 174 | fend = j; 175 | } 176 | if(fend == -1) 177 | ErrorHelper.error(new RuntimeException("Dangling {"), Errors.SYNTAX, this); 178 | else { 179 | char label; 180 | if(!VarMap.isEnglish(label = sec[i-1]) || i-1 < 0) { 181 | ErrorHelper.error(new RuntimeException("Unlabeled func"), Errors.SYNTAX, this); 182 | } else { 183 | final String str = new String(sec, fstart, fend - fstart); 184 | map.put(label, new Func(str)); 185 | } 186 | } 187 | } 188 | } 189 | } 190 | } 191 | 192 | private final void debug(int delay) { 193 | try { 194 | Thread.sleep(delay); 195 | } catch (Exception e) {} 196 | System.out.println("Execptr: "+flowPointer+" executing: '" + (flowPointer < 0 ? "undef" : program[flowPointer]) + "' Loopptr: " + loopPointer + " cellvalue: " + program[cellPointer] + " cellptr: "+ cellPointer); 197 | } 198 | 199 | public void run() throws Exception { 200 | if(bf) { 201 | BrainFuck.interpret(src); 202 | System.exit(0); 203 | } else { 204 | preallocate(); 205 | 206 | //System.exit(0); 207 | 208 | while(flowPointer < program.length) { 209 | try { 210 | nextCharacter(program[flowPointer]); 211 | flowPointer++; 212 | } catch (Exception e) { 213 | ErrorHelper.error(e, Errors.OTHER, this); 214 | } 215 | } 216 | } 217 | 218 | } 219 | } --------------------------------------------------------------------------------