Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/tools/jstat/Parser.java
38918 views
/*1* Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.tools.jstat;2627import java.io.*;28import java.util.*;2930/**31* A class implementing a simple predictive parser for output format32* specification language for the jstat command.33*34* @author Brian Doherty35* @since 1.536*/37public class Parser {3839private static boolean pdebug = Boolean.getBoolean("jstat.parser.debug");40private static boolean ldebug = Boolean.getBoolean("jstat.lex.debug");4142private static final char OPENBLOCK = '{';43private static final char CLOSEBLOCK = '}';44private static final char DOUBLEQUOTE = '"';45private static final char PERCENT_CHAR = '%';46private static final char OPENPAREN = '(';47private static final char CLOSEPAREN = ')';4849private static final char OPERATOR_PLUS = '+';50private static final char OPERATOR_MINUS = '-';51private static final char OPERATOR_MULTIPLY = '*';52private static final char OPERATOR_DIVIDE = '/';5354private static final String OPTION = "option";55private static final String COLUMN = "column";56private static final String DATA = "data";57private static final String HEADER = "header";58private static final String WIDTH = "width";59private static final String FORMAT = "format";60private static final String ALIGN = "align";61private static final String SCALE = "scale";6263private static final String START = OPTION;6465private static final Set scaleKeyWords = Scale.keySet();66private static final Set alignKeyWords = Alignment.keySet();67private static String[] otherKeyWords = {68OPTION, COLUMN, DATA, HEADER, WIDTH, FORMAT, ALIGN, SCALE69};7071private static char[] infixOps = {72OPERATOR_PLUS, OPERATOR_MINUS, OPERATOR_MULTIPLY, OPERATOR_DIVIDE73};7475private static char[] delimiters = {76OPENBLOCK, CLOSEBLOCK, PERCENT_CHAR, OPENPAREN, CLOSEPAREN77};787980private static Set<String> reservedWords;8182private StreamTokenizer st;83private String filename;84private Token lookahead;85private Token previous;86private int columnCount;87private OptionFormat optionFormat;8889public Parser(String filename) throws FileNotFoundException {90this.filename = filename;91Reader r = new BufferedReader(new FileReader(filename));92}9394public Parser(Reader r) {95st = new StreamTokenizer(r);9697// allow both c++ style comments98st.ordinaryChar('/');99st.wordChars('_','_');100st.slashSlashComments(true);101st.slashStarComments(true);102103reservedWords = new HashSet<String>();104for (int i = 0; i < otherKeyWords.length; i++) {105reservedWords.add(otherKeyWords[i]);106}107108for (int i = 0; i < delimiters.length; i++ ) {109st.ordinaryChar(delimiters[i]);110}111112for (int i = 0; i < infixOps.length; i++ ) {113st.ordinaryChar(infixOps[i]);114}115}116117/**118* push back the lookahead token and restore the lookahead token119* to the previous token.120*/121private void pushBack() {122lookahead = previous;123st.pushBack();124}125126/**127* retrieve the next token, placing the token value in the lookahead128* member variable, storing its previous value in the previous member129* variable.130*/131private void nextToken() throws ParserException, IOException {132int t = st.nextToken();133previous = lookahead;134lookahead = new Token(st.ttype, st.sval, st.nval);135log(ldebug, "lookahead = " + lookahead);136}137138/**139* match one of the token values in the given set of key words140* token is assumed to be of type TT_WORD, and the set is assumed141* to contain String objects.142*/143private Token matchOne(Set keyWords) throws ParserException, IOException {144if ((lookahead.ttype == StreamTokenizer.TT_WORD)145&& keyWords.contains(lookahead.sval)) {146Token t = lookahead;147nextToken();148return t;149}150throw new SyntaxException(st.lineno(), keyWords, lookahead);151}152153/**154* match a token with TT_TYPE=type, and the token value is a given sequence155* of characters.156*/157private void match(int ttype, String token)158throws ParserException, IOException {159if (lookahead.ttype == ttype && lookahead.sval.compareTo(token) == 0) {160nextToken();161} else {162throw new SyntaxException(st.lineno(), new Token(ttype, token),163lookahead);164}165}166167/**168* match a token with TT_TYPE=type169*/170private void match(int ttype) throws ParserException, IOException {171if (lookahead.ttype == ttype) {172nextToken();173} else {174throw new SyntaxException(st.lineno(), new Token(ttype), lookahead);175}176}177178/**179* match a token with TT_TYPE=char, where the token value is the given char.180*/181private void match(char ttype) throws ParserException, IOException {182if (lookahead.ttype == (int)ttype) {183nextToken();184}185else {186throw new SyntaxException(st.lineno(), new Token((int)ttype),187lookahead);188}189}190191/**192* match a token with TT_TYPE='"', where the token value is a sequence193* of characters between matching quote characters.194*/195private void matchQuotedString() throws ParserException, IOException {196match(DOUBLEQUOTE);197}198199/**200* match a TT_NUMBER token that matches a parsed number value201*/202private void matchNumber() throws ParserException, IOException {203match(StreamTokenizer.TT_NUMBER);204}205206/**207* match a TT_WORD token that matches an arbitrary, not quoted token.208*/209private void matchID() throws ParserException, IOException {210match(StreamTokenizer.TT_WORD);211}212213/**214* match a TT_WORD token that matches the given string215*/216private void match(String token) throws ParserException, IOException {217match(StreamTokenizer.TT_WORD, token);218}219220/**221* determine if the given word is a reserved key word222*/223private boolean isReservedWord(String word) {224return reservedWords.contains(word);225}226227/**228* determine if the give work is a reserved key word229*/230private boolean isInfixOperator(char op) {231for (int i = 0; i < infixOps.length; i++) {232if (op == infixOps[i]) {233return true;234}235}236return false;237}238239/**240* scalestmt -> 'scale' scalespec241* scalespec -> <see above scaleTerminals array>242*/243private void scaleStmt(ColumnFormat cf)244throws ParserException, IOException {245match(SCALE);246Token t = matchOne(scaleKeyWords);247cf.setScale(Scale.toScale(t.sval));248String scaleString = t.sval;249log(pdebug, "Parsed: scale -> " + scaleString);250}251252/**253* alignstmt -> 'align' alignspec254* alignspec -> <see above alignTerminals array>255*/256private void alignStmt(ColumnFormat cf)257throws ParserException, IOException {258match(ALIGN);259Token t = matchOne(alignKeyWords);260cf.setAlignment(Alignment.toAlignment(t.sval));261String alignString = t.sval;262log(pdebug, "Parsed: align -> " + alignString);263}264265/**266* headerstmt -> 'header' quotedstring267*/268private void headerStmt(ColumnFormat cf)269throws ParserException, IOException {270match(HEADER);271String headerString = lookahead.sval;272matchQuotedString();273cf.setHeader(headerString);274log(pdebug, "Parsed: header -> " + headerString);275}276277/**278* widthstmt -> 'width' integer279*/280private void widthStmt(ColumnFormat cf)281throws ParserException, IOException {282match(WIDTH);283double width = lookahead.nval;284matchNumber();285cf.setWidth((int)width);286log(pdebug, "Parsed: width -> " + width );287}288289/**290* formatstmt -> 'format' quotedstring291*/292private void formatStmt(ColumnFormat cf)293throws ParserException, IOException {294match(FORMAT);295String formatString = lookahead.sval;296matchQuotedString();297cf.setFormat(formatString);298log(pdebug, "Parsed: format -> " + formatString);299}300301/**302* Primary -> Literal | Identifier | '(' Expression ')'303*/304private Expression primary() throws ParserException, IOException {305Expression e = null;306307switch (lookahead.ttype) {308case OPENPAREN:309match(OPENPAREN);310e = expression();311match(CLOSEPAREN);312break;313case StreamTokenizer.TT_WORD:314String s = lookahead.sval;315if (isReservedWord(s)) {316throw new SyntaxException(st.lineno(), "IDENTIFIER",317"Reserved Word: " + lookahead.sval);318}319matchID();320e = new Identifier(s);321log(pdebug, "Parsed: ID -> " + s);322break;323case StreamTokenizer.TT_NUMBER:324double literal = lookahead.nval;325matchNumber();326e = new Literal(new Double(literal));327log(pdebug, "Parsed: number -> " + literal);328break;329default:330throw new SyntaxException(st.lineno(), "IDENTIFIER", lookahead);331}332log(pdebug, "Parsed: primary -> " + e);333return e;334}335336/**337* Unary -> ('+'|'-') Unary | Primary338*/339private Expression unary() throws ParserException, IOException {340Expression e = null;341Operator op = null;342343while (true) {344switch (lookahead.ttype) {345case OPERATOR_PLUS:346match(OPERATOR_PLUS);347op = Operator.PLUS;348break;349case OPERATOR_MINUS:350match(OPERATOR_MINUS);351op = Operator.MINUS;352break;353default:354e = primary();355log(pdebug, "Parsed: unary -> " + e);356return e;357}358Expression e1 = new Expression();359e1.setOperator(op);360e1.setRight(e);361log(pdebug, "Parsed: unary -> " + e1);362e1.setLeft(new Literal(new Double(0)));363e = e1;364}365}366367/**368* MultExpression -> Unary (('*' | '/') Unary)*369*/370private Expression multExpression() throws ParserException, IOException {371Expression e = unary();372Operator op = null;373374while (true) {375switch (lookahead.ttype) {376case OPERATOR_MULTIPLY:377match(OPERATOR_MULTIPLY);378op = Operator.MULTIPLY;379break;380case OPERATOR_DIVIDE:381match(OPERATOR_DIVIDE);382op = Operator.DIVIDE;383break;384default:385log(pdebug, "Parsed: multExpression -> " + e);386return e;387}388Expression e1 = new Expression();389e1.setOperator(op);390e1.setLeft(e);391e1.setRight(unary());392e = e1;393log(pdebug, "Parsed: multExpression -> " + e);394}395}396397/**398* AddExpression -> MultExpression (('+' | '-') MultExpression)*399*/400private Expression addExpression() throws ParserException, IOException {401Expression e = multExpression();402Operator op = null;403404while (true) {405switch (lookahead.ttype) {406case OPERATOR_PLUS:407match(OPERATOR_PLUS);408op = Operator.PLUS;409break;410case OPERATOR_MINUS:411match(OPERATOR_MINUS);412op = Operator.MINUS;413break;414default:415log(pdebug, "Parsed: addExpression -> " + e);416return e;417}418Expression e1 = new Expression();419e1.setOperator(op);420e1.setLeft(e);421e1.setRight(multExpression());422e = e1;423log(pdebug, "Parsed: addExpression -> " + e);424}425}426427/**428* Expression -> AddExpression429*/430private Expression expression() throws ParserException, IOException {431Expression e = addExpression();432log(pdebug, "Parsed: expression -> " + e);433return e;434}435436/**437* datastmt -> 'data' expression438*/439private void dataStmt(ColumnFormat cf) throws ParserException, IOException {440match(DATA);441Expression e = expression();442cf.setExpression(e);443log(pdebug, "Parsed: data -> " + e);444}445446/**447* statementlist -> optionalstmt statementlist448* optionalstmt -> 'data' expression449* 'header' quotedstring450* 'width' integer451* 'format' formatstring452* 'align' alignspec453* 'scale' scalespec454*/455private void statementList(ColumnFormat cf)456throws ParserException, IOException {457while (true) {458if (lookahead.ttype != StreamTokenizer.TT_WORD) {459return;460}461462if (lookahead.sval.compareTo(DATA) == 0) {463dataStmt(cf);464} else if (lookahead.sval.compareTo(HEADER) == 0) {465headerStmt(cf);466} else if (lookahead.sval.compareTo(WIDTH) == 0) {467widthStmt(cf);468} else if (lookahead.sval.compareTo(FORMAT) == 0) {469formatStmt(cf);470} else if (lookahead.sval.compareTo(ALIGN) == 0) {471alignStmt(cf);472} else if (lookahead.sval.compareTo(SCALE) == 0) {473scaleStmt(cf);474} else {475return;476}477}478}479480/**481* optionlist -> columspec optionlist482* null483* columspec -> 'column' '{' statementlist '}'484*/485private void optionList(OptionFormat of)486throws ParserException, IOException {487while (true) {488if (lookahead.ttype != StreamTokenizer.TT_WORD) {489return;490}491492match(COLUMN);493match(OPENBLOCK);494ColumnFormat cf = new ColumnFormat(columnCount++);495statementList(cf);496match(CLOSEBLOCK);497cf.validate();498of.addSubFormat(cf);499}500}501502/**503* optionstmt -> 'option' ID '{' optionlist '}'504*/505private OptionFormat optionStmt() throws ParserException, IOException {506match(OPTION);507String optionName=lookahead.sval;508matchID();509match(OPENBLOCK);510OptionFormat of = new OptionFormat(optionName);511optionList(of);512match(CLOSEBLOCK);513return of;514}515516/**517* parse the specification for the given option identifier518*/519public OptionFormat parse(String option)520throws ParserException, IOException {521nextToken();522523/*524* this search stops on the first occurance of an option525* statement with a name matching the given option. Any526* duplicate options are ignored.527*/528while (lookahead.ttype != StreamTokenizer.TT_EOF) {529// look for the start symbol530if ((lookahead.ttype != StreamTokenizer.TT_WORD)531|| (lookahead.sval.compareTo(START) != 0)) {532// skip tokens until a start symbol is found533nextToken();534continue;535}536537// check if the option name is the one we are interested in538match(START);539540if ((lookahead.ttype == StreamTokenizer.TT_WORD)541&& (lookahead.sval.compareTo(option) == 0)) {542// this is the one we are looking for, parse it543pushBack();544return optionStmt();545} else {546// not what we are looking for, start skipping tokens547nextToken();548}549}550return null;551}552553public Set<OptionFormat> parseOptions() throws ParserException, IOException {554Set<OptionFormat> options = new HashSet<OptionFormat>();555556nextToken();557558while (lookahead.ttype != StreamTokenizer.TT_EOF) {559// look for the start symbol560if ((lookahead.ttype != StreamTokenizer.TT_WORD)561|| (lookahead.sval.compareTo(START) != 0)) {562// skip tokens until a start symbol is found563nextToken();564continue;565}566567// note: if a duplicate option statement exists, then568// first one encountered is the chosen definition.569OptionFormat of = optionStmt();570options.add(of);571}572return options;573}574575OptionFormat getOptionFormat() {576return optionFormat;577}578579private void log(boolean logging, String s) {580if (logging) {581System.out.println(s);582}583}584}585586587