Strip.java revision 324c4644fee44b9898524c09511bd33c3f12e2df
1/*
2 * [The "BSD license"]
3 *  Copyright (c) 2010 Terence Parr
4 *  All rights reserved.
5 *
6 *  Redistribution and use in source and binary forms, with or without
7 *  modification, are permitted provided that the following conditions
8 *  are met:
9 *  1. Redistributions of source code must retain the above copyright
10 *      notice, this list of conditions and the following disclaimer.
11 *  2. Redistributions in binary form must reproduce the above copyright
12 *      notice, this list of conditions and the following disclaimer in the
13 *      documentation and/or other materials provided with the distribution.
14 *  3. The name of the author may not be used to endorse or promote products
15 *      derived from this software without specific prior written permission.
16 *
17 *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29package org.antlr.tool;
30
31import org.antlr.grammar.v3.ANTLRv3Lexer;
32import org.antlr.grammar.v3.ANTLRv3Parser;
33import org.antlr.runtime.*;
34import org.antlr.runtime.tree.CommonTree;
35import org.antlr.runtime.tree.TreeAdaptor;
36import org.antlr.runtime.tree.TreeWizard;
37
38import java.util.List;
39
40/** A basic action stripper. */
41public class Strip {
42    protected String filename;
43    protected TokenRewriteStream tokens;
44    protected boolean tree_option = false;
45    protected String args[];
46
47    public static void main(String args[]) throws Exception {
48        Strip s = new Strip(args);
49        s.parseAndRewrite();
50        System.out.println(s.tokens);
51    }
52
53    public Strip(String[] args) { this.args = args; }
54
55    public TokenRewriteStream getTokenStream() { return tokens; }
56
57    public void parseAndRewrite() throws Exception {
58        processArgs(args);
59        CharStream input = null;
60        if ( filename!=null ) input = new ANTLRFileStream(filename);
61        else input = new ANTLRInputStream(System.in);
62        // BUILD AST
63        ANTLRv3Lexer lex = new ANTLRv3Lexer(input);
64        tokens = new TokenRewriteStream(lex);
65        ANTLRv3Parser g = new ANTLRv3Parser(tokens);
66        ANTLRv3Parser.grammarDef_return r = g.grammarDef();
67        CommonTree t = (CommonTree)r.getTree();
68        if (tree_option) System.out.println(t.toStringTree());
69        rewrite(g.getTreeAdaptor(),t,g.getTokenNames());
70    }
71
72    public void rewrite(TreeAdaptor adaptor, CommonTree t, String[] tokenNames) throws Exception {
73        TreeWizard wiz = new TreeWizard(adaptor, tokenNames);
74
75        // ACTIONS STUFF
76        wiz.visit(t, ANTLRv3Parser.ACTION,
77           new TreeWizard.Visitor() {
78               public void visit(Object t) { ACTION(tokens, (CommonTree)t); }
79           });
80
81        wiz.visit(t, ANTLRv3Parser.AT,  // ^('@' id ACTION) rule actions
82            new TreeWizard.Visitor() {
83              public void visit(Object t) {
84                  CommonTree a = (CommonTree)t;
85                  CommonTree action = null;
86                  if ( a.getChildCount()==2 ) action = (CommonTree)a.getChild(1);
87                  else if ( a.getChildCount()==3 ) action = (CommonTree)a.getChild(2);
88                  if ( action.getType()==ANTLRv3Parser.ACTION ) {
89                      tokens.delete(a.getTokenStartIndex(),
90                                    a.getTokenStopIndex());
91                      killTrailingNewline(tokens, action.getTokenStopIndex());
92                  }
93              }
94            });
95        wiz.visit(t, ANTLRv3Parser.ARG, // wipe rule arguments
96                  new TreeWizard.Visitor() {
97              public void visit(Object t) {
98                  CommonTree a = (CommonTree)t;
99                  a = (CommonTree)a.getChild(0);
100                  tokens.delete(a.token.getTokenIndex());
101                  killTrailingNewline(tokens, a.token.getTokenIndex());
102              }
103            });
104        wiz.visit(t, ANTLRv3Parser.RET, // wipe rule return declarations
105            new TreeWizard.Visitor() {
106                public void visit(Object t) {
107                    CommonTree a = (CommonTree)t;
108                    CommonTree ret = (CommonTree)a.getChild(0);
109                    tokens.delete(a.token.getTokenIndex(),
110                                  ret.token.getTokenIndex());
111                }
112            });
113        wiz.visit(t, ANTLRv3Parser.SEMPRED, // comment out semantic predicates
114            new TreeWizard.Visitor() {
115                public void visit(Object t) {
116                    CommonTree a = (CommonTree)t;
117                    tokens.replace(a.token.getTokenIndex(), "/*"+a.getText()+"*/");
118                }
119            });
120        wiz.visit(t, ANTLRv3Parser.GATED_SEMPRED, // comment out semantic predicates
121            new TreeWizard.Visitor() {
122                public void visit(Object t) {
123                    CommonTree a = (CommonTree)t;
124                    String text = tokens.toString(a.getTokenStartIndex(),
125                                                  a.getTokenStopIndex());
126                    tokens.replace(a.getTokenStartIndex(),
127                                   a.getTokenStopIndex(),
128                                   "/*"+text+"*/");
129                }
130            });
131        wiz.visit(t, ANTLRv3Parser.SCOPE, // comment scope specs
132            new TreeWizard.Visitor() {
133                public void visit(Object t) {
134                    CommonTree a = (CommonTree)t;
135                    tokens.delete(a.getTokenStartIndex(),
136                                  a.getTokenStopIndex());
137                    killTrailingNewline(tokens, a.getTokenStopIndex());
138                }
139            });
140        wiz.visit(t, ANTLRv3Parser.ARG_ACTION, // args r[x,y] -> ^(r [x,y])
141            new TreeWizard.Visitor() {
142                public void visit(Object t) {
143                    CommonTree a = (CommonTree)t;
144                    if ( a.getParent().getType()==ANTLRv3Parser.RULE_REF ) {
145                        tokens.delete(a.getTokenStartIndex(),
146                                      a.getTokenStopIndex());
147                    }
148                }
149            });
150        wiz.visit(t, ANTLRv3Parser.LABEL_ASSIGN, // ^('=' id ^(RULE_REF [arg])), ...
151            new TreeWizard.Visitor() {
152                public void visit(Object t) {
153                    CommonTree a = (CommonTree)t;
154                    if ( !a.hasAncestor(ANTLRv3Parser.OPTIONS) ) { // avoid options
155                        CommonTree child = (CommonTree)a.getChild(0);
156                        tokens.delete(a.token.getTokenIndex());     // kill "id="
157                        tokens.delete(child.token.getTokenIndex());
158                    }
159                }
160            });
161        wiz.visit(t, ANTLRv3Parser.LIST_LABEL_ASSIGN, // ^('+=' id ^(RULE_REF [arg])), ...
162            new TreeWizard.Visitor() {
163              public void visit(Object t) {
164                  CommonTree a = (CommonTree)t;
165                  CommonTree child = (CommonTree)a.getChild(0);
166                  tokens.delete(a.token.getTokenIndex());     // kill "id+="
167                  tokens.delete(child.token.getTokenIndex());
168              }
169            });
170
171
172        // AST STUFF
173        wiz.visit(t, ANTLRv3Parser.REWRITE,
174            new TreeWizard.Visitor() {
175              public void visit(Object t) {
176                  CommonTree a = (CommonTree)t;
177                  CommonTree child = (CommonTree)a.getChild(0);
178                  int stop = child.getTokenStopIndex();
179                  if ( child.getType()==ANTLRv3Parser.SEMPRED ) {
180                      CommonTree rew = (CommonTree)a.getChild(1);
181                      stop = rew.getTokenStopIndex();
182                  }
183                  tokens.delete(a.token.getTokenIndex(), stop);
184                  killTrailingNewline(tokens, stop);
185              }
186            });
187        wiz.visit(t, ANTLRv3Parser.ROOT,
188           new TreeWizard.Visitor() {
189               public void visit(Object t) {
190                   tokens.delete(((CommonTree)t).token.getTokenIndex());
191               }
192           });
193        wiz.visit(t, ANTLRv3Parser.BANG,
194           new TreeWizard.Visitor() {
195               public void visit(Object t) {
196                   tokens.delete(((CommonTree)t).token.getTokenIndex());
197               }
198           });
199    }
200
201    public static void ACTION(TokenRewriteStream tokens, CommonTree t) {
202        CommonTree parent = (CommonTree)t.getParent();
203        int ptype = parent.getType();
204        if ( ptype==ANTLRv3Parser.SCOPE || // we have special rules for these
205             ptype==ANTLRv3Parser.AT )
206        {
207            return;
208        }
209        //System.out.println("ACTION: "+t.getText());
210        CommonTree root = (CommonTree)t.getAncestor(ANTLRv3Parser.RULE);
211        if ( root!=null ) {
212            CommonTree rule = (CommonTree)root.getChild(0);
213            //System.out.println("rule: "+rule);
214            if ( !Character.isUpperCase(rule.getText().charAt(0)) ) {
215                tokens.delete(t.getTokenStartIndex(),t.getTokenStopIndex());
216                killTrailingNewline(tokens, t.token.getTokenIndex());
217            }
218        }
219    }
220
221    private static void killTrailingNewline(TokenRewriteStream tokens, int index) {
222        List all = tokens.getTokens();
223        Token tok = (Token)all.get(index);
224        Token after = (Token)all.get(index+1);
225        String ws = after.getText();
226        if ( ws.startsWith("\n") ) {
227            //System.out.println("killing WS after action");
228            if ( ws.length()>1 ) {
229                int space = ws.indexOf(' ');
230                int tab = ws.indexOf('\t');
231                if ( ws.startsWith("\n") &&
232                     space>=0 || tab>=0 )
233                {
234                    return; // do nothing if \n + indent
235                }
236                // otherwise kill all \n
237                ws = ws.replaceAll("\n", "");
238                tokens.replace(after.getTokenIndex(), ws);
239            }
240            else {
241                tokens.delete(after.getTokenIndex());
242            }
243        }
244    }
245
246    public void processArgs(String[] args) {
247		if ( args==null || args.length==0 ) {
248			help();
249			return;
250		}
251		for (int i = 0; i < args.length; i++) {
252			if (args[i].equals("-tree")) tree_option = true;
253			else {
254				if (args[i].charAt(0) != '-') {
255					// Must be the grammar file
256                    filename = args[i];
257				}
258			}
259		}
260	}
261
262    private static void help() {
263        System.err.println("usage: java org.antlr.tool.Strip [args] file.g");
264        System.err.println("  -tree      print out ANTLR grammar AST");
265    }
266
267}
268