22
\$\begingroup\$

For some of those in The 2nd Monitor, you know that I was creating a new golfing language. For one or two (@Quill), you have heard from me how tough it is.

A-Ray (that's what it's called) is a functional programming language. A sample program looks something like:

T0~f"Fizz"~b"Buzz"f{?=%t+960{p`fp`b}{?=%t30{p`f} ?=%t50{p`b} }T+t1p"
"}

(Note the physical newline; will mention that later)

This is FizzBuzz forever. I do realise it can easily be golfed down more, but the whole point of this is to explain how A-Ray works.

In the language interpreter, it starts at index 0.

T0~f"Fizz"~b"Buzz"f{?=%t+960{p`fp`b}{?=%t30{p`f} ?=%t50{p`b} }T+t1p"
^
"}

It recursively checks:

  • What the function is -> this decides which function to execute
  • How many arguments are needed:
    • This is how many calls of itself, at the next index, it will do

Explanation of code:

T0                The function 'T' sets the temporary variable to the argument,
                  in this case, '0'

~f"Fizz"          The special ~ function is a set variable function.
                  This sets the value of 'f' to "Fizz"

~b"Buzz"          Same function as last time. Can you guess what it does?

f{                The function 'f' means to loop until a 'b' is reached.
                  Braces surround the code.

    ?=%t+960{     The ? command checks if the given boolean is true. If it
                  is, it will execute the first block. Otherwise, it will
                  execute the second. Breakdown of the commands inside:
                      =       Equality check
                        %       Modulo operator
                          t       Get temporary variable value
                          +       Add
                            9       Nine
                            6       Six (This is the faster way of getting
                                    '15', because for the integer value 
                                    '15', 4 bytes ('15') is required.
                        0       Zero

                  Executing the block in steps:
                  =%t+960      Add 9 and 6
                  =%t'15'0     Modulo 't' and 15 (let's assume t = 10)
                               Since 10 % 15 = 10, then the result is 10.
                  ='10'0       10 == 0
                  (false)

        p`fp`b    Print values inside f and b, resulting in "FizzBuzz"
    }{
        ?=%t30{   Checks if 't' is divisible by 3
            p`f   Prints value inside f ("Fizz")
        }         Here, the extra space is important. It means no 'else'
                  case.

        ?=%t50{   Checks if 't' is divisible by 5
            p`b   Prints value inside b ("Buzz")
        }         Again, extra space here.
    }

    T+t1          Increments t
    p"\n"         Here, the '\n' should be replaced with a real newline.
                  Prints the newline.
}

The entire list of functions is here.

Note: This language is very strict. One extra space, and your program may crash. All characters in a program are treated as a function (or part of one); therefore, it is not exactly safe to add anything.

Code

(Please ignore the //TODO flags, they're there to remind me of some small bug, or they're there for a feature waiting to be implemented)

A_RayCode.java

package library;

import java.io.File;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.regex.Pattern;

public class A_RayCode {

    public static final String FILENAME_EXTENSTION = ".br";

    protected final String code;
    protected final InputIterator input;
    protected final StringBuilder output;
    protected final List<Object> memory;
    protected final MutableObject temporaryVariable;
    protected final Map<String, Object> variables;

    protected static final Map<String, Function<?>> functions = new HashMap<>();

    protected static Predicate<Character> isSeparator = new Predicate<Character>() {

        @Override
        public boolean test(Character c) {
            return c == ',' || c == ' ' || c == ';' || c == '\n';
        }

    };

    protected static Predicate<Character> isNumberSeparator = new Predicate<Character>() {

        @Override
        public boolean test(Character c) {
            return !Character.isDigit(c);
        }

    };

    static {
        // template
        functions.put("", new Function<Void>(new Type[] {},
                new RunnableFunction<Void>() {

                    @Override
                    public Void run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return null;
                    }

                }));
        functions.put("i", new Function<String>(new Type[] {},
                new RunnableFunction<String>() {

                    @Override
                    public String run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return input.nextCharsUntil(isSeparator);
                    }

                }));
        functions.put("I", new Function<BigInteger>(new Type[] {},
                new RunnableFunction<BigInteger>() {

                    @Override
                    public BigInteger run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return new BigInteger(input.nextCharsUntil(
                                isNumberSeparator));
                    }

                }));
        functions.put("s", new Function<Boolean>(new Type[] {},
                new RunnableFunction<Boolean>() {

                    @Override
                    public Boolean run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        char result = input.next();
                        return result != '\0';
                    }

                }));
        functions.put("S", new Function<Boolean>(new Type[] {},
                new RunnableFunction<Boolean>() {

                    @Override
                    public Boolean run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        String result = input.nextCharsUntil(isSeparator
                                .negate());
                        return !result.isEmpty();
                    }

                }));
        functions.put("p", new Function<Boolean>(new Type[] { Type.OBJECT },
                new RunnableFunction<Boolean>() {

                    @Override
                    public Boolean run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        output.append(Function.toString(args[0]));
                        return true;
                    }

                }));
        functions.put("c", new Function<Character>(new Type[] {},
                new RunnableFunction<Character>() {

                    @Override
                    public Character run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return input.next();
                    }

                }));
        functions.put("C", new Function<BigInteger>(new Type[] {},
                new RunnableFunction<BigInteger>() {

                    @Override
                    public BigInteger run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return BigInteger.valueOf(input.next());
                    }

                }));
        functions.put("r", new Function<String>(new Type[] {},
                new RunnableFunction<String>() {

                    @Override
                    public String run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return input.nextChars(input.getNumRemaining());
                    }

                }));
        functions.put("d", new Function<List<Object>>(new Type[] {
                Type.STRING }, new RunnableFunction<List<Object>>() {

                    @Override
                    public List<Object> run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        List<Object> result = new ArrayList<>();
                        new ArrayList<String>(args[0].toString().split(
                                "(,| |;|\n)+")).stream().forEach(e -> result
                                        .add(e));
                        return result;
                    }

                }));
        functions.put("a", new Function<List<Object>>(new Type[] { Type.ARRAY,
                Type.OBJECT }, new RunnableFunction<List<Object>>() {

                    @Override
                    public List<Object> run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        List<Object> result = Function.toArray(args[0]);
                        result.add(args[1]);
                        return result;
                    }

                }));
        functions.put("A", new Function<List<Object>>(new Type[] { Type.ARRAY,
                Type.OBJECT, Type.INTEGER },
                new RunnableFunction<List<Object>>() {

                    @Override
                    public List<Object> run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        List<Object> result = Function.toArray(args[0]);
                        result.add(((BigInteger) args[2]).intValue(), args[1]);
                        return result;
                    }

                }));
        functions.put("g", new Function<Object>(new Type[] { Type.ARRAY,
                Type.INTEGER }, new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        List<Object> result = Function.toArray(args[0]);
                        return result.get(Function.toInteger(args[1])
                                .intValue());
                    }

                }));
        functions.put("G", new Function<Object>(new Type[] { Type.ARRAY,
                Type.INTEGER, Type.OBJECT }, new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        List<Object> result = Function.toArray(args[0]);
                        return result.set(Function.toInteger(args[1])
                                .intValue(), args[2]);
                    }

                }));
        functions.put("f", new Function<Boolean>(new Type[] { Type.FUNCTION },
                new RunnableFunction<Boolean>() {

                    @Override
                    public Boolean run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        boolean result = false;
                        LoopCode code = new LoopCode(((A_RayCode) args[0]).code,
                                memory, input, output, temporaryVariable,
                                variables);
                        boolean keepRunning = true;
                        while (keepRunning) {
                            try {
                                keepRunning = (boolean) code.run().result;
                            } catch (LoopFlag e) {
                                if (e.getAction() == Action.BREAK) {
                                    keepRunning = false;
                                }
                            }
                            result = true;
                        }
                        return result;
                    }

                }));
        functions.put("F", new Function<Boolean>(new Type[] { Type.ARRAY,
                Type.FUNCTION }, new RunnableFunction<Boolean>() {

                    @Override
                    public Boolean run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        boolean result = false;
                        IteratorCode code = new IteratorCode(Function.toArray(
                                args[0]), ((A_RayCode) args[0]).code, memory,
                                input, output, temporaryVariable, variables);
                        boolean keepRunning = true;
                        while (keepRunning) {
                            try {
                                keepRunning = (boolean) code.run().result;
                            } catch (LoopFlag e) {
                                if (e.getAction() == Action.BREAK) {
                                    keepRunning = false;
                                }
                            }
                            result = true;
                        }
                        return result;
                    }

                }));
        functions.put("?", new Function<Boolean>(new Type[] { Type.BOOLEAN,
                Type.FUNCTION, Type.FUNCTION },
                new RunnableFunction<Boolean>() {

                    @Override
                    public Boolean run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        boolean result = Function.toBoolean(args[0]);
                        A_RayCode code = (A_RayCode) (result ? args[1]
                                : args[2]);
                        code.run();
                        return result;
                    }

                }));
        functions.put(" ", new Function<A_RayCode>(new Type[] {},
                new RunnableFunction<A_RayCode>() {

                    @Override
                    public A_RayCode run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return new A_RayCode("", memory, input, output,
                                temporaryVariable, variables);
                    }

                }));
        functions.put("e", new Function<List<Object>>(new Type[] {},
                new RunnableFunction<List<Object>>() {

                    @Override
                    public List<Object> run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return memory;
                    }

                }));
        functions.put("E", new Function<List<Object>>(new Type[] { Type.ARRAY },
                new RunnableFunction<List<Object>>() {

                    @Override
                    public List<Object> run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        List<Object> result = new ArrayList<>(memory);
                        memory.clear();
                        memory.addAll(Function.toArray(args[0]));
                        return result;
                    }

                }));
        functions.put("l", new Function<BigInteger>(new Type[] { Type.ARRAY },
                new RunnableFunction<BigInteger>() {

                    @Override
                    public BigInteger run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return BigInteger.valueOf(Function.toArray(args[0])
                                .size());
                    }

                }));
        functions.put("t", new Function<Object>(new Type[] {},
                new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return temporaryVariable.getValue();
                    }

                }));
        functions.put("T", new Function<Object>(new Type[] { Type.OBJECT },
                new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        Object result = temporaryVariable.getValue();
                        temporaryVariable.setValue(args[0]);
                        return result == null ? new Object() : result;
                    }

                }));
        functions.put(":", new Function<List<Object>>(new Type[] { Type.ARRAY,
                Type.INTEGER }, new RunnableFunction<List<Object>>() {

                    @Override
                    public List<Object> run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        int length = Function.toInteger(args[1]).intValue();
                        List<Object> result = new ArrayList<>();
                        getAllPerms(Function.toArray(args[0]), result,
                                new ArrayList<>(), length, 0, 0);
                        return result;
                    }

                    private void getAllPerms(List<Object> array,
                            List<Object> fullList, List<Object> currentList,
                            int requiredLength, int currentLength, int index) {
                        if (requiredLength == currentLength) {
                            fullList.add(new ArrayList<>(currentList));
                            currentList.remove(currentLength - 1);
                            return;
                        }
                        for (int i = index, max = array.size() - (requiredLength
                                - currentLength); i <= max; i++) {
                            Object object = array.get(i);
                            currentList.add(object);
                            getAllPerms(array, fullList, currentList,
                                    requiredLength, currentLength + 1, i + 1);
                        }
                        if (currentLength > 0) {
                            currentList.remove(currentLength - 1);
                        }
                    }

                }));

        final Comparator<Object> comparator = new Comparator<Object>() {

            @Override
            public int compare(Object object1, Object object2) {
                try {
                    return Function.compare(object1, object2);
                } catch (LoopFlag e) {
                    // TODO catch
                    return 0;
                }
            }

        };

        functions.put("m", new Function<Object>(new Type[] { Type.ARRAY },
                new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        List<Object> array = Function.toArray(args[0]);
                        return Collections.min(array, comparator);
                    }

                }));
        functions.put("M", new Function<Object>(new Type[] { Type.ARRAY },
                new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        List<Object> array = Function.toArray(args[0]);
                        return Collections.max(array, comparator);
                    }

                }));
        functions.put("b", new Function<Object>(new Type[] {},
                new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        throw new LoopFlag(Action.BREAK);
                    }

                }));
        functions.put("B", new Function<Object>(new Type[] {},
                new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        throw new LoopFlag(Action.CONTINUE);
                    }

                }));
        functions.put("R", new Function<Boolean>(new Type[] { Type.STRING,
                Type.STRING }, new RunnableFunction<Boolean>() {

                    @Override
                    public Boolean run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        return Function.toString(args[1]).matches(Function
                                .toString(args[0]));
                    }

                }));
        functions.put("u", new Function<List<Object>>(new Type[] {
                Type.INTEGER }, new RunnableFunction<List<Object>>() {

                    @Override
                    public List<Object> run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        BigInteger to = Function.toInteger(args[0]);
                        List<Object> result = new ArrayList<>(to.intValue());
                        while (!to.equals(BigInteger.ONE)) {
                            to = to.subtract(BigInteger.ONE);
                            result.add(0, to);
                        }
                        return result;
                    }

                }));
        functions.put("U", new Function<List<Object>>(new Type[] { Type.INTEGER,
                Type.INTEGER }, new RunnableFunction<List<Object>>() {

                    @Override
                    public List<Object> run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        BigInteger from = Function.toInteger(args[0]);
                        BigInteger to = Function.toInteger(args[1]);
                        List<Object> result = new ArrayList<>();
                        while (!from.equals(to)) {
                            result.add(0, to);
                            from = from.add(BigInteger.ONE);
                        }
                        return result;
                    }

                }));
        functions.put("L", new Function<List<Object>>(new Type[] { Type.INTEGER,
                Type.OBJECT }, new RunnableFunction<List<Object>>() {

                    @Override
                    public List<Object> run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        int size = Function.toInteger(args[0]).intValue();
                        List<Object> list = new ArrayList<>(size);
                        for (; size > 0; size--) {
                            list.add(Function.copy(args[1]));
                        }
                        return list;
                    }

                }));
        functions.put("N", new Function<Object>(new Type[] {},
                new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return null;
                    }

                }));

        final BigInteger TWO = BigInteger.valueOf(2L);
        final BigInteger THREE = BigInteger.valueOf(3L);
        final BigInteger FOUR = BigInteger.valueOf(4L);
        final BigInteger FIVE = BigInteger.valueOf(5L);
        final BigInteger SIX = BigInteger.valueOf(6L);
        final BigInteger SEVEN = BigInteger.valueOf(7L);
        final BigInteger EIGHT = BigInteger.valueOf(8L);
        final BigInteger NINE = BigInteger.valueOf(9L);

        functions.put("0", new Function<BigInteger>(new Type[] {},
                new RunnableFunction<BigInteger>() {

                    @Override
                    public BigInteger run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return BigInteger.ZERO;
                    }

                }));
        functions.put("1", new Function<BigInteger>(new Type[] {},
                new RunnableFunction<BigInteger>() {

                    @Override
                    public BigInteger run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return BigInteger.ONE;
                    }

                }));
        functions.put("2", new Function<BigInteger>(new Type[] {},
                new RunnableFunction<BigInteger>() {

                    @Override
                    public BigInteger run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return TWO;
                    }

                }));
        functions.put("3", new Function<BigInteger>(new Type[] {},
                new RunnableFunction<BigInteger>() {

                    @Override
                    public BigInteger run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return THREE;
                    }

                }));
        functions.put("4", new Function<BigInteger>(new Type[] {},
                new RunnableFunction<BigInteger>() {

                    @Override
                    public BigInteger run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return FOUR;
                    }

                }));
        functions.put("5", new Function<BigInteger>(new Type[] {},
                new RunnableFunction<BigInteger>() {

                    @Override
                    public BigInteger run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return FIVE;
                    }

                }));
        functions.put("6", new Function<BigInteger>(new Type[] {},
                new RunnableFunction<BigInteger>() {

                    @Override
                    public BigInteger run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return SIX;
                    }

                }));
        functions.put("7", new Function<BigInteger>(new Type[] {},
                new RunnableFunction<BigInteger>() {

                    @Override
                    public BigInteger run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return SEVEN;
                    }

                }));
        functions.put("8", new Function<BigInteger>(new Type[] {},
                new RunnableFunction<BigInteger>() {

                    @Override
                    public BigInteger run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return EIGHT;
                    }

                }));
        functions.put("9", new Function<BigInteger>(new Type[] {},
                new RunnableFunction<BigInteger>() {

                    @Override
                    public BigInteger run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return NINE;
                    }

                }));
        functions.put("+", new Function<Object>(new Type[] { Type.OBJECT,
                Type.OBJECT }, new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        Type type1 = Type.getMatch(args[0]);
                        Type type2 = Type.getMatch(args[1]);
                        if ((type1 == Type.DECIMAL || type1 == Type.INTEGER)
                                && (type2 == Type.DECIMAL
                                        || type2 == Type.INTEGER)) {
                            return addNumbers(args[0], type1, args[1], type2);
                        } else if (type1 == Type.ARRAY) {
                            return append(Function.toArray(args[0]), args[1]);
                        } else if (type2 == Type.ARRAY) {
                            return append(Function.toArray(args[1]), args[0]);
                        } else if (type1 == Type.STRING
                                || type2 == Type.STRING) {
                            return concat(args[0], args[1], type2);
                        }
                        return concat(args[0], args[1], type2);
                    }

                    private Object append(List<Object> list, Object object) {
                        list.add(object);
                        return list;
                    }

                    private Object addNumbers(Object number1, Type type1,
                            Object number2, Type type2) {
                        return toBigDecimal(number1, type1).add(toBigDecimal(
                                number2, type2));
                    }

                    private BigDecimal toBigDecimal(Object number, Type type) {
                        return type == Type.INTEGER ? new BigDecimal(
                                (BigInteger) number) : (BigDecimal) number;
                    }

                    private String concat(Object object1, Object object2,
                            Type type2) throws LoopFlag {
                        return Function.toString(object1) + Function.toString(
                                object2);
                    }

                }));
        functions.put("-", new Function<Object>(new Type[] { Type.OBJECT,
                Type.OBJECT }, new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        Type type1 = Type.getMatch(args[0]);
                        Type type2 = Type.getMatch(args[1]);
                        if ((type1 == Type.DECIMAL || type1 == Type.INTEGER)
                                && (type2 == Type.DECIMAL
                                        || type2 == Type.INTEGER)) {
                            return subtractNumbers(args[0], type1, args[1],
                                    type2);
                        } else if (type1 == Type.ARRAY) {
                            return remove(Function.toArray(args[0]), Function
                                    .toInteger(args[1]));
                        } else if (type1 == Type.STRING
                                && type2 == Type.CHARACTER) {
                            return Function.toString(args[0]).replaceAll(Pattern
                                    .quote(Function.toString(args[1])), "");
                        }
                        return null;
                    }

                    private List<Object> remove(List<Object> list,
                            BigInteger index) {
                        list.remove(index.intValue());
                        return list;
                    }

                    private Object subtractNumbers(Object number1, Type type1,
                            Object number2, Type type2) {
                        return toBigDecimal(number1, type1).subtract(
                                toBigDecimal(number2, type2));
                    }

                    private BigDecimal toBigDecimal(Object number, Type type) {
                        return type == Type.INTEGER ? new BigDecimal(
                                (BigInteger) number) : (BigDecimal) number;
                    }

                }));
        functions.put("*", new Function<Object>(new Type[] { Type.OBJECT,
                Type.OBJECT }, new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        Type type1 = Type.getMatch(args[0]);
                        Type type2 = Type.getMatch(args[1]);
                        if ((type1 == Type.DECIMAL || type1 == Type.INTEGER)
                                && (type2 == Type.DECIMAL
                                        || type2 == Type.INTEGER)) {
                            return multiplyNumbers(args[0], type1, args[1],
                                    type2);
                        }
                        return null;
                    }

                    private Object multiplyNumbers(Object number1, Type type1,
                            Object number2, Type type2) {
                        return toBigDecimal(number1, type1).multiply(
                                toBigDecimal(number2, type2));
                    }

                    private BigDecimal toBigDecimal(Object number, Type type) {
                        return type == Type.INTEGER ? new BigDecimal(
                                (BigInteger) number) : (BigDecimal) number;
                    }

                }));
        functions.put("/", new Function<Object>(new Type[] { Type.OBJECT,
                Type.OBJECT }, new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        Type type1 = Type.getMatch(args[0]);
                        Type type2 = Type.getMatch(args[1]);
                        if ((type1 == Type.DECIMAL || type1 == Type.INTEGER)
                                && (type2 == Type.DECIMAL
                                        || type2 == Type.INTEGER)) {
                            return divideNumbers(args[0], type1, args[1],
                                    type2);
                        }
                        return null;
                    }

                    private Object divideNumbers(Object number1, Type type1,
                            Object number2, Type type2) {
                        return toBigDecimal(number1, type1).divide(toBigDecimal(
                                number2, type2));
                    }

                    private BigDecimal toBigDecimal(Object number, Type type) {
                        return type == Type.INTEGER ? new BigDecimal(
                                (BigInteger) number) : (BigDecimal) number;
                    }

                }));
        functions.put("^", new Function<Object>(new Type[] { Type.OBJECT,
                Type.OBJECT }, new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        Type type1 = Type.getMatch(args[0]);
                        Type type2 = Type.getMatch(args[1]);
                        if ((type1 == Type.DECIMAL || type1 == Type.INTEGER)
                                && (type2 == Type.DECIMAL
                                        || type2 == Type.INTEGER)) {
                            return pow(args[0], type1, args[1], type2);
                        }
                        return null;
                    }

                    private Object pow(Object number1, Type type1,
                            Object number2, Type type2) {
                        return toBigDecimal(number1, type1).pow(toBigDecimal(
                                number2, type2).intValue());
                    }

                    private BigDecimal toBigDecimal(Object number, Type type) {
                        return type == Type.INTEGER ? new BigDecimal(
                                (BigInteger) number) : (BigDecimal) number;
                    }

                }));
        functions.put("%", new Function<Object>(new Type[] { Type.OBJECT,
                Type.OBJECT }, new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        Type type1 = Type.getMatch(args[0]);
                        Type type2 = Type.getMatch(args[1]);
                        if ((type1 == Type.DECIMAL || type1 == Type.INTEGER)
                                && (type2 == Type.DECIMAL
                                        || type2 == Type.INTEGER)) {
                            return mod(args[0], type1, args[1], type2);
                        }
                        return null;
                    }

                    private Object mod(Object number1, Type type1,
                            Object number2, Type type2) {
                        return toBigDecimal(number1, type1).remainder(
                                toBigDecimal(number2, type2));
                    }

                    private BigDecimal toBigDecimal(Object number, Type type) {
                        return type == Type.INTEGER ? new BigDecimal(
                                (BigInteger) number) : (BigDecimal) number;
                    }

                }));
        functions.put("=", new Function<Boolean>(new Type[] { Type.OBJECT,
                Type.OBJECT }, new RunnableFunction<Boolean>() {

                    @Override
                    public Boolean run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        return Function.toString(args[0]).equals(Function
                                .toString(args[1]));
                    }

                }));
        functions.put("<", new Function<Boolean>(new Type[] { Type.OBJECT,
                Type.OBJECT }, new RunnableFunction<Boolean>() {

                    @Override
                    public Boolean run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        return Function.compare(args[0], args[1]) < 0;
                    }

                }));
        functions.put(">", new Function<Boolean>(new Type[] { Type.OBJECT,
                Type.OBJECT }, new RunnableFunction<Boolean>() {

                    @Override
                    public Boolean run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        return Function.compare(args[0], args[1]) > 0;
                    }

                }));
        functions.put("&", new Function<Object>(new Type[] { Type.OBJECT,
                Type.OBJECT }, new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        Type type1 = Type.getMatch(args[0]);
                        Type type2 = Type.getMatch(args[1]);
                        if (type1 == Type.INTEGER && type2 == Type.INTEGER) {
                            return Function.toInteger(args[0]).and(Function
                                    .toInteger(args[1]));
                        }
                        return Function.toBoolean(args[0]) && Function
                                .toBoolean(args[1]);
                    }

                }));
        functions.put("|", new Function<Object>(new Type[] { Type.OBJECT,
                Type.OBJECT }, new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        Type type1 = Type.getMatch(args[0]);
                        Type type2 = Type.getMatch(args[1]);
                        if (type1 == Type.INTEGER && type2 == Type.INTEGER) {
                            return Function.toInteger(args[0]).or(Function
                                    .toInteger(args[1]));
                        }
                        return Function.toBoolean(args[0]) || Function
                                .toBoolean(args[1]);
                    }

                }));
        functions.put("!", new Function<Object>(new Type[] { Type.OBJECT },
                new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args)
                                    throws LoopFlag {
                        Type type1 = Type.getMatch(args[0]);
                        Type type2 = Type.getMatch(args[1]);
                        if (type1 == Type.INTEGER && type2 == Type.INTEGER) {
                            return Function.toInteger(args[0]).not();
                        }
                        return !Function.toBoolean(args[0]);
                    }

                }));

        // TODO add built-ins
    }

    public A_RayCode(String code, File input) {
        this(code, new ArrayList<>(), new InputIterator(input),
                new StringBuilder(), new MutableObject(null), new HashMap<>());
    }

    public A_RayCode(String code, String input) {
        this(code, new ArrayList<>(), new InputIterator(input),
                new StringBuilder(), new MutableObject(null), new HashMap<>());
    }

    protected A_RayCode(String code, List<Object> memory, InputIterator input,
            StringBuilder output, MutableObject temporaryVariable,
            Map<String, Object> variables) {
        this.code = code;
        this.input = input;
        this.output = output;
        this.memory = memory;
        this.temporaryVariable = temporaryVariable;
        this.variables = variables;
    }

    public String runAndGetOutput() {
        input.reset();
        output.delete(0, output.length());
        memory.clear();

        try {
            run();
        } catch (LoopFlag e) {
            return "Loop flag not resolved\n";
        }

        return output.toString();
    }

    @Override
    public String toString() {
        return code;
    }

    protected FunctionResult run() throws LoopFlag {
        // run
        FunctionResult value = new FunctionResult(null, 0);
        FunctionResult previous = null;
        do {
            previous = value;
            value = run(value.currentIndex);
        } while (value.result != null);
        return previous;
    }

    protected FunctionResult run(int index) throws LoopFlag {
        if (index >= code.length()) {
            return new FunctionResult(null, index);
        }
        char c = code.charAt(index);
        String functionName = Character.toString(c);
        int endIndex = index;
        final String functionCode;
        Function<?> function;
        switch (c) {
        case '$':
            endIndex = code.indexOf('$', ++index);
            functionName = code.substring(index, endIndex);
            break;
        case '#':
            endIndex = code.indexOf('{', ++index);
            functionName = code.substring(index, endIndex);
            functionCode = code.substring(endIndex + 1,
                    endIndex = indexOfMatchingClose(endIndex, '}'));
            function = new Function<Object>(new Type[] {},
                    new RunnableFunction<Object>() {

                        @Override
                        public Object run(List<Object> memory,
                                InputIterator input, StringBuilder output,
                                MutableObject temporaryVariable,
                                Map<String, Object> variables, Object[] args) {
                            return new A_RayCode(functionCode, memory, input,
                                    output, temporaryVariable, variables);
                        }

                    });
            functions.put(functionName, function);
            return new FunctionResult(function.run(memory, input, output,
                    temporaryVariable, variables, new Object[] {}), endIndex
                            + 1);
        case ',':
        case '{':
            return new FunctionResult(new A_RayCode(code.substring(index + 1,
                    endIndex = indexOfMatchingCloseBrace(index++)), memory,
                    input, output, temporaryVariable, variables), endIndex
                            + (code.charAt(endIndex) == ',' ? 0 : 1));
        case '`':
        case '~':
            String name = Character.toString(code.charAt(index++));
            if (name.charAt(0) == '"') {
                endIndex = code.indexOf('"', ++index);
                name = code.substring(index, endIndex++);
            }
            if (c == '`') {
                return new FunctionResult(variables.get(name), endIndex);
            }
            FunctionResult result = run(endIndex);
            index = result.currentIndex;
            Object value = result.result;
            variables.put(name, value);
            return new FunctionResult(true, index);
        case '"':
            endIndex = code.indexOf('"', ++index);
            return new FunctionResult(code.substring(index, endIndex), endIndex
                    + 1);
        case '\'':
            endIndex = code.indexOf('\'', ++index);
            try {
                return new FunctionResult(new BigDecimal(code.substring(index,
                        endIndex)), endIndex + 1);
            } catch (NumberFormatException e) {
                return new FunctionResult(null, endIndex + 1);
            }
        case '[':
            endIndex = indexOfMatchingClose(index, ']');
            return new FunctionResult(createNewArray(code.substring(index + 1,
                    endIndex++)), endIndex);
        }
        index = endIndex;
        function = functions.get(functionName);
        int numOfArguments = function.getParameterTypes().length;
        Object[] args = new Object[numOfArguments];
        index++;
        for (int i = 0; i < numOfArguments; i++) {
            FunctionResult argResult = run(index);
            args[i] = argResult.result;
            index = argResult.currentIndex;
        }
        return new FunctionResult(function.run(memory, input, output,
                temporaryVariable, variables, args), index);
    }

    private List<Object> createNewArray(String substring) throws LoopFlag {
        String[] array = substring.split(",");
        List<Object> result = new ArrayList<>();
        for (String string : array) {
            result.add(new A_RayCode(string, memory, input, output,
                    temporaryVariable, variables).run().result);
        }
        return result;
    }

    private int indexOfMatchingClose(int index, char close) {
        char open = code.charAt(index);
        for (int i = index + 1, count = 1; i < code.length(); i++) {
            if (code.charAt(i) == open) {
                count++;
            } else if (code.charAt(i) == close) {
                count--;
            }
            if (count == 0) {
                return i;
            }
        }
        return -1; // TODO error
    }

    private int indexOfMatchingCloseBrace(int index) {
        return Math.min(indexOfMatchingClose(index, ','), indexOfMatchingClose(
                index, '}'));
    }

    protected static class FunctionResult {

        protected final Object result;
        protected final int currentIndex;

        protected FunctionResult(Object result, int currentIndex) {
            this.result = result;
            this.currentIndex = currentIndex;
        }

    }

}

