SLAN in Java

Frank Mitchell

Posted: 2025-06-08
Last Modified: 2025-06-08
Word Count: 783
Tags: java programming

Table of Contents

These are the Java APIs that might implement SLAN.

SLANPP: The SLAN Pull Parser

One might write a Pull Parser to parse SLAN with an interface like this.

public interface SlanPullParser {
    public enum SlanEvent {
        SYNTAX ERROR,
        START_STREAM,
        START_LIST,
        END_LIST,
        SYMBOL,
        STRING,
        NUMBER,
        BOOLEAN_TRUE,
        BOOLEAN_FALSE,
        EMPTY_LIST,
        END_STREAM
    }

    public boolean hasNext() throws IOException;

    public void next() throws IOException;

    public SlanEvent getEvent();

    public String getString();    // value of SYMBOL or STRING

    public boolean isNumber();    // if NUMBER or maybe number-like STRING

    public Number getNumber();    // converts string ... if possible

    public boolean isTrue();      // not BOOLEAN_FALSE or EMPTY_LIST
}

And here’s one way one might use it:

try {
    while (parser.hasNext()) {
        parser.next()
        switch (parser.getEvent())
            case START_STREAM:
                ...
                break;
            case START_LIST:
                ...
                break;
            case STRING:
                String str = parser.getString();
                ...
                break;
            case SYMBOL:
                String sym = parser.getSymbol();
                ...
                break;
            case BOOLEAN_TRUE:
            case BOOLEAN_FALSE:
            case EMPTY_LIST:
                boolean flag = parser.isTrue();
                ...
                break;
            case END_LIST:
                ...
                break;
            case END_STREAM:
                ...
                break;
            default:
                // ERROR ERROR ERROR
                ...
                break;
        }
    }
} finally {
    parser.close();
}

SLAN-List: Linked List

A parser could also simply build nested lists, just like John McCarthy used to make. The application would have to walk it just like any other parse tree.

public interface SlanValue {
    enum Type { LIST, SYMBOL, STRING, NUMBER, BOOLEAN, EMPTY_LIST }

    public Type getType();

    public String getSymbol();    // SYMBOL (using String#intern())

    public String getString();    // SYMBOL, STRING, NUMBER, BOOLEAN (Java)

    public boolean isNumber();    // if NUMBER

    public Number getNumber();    // NUMBER

    public Boolean isTrue();      // any (true if not `#f` or `()`)

    public boolean isList();      // if LIST or EMPTY_LIST

    public SlanValue getHead();   // start of LIST (null if `()`)

    public SlanValue getTail();   // rest of LIST (null if end of list)

    public String toString();     // serializes self as SLAN
}

SLAN-B: The SLAN Builder

This interface allows one to build a SLAN document with methods that (almost) guarantee valid SLAN at the end.

The API providing a Builder assumes responsibility for writing the SLAN out to a file or URL, flushing the connection, and closing it properly.

public interface SlanBuilder {
    /**
     * Add a String.
     * The builder will escape any characters necessary.
     * @param value string
     * @return this
     */
    SlanBuilder addString(String value);

    /**
     * Add a symbol.
     * If value contains characters not allowed in a symbol the
     * resulting symbol will substitute '_'.
     * @param value symbol
     * @return this
     */
    SlanBuilder addSymbol(String value);

    /**
     * Add a number.
     * If a Double or Float, a NaN or +/-Infinity value will substitute
     * the Empty List.
     * @param value number 
     * @return this
     */
    SlanBuilder addNumber(Number value);

    /**
     * Add a SLAN Boolean, "#t" or "#f".
     * @param value boolean value
     * @return this
     */
    SlanBuilder addBoolean(boolean value);

    /**
     * Add an Empty List "()".
     * @return this
     */
    SlanBuilder addEmptyList();

    /**
     * Begin nested list. 
     * Subsequent calls until {@link #addListEnd} add to the nested list.
     * @return a builder for the new list
     */
    SlanBuilder openList();

    /**
     * End nested list.
     * @return the builder for the parent list.
     */
    SlanBuilder closeList();

    /** 
     * Calls a procedure that builds a sublist.
     * Roughly equivalent to:
     * <pre>
     *    this.openList(); 
     *    proc.build(this);
     *    this.closeList();
     * </pre>
     */
    default SlanBuilder addList(SlanBuilder.Procedure proc) {
        SlanBuilder result = this;
        try {
            result = this.openList();
            proc.build(result);
        } finally {
            result = closeList();
        }
        return result;
    }

    /**
     * Interface for Ruby-like method procedures.
     */
    @FunctionalInterface
    public interface Procedure {
        void build(SlanBuilder builder);
    }
}

Other Implementations

One could write SLAN parsers and emitters in any other language: C, Go, Python, Ruby, even Scheme. The APIs might vaguely resemble the ones above, adjusted for the capabilities and idioms of the language.

MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.