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