java/ch/wlkl/wsm/Parser.java

package ch.wlkl.wsm;

import static ch.wlkl.env.Env.*;

import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Parser {
    public static final Pattern eol = Pattern.compile("(\r\n)|\r|\n");
    public static final Pattern space = Pattern.compile("\\s+");
    public static final Pattern notSpace = Pattern.compile("\\S+");
    public static final Pattern notStarSlash = Pattern.compile("[^/*]*");
    public static final Pattern number = Pattern.compile("[+-]?((\\d+(\\.\\d*)?)|(\\.\\d+))");
    public static final Pattern name = Pattern.compile("[\\w&&[^\\d]][\\w]*");
    public static final Pattern string = Pattern.compile("\"([^\\\\\"]|\\\\.)*\"");
    public static final Pattern shWord = Pattern.compile("[^${}=�:\\s]+");

    int cx;
    public String src, tok;
    Matcher match = space.matcher("");

    public Parser(String s) {
        reset(s);
    }


    void reset (String s) {
        src = s;
        match.reset(src);
        cx = 0;
        tok = null;
    }
    

    public boolean next(Pattern pat) {
        match.region(cx, src.length());
        match.usePattern(pat);
        if (match.lookingAt()) {
            tok = match.group();
            cx = match.end();
            return true;
        } else {
            tok = null;
            return false;
        }
    }

    public void back() {
        cx -= tok.length();
        tok = null;
    }

    public String name(String... names) {
        int old = cx;
        if (next(name)) {
            for (String nm : names) {
                if (tok.equals(nm))
                    return nm;
            }
        }
        cx = old;
        fail(Arrays.toString(names) + " expected");
        return null;
    }

    public String look() {
        return src.substring(cx);
    }

    public String look(int len) {
        return src.substring(cx, cx + len > src.length() ? src.length() : cx + len);
    }

    public boolean lit(String lit) {
        if (cx + lit.length() > src.length() || ! lit.equals(src.substring(cx, cx +lit.length()))) {
            tok = null;
            return false;
        } else {
            tok = lit;
            cx += lit.length();
            return true;
        }
    }
    
    public boolean comment() {
        if  (!  lit("/*")) 
            return false;
        int depth = 1;
        while (depth > 0) {
            next(notStarSlash);
            if  (lit("/*"))
                depth++;
            else if  (lit("*/"))
                depth--;
        }
        return true;
    }
    
    public boolean spaceComment() {
        boolean found = false;
        while (next(space) || comment())
            found = true;
        return found;
    }
    
    public boolean spaceNlComment() {
        boolean found = false;
        while (next(space) || comment())
            found = true;
        return found;
    }

    public boolean end() {
        return cx >= src.length();
    }
    public Parser skip() {
        spaceNlComment();
        return this;
    }
    
    
    public void fail(String msg) {
        err(msg + "\ntok " + tok + " cx " + cx + ": " + src.substring(cx) +  "\nin line " + src);
    }

    final private static Matcher xQuoteMatcher = Pattern.compile("[^\"\\\\\n]+").matcher("");
    public static String xQuote(String src) {
        if (src == null)
            return "\"\"";
        StringBuffer buf = new StringBuffer("\"");
        int nx = 0;
        xQuoteMatcher.reset(src);
        while (true) {
            if (xQuoteMatcher.lookingAt()) {
                buf.append(xQuoteMatcher.group());
                nx = xQuoteMatcher.end();
            } 
            if (nx >= src.length())
                return buf.append('"').toString();
            buf.append('\\').append(src.charAt(nx) == '\n' ? 'n' : src.charAt(nx));
            xQuoteMatcher.region(++nx, src.length());
        }
    }

    public static String xUnquote(String src) {
        if (src == null || src.length() <= 1)
            err("cannot unquote this string: <" + src + ">");
        
        StringBuffer buf = new StringBuffer("");
        final char qu = src.charAt(0);
        char c = 0;
        int sx = 1, qx = 0, bx = 0; 
        
        while (true) {
            if (qx < sx)
                if ((qx = src.indexOf(qu, sx)) < 0)
                    qx = src.length();
            if (bx < sx)
                if ((bx = src.indexOf('\\', sx)) < 0)
                    bx = src.length();
            if (bx < qx) {
                if (bx + 1>= src.length())
                    err("backslash at end of String: <" + src + ">");
                buf.append(src, sx, bx).append((c = src.charAt(bx+1)) == 'n' ? '\n' : c == 't' ? 't' : c);
                sx = bx+2;
            } else {
                if (qx + 1 == src.length())
                    return buf.append(src, sx, qx).toString();
                else 
                    err((qx  >= src.length() ? "ending quote missing " : "quote with") + "in String: <" + src + ">");
            }
        }
    }

}