Function.java

package library;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;

public final class Function<T> {

    private final Type[] parameterTypes;
    private final RunnableFunction<T> function;

    public Function(Type[] parameterTypes, RunnableFunction<T> function) {
        this.parameterTypes = parameterTypes;
        this.function = function;
    }

    public T run(List<Object> memory, InputIterator input, StringBuilder output,
            MutableObject temporaryVariable, Map<String, Object> variables,
            Object[] args) throws LoopFlag {
        if (args.length != parameterTypes.length) {
            throw new IllegalArgumentException(); // TODO
        }
        for (int i = 0; i < args.length; i++) {
            switch (parameterTypes[i]) {
            case ARRAY:
                args[i] = toArray(args[i]);
                break;
            case BOOLEAN:
                args[i] = toBoolean(args[i]);
                break;
            case CHARACTER:
                args[i] = toChar(args[i]);
                break;
            case DECIMAL:
                args[i] = toDecimal(args[i]);
                break;
            case INTEGER:
                args[i] = toInteger(args[i]);
                break;
            case STRING:
                args[i] = toString(args[i]);
                break;
            case FUNCTION:
                args[i] = toFunction(args[i]);
                break;
            case OBJECT:
                args[i] = toObject(args[i]);
            }
        }
        return function.run(memory, input, output, temporaryVariable, variables,
                args);
    }

