HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Golf some Code with A-Ray

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
golfwithraysomecode

Problem

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{pfpb}{?=%t30{pf} ?=%t50{pb} }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{pfpb}{?=%t30{pf} ?=%t50{pb} }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.

Solution

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).

  • Within your anonymous functions you parse parameters: "input.nextCharsUntil(Predicate)"



  • Within the "A_RayCode.run(int index)"-method you are parsing bracket contents



  • 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

  • interpretation



  • 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 parse = new NumberParser(" 10 22 32 ").parse();

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

}

private List 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 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());

Code Snippets

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*/;
                }

            }));
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());
               
private void recognizeNumber() {
        BigInteger bigInteger = new BigInteger(buffer.toString());
        numbers.add(bigInteger);
        clearBuffer();
        notifyOnRecognizeNumber(bigInteger); /* <-- notify listeners */
    }

Context

StackExchange Code Review Q#118099, answer score: 4

Revisions (0)

No revisions yet.