src/jdk.jcmd/share/classes/sun/tools/jstat/Parser.java
changeset 47216 71c04702a3d5
parent 37521 b6e0f285c998
child 49891 61b0342b5711
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jcmd/share/classes/sun/tools/jstat/Parser.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,585 @@
+/*
+ * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.tools.jstat;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * A class implementing a simple predictive parser for output format
+ * specification language for the jstat command.
+ *
+ * @author Brian Doherty
+ * @since 1.5
+ */
+public class Parser {
+
+    private static boolean pdebug = Boolean.getBoolean("jstat.parser.debug");
+    private static boolean ldebug = Boolean.getBoolean("jstat.lex.debug");
+
+    private static final char OPENBLOCK = '{';
+    private static final char CLOSEBLOCK = '}';
+    private static final char DOUBLEQUOTE = '"';
+    private static final char PERCENT_CHAR = '%';
+    private static final char OPENPAREN = '(';
+    private static final char CLOSEPAREN = ')';
+
+    private static final char OPERATOR_PLUS = '+';
+    private static final char OPERATOR_MINUS = '-';
+    private static final char OPERATOR_MULTIPLY = '*';
+    private static final char OPERATOR_DIVIDE = '/';
+
+    private static final String OPTION = "option";
+    private static final String COLUMN = "column";
+    private static final String DATA = "data";
+    private static final String HEADER = "header";
+    private static final String WIDTH = "width";
+    private static final String FORMAT = "format";
+    private static final String ALIGN = "align";
+    private static final String SCALE = "scale";
+
+    private static final String START = OPTION;
+
+    private static final Set<String> scaleKeyWords = Scale.keySet();
+    private static final Set<String> alignKeyWords = Alignment.keySet();
+    private static String[] otherKeyWords = {
+        OPTION, COLUMN, DATA, HEADER, WIDTH, FORMAT, ALIGN, SCALE
+    };
+
+    private static char[] infixOps = {
+        OPERATOR_PLUS, OPERATOR_MINUS, OPERATOR_MULTIPLY, OPERATOR_DIVIDE
+    };
+
+    private static char[] delimiters = {
+        OPENBLOCK, CLOSEBLOCK, PERCENT_CHAR, OPENPAREN, CLOSEPAREN
+    };
+
+
+    private static Set<String> reservedWords;
+
+    private StreamTokenizer st;
+    private String filename;
+    private Token lookahead;
+    private Token previous;
+    private int columnCount;
+    private OptionFormat optionFormat;
+
+    public Parser(String filename) throws FileNotFoundException {
+        this.filename = filename;
+        Reader r = new BufferedReader(new FileReader(filename));
+    }
+
+    public Parser(Reader r) {
+        st = new StreamTokenizer(r);
+
+        // allow both c++ style comments
+        st.ordinaryChar('/');
+        st.wordChars('_','_');
+        st.slashSlashComments(true);
+        st.slashStarComments(true);
+
+        reservedWords = new HashSet<String>();
+        for (int i = 0; i < otherKeyWords.length; i++) {
+            reservedWords.add(otherKeyWords[i]);
+        }
+
+        for (int i = 0; i < delimiters.length; i++ ) {
+            st.ordinaryChar(delimiters[i]);
+        }
+
+        for (int i = 0; i < infixOps.length; i++ ) {
+            st.ordinaryChar(infixOps[i]);
+        }
+    }
+
+    /**
+     * push back the lookahead token and restore the lookahead token
+     * to the previous token.
+     */
+    private void pushBack() {
+        lookahead = previous;
+        st.pushBack();
+    }
+
+    /**
+     * retrieve the next token, placing the token value in the lookahead
+     * member variable, storing its previous value in the previous member
+     * variable.
+     */
+    private void nextToken() throws ParserException, IOException {
+        int t = st.nextToken();
+        previous = lookahead;
+        lookahead = new Token(st.ttype, st.sval, st.nval);
+        log(ldebug, "lookahead = " + lookahead);
+    }
+
+    /**
+     * match one of the token values in the given set of key words
+     * token is assumed to be of type TT_WORD, and the set is assumed
+     * to contain String objects.
+     */
+    private Token matchOne(Set<String> keyWords) throws ParserException, IOException {
+        if ((lookahead.ttype == StreamTokenizer.TT_WORD)
+                && keyWords.contains(lookahead.sval)) {
+            Token t = lookahead;
+            nextToken();
+            return t;
+        }
+        throw new SyntaxException(st.lineno(), keyWords, lookahead);
+    }
+
+    /**
+     * match a token with TT_TYPE=type, and the token value is a given sequence
+     * of characters.
+     */
+    private void match(int ttype, String token)
+                 throws ParserException, IOException {
+        if (lookahead.ttype == ttype && lookahead.sval.compareTo(token) == 0) {
+            nextToken();
+        } else {
+           throw new SyntaxException(st.lineno(), new Token(ttype, token),
+                                     lookahead);
+        }
+    }
+
+    /**
+     * match a token with TT_TYPE=type
+     */
+    private void match(int ttype) throws ParserException, IOException {
+        if (lookahead.ttype == ttype) {
+            nextToken();
+        } else {
+           throw new SyntaxException(st.lineno(), new Token(ttype), lookahead);
+        }
+    }
+
+    /**
+     * match a token with TT_TYPE=char, where the token value is the given char.
+     */
+    private void match(char ttype) throws ParserException, IOException {
+      if (lookahead.ttype == (int)ttype) {
+          nextToken();
+      }
+      else {
+          throw new SyntaxException(st.lineno(), new Token((int)ttype),
+                                    lookahead);
+      }
+    }
+
+    /**
+     * match a token with TT_TYPE='"', where the token value is a sequence
+     * of characters between matching quote characters.
+     */
+    private void matchQuotedString() throws ParserException, IOException {
+        match(DOUBLEQUOTE);
+    }
+
+    /**
+     * match a TT_NUMBER token that matches a parsed number value
+     */
+    private void matchNumber() throws ParserException, IOException {
+        match(StreamTokenizer.TT_NUMBER);
+    }
+
+    /**
+     * match a TT_WORD token that matches an arbitrary, not quoted token.
+     */
+    private void matchID() throws ParserException, IOException {
+        match(StreamTokenizer.TT_WORD);
+    }
+
+    /**
+     * match a TT_WORD token that matches the given string
+     */
+    private void match(String token) throws ParserException, IOException {
+        match(StreamTokenizer.TT_WORD, token);
+    }
+
+    /**
+     * determine if the given word is a reserved key word
+     */
+    private boolean isReservedWord(String word) {
+        return reservedWords.contains(word);
+    }
+
+    /**
+     * determine if the give work is a reserved key word
+     */
+    private boolean isInfixOperator(char op) {
+        for (int i = 0; i < infixOps.length; i++) {
+            if (op == infixOps[i]) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * scalestmt -> 'scale' scalespec
+     * scalespec -> <see above scaleTerminals array>
+     */
+    private void scaleStmt(ColumnFormat cf)
+                 throws ParserException, IOException {
+        match(SCALE);
+        Token t = matchOne(scaleKeyWords);
+        cf.setScale(Scale.toScale(t.sval));
+        String scaleString = t.sval;
+        log(pdebug, "Parsed: scale -> " + scaleString);
+    }
+
+    /**
+     * alignstmt -> 'align' alignspec
+     * alignspec -> <see above alignTerminals array>
+     */
+    private void alignStmt(ColumnFormat cf)
+                 throws ParserException, IOException {
+        match(ALIGN);
+        Token t = matchOne(alignKeyWords);
+        cf.setAlignment(Alignment.toAlignment(t.sval));
+        String alignString = t.sval;
+        log(pdebug, "Parsed: align -> " + alignString);
+    }
+
+    /**
+     * headerstmt -> 'header' quotedstring
+     */
+    private void headerStmt(ColumnFormat cf)
+                 throws ParserException, IOException {
+        match(HEADER);
+        String headerString = lookahead.sval;
+        matchQuotedString();
+        cf.setHeader(headerString);
+        log(pdebug, "Parsed: header -> " + headerString);
+    }
+
+    /**
+     * widthstmt -> 'width' integer
+     */
+    private void widthStmt(ColumnFormat cf)
+                 throws ParserException, IOException {
+        match(WIDTH);
+        double width = lookahead.nval;
+        matchNumber();
+        cf.setWidth((int)width);
+        log(pdebug, "Parsed: width -> " + width );
+    }
+
+    /**
+     * formatstmt -> 'format' quotedstring
+     */
+    private void formatStmt(ColumnFormat cf)
+                 throws ParserException, IOException {
+        match(FORMAT);
+        String formatString = lookahead.sval;
+        matchQuotedString();
+        cf.setFormat(formatString);
+        log(pdebug, "Parsed: format -> " + formatString);
+    }
+
+    /**
+     *  Primary -> Literal | Identifier | '(' Expression ')'
+     */
+    private Expression primary() throws ParserException, IOException {
+        Expression e = null;
+
+        switch (lookahead.ttype) {
+        case OPENPAREN:
+            match(OPENPAREN);
+            e = expression();
+            match(CLOSEPAREN);
+            break;
+        case StreamTokenizer.TT_WORD:
+            String s = lookahead.sval;
+            if (isReservedWord(s)) {
+                throw new SyntaxException(st.lineno(), "IDENTIFIER",
+                                          "Reserved Word: " + lookahead.sval);
+            }
+            matchID();
+            e = new Identifier(s);
+            log(pdebug, "Parsed: ID -> " + s);
+            break;
+        case StreamTokenizer.TT_NUMBER:
+            double literal = lookahead.nval;
+            matchNumber();
+            e = new Literal(Double.valueOf(literal));
+            log(pdebug, "Parsed: number -> " + literal);
+            break;
+        default:
+            throw new SyntaxException(st.lineno(), "IDENTIFIER", lookahead);
+        }
+        log(pdebug, "Parsed: primary -> " + e);
+        return e;
+    }
+
+    /**
+     * Unary -> ('+'|'-') Unary | Primary
+     */
+    private Expression unary() throws ParserException, IOException {
+        Expression e = null;
+        Operator op = null;
+
+        while (true) {
+            switch (lookahead.ttype) {
+            case OPERATOR_PLUS:
+                match(OPERATOR_PLUS);
+                op = Operator.PLUS;
+                break;
+            case OPERATOR_MINUS:
+                match(OPERATOR_MINUS);
+                op = Operator.MINUS;
+                break;
+            default:
+                e = primary();
+                log(pdebug, "Parsed: unary -> " + e);
+                return e;
+            }
+            Expression e1 = new Expression();
+            e1.setOperator(op);
+            e1.setRight(e);
+            log(pdebug, "Parsed: unary -> " + e1);
+            e1.setLeft(new Literal(Double.valueOf(0)));
+            e = e1;
+        }
+    }
+
+    /**
+     *  MultExpression -> Unary (('*' | '/') Unary)*
+     */
+    private Expression multExpression() throws ParserException, IOException {
+        Expression e = unary();
+        Operator op = null;
+
+        while (true) {
+            switch (lookahead.ttype) {
+            case OPERATOR_MULTIPLY:
+                match(OPERATOR_MULTIPLY);
+                op = Operator.MULTIPLY;
+                break;
+            case OPERATOR_DIVIDE:
+                match(OPERATOR_DIVIDE);
+                op = Operator.DIVIDE;
+                break;
+            default:
+                log(pdebug, "Parsed: multExpression -> " + e);
+                return e;
+            }
+            Expression e1 = new Expression();
+            e1.setOperator(op);
+            e1.setLeft(e);
+            e1.setRight(unary());
+            e = e1;
+            log(pdebug, "Parsed: multExpression -> " + e);
+        }
+    }
+
+    /**
+     *  AddExpression -> MultExpression (('+' | '-') MultExpression)*
+     */
+    private Expression addExpression() throws ParserException, IOException {
+        Expression e = multExpression();
+        Operator op = null;
+
+        while (true) {
+            switch (lookahead.ttype) {
+            case OPERATOR_PLUS:
+                match(OPERATOR_PLUS);
+                op = Operator.PLUS;
+                break;
+            case OPERATOR_MINUS:
+                match(OPERATOR_MINUS);
+                op = Operator.MINUS;
+                break;
+            default:
+                log(pdebug, "Parsed: addExpression -> " + e);
+                return e;
+            }
+            Expression e1 = new Expression();
+            e1.setOperator(op);
+            e1.setLeft(e);
+            e1.setRight(multExpression());
+            e = e1;
+            log(pdebug, "Parsed: addExpression -> " + e);
+        }
+    }
+
+    /**
+     *  Expression -> AddExpression
+     */
+    private Expression expression() throws ParserException, IOException {
+        Expression e = addExpression();
+        log(pdebug, "Parsed: expression -> " + e);
+        return e;
+    }
+
+    /**
+     * datastmt -> 'data' expression
+     */
+    private void dataStmt(ColumnFormat cf) throws ParserException, IOException {
+        match(DATA);
+        Expression e = expression();
+        cf.setExpression(e);
+        log(pdebug, "Parsed: data -> " + e);
+    }
+
+    /**
+     * statementlist -> optionalstmt statementlist
+     * optionalstmt -> 'data' expression
+     *                 'header' quotedstring
+     *                 'width' integer
+     *                 'format' formatstring
+     *                 'align' alignspec
+     *                 'scale' scalespec
+     */
+    private void statementList(ColumnFormat cf)
+                 throws ParserException, IOException {
+        while (true) {
+            if (lookahead.ttype != StreamTokenizer.TT_WORD) {
+                return;
+            }
+
+            if (lookahead.sval.compareTo(DATA) == 0) {
+                dataStmt(cf);
+            } else if (lookahead.sval.compareTo(HEADER) == 0) {
+                headerStmt(cf);
+            } else if (lookahead.sval.compareTo(WIDTH) == 0) {
+                widthStmt(cf);
+            } else if (lookahead.sval.compareTo(FORMAT) == 0) {
+                formatStmt(cf);
+            } else if (lookahead.sval.compareTo(ALIGN) == 0) {
+                alignStmt(cf);
+            } else if (lookahead.sval.compareTo(SCALE) == 0) {
+                scaleStmt(cf);
+            } else {
+                return;
+            }
+        }
+    }
+
+    /**
+     * optionlist -> columspec optionlist
+     *               null
+     * columspec -> 'column' '{' statementlist '}'
+     */
+    private void optionList(OptionFormat of)
+                 throws ParserException, IOException {
+        while (true) {
+            if (lookahead.ttype != StreamTokenizer.TT_WORD) {
+                return;
+            }
+
+            match(COLUMN);
+            match(OPENBLOCK);
+            ColumnFormat cf = new ColumnFormat(columnCount++);
+            statementList(cf);
+              match(CLOSEBLOCK);
+            cf.validate();
+            of.addSubFormat(cf);
+        }
+    }
+
+    /**
+     * optionstmt -> 'option' ID '{' optionlist '}'
+     */
+    private OptionFormat optionStmt() throws ParserException, IOException {
+        match(OPTION);
+        String optionName=lookahead.sval;
+        matchID();
+        match(OPENBLOCK);
+        OptionFormat of = new OptionFormat(optionName);
+        optionList(of);
+        match(CLOSEBLOCK);
+        return of;
+    }
+
+    /**
+     * parse the specification for the given option identifier
+     */
+    public OptionFormat parse(String option)
+                        throws ParserException, IOException {
+        nextToken();
+
+        /*
+         * this search stops on the first occurance of an option
+         * statement with a name matching the given option. Any
+         * duplicate options are ignored.
+         */
+        while (lookahead.ttype != StreamTokenizer.TT_EOF) {
+            // look for the start symbol
+            if ((lookahead.ttype != StreamTokenizer.TT_WORD)
+                    || (lookahead.sval.compareTo(START) != 0)) {
+                // skip tokens until a start symbol is found
+                nextToken();
+                continue;
+            }
+
+            // check if the option name is the one we are interested in
+            match(START);
+
+            if ((lookahead.ttype == StreamTokenizer.TT_WORD)
+                    && (lookahead.sval.compareTo(option) == 0)) {
+                // this is the one we are looking for, parse it
+                pushBack();
+                return optionStmt();
+            } else {
+                // not what we are looking for, start skipping tokens
+                nextToken();
+            }
+        }
+        return null;
+    }
+
+    public Set<OptionFormat> parseOptions() throws ParserException, IOException {
+        Set<OptionFormat> options = new HashSet<OptionFormat>();
+
+        nextToken();
+
+        while (lookahead.ttype != StreamTokenizer.TT_EOF) {
+            // look for the start symbol
+            if ((lookahead.ttype != StreamTokenizer.TT_WORD)
+                    || (lookahead.sval.compareTo(START) != 0)) {
+                // skip tokens until a start symbol is found
+                nextToken();
+                continue;
+            }
+
+            // note: if a duplicate option statement exists, then
+            // first one encountered is the chosen definition.
+            OptionFormat of = optionStmt();
+            options.add(of);
+        }
+        return options;
+    }
+
+    OptionFormat getOptionFormat() {
+       return optionFormat;
+    }
+
+    private void log(boolean logging, String s) {
+        if (logging) {
+            System.out.println(s);
+        }
+    }
+}