    public Type[] getParameterTypes() {
        return parameterTypes;
    }

    @SuppressWarnings("unchecked")
    public static int compare(Object object1, Object object2) throws LoopFlag {
        if (object1 == null) {
            if (object2 == null) {
                return 0;
            }
            return -1;
        }
        if (object2 == null) {
            return 1;
        }
        Type type1 = Type.getMatch(object1);
        Type type2 = Type.getMatch(object2);
        if (type1 == Type.FUNCTION) {
            object1 = ((A_RayCode) object1).run(0).result;
            type1 = Type.getMatch(object1);
        }
        if (type2 == Type.FUNCTION) {
            object2 = ((A_RayCode) object2).run(0).result;
            type2 = Type.getMatch(object2);
        }
        if (type1 == type2) {
            return ((Comparable<Object>) object1).compareTo(object2);
        }
        if (type1 == Type.BOOLEAN || type2 == Type.BOOLEAN) {
            boolean bool1 = toBoolean(object1);
            boolean bool2 = toBoolean(object2);
            if (bool1) {
                return bool2 ? 0 : 1;
            } else {
                return bool2 ? -1 : 0;
            }
        }
        // TODO

        return 0;
    }

    private Object toObject(Object object) throws LoopFlag {
        if (Type.getMatch(object) == Type.FUNCTION) {
            return ((A_RayCode) object).run();
        }
        return object;
    }

