/* * [The "BSD license"] * Copyright (c) 2010 Terence Parr * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.antlr.tool; import org.antlr.Tool; import org.antlr.analysis.Label; import org.antlr.analysis.NFAState; import org.antlr.analysis.RuleClosureTransition; import org.antlr.analysis.Transition; import org.antlr.misc.IntervalSet; import org.antlr.misc.Utils; import java.io.BufferedReader; import java.io.FileReader; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.Stack; /** Generate a random phrase given a grammar. * Usage: * java org.antlr.tool.RandomPhrase grammarFile.g startRule [seed] * * For example: * java org.antlr.tool.RandomPhrase simple.g program 342 * * The seed acts like a unique identifier so you can get the same random * phrase back during unit testing, for example. * * If you do not specify a seed then the current time in milliseconds is used * guaranteeing that you'll never see that seed again. * * NOTE: this does not work well for large grammars...it tends to recurse * too much and build really long strings. I need throttle control; later. */ public class RandomPhrase { public static final boolean debug = false; protected static Random random; /** an experimental method to generate random phrases for a given * grammar given a start rule. Return a list of token types. */ protected static void randomPhrase(Grammar g, List tokenTypes, String startRule) { NFAState state = g.getRuleStartState(startRule); NFAState stopState = g.getRuleStopState(startRule); Stack ruleInvocationStack = new Stack(); while ( true ) { if ( state==stopState && ruleInvocationStack.size()==0 ) { break; } if ( debug ) System.out.println("state "+state); if ( state.getNumberOfTransitions()==0 ) { if ( debug ) System.out.println("dangling state: "+state); return; } // end of rule node if ( state.isAcceptState() ) { NFAState invokingState = (NFAState)ruleInvocationStack.pop(); if ( debug ) System.out.println("pop invoking state "+invokingState); //System.out.println("leave "+state.enclosingRule.name); RuleClosureTransition invokingTransition = (RuleClosureTransition)invokingState.transition[0]; // move to node after state that invoked this rule state = invokingTransition.followState; continue; } if ( state.getNumberOfTransitions()==1 ) { // no branching, just take this path Transition t0 = state.transition[0]; if ( t0 instanceof RuleClosureTransition ) { ruleInvocationStack.push(state); if ( debug ) System.out.println("push state "+state); //System.out.println("call "+((RuleClosureTransition)t0).rule.name); //System.out.println("stack depth="+ruleInvocationStack.size()); } else if ( t0.label.isSet() || t0.label.isAtom() ) { tokenTypes.add( getTokenType(t0.label) ); } state = (NFAState)t0.target; continue; } int decisionNumber = state.getDecisionNumber(); if ( decisionNumber==0 ) { System.out.println("weird: no decision number but a choice node"); continue; } // decision point, pick ith alternative randomly int n = g.getNumberOfAltsForDecisionNFA(state); int randomAlt = random.nextInt(n) + 1; if ( debug ) System.out.println("randomAlt="+randomAlt); NFAState altStartState = g.getNFAStateForAltOfDecision(state, randomAlt); Transition t = altStartState.transition[0]; state = (NFAState)t.target; } } protected static Integer getTokenType(Label label) { if ( label.isSet() ) { // pick random element of set IntervalSet typeSet = (IntervalSet)label.getSet(); int randomIndex = random.nextInt(typeSet.size()); return typeSet.get(randomIndex); } else { return Utils.integer(label.getAtom()); } //System.out.println(t0.label.toString(g)); } /** Used to generate random strings */ public static void main(String[] args) { if ( args.length < 2 ) { System.err.println("usage: java org.antlr.tool.RandomPhrase grammarfile startrule"); return; } String grammarFileName = args[0]; String startRule = args[1]; long seed = System.currentTimeMillis(); // use random seed unless spec. if ( args.length==3 ) { String seedStr = args[2]; seed = Long.parseLong(seedStr); } try { random = new Random(seed); CompositeGrammar composite = new CompositeGrammar(); Tool tool = new Tool(); Grammar parser = new Grammar(tool, grammarFileName, composite); composite.setDelegationRoot(parser); FileReader fr = new FileReader(grammarFileName); BufferedReader br = new BufferedReader(fr); parser.parseAndBuildAST(br); br.close(); parser.composite.assignTokenTypes(); parser.composite.defineGrammarSymbols(); parser.composite.createNFAs(); List leftRecursiveRules = parser.checkAllRulesForLeftRecursion(); if ( leftRecursiveRules.size()>0 ) { return; } if ( parser.getRule(startRule)==null ) { System.out.println("undefined start rule "+startRule); return; } String lexerGrammarText = parser.getLexerGrammar(); Grammar lexer = new Grammar(tool); lexer.importTokenVocabulary(parser); lexer.fileName = grammarFileName; if ( lexerGrammarText!=null ) { lexer.setGrammarContent(lexerGrammarText); } else { System.err.println("no lexer grammar found in "+grammarFileName); } lexer.buildNFA(); leftRecursiveRules = lexer.checkAllRulesForLeftRecursion(); if ( leftRecursiveRules.size()>0 ) { return; } //System.out.println("lexer:\n"+lexer); List tokenTypes = new ArrayList(100); randomPhrase(parser, tokenTypes, startRule); System.out.println("token types="+tokenTypes); for (int i = 0; i < tokenTypes.size(); i++) { Integer ttypeI = (Integer) tokenTypes.get(i); int ttype = ttypeI.intValue(); String ttypeDisplayName = parser.getTokenDisplayName(ttype); if ( Character.isUpperCase(ttypeDisplayName.charAt(0)) ) { List charsInToken = new ArrayList(10); randomPhrase(lexer, charsInToken, ttypeDisplayName); System.out.print(" "); for (int j = 0; j < charsInToken.size(); j++) { java.lang.Integer cI = (java.lang.Integer) charsInToken.get(j); System.out.print((char)cI.intValue()); } } else { // it's a literal String literal = ttypeDisplayName.substring(1,ttypeDisplayName.length()-1); System.out.print(" "+literal); } } System.out.println(); } catch (Error er) { System.err.println("Error walking "+grammarFileName+" rule "+startRule+" seed "+seed); er.printStackTrace(System.err); } catch (Exception e) { System.err.println("Exception walking "+grammarFileName+" rule "+startRule+" seed "+seed); e.printStackTrace(System.err); } } }