LexerStack.java

/*
 * Copyright 2017 Michael Mackenzie High
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mackenziehigh.sexpr.internal;

import com.mackenziehigh.sexpr.SAtom;
import com.mackenziehigh.sexpr.SList;
import com.mackenziehigh.sexpr.Sexpr;
import com.mackenziehigh.sexpr.SourceLocation;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedList;

/**
 * Used by the Lexer in order to transform <code>String</code>s into <code>SList</code>s.
 */
final class LexerStack
{
    private final Sexpr MARKER = SAtom.fromString("BEGIN");

    private final Deque<Sexpr<?>> stack = new ArrayDeque<>();

    private final Deque<SourceLocation> locations = new ArrayDeque<>();

    private volatile String source;

    public void setSource (final String source)
    {
        this.source = source;
    }

    public void parenOpen (final int line,
                           final int column)
    {
        stack.push(MARKER);
        locations.push(new SourceLocation(source, line, column));
    }

    public void parenClose ()
    {
        final LinkedList<Sexpr<?>> elements = new LinkedList<>();

        while (!stack.isEmpty() && stack.peek() != MARKER) // identity equality
        {
            elements.addFirst(stack.pop());
        }

        if (stack.isEmpty())
        {
            throw new IllegalStateException("Unbalanced call to end()");
        }
        else
        {
            stack.pop();
        }
        final SourceLocation location = locations.pop();
        final SList list = SList.copyOf(location, elements);
        stack.push(list);
    }

    public Sexpr<?> top ()
    {
        final Sexpr<?> result = stack.isEmpty() ? null : stack.peek();
        return result;
    }

    public int size ()
    {
        return stack.size();
    }

    public void pushAtomForm1 (final String yytext,
                               final int yyline,
                               final int yycolum)
    {
        final int length = yytext.length();
        final String text = yytext.substring(2, length - 1);
        final SourceLocation location = new SourceLocation(source, yyline, yycolum);
        final SAtom atom = SAtom.fromString(location, text);
        stack.push(atom);
    }

    public void pushAtomForm2 (final String yytext,
                               final int yyline,
                               final int yycolum)
    {
        final int length = yytext.length();
        final String text = yytext.substring(2, length - 1);
        final SourceLocation location = new SourceLocation(source, yyline, yycolum);
        final SAtom atom = SAtom.fromString(location, text);
        stack.push(atom);
    }

    public void pushAtomForm3 (final String yytext,
                               final int yyline,
                               final int yycolum)
    {
        final int length = yytext.length();
        final String text = yytext.substring(1, length - 1);
        final String expanded = new String(Escaper.instance.expand(text));
        final SourceLocation location = new SourceLocation(source, yyline, yycolum);
        final SAtom atom = SAtom.fromString(location, expanded);
        stack.push(atom);
    }

    public void pushAtomForm4 (final String yytext,
                               final int yyline,
                               final int yycolum)
    {
        final int length = yytext.length();
        final String text = yytext.substring(1, length - 1);
        final String expanded = new String(Escaper.instance.expand(text));
        final SourceLocation location = new SourceLocation(source, yyline, yycolum);
        final SAtom atom = SAtom.fromString(location, expanded);
        stack.push(atom);
    }

    public void pushAtomForm5 (final String yytext,
                               final int yyline,
                               final int yycolum)
    {
        final SourceLocation location = new SourceLocation(source, yyline, yycolum);
        final SAtom atom = SAtom.fromString(location, yytext);
        stack.push(atom);
    }
}