    @SuppressWarnings("unchecked")
    public static String toString(Object object) throws LoopFlag {
        Type type = Type.getMatch(object);
        if (type == null) {
            return null;
        }
        switch (type) {
        case ARRAY:
            List<Object> array = (List<Object>) object;
            Object value = array.get(0);
            if (Type.CHARACTER.isMatch(value) || Type.STRING.isMatch(value)) {
                StringBuilder result = new StringBuilder();
                for (Object item : array) {
                    result.append(item);
                }
                return result.toString();
            }
            return ((List<Object>) object).toString();
        case BOOLEAN:
            return Boolean.toString((boolean) object);
        case CHARACTER:
            return Character.toString((char) object);
        case DECIMAL:
            return ((BigDecimal) object).toPlainString();
        case INTEGER:
            return ((BigInteger) object).toString();
        case OBJECT:
            return object.toString();
        case STRING:
            return ((CharSequence) object).toString();
        case FUNCTION:
            return toString(((A_RayCode) object).run(0).result); // TODO
        }
        return null;
    }

    public static BigDecimal toDecimal(Object object) throws LoopFlag {
        Type type = Type.getMatch(object);
        if (type == null) {
            return null;
        }
        switch (type) {
        case ARRAY:
            return null;
        case BOOLEAN:
            return BigDecimal.ONE;
        case CHARACTER:
            return BigDecimal.valueOf((char) object);
        case DECIMAL:
            return (BigDecimal) object;
        case INTEGER:
            return new BigDecimal((BigInteger) object);
        case OBJECT:
            return null;
        case STRING:
            return new BigDecimal((String) object);
        case FUNCTION:
            return toDecimal(((A_RayCode) object).run(0).result); // TODO
        }
        return null;
    }

