package org.yinwang.yin.parser;

import org.yinwang.yin.Constants;
import org.yinwang.yin.Util;
import org.yinwang.yin.ast.Delimeter;
import org.yinwang.yin.ast.Name;
import org.yinwang.yin.ast.Node;
import org.yinwang.yin.ast.Tuple;

import java.util.ArrayList;
import java.util.List;


/**
 * parse text into a meanlingless but more structured format
 * similar to S-expressions but with less syntax
 * (just matched (..) [..] and {..})
 */
public class PreParser {

    String file;
    Lexer lexer;


    public PreParser(String file) {
        this.file = Util.unifyPath(file);
        this.lexer = new Lexer(file);
    }


    /**
     * Get next node from token stream
     */
    public Node nextNode() throws ParserException {
        return nextNode1(0);
    }


    /**
     * Helper for nextNode, which does the real work
     *
     * @return a Node or null if file ends
     */
    public Node nextNode1(int depth) throws ParserException {
        Node first = lexer.nextToken();

        // end of file
        if (first == null) {
            return null;
        }

        if (Delimeter.isOpen(first)) {   // try to get matched (...)
            List<Node> elements = new ArrayList<>();
            Node next;
            for (next = nextNode1(depth + 1);
                 !Delimeter.match(first, next);
                 next = nextNode1(depth + 1))
            {
                if (next == null) {
                    throw new ParserException("unclosed delimeter: " + first.toString(), first);
                } else if (Delimeter.isClose(next)) {
                    throw new ParserException("unmatched closing delimeter: " +
                            next.toString() + " does not close " + first.toString(), next);
                } else {
                    elements.add(next);
                }
            }
            return new Tuple(elements, first, next, first.file, first.start, next.end, first.line, first.col);
        } else if (depth == 0 && Delimeter.isClose(first)) {
            throw new ParserException("unmatched closing delimeter: " + first.toString() +
                    " does not close any open delimeter", first);
        } else {
            return first;
        }
    }


    /**
     * Parse file into a Node
     *
     * @return a Tuple containing the file's parse tree
     */
    public Node parse() throws ParserException {
        List<Node> elements = new ArrayList<>();
        elements.add(Name.genName(Constants.SEQ_KEYWORD));      // synthetic block keyword

        Node s = nextNode();
        Node first = s;
        Node last = null;
        for (; s != null; last = s, s = nextNode()) {
            elements.add(s);
        }

        return new Tuple(
                elements,
                Name.genName(Constants.PAREN_BEGIN),
                Name.genName(Constants.PAREN_END),
                file,
                first == null ? 0 : first.start,
                last == null ? 0 : last.end,
                0, 0
        );
    }


    public static void main(String[] args) throws ParserException {
        PreParser p = new PreParser(args[0]);
        Util.msg("preparser result: " + p.parse());
    }

}

@@ -1468,32 +1468,49 @@
 closed delimeter
+ till end of file
 : %22 + first.toSt

improve error message