    public static Character toChar(Object object) throws LoopFlag {
        Type type = Type.getMatch(object);
        if (type == null) {
            return null;
        }
        switch (type) {
        case ARRAY:
            return null;
        case BOOLEAN:
            return (boolean) object ? 't' : 'f';
        case CHARACTER:
            return (char) object;
        case DECIMAL:
            return null;
        case INTEGER:
            BigInteger num = (BigInteger) object;
            return num.bitCount() > Character.SIZE ? null
                    : (char) num.intValue();
        case OBJECT:
            return null;
        case STRING:
            return ((CharSequence) object).charAt(0);
        case FUNCTION:
            return toChar(((A_RayCode) object).run(0).result); // TODO
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    public static List<Object> toArray(Object object) {
        Type type = Type.getMatch(object);
        if (type == null) {
            return null;
        }
        List<Object> array = new ArrayList<>();
        switch (type) {
        case ARRAY:
            return ((List<Object>) object);
        case STRING:
            ((String) object).chars().forEach(e -> array.add((char) e));
            return array;
        default:
            array.add(object);
            return array;
        }
    }

    @SuppressWarnings("unchecked")
    public static Boolean toBoolean(Object object) throws LoopFlag {
        Type type = Type.getMatch(object);
        if (type == null) {
            return null;
        }
        switch (type) {
        case ARRAY:
            return ((List<Object>) object).size() != 0;
        case BOOLEAN:
            return (boolean) object;
        case CHARACTER:
            return (char) object != '\0';
        case DECIMAL:
            return !((BigDecimal) object).equals(BigDecimal.ZERO);
        case INTEGER:
            return !((BigInteger) object).equals(BigInteger.ZERO);
        case OBJECT:
            return object != null;
        case STRING:
            return ((CharSequence) object).length() != 0;
        case FUNCTION:
            return toBoolean(((A_RayCode) object).run(0).result); // TODO
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    public static BigInteger toInteger(Object object) throws LoopFlag {
        Type type = Type.getMatch(object);
        if (type == null) {
            return null;
        }
        switch (type) {
        case ARRAY:
            return BigInteger.valueOf(((List<Object>) object).size());
        case BOOLEAN:
            return (boolean) object ? BigInteger.ONE : BigInteger.ZERO;
        case CHARACTER:
            return BigInteger.valueOf((char) object);
        case DECIMAL:
            return ((BigDecimal) object).toBigInteger();
        case INTEGER:
            return (BigInteger) object;
        case OBJECT:
            return null;
        case STRING:
            return new BigInteger((String) object);
        case FUNCTION:
            return toInteger(((A_RayCode) object).run(0).result); // TODO
        }
        return null;
    }

    public static A_RayCode toFunction(Object object) {
        if (Type.getMatch(object) == Type.FUNCTION) {
            return (A_RayCode) object;
        }
        return new A_RayCode("", "") {

            @Override
            protected FunctionResult run(int index) {
                return new FunctionResult(object, 1);
            }

        };
    }

    @SuppressWarnings("unchecked")
    public static Object copy(Object object) {
        Type type = Type.getMatch(object);
        if (type == null) {
            return null;
        } else if (type == Type.ARRAY) {
            return new ArrayList<>(((List<Object>) object));
        }
        return object;
    }

}

RunnableFunction.java

package library;

import java.util.List;
import java.util.Map;

public interface RunnableFunction<T> {

    public T run(List<Object> memory, InputIterator input, StringBuilder output,
            MutableObject temporaryVariable, Map<String, Object> variables,
            Object[] args) throws LoopFlag;

}

Action.java

package library;

public enum Action {
    BREAK, CONTINUE
}

InputIterator.java

package library;

import java.io.File;
import java.util.Iterator;
import java.util.function.Predicate;

public class InputIterator implements Iterator<Character> {

    private final String input;
    private final int length;
    private int index = 0;

    public InputIterator(File input) {
        this(FileUtils.readAll(input));
    }

    public InputIterator(String input) {
        this.input = input;
        this.length = this.input.length();
    }

    @Override
    public boolean hasNext() {
        return length >= index;
    }

    @Override
    public Character next() {
        return index >= length ? '\0' : input.charAt(index++);
    }

    public String nextChars(int numOfChars) {
        if (index + numOfChars > length) {
            numOfChars = length - index;
        }
        String result = input.substring(index, index + numOfChars);
        index += numOfChars;
        return result;
    }

    public String nextCharsUntil(Predicate<Character> predicate) {
        if (!hasNext()) {
            return "";
        }
        int endIndex = index;
        while (endIndex < length && !predicate.test(input.charAt(endIndex))) {
            endIndex++;
        }
        String result = input.substring(index, endIndex);
        index = endIndex;
        while (index != length && predicate.test(input.charAt(index))) {
            index++;
        }
        return result;
    }

    public int getNumRemaining() {
        return length - index;
    }

    public void reset() {
        index = 0;
    }

}

IteratorCode.java

package library;

import java.util.List;
import java.util.Map;

public class IteratorCode extends LoopCode {

    public IteratorCode(List<Object> array, String code, List<Object> memory,
            InputIterator input, StringBuilder output,
            MutableObject temporaryVariable, Map<String, Object> variables) {
        super(code, memory, input, output, temporaryVariable, variables);
        functions.put(LoopCode.X, new Function<Object>(new Type[] {},
                new RunnableFunction<Object>() {

                    @Override
                    public Object run(List<Object> memory, InputIterator input,
                            StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return array.get(count.intValue());
                    }

                }));
    }

}

LoopCode.java

package library;

import java.math.BigInteger;
import java.util.List;
import java.util.Map;

public class LoopCode extends A_RayCode {

    protected static final String X = "x";

    protected BigInteger count = BigInteger.ZERO;
    protected boolean out = false;

    private final Function<?> oldXFunction;

    public LoopCode(String code, List<Object> memory, InputIterator input,
            StringBuilder output, MutableObject temporaryVariable,
            Map<String, Object> variables) {
        super(code, memory, input, output, temporaryVariable, variables);
        oldXFunction = functions.get(X);
        functions.put(X, new Function<BigInteger>(new Type[] {},
                new RunnableFunction<BigInteger>() {

                    @Override
                    public BigInteger run(List<Object> memory,
                            InputIterator input, StringBuilder output,
                            MutableObject temporaryVariable,
                            Map<String, Object> variables, Object[] args) {
                        return count;
                    }

                }));
    }

    @Override
    public FunctionResult run() throws LoopFlag {
        // run
        FunctionResult value = new FunctionResult(null, 0);
        do {
            value = run(value.currentIndex);
        } while (value.result != null);
        count = count.add(BigInteger.ONE);
        if (out) {
            functions.put(X, oldXFunction);
        }
        return new FunctionResult(!out, value.currentIndex);
    }

    @Override
    protected FunctionResult run(int index) throws LoopFlag {
        if (index >= code.length()) {
            return new FunctionResult(null, index);
        }
        switch (code.charAt(index)) {
        case 'b':
            out = true;
        case 'B':
            index = code.length();
        }
        return super.run(index); // TODO args
    }

}

LoopFlag.java

package library;

public class LoopFlag extends Exception {

    private static final long serialVersionUID = 2275239478370374878L;

    private final Action action;

    public LoopFlag(Action action) {
        this.action = action;
    }

    public Action getAction() {
        return action;
    }

}

Type.java

package library;

import java.math.BigDecimal;
import java.math.BigInteger;

public enum Type {

    INTEGER(BigInteger.class), DECIMAL(BigDecimal.class), STRING(
            CharSequence.class), CHARACTER(Character.class), BOOLEAN(
                    Boolean.class), ARRAY(ArrayList.class), FUNCTION(
                            A_RayCode.class), OBJECT(Object.class);

    private final Class<?> c;

    private <T> Type(Class<T> c) {
        this.c = c;
    }

    public boolean isMatch(Object object) {
        switch (this) {
        case ARRAY:
        case BOOLEAN:
        case CHARACTER:
        case DECIMAL:
        case INTEGER:
        case STRING:
        case FUNCTION:
        case OBJECT:
            return c.isInstance(object);
        }
        return false;
    }

    public Class<?> getClassOfType() {
        return c;
    }

    public static Type getMatch(Object object) {
        for (Type type : Type.values()) {
            if (type.isMatch(object)) {
                return type;
            }
        }
        return OBJECT;
    }

}

The remaining library classes and the GUI class is available on GitHub (too much for one post).

Concerns:

  1. Is my structure good?
  2. Are there minor bugs I didn't notice?
  3. Anything else?
\$\endgroup\$
5
  • 9
    \$\begingroup\$ You're one of us now. \$\endgroup\$
    – Alex A.
    Jan 28, 2016 at 0:53
  • 11
    \$\begingroup\$ Writing good code to facilitate writing bad code? \$\endgroup\$
    – user34073
    Jan 28, 2016 at 0:56
  • \$\begingroup\$ So far as I understand you are implementing an interpreter for a functional language. Did I get it? \$\endgroup\$
    – oopexpert
    Feb 12, 2016 at 15:59
  • \$\begingroup\$ @oopexpert Yep. \$\endgroup\$ Feb 12, 2016 at 18:30
  • \$\begingroup\$ I'd consider it a bug (or at least a feature) that your languag only can parse single-digit numbers \$\endgroup\$
    – holroy
    Feb 23, 2016 at 20:23

1 Answer 1

4
\$\begingroup\$

Anonymous classes

Registering objects (functions) in a HashMap together with its character representation is ok. What I am missing here are structures that have expressive names for the functions. I'd resolve the anonymous class to a named class.

Avoid static initializer

As parsing itself is a complex thing you should keeps things around it as simple as possible. Static initializer were executed out of your control when the corresponding class is loaded. I do not say static initializer should never be used. But in a business context you should rely on constructors and methods in objects as the primary way to execute code.

Multiple parsing concepts

You are following different parsing concepts and your parsing is spread all over the place (inheritance, different methods and semantically different classes).

  1. Within your anonymous functions you parse parameters: "input.nextCharsUntil(Predicate)"
  2. Within the "A_RayCode.run(int index)"-method you are parsing bracket contents
  3. Within the LoopCode you prepend parsing in the overriden method "run(int index)" before you call super.

I'd like to have as less parsing concepts as possible. Harmonize your parsing through ONE general parrsing concept.

Favor composition over inheritance

You currently derive two levels from A_RayCode: A_RayCode<-LoopCode<-IteratorCode. I suggest to have a strategy pattern here.

Longer functions names

Your concept does not consider functions with names that have more than one characters. Introducing such functions you will have to think over everything. Even if this artifical language seems to suppose to have very short function names (at least one I guess) this can be a serious problem in real world parsing. You are closed for extension.

Responsibilities

Parsing responsibilities are divided and as I mentioned you have different parsing concepts. Furthermore you mixed parsing and execution.

    functions.put("C", new Function<BigInteger>(new Type[] {},
            new RunnableFunction<BigInteger>() {

                @Override
                public BigInteger run(List<Object> memory,
                        InputIterator input, StringBuilder output,
                        MutableObject temporaryVariable,
                        Map<String, Object> variables, Object[] args) {
                    return BigInteger.valueOf(input.next() /*parsing*/) /*execution*/;
                }

            }));

I suggest to have ready parsed parameters that will be given to the function and not let the function itself parse its parameters.

Separate following concerns:

  1. parsing
  2. interpretation
  3. execution

Currently you are not able to execute a function without parsing. Decoupling helps in different ways like in testing or reusablitity.

Example structure for parsing

I only will provide an example for parsing as this is often the most complex issue.

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;

public class NumberParser {


    public static void main(String[] args) {

        List<BigInteger> parse = new NumberParser("   10   22  32  ").parse();

        for (BigInteger bigInteger : parse) {
            System.out.println(bigInteger);
        }

    }


    private List<BigInteger> numbers;


    private final StringBuffer stringToParse;
    private final StringBuffer buffer;


    private State state;


    public NumberParser(String string) {
        this.stringToParse = new StringBuffer(string);
        this.buffer = new StringBuffer();
        this.setState(new Unknown());
    }


    private boolean hasCurrentChar() {
        return this.stringToParse.length() > 0;
    }


    private char removeCurrentChar() {
        if (hasCurrentChar()) {
            char ch = this.stringToParse.charAt(0);
            this.stringToParse.deleteCharAt(0);
            return ch;
        } else {
            throw new NoSuchElementException();
        }
    }

    private char currentChar() {
        if (this.stringToParse.length() > 0) {
            return this.stringToParse.charAt(0);
        } else {
            throw new NoSuchElementException();
        }
    }


    private void clearBuffer() {
        buffer.setLength(0);
    }


    private void recognizeNumber() {
        numbers.add(new BigInteger(buffer.toString()));
        clearBuffer();
    }


    public List<BigInteger> parse() {

        if (numbers == null) {

            this.numbers = new ArrayList<>();

            while (!(getState() instanceof End)) {
                getState().parse();
            }

        }

        return this.numbers;

    }


    private State getState() {
        return state;
    }


    private void setState(State state) {
        System.out.println(state.getStateInfo());
        this.state = state;
    }


    private interface State {
        public String getStateInfo();
        public void parse();
    }


    private interface End extends State {
    }


    private class Error implements End {

        @Override
        public String getStateInfo() {
            return "Something went wrong ...";
        }

        @Override
        public void parse() {
        }

    }


    private class NoMoreChars implements End {

        @Override
        public String getStateInfo() {
            return "No chars left.";
        }

        @Override
        public void parse() {
        }

    }


    private class RemoveWhiteSpaces implements State {

        @Override
        public String getStateInfo() {
            return "Removing white spaces.";
        }

        @Override
        public void parse() {

            if (hasCurrentChar()) {

                if (Character.isWhitespace(currentChar())) {
                    removeCurrentChar();
                } else {
                    setState(new Unknown());
                }

            } else {
                setState(new NoMoreChars());
            }

        }

    }


    private class Number implements State {

        @Override
        public String getStateInfo() {
            return "Parse digits.";
        }

        @Override
        public void parse() {

            if (hasCurrentChar()) {

                if (Character.isDigit(currentChar())) {
                    buffer.append(currentChar());
                    removeCurrentChar();
                } else {
                    recognizeNumber();
                    setState(new Unknown());
                }

            } else {
                recognizeNumber();
                setState(new NoMoreChars());
            }

        }

    }


    private class Unknown implements State {

        @Override
        public String getStateInfo() {
            return "Search ...";
        }

        @Override
        public void parse() {

            if (hasCurrentChar()) {

                if (Character.isWhitespace(currentChar())) {
                    setState(new RemoveWhiteSpaces());
                } else if (Character.isDigit(currentChar())){
                    setState(new Number());
                } else {
                    setState(new Error());
                }

            } else {
                setState(new NoMoreChars());
            }

        }

    }

}

This parser searches for numbers and returns them. Whitespaces separate numbers as whitespaces are allowed to occur multiple times. If you input alphanumeric characters the machine goes into the Error-State.

This example uses the state pattern to harmonize the parsing. This corresponds to a state chart (like UML state machine) that should be created before.

Interpreting

The interpreter will be informed by new semantic elements from the parser and decides when to put functions and parameters together so they can execute. This could be done with a standard listener pattern. The interpreter registers at the parser as a listener so it will get functions and parameters as they are available (parsed correctly). In the parser example provided we have to notify the listeners if a new number could be determined:

    private void recognizeNumber() {
        BigInteger bigInteger = new BigInteger(buffer.toString());
        numbers.add(bigInteger);
        clearBuffer();
        notifyOnRecognizeNumber(bigInteger); /* <-- notify listeners */
    }
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.