Added Antlr compiler/runtime and wrote initial grammar.

Grammar is just a collection of rules.

Change-Id: I556a8f74668b11b80f673fc7d665704322c2dac9
This commit is contained in:
Tim Hinrichs 2013-08-05 10:59:20 -07:00 committed by Peter Balland
parent a1bd87e7b2
commit 8242986221
2241 changed files with 441194 additions and 0 deletions

9
Makefile Normal file
View File

@ -0,0 +1,9 @@
all: CongressLexer.py
CongressLexer.py CongressParser.py: policy/Congress.g
java -jar outside/antlr-3.5-complete.jar policy/Congress.g

Binary file not shown.

30
outside/antlr3-antlr-3.5/.gitignore vendored Normal file
View File

@ -0,0 +1,30 @@
# Maven build folders
target/
# IntelliJ project files
*.iml
*.ipr
*.iws
.idea/
# Eclipse project files
.project
.classpath
.settings/
# NetBeans user configuration
nbactions.xml
nb-configuration.xml
# Python runtime files
*.py[co]
# ANTLR C# target build folders
/runtime/CSharp3/Sources/Antlr3.Runtime/bin/
/runtime/CSharp3/Sources/Antlr3.Runtime/obj/
/runtime/CSharp3/Sources/Antlr3.Runtime.Debug/bin/
/runtime/CSharp3/Sources/Antlr3.Runtime.Debug/obj/
/runtime/CSharp3/Sources/Antlr3.Runtime.JavaExtensions/bin/
/runtime/CSharp3/Sources/Antlr3.Runtime.JavaExtensions/obj/
/runtime/CSharp3/Sources/Antlr3.Runtime.Test/bin/
/runtime/CSharp3/Sources/Antlr3.Runtime.Test/obj/

View File

@ -0,0 +1,6 @@
We have moved instructions for building ANTLR with the maven build system to:
http://www.antlr.org/wiki/display/ANTLR3/Building+ANTLR+with+Maven
The notes are by Jim Idle (and are a bit out of date but we hope to
update them).

View File

@ -0,0 +1,142 @@
ANTLR v3.5
January 4, 2013
Terence Parr, parrt at cs usfca edu
ANTLR project lead and supreme dictator for life
University of San Francisco
INTRODUCTION
Welcome to ANTLR v3! ANTLR (ANother Tool for Language Recognition) is
a language tool that provides a framework for constructing
recognizers, interpreters, compilers, and translators from grammatical
descriptions containing actions in a variety of target
languages. ANTLR provides excellent support for tree construction,
tree walking, translation, error recovery, and error reporting. I've
been working on parser generators for 25 years and on this particular
version of ANTLR for 9 years.
You should use v3 in conjunction with ANTLRWorks:
http://www.antlr.org/works/index.html
and gUnit (grammar unit testing tool included in distribution):
http://www.antlr.org/wiki/display/ANTLR3/gUnit+-+Grammar+Unit+Testing
The book will also help you a great deal (printed May 15, 2007); you
can also buy the PDF:
http://www.pragmaticprogrammer.com/titles/tpantlr/index.html
2nd book, Language Implementation Patterns:
http://pragprog.com/titles/tpdsl/language-implementation-patterns
See the getting started document:
http://www.antlr.org/wiki/display/ANTLR3/FAQ+-+Getting+Started
You also have the examples plus the source to guide you.
See the wiki FAQ:
http://www.antlr.org/wiki/display/ANTLR3/ANTLR+v3+FAQ
and general doc root:
http://www.antlr.org/wiki/display/ANTLR3/ANTLR+3+Wiki+Home
Please help add/update FAQ entries.
If all else fails, you can buy support or ask the antlr-interest list:
http://www.antlr.org/support.html
Per the license in LICENSE.txt, this software is not guaranteed to
work and might even destroy all life on this planet:
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.
----------------------------------------------------------------------
EXAMPLES
ANTLR v3 sample grammars:
https://github.com/antlr/examples-v3
Examples from Language Implementation Patterns:
http://www.pragprog.com/titles/tpdsl/source_code
----------------------------------------------------------------------
What is ANTLR?
ANTLR stands for (AN)other (T)ool for (L)anguage (R)ecognition
and generates LL(*) recursive-descent parsers. ANTLR is a language tool
that provides a framework for constructing recognizers, compilers, and
translators from grammatical descriptions containing actions.
Target language list:
http://www.antlr.org/wiki/display/ANTLR3/Code+Generation+Targets
----------------------------------------------------------------------
How is ANTLR v3 different than ANTLR v2?
See "What is the difference between ANTLR v2 and v3?"
http://www.antlr.org/wiki/pages/viewpage.action?pageId=719
See migration guide:
http://www.antlr.org/wiki/display/ANTLR3/Migrating+from+ANTLR+2+to+ANTLR+3
----------------------------------------------------------------------
How do I install this damn thing?
You will have grabbed either of these:
http://antlr.org/download/antlr-3.5-complete-no-antlrv2.jar
http://antlr.org/download/antlr-3.5-complete.jar
It has all of the jars you need combined into one. Then you need to
add antlr-3.5-complete.jar to your CLASSPATH or add to arg list; e.g., on unix:
$ java -cp "/usr/local/lib/antlr-3.5-complete.jar:$CLASSPATH" org.antlr.Tool Test.g
Source + java binaries: Just untar antlr-3.5.tar.gz and you'll get:
antlr-3.5/BUILD.txt
antlr-3.5/antlr3-maven-plugin
antlr-3.5/antlrjar.xml
antlr-3.5/antlrsources.xml
antlr-3.5/gunit
antlr-3.5/gunit-maven-plugin
antlr-3.5/pom.xml
antlr-3.5/runtime
antlr-3.5/tool
antlr-3.5/lib
Please see the FAQ
http://www.antlr.org/wiki/display/ANTLR3/ANTLR+v3+FAQ
-------------------------
How can I contribute to ANTLR v3?
http://www.antlr.org/wiki/pages/viewpage.action?pageId=33947666

View File

@ -0,0 +1,61 @@
Changes:
19.08.2008
Dependency check for composed grammars added.
Might need some further improvements.
09.08.2008
Inspecting environment variable ANTLR_HOME to detect and add
antlr- and stringtemplate libraries to the classpath
09.08.2008
Removed routine checkGenerateFile. It got feeble with the
introduction of composed grammars, e.g. "import T.g".
From now one it is always antlr itself via the depend option
which decides about dependecies
31.12.2007
With option "depend=true" proceed even if first pass failed so
that ANTLR can spit out its errors
31.12.2007
Support the -Xconversiontimeout option (Jim Idle)
21.10.2007
Evaluation of dependencies via ANTLRs 'depend' option.
Added noprune and nocollapse option.
'grammar parser' will be recognized.
17.05.2007
Adapted the grammar type recognition to the changed naming conventions for tree parsers.
Compiled the antlr3 taks with -source 5 -target 5 for compatibility with Java 5.
Dropped trace, tracelexer and traceparser options as they are not supported by antlr any more.
Added depend and dbgST options.
Added project "SimpleTreeWalker" as an example for a multi grammar project.
How to build the antlr3 task:
Prerequisites:
1) apache-ant-1.7.0 installed
2) antlr jar files (antlr-3.1b1.jar, antlr-2.7.7.jar and stringtemplate-3.1b1.jar)
contained in the CLASSPATH environment variable
3) Java 5 or Java 6 installed
javac -source 5 -target 5 -classpath C:/Programme/apache-ant-1.7.0/lib/ant.jar org/apache/tools/ant/antlr/ANTLR3.java
jar cvf antlr3.jar org/apache/tools/ant/antlr/antlib.xml org/apache/tools/ant/antlr/ANTLR3.class
a) d2u.tp
Simple example on how to use the antlr3 task with ant
b) simplecTreeParser.tp
Example on how to build a multi grammar project with ant.
c) polydiff_build.zip
Example build file for polydiff example grammar added.
d) composite-java_build.zip
Example build file for composite-java example grammar added

View File

@ -0,0 +1,776 @@
/*
* Copyright 2000-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* 2006-12-29: Modified to work for antlr3 by Jürgen Pfundt
* 2007-01-04: Some minor correction after checking code with findBugs tool
* 2007-02-10: Adapted the grammar type recognition to the changed naming
* conventions for Tree Parser
* 2007-10-17: Options "trace", "traceLexer", "traceParser" and "glib" emit
* warnings when being used.
* Added recognition of "parser grammar T".
* Added options "nocollapse", "noprune".
* ANTLR option "depend" is being used to resolve build dependencies.
* 2007-11-15: Embedded Classpath statement had not been observed
* with option depend="true" (Reported by Mats Behre)
* 2008-03-31: Support the option conversiontimeout. (Jim Idle)
* 2007-12-31: With option "depend=true" proceed even if first pass failed so
* that ANTLR can spit out its errors
* 2008-08-09: Inspecting environment variable ANTLR_HOME to detect and add
* antlr- and stringtemplate libraries to the classpath
* 2008-08-09: Removed routine checkGenerateFile. It got feeble with the
* introduction of composed grammars, e.g. "import T.g" and after
* a short struggle it started it's journey to /dev/null.
* From now one it is always antlr itself via the depend option
* which decides about dependecies
* 2008-08-19: Dependency check for composed grammars added.
* Might need some further improvements.
*/
package org.apache.tools.ant.antlr;
import java.util.regex.*;
import java.io.*;
import java.util.Map;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Execute;
import org.apache.tools.ant.taskdefs.LogOutputStream;
import org.apache.tools.ant.taskdefs.PumpStreamHandler;
import org.apache.tools.ant.taskdefs.Redirector;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.CommandlineJava;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.util.JavaEnvUtils;
import org.apache.tools.ant.util.LoaderUtils;
import org.apache.tools.ant.util.TeeOutputStream;
import org.apache.tools.ant.util.FileUtils;
/**
* Invokes the ANTLR3 Translator generator on a grammar file.
*
*/
public class ANTLR3 extends Task {
private CommandlineJava commandline = new CommandlineJava();
/** the file to process */
private File target = null;
/** where to output the result */
private File outputDirectory = null;
/** location of token files */
private File libDirectory = null;
/** an optional super grammar file */
private File superGrammar;
/** depend */
private boolean depend = false;
/** fork */
private boolean fork;
/** name of output style for messages */
private String messageFormatName;
/** optional flag to print out a diagnostic file */
private boolean diagnostic;
/** optional flag to add methods */
private boolean trace;
/** optional flag to add trace methods to the parser only */
private boolean traceParser;
/** optional flag to add trace methods to the lexer only */
private boolean traceLexer;
/** working directory */
private File workingdir = null;
/** captures ANTLR's output */
private ByteArrayOutputStream bos = new ByteArrayOutputStream();
/** The debug attribute */
private boolean debug;
/** The report attribute */
private boolean report;
/** The print attribute */
private boolean print;
/** The profile attribute */
private boolean profile;
/** The nfa attribute */
private boolean nfa;
/** The dfa attribute */
private boolean dfa;
/** multi threaded analysis */
private boolean multiThreaded;
/** collapse incident edges into DFA states */
private boolean nocollapse;
/** test lookahead against EBNF block exit branches */
private boolean noprune;
/** put tags at start/stop of all templates in output */
private boolean dbgST;
/** print AST */
private boolean grammarTree;
/** Instance of a utility class to use for file operations. */
private FileUtils fileUtils;
/**
* Whether to override the default conversion timeout with -Xconversiontimeout nnnn
*/
private String conversiontimeout;
public ANTLR3() {
commandline.setVm(JavaEnvUtils.getJreExecutable("java"));
commandline.setClassname("org.antlr.Tool");
fileUtils = FileUtils.getFileUtils();
}
/**
* The grammar file to process.
*/
public void setTarget(File targetFile) {
log("Setting target to: " + targetFile.toString(), Project.MSG_VERBOSE);
this.target = targetFile;
}
/**
* The directory to write the generated files to.
*/
public void setOutputdirectory(File outputDirectoryFile) {
log("Setting output directory to: " + outputDirectoryFile.toString(), Project.MSG_VERBOSE);
this.outputDirectory = outputDirectoryFile;
}
/**
* The directory to write the generated files to.
*/
public File getOutputdirectory() {
return outputDirectory;
}
/**
* The token files output directory.
*/
public void setLibdirectory(File libDirectoryFile) {
log("Setting lib directory to: " + libDirectoryFile.toString(), Project.MSG_VERBOSE);
this.libDirectory = libDirectoryFile;
}
/**
* The output style for messages.
*/
public void setMessageformat(String name) {
log("Setting message-format to: " + name, Project.MSG_VERBOSE);
this.messageFormatName = name;
}
/**
* Sets an optional super grammar file
* @deprecated
*/
public void setGlib(File superGrammarFile) {
this.superGrammar = superGrammarFile;
}
/**
* Sets a flag to enable ParseView debugging
*/
public void setDebug(boolean enable) {
this.debug = enable;
}
/**
* Sets a flag to enable report statistics
*/
public void setReport(boolean enable) {
this.report = enable;
}
/**
* Sets a flag to print out the grammar without actions
*/
public void setPrint(boolean enable) {
this.print = enable;
}
/**
* Sets a flag to enable profiling
*/
public void setProfile(boolean enable) {
this.profile = enable;
}
/**
* Sets a flag to enable nfa generation
*/
public void setNfa(boolean enable) {
this.nfa = enable;
}
/**
* Sets a flag to enable nfa generation
*/
public void setDfa(boolean enable) {
this.dfa = enable;
}
/**
* Run the analysis multithreaded
* @param enable
*/
public void setMultithreaded(boolean enable) {
multiThreaded = enable;
}
/**
* collapse incident edges into DFA states
* @param enable
*/
public void setNocollapse(boolean enable) {
nocollapse = enable;
}
/**
* test lookahead against EBNF block exit branches
* @param enable
*/
public void setNoprune(boolean enable) {
noprune = enable;
}
/**
* test lookahead against EBNF block exit branches
* @param enable
*/
public void setDbgST(boolean enable) {
dbgST = enable;
}
/**
* override the default conversion timeout with -Xconversiontimeout nnnn
* @param conversiontimeoutString
*/
public void setConversiontimeout(String conversiontimeoutString) {
log("Setting conversiontimeout to: " + conversiontimeoutString, Project.MSG_VERBOSE);
try {
int timeout = Integer.valueOf(conversiontimeoutString);
this.conversiontimeout = conversiontimeoutString;
} catch (NumberFormatException e) {
log("Option ConversionTimeOut ignored due to illegal value: '" + conversiontimeoutString + "'", Project.MSG_ERR);
}
}
/**
* Set a flag to enable printing of the grammar tree
*/
public void setGrammartree(boolean enable) {
grammarTree = enable;
}
/**
* Set a flag to enable dependency checking by ANTLR itself
* The depend option is always used implicitely inside the antlr3 task
* @deprecated
*/
public void setDepend(boolean s) {
this.depend = s;
}
/**
* Sets a flag to emit diagnostic text
*/
public void setDiagnostic(boolean enable) {
diagnostic = enable;
}
/**
* If true, enables all tracing.
* @deprecated
*/
public void setTrace(boolean enable) {
trace = enable;
}
/**
* If true, enables parser tracing.
* @deprecated
*/
public void setTraceParser(boolean enable) {
traceParser = enable;
}
/**
* If true, enables lexer tracing.
* @deprecated
*/
public void setTraceLexer(boolean enable) {
traceLexer = enable;
}
// we are forced to fork ANTLR since there is a call
// to System.exit() and there is nothing we can do
// right now to avoid this. :-( (SBa)
// I'm not removing this method to keep backward compatibility
/**
* @ant.attribute ignore="true"
*/
public void setFork(boolean s) {
this.fork = s;
}
/**
* The working directory of the process
*/
public void setDir(File d) {
this.workingdir = d;
}
/**
* Adds a classpath to be set
* because a directory might be given for Antlr debug.
*/
public Path createClasspath() {
return commandline.createClasspath(getProject()).createPath();
}
/**
* Adds a new JVM argument.
* @return create a new JVM argument so that any argument can be passed to the JVM.
* @see #setFork(boolean)
*/
public Commandline.Argument createJvmarg() {
return commandline.createVmArgument();
}
/**
* Adds the jars or directories containing Antlr and associates.
* This should make the forked JVM work without having to
* specify it directly.
*/
@Override
public void init() throws BuildException {
/* Inquire environment variables */
Map<String, String> variables = System.getenv();
/* Get value for key "ANTLR_HOME" which should hopefully point to
* the directory where the current version of antlr3 is installed */
String antlrHome = variables.get("ANTLR_HOME");
if (antlrHome != null) {
/* Environment variable ANTLR_HOME has been defined.
* Now add all antlr and stringtemplate libraries to the
* classpath */
addAntlrJarsToClasspath(antlrHome + "/lib");
}
addClasspathEntry("/antlr/ANTLRGrammarParseBehavior.class", "AntLR2");
addClasspathEntry("/org/antlr/tool/ANTLRParser.class", "AntLR3");
addClasspathEntry("/org/antlr/stringtemplate/StringTemplate.class", "Stringtemplate");
}
/**
* Search for the given resource and add the directory or archive
* that contains it to the classpath.
*
* <p>Doesn't work for archives in JDK 1.1 as the URL returned by
* getResource doesn't contain the name of the archive.</p>
*/
protected void addClasspathEntry(String resource, String msg) {
/*
* pre Ant 1.6 this method used to call getClass().getResource
* while Ant 1.6 will call ClassLoader.getResource().
*
* The difference is that Class.getResource expects a leading
* slash for "absolute" resources and will strip it before
* delegating to ClassLoader.getResource - so we now have to
* emulate Class's behavior.
*/
if (resource.startsWith("/")) {
resource = resource.substring(1);
} else {
resource = "org/apache/tools/ant/taskdefs/optional/" + resource;
}
File f = LoaderUtils.getResourceSource(getClass().getClassLoader(), resource);
if (f != null) {
log("Found via classpath: " + f.getAbsolutePath(), Project.MSG_VERBOSE);
createClasspath().setLocation(f);
} else {
log("Couldn\'t find resource " + resource + " for library " + msg + " in external classpath", Project.MSG_VERBOSE);
}
}
/**
* If the environment variable ANTLR_HOME is defined and points
* to the installation directory of antlr3 then look for all antlr-*.jar and
* stringtemplate-*.jar files in the lib directory and add them
* to the classpath.
* This feature should make working with eclipse or netbeans projects a
* little bit easier. As wildcards are being used for the version part
* of the jar-archives it makes the task independent of
* new releases. Just let ANTLR_HOME point to the new installation
* directory.
*/
private void addAntlrJarsToClasspath(String antlrLibDir) {
String[] includes = {"antlr-*.jar", "stringtemplate-*.jar"};
DirectoryScanner ds = new DirectoryScanner();
ds.setIncludes(includes);
ds.setBasedir(new File(antlrLibDir));
ds.setCaseSensitive(true);
ds.scan();
String separator = System.getProperty("file.separator");
String[] files = ds.getIncludedFiles();
for (String file : files) {
File f = new File(antlrLibDir + separator + file);
log("Found via ANTLR_HOME: " + f.getAbsolutePath(), Project.MSG_VERBOSE);
createClasspath().setLocation(f);
}
}
@Override
public void execute() throws BuildException {
validateAttributes();
// Use ANTLR itself to resolve dependencies and decide whether
// to invoke ANTLR for compilation
if (dependencyCheck()) {
populateAttributes();
commandline.createArgument().setValue(target.toString());
log(commandline.describeCommand(), Project.MSG_VERBOSE);
int err = 0;
try {
err = run(commandline.getCommandline(), new LogOutputStream(this, Project.MSG_INFO), new LogOutputStream(this, Project.MSG_WARN));
} catch (IOException e) {
throw new BuildException(e, getLocation());
} finally {
try {
bos.close();
} catch (IOException e) {
// ignore
}
}
if (err != 0) {
throw new BuildException("ANTLR returned: " + err, getLocation());
} else {
Pattern p = Pattern.compile("error\\([0-9]+\\):");
Matcher m = p.matcher(bos.toString());
if (m.find()) {
throw new BuildException("ANTLR signaled an error.", getLocation());
}
}
} else {
try {
log("All dependencies of grammar file \'" + target.getCanonicalPath() + "\' are up to date.", Project.MSG_VERBOSE);
} catch (IOException ex) {
log("All dependencies of grammar file \'" + target.toString() + "\' are up to date.", Project.MSG_VERBOSE);
}
}
}
/**
* A refactored method for populating all the command line arguments based
* on the user-specified attributes.
*/
private void populateAttributes() {
commandline.createArgument().setValue("-o");
commandline.createArgument().setValue(outputDirectory.toString());
commandline.createArgument().setValue("-lib");
commandline.createArgument().setValue(libDirectory.toString());
if (superGrammar != null) {
log("Option 'glib' is not supported by ANTLR v3. Option ignored!", Project.MSG_WARN);
}
if (diagnostic) {
commandline.createArgument().setValue("-diagnostic");
}
if (depend) {
log("Option 'depend' is implicitely always used by ANTLR v3. Option can safely be omitted!", Project.MSG_WARN);
}
if (trace) {
log("Option 'trace' is not supported by ANTLR v3. Option ignored!", Project.MSG_WARN);
}
if (traceParser) {
log("Option 'traceParser' is not supported by ANTLR v3. Option ignored!", Project.MSG_WARN);
}
if (traceLexer) {
log("Option 'traceLexer' is not supported by ANTLR v3. Option ignored!", Project.MSG_WARN);
}
if (debug) {
commandline.createArgument().setValue("-debug");
}
if (report) {
commandline.createArgument().setValue("-report");
}
if (print) {
commandline.createArgument().setValue("-print");
}
if (profile) {
commandline.createArgument().setValue("-profile");
}
if (messageFormatName != null) {
commandline.createArgument().setValue("-message-format");
commandline.createArgument().setValue(messageFormatName);
}
if (nfa) {
commandline.createArgument().setValue("-nfa");
}
if (dfa) {
commandline.createArgument().setValue("-dfa");
}
if (multiThreaded) {
commandline.createArgument().setValue("-Xmultithreaded");
}
if (nocollapse) {
commandline.createArgument().setValue("-Xnocollapse");
}
if (noprune) {
commandline.createArgument().setValue("-Xnoprune");
}
if (dbgST) {
commandline.createArgument().setValue("-XdbgST");
}
if (conversiontimeout != null) {
commandline.createArgument().setValue("-Xconversiontimeout");
commandline.createArgument().setValue(conversiontimeout);
}
if (grammarTree) {
commandline.createArgument().setValue("-Xgrtree");
}
}
private void validateAttributes() throws BuildException {
if (target == null) {
throw new BuildException("No target grammar, lexer grammar or tree parser specified!");
} else if (!target.isFile()) {
throw new BuildException("Target: " + target + " is not a file!");
}
// if no output directory is specified, use the target's directory
if (outputDirectory == null) {
setOutputdirectory(new File(target.getParent()));
}
if (!outputDirectory.isDirectory()) {
throw new BuildException("Invalid output directory: " + outputDirectory);
}
if (workingdir != null && !workingdir.isDirectory()) {
throw new BuildException("Invalid working directory: " + workingdir);
}
// if no libDirectory is specified, use the target's directory
if (libDirectory == null) {
setLibdirectory(new File(target.getParent()));
}
if (!libDirectory.isDirectory()) {
throw new BuildException("Invalid lib directory: " + libDirectory);
}
}
private boolean dependencyCheck() throws BuildException {
// using "antlr -o <OutputDirectory> -lib <LibDirectory> -depend <T>"
// to get the list of dependencies
CommandlineJava cmdline;
try {
cmdline = (CommandlineJava) commandline.clone();
} catch (java.lang.CloneNotSupportedException e) {
throw new BuildException("Clone of commandline failed: " + e);
}
cmdline.createArgument().setValue("-depend");
cmdline.createArgument().setValue("-o");
cmdline.createArgument().setValue(outputDirectory.toString());
cmdline.createArgument().setValue("-lib");
cmdline.createArgument().setValue(libDirectory.toString());
cmdline.createArgument().setValue(target.toString());
log(cmdline.describeCommand(), Project.MSG_VERBOSE);
// redirect output generated by ANTLR to temporary file
Redirector r = new Redirector(this);
File f;
try {
f = File.createTempFile("depend", null, getOutputdirectory());
f.deleteOnExit();
log("Write dependencies for '" + target.toString() + "' to file '" + f.getCanonicalPath() + "'", Project.MSG_VERBOSE);
r.setOutput(f);
r.setAlwaysLog(false);
r.createStreams();
} catch (IOException e) {
throw new BuildException("Redirection of output failed: " + e);
}
// execute antlr -depend ...
int err = 0;
try {
err = run(cmdline.getCommandline(), r.getOutputStream(), null);
} catch (IOException e) {
try {
r.complete();
log("Redirection of output terminated.", Project.MSG_VERBOSE);
} catch (IOException ex) {
log("Termination of output redirection failed: " + ex, Project.MSG_ERR);
}
throw new BuildException(e, getLocation());
} finally {
try {
bos.close();
} catch (IOException e) {
// ignore
}
}
try {
r.complete();
log("Redirection of output terminated.", Project.MSG_VERBOSE);
} catch (IOException e) {
log("Termination of output redirection failed: " + e, Project.MSG_ERR);
}
if (err != 0) {
if (f.exists()) {
f.delete();
}
if (cmdline.getClasspath() == null) {
log("Antlr libraries not found in external classpath or embedded classpath statement ", Project.MSG_ERR);
}
log("Dependency check failed. ANTLR returned: " + err, Project.MSG_ERR);
return true;
} else {
Pattern p = Pattern.compile("error\\([0-9]+\\):");
Matcher m = p.matcher(bos.toString());
if (m.find()) {
if (f.exists()) {
f.delete();
}
// On error always recompile
return true;
}
}
boolean compile = false;
// open temporary file
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader(f));
} catch (IOException e) {
try {
if (in != null) {
in.close();
}
} catch (IOException ex) {
throw new BuildException("Could not close file\'" + f.toString() + "\'.");
}
if (f.exists()) {
f.delete();
}
try {
throw new BuildException("Could not open \'" + f.getCanonicalPath() + "\' for reading.");
} catch (IOException ex) {
throw new BuildException("Could not open \'" + f.toString() + "\' for reading.");
}
}
// evaluate dependencies in temporary file
String s;
try {
while ((s = in.readLine()) != null) {
String a;
String b;
// As the separator string in lines emitted by the depend option
// is either " : " or ": " trim is invoked for the first file name of a line
int to = s.indexOf(": ");
if (to >= 0) {
a = s.substring(0, to).trim();
File lhs = new File(a);
if (!lhs.isFile()) {
log("File '" + a + "' is not a regular file", Project.MSG_VERBOSE);
String name = lhs.getName();
String[] parts = splitRightHandSide(name, "\\u002E");
if (parts.length <= 1) {
a += ".java";
lhs = new File(a);
if (lhs.isFile()) {
log("File '" + a + "' is a regular file last modified at " + lhs.lastModified(), Project.MSG_VERBOSE);
}
}
}
b = s.substring(to + ": ".length());
String[] names = splitRightHandSide(b, ", ?");
File aFile = new File(a);
for (String name : names) {
File bFile = new File(name);
log("File '" + a + "' depends on file '" + name + "'", Project.MSG_VERBOSE);
log("File '" + a + "' modified at " + aFile.lastModified(), Project.MSG_VERBOSE);
log("File '" + name + "' modified at " + bFile.lastModified(), Project.MSG_VERBOSE);
if (fileUtils.isUpToDate(aFile, bFile)) {
log("Compiling " + target + " as '" + name + "' is newer than '" + a + "'", Project.MSG_VERBOSE);
// Feeling not quite comfortable with touching the file
fileUtils.setFileLastModified(aFile, -1);
log("Touching file '" + a + "'", Project.MSG_VERBOSE);
compile = true;
break;
}
}
if (compile) {
break;
}
}
}
in.close();
} catch (IOException e) {
if (f.exists()) {
f.delete();
}
throw new BuildException("Error reading file '" + f.toString() + "'");
}
if (f.exists()) {
f.delete();
}
return compile;
}
private String[] splitRightHandSide(String fileNames, String pattern) {
String[] names = fileNames.split(pattern);
for (String name : names) {
log("Split right hand side '" + name + "'", Project.MSG_VERBOSE);
}
return names;
}
/** execute in a forked VM */
private int run(String[] command, OutputStream out, OutputStream err) throws IOException {
PumpStreamHandler psh;
if (err == null) {
psh = new PumpStreamHandler(out, bos);
} else {
psh = new PumpStreamHandler(out, new TeeOutputStream(err, bos));
}
Execute exe = new Execute(psh, null);
exe.setAntRun(getProject());
if (workingdir != null) {
exe.setWorkingDirectory(workingdir);
}
exe.setCommandline(command);
return exe.execute();
}
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<antlib>
<taskdef
name="antlr3"
classname="org.apache.tools.ant.antlr.ANTLR3"
/>
</antlib>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>antlr-complete</artifactId>
<packaging>jar</packaging>
<name>ANTLR 3 Complete</name>
<description>Complete distribution for ANTLR 3</description>
<!--
Inherit from the ANTLR master pom, which tells us what
version we are and allows us to inherit dependencies
and so on.
-->
<parent>
<groupId>org.antlr</groupId>
<artifactId>antlr-master</artifactId>
<version>3.5</version>
</parent>
<url>http://antlr.org/</url>
<!--
The complete distribution includes the following modules and their dependencies:
ANTLR 3 Tool
ANTLR 3 Runtime
gUnit for ANTLR 3
StringTemplate 4 (dependency of code generator in the ANTLR 3 Tool)
StringTemplate 3 (dependency of grammars with output=template)
ANTLR 2.7.7 (dependency of template parser in StringTemplate 3)
-->
<dependencies>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr-runtime</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>gunit</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr3-maven-plugin</artifactId>
<version>${project.version}</version>
<configuration></configuration>
<executions>
<execution>
<goals>
<goal>antlr</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.0</version>
<configuration>
<minimizeJar>false</minimizeJar>
<createSourcesJar>true</createSourcesJar>
<filters>
<filter>
<artifact>org.antlr:antlr-complete</artifact>
<includes>
<include>META-INF/**</include>
</includes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.antlr.Tool</mainClass>
</transformer>
</transformers>
</configuration>
<executions>
<execution>
<id>complete-no-st3</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>no-st3</shadedClassifierName>
<filters>
<filter>
<artifact>antlr:antlr</artifact>
<excludes>
<exclude>**</exclude>
</excludes>
</filter>
<filter>
<artifact>org.antlr:stringtemplate</artifact>
<excludes>
<exclude>**</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
<execution>
<id>complete</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<shadedArtifactAttached>false</shadedArtifactAttached>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.antlr</groupId>
<artifactId>antlr3-maven-archetype</artifactId>
<packaging>maven-archetype</packaging>
<name>ANTLR 3 Maven Archetype</name>
<description>ANTLR 3 Maven Archetype</description>
<!--
Inherit from the ANTLR master pom, which tells us what
version we are and allows us to inherit dependencies
and so on.
-->
<parent>
<groupId>org.antlr</groupId>
<artifactId>antlr-master</artifactId>
<version>3.5</version>
</parent>
<build>
<extensions>
<extension>
<groupId>org.apache.maven.archetype</groupId>
<artifactId>archetype-packaging</artifactId>
<version>2.2</version>
</extension>
</extensions>
<plugins>
<plugin>
<artifactId>maven-archetype-plugin</artifactId>
<version>2.2</version>
<extensions>true</extensions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?><archetype-descriptor name="antlr3-maven-archetype">
<fileSets>
<fileSet filtered="true" packaged="true" encoding="UTF-8">
<directory>src/main/java</directory>
<includes>
<include>**/*.java</include>
</includes>
</fileSet>
<fileSet filtered="true" packaged="true" encoding="UTF-8">
<directory>src/main/antlr3</directory>
<includes>
<include>**/*.g</include>
</includes>
<excludes>
<exclude>**/imports/*.*</exclude>
</excludes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
<directory>src/main/antlr3/imports</directory>
<includes>
<include>**/*.g</include>
</includes>
</fileSet>
</fileSets>
</archetype-descriptor>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<archetype>
<id>antlr3-maven-archetype</id>
<sources>
<source>src\main\java\org\antlr\demo\AbstractTLexer.java</source>
<source>src\main\java\org\antlr\demo\AbstractTParser.java</source>
<source>src\main\java\org\antlr\demo\Main.java</source>
</sources>
<resources>
<resource>src\main\antlr3\imports\Ruleb.g</resource>
<resource>src\main\antlr3\TLexer.g</resource>
<resource>src\main\antlr3\TParser.g</resource>
<resource>src\main\antlr3\TTree.g</resource>
</resources>
</archetype>

View File

@ -0,0 +1,182 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- =======================================================================
A quickstart pom.xml that creates a sample project that uses ANTLR 3.x
grammars. You should replace the sample grammars in src/main/antlr3
with your own grammar files and use packages.
A .g file in
src/main/antlr3/com/temporalwave
belongs in the package
com.temporalwave
See http://antlr.org/antlr3-maven-plugin for more details.
This project produces both a jar file of the project and an executeable
jar file that contains all the dependencies so you can run it standalone.
See below for more details.
Archetype by Jim Idle (jimi@temporal-wave.com) - Oct 2009
Report bugs to the ANTLR interest list at http://www.antlr.org
Generated by antlr3-maven-archetype version 3.4.1-SNAPSHOT
=======================================================================
-->
<!-- This is your organizations normal group name
such as org.antlr
All the artifacts you create will be under this
group id.
-->
<groupId>${groupId}</groupId>
<!-- This is how maven knows your artifact
-->
<artifactId>${artifactId}</artifactId>
<!-- This is the human oriented name for the package
so you can call it anything you like
-->
<name>ANTLR3 project: ${package}</name>
<!-- This is the version of YOUR project -->
<version>${version}</version>
<packaging>jar</packaging>
<url>http://antlr.org</url>
<dependencies>
<!--
We need to have the ANTLR runtime jar when running and compiling.
-->
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr-runtime</artifactId>
<version>3.4.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
<!--
Tell Maven which other artifacts we need in order to
build with the ANTLR Tool. Here we also make the default
goal be install so that you can just type mvn at the command
line instead of mvn install. And we add the java compiler plugin
for convenience to show how you can use 1.6 source files but
generate 1.4 compatible .class files (as few people seem to
know about the jsr14 target).
-->
<build>
<defaultGoal>install</defaultGoal>
<plugins>
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr3-maven-plugin</artifactId>
<version>3.4.1-SNAPSHOT</version>
<executions>
<execution>
<goals>
<goal>antlr</goal>
</goals>
</execution>
</executions>
</plugin>
<!--
Strictly speaking, we did not need to generate this for you from
the prototype, but we use it to illustrate how you can get
the JDK 6 Java compiler to accept 1.5 or 1.6 targeted source code
but produce class files that are compatible with JRE 1.4. As
Michael Caine might say, "Not a lot of people know that!"
-->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<source>1.6</source>
<target>jsr14</target>
<sourceDirectory>src</sourceDirectory>
</configuration>
</plugin>
<plugin>
<!--
Build an uber-jar that is packaged with all the other dependencies,
such as the antlr-runtime and so on. This will be useful
for developers, who then do not need to download anything else or
remember that they need antlr.jar in their CLASSPATH and so
on.
You can delete this plugin of course and you will then
get a jar file with only the code generated and included
directly in this project. With this plugin though you will
find that when you build with:
mvn install
There will be an executable jar generated. You can run this
as:
java -jar ${artifactId}-${version}-jar-with-dependencies.jar demosource.dmo
assuming you have a file called demosource.dmo to attempt a parse.
-->
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<!--
Specify that we want the resulting jar to be executable
via java -jar, which we do by modifying the manifest
of course.
-->
<archive>
<manifest>
<mainClass>${package}.Main</mainClass>
</manifest>
</archive>
</configuration>
<!--
We don't want to have to specifically ask for the uber jar, so we attach the
running of this plugin to the execution of the package life-cycle
phase.
-->
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>attached</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,80 @@
// This is a sample lexer generated by the ANTLR3 Maven Archetype
// generator. It shows how to use a superclass to implement
// support methods and variables you may use in the lexer actions
// rather than create a messy lexer that has the Java code embedded
// in it.
//
lexer grammar TLexer;
options {
language=Java; // Default
// Tell ANTLR to make the generated lexer class extend the
// the named class, which is where any supporting code and
// variables will be placed.
//
superClass = AbstractTLexer;
}
// What package should the generated source exist in?
//
@header {
package ${package};
}
// This is just a simple lexer that matches the usual suspects
//
KEYSER : 'Keyser' ;
SOZE : 'Soze' ;
ADD : '+' ;
SEMI: ';' ;
ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
;
INT : '0'..'9'+
;
COMMENT
: '//' ~('\n'|'\r')* '\r'? '\n' {skip();}
| '/*' ( options {greedy=false;} : . )* '*/' {skip();}
;
WS : ( ' '
| '\t'
| '\r'
| '\n'
) {skip();}
;
STRING
: '"' ( ESC_SEQ | ~('\\'|'"') )* '"'
;
fragment
HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;
fragment
ESC_SEQ
: '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
| UNICODE_ESC
| OCTAL_ESC
;
fragment
OCTAL_ESC
: '\\' ('0'..'3') ('0'..'7') ('0'..'7')
| '\\' ('0'..'7') ('0'..'7')
| '\\' ('0'..'7')
;
fragment
UNICODE_ESC
: '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
;

View File

@ -0,0 +1,74 @@
parser grammar TParser;
options {
// Default language but name it anyway
//
language = Java;
// Produce an AST
//
output = AST;
// Use a superclass to implement all helper
// methods, instance variables and overrides
// of ANTLR default methods, such as error
// handling.
//
superClass = AbstractTParser;
// Use the vocabulary generated by the accompanying
// lexer. Maven knows how to work out the relationship
// between the lexer and parser and will build the
// lexer before the parser. It will also rebuild the
// parser if the lexer changes.
//
tokenVocab = TLexer;
}
// Import a grammar file, even though it does not really need it in this
// simle demo parser. We do the import to show where imported grammars should be
// stored for maven builds.
//
import Ruleb;
// Some imaginary tokens for tree rewrites
//
tokens {
SCRIPT;
}
// What package should the generated source exist in?
//
@header {
package ${package};
}
// This is just a simple parser for demo purpose
//
a : b* EOF
-> ^(SCRIPT b*)
;
keyser
: KEYSER^ SOZE
;
expression
: addExpr (ADD^ addExpr)*
;
addExpr
: ID
| INT
| STRING
;

View File

@ -0,0 +1,49 @@
tree grammar TTree;
options {
// Default but name it anyway
//
language = Java;
// Use the vocab from the parser (not the lexer)
// The ANTLR Maven plugin knows how to work out the
// relationships between the .g files and it will build
// the tree parser after the parser. It will also rebuild
// the tree parser if the parser is rebuilt.
//
tokenVocab = TParser;
// Use ANTLR built-in CommonToken for tree nodes
//
ASTLabelType = CommonToken;
}
// What package should the generated source exist in?
//
@header {
package ${package};
}
a : ^(SCRIPT stuff+)
| SCRIPT
;
stuff
: keyser
| expression
;
keyser
: ^(KEYSER SOZE)
{ System.out.println("Found Keyser Soze!!"); }
;
expression
: ^(ADD expression expression)
| ID
| INT
| STRING
;

View File

@ -0,0 +1,11 @@
// A single rule in a grammar that must be imported by the demo
// parser Tparser.g
// This is just here to show that import grammars are stored in their
// own imports directory: src/main/antlr3/imports
//
parser grammar Ruleb;
b
: keyser SEMI!
| expression SEMI!
;

View File

@ -0,0 +1,55 @@
#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
package ${package};
import org.antlr.runtime.CharStream;
import org.antlr.runtime.Lexer;
import org.antlr.runtime.RecognizerSharedState;
/**
* This is the super class for the lexer. It is extended by the lexer class
* generated from TLexer.g.
*
* Do not place code and declarations in the lexer .g files, use
* a superclass like this and place all the support methods and
* error overrides etc in the super class. This way you will keep
* the lexer grammar clean and hunky dory.
*
* @author Jim Idle - Temporal Wave LLC (jimi@idle.ws)
*/
public abstract class AbstractTLexer
extends Lexer
{
/**
* Default constructor for the lexer, when you do not yet know what
* the character stream to be provided is.
*/
public AbstractTLexer() {
}
/**
* Create a new instance of the lexer using the given character stream as
* the input to lex into tokens.
*
* @param input A valid character stream that contains the ruleSrc code you
* wish to compile (or lex at least)
*/
public AbstractTLexer(CharStream input) {
this(input, new RecognizerSharedState());
}
/**
* Internal constructor for ANTLR - do not use.
*
* @param input The character stream we are going to lex
* @param state The shared state object, shared between all lexer comonents
*/
public AbstractTLexer(CharStream input, RecognizerSharedState state) {
super(input,state);
}
}

View File

@ -0,0 +1,64 @@
#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
package ${package};
import org.antlr.runtime.*;
/**
* The super class of the generated parser. It is extended by the generated
* code because of the superClass optoin in the .g file.
*
* This class contains any helper functions used within the parser
* grammar itself, as well as any overrides of the standard ANTLR Java
* runtime methods, such as an implementation of a custom error reporting
* method, symbol table populating methods and so on.
*
* @author Jim Idle - Temporal Wave LLC - jimi@temporal-wave.com
*/
public abstract class AbstractTParser
extends Parser
{
/**
* Create a new parser instance, pre-supplying the input token stream.
*
* @param input The stream of tokens that will be pulled from the lexer
*/
protected AbstractTParser(TokenStream input) {
super(input);
}
/**
* Create a new parser instance, pre-supplying the input token stream
* and the shared state.
*
* This is only used when a grammar is imported into another grammar, but
* we must supply this constructor to satisfy the super class contract.
*
* @param input The stream of tokesn that will be pulled from the lexer
* @param state The shared state object created by an interconnectd grammar
*/
protected AbstractTParser(TokenStream input, RecognizerSharedState state) {
super(input, state);
}
/**
* Creates the error/warning message that we need to show users/IDEs when
* ANTLR has found a parsing error, has recovered from it and is now
* telling us that a parsing exception occurred.
*
* @param tokenNames token names as known by ANTLR (which we ignore)
* @param e The exception that was thrown
*/
@Override
public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
// This is just a place holder that shows how to override this method
//
super.displayRecognitionError(tokenNames, e);
}
}

View File

@ -0,0 +1,250 @@
#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
package ${package};
import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.stringtemplate.*;
import java.io.*;
import ${package}.TParser.a_return;
/**
* Test driver program for the ANTLR3 Maven Architype demo
*
* @author Jim Idle (jimi@temporal-wave.com)
*/
class Main {
private static boolean makeDot = false;
static TLexer lexer;
/** Just a simple test driver for the ASP parser
* to show how to call it.
*/
public static void main(String[] args)
{
try
{
// Create the lexer, which we can keep reusing if we like
//
lexer = new TLexer();
if (args.length > 0)
{
int s = 0;
if (args[0].startsWith("-dot"))
{
makeDot = true;
s = 1;
}
// Recursively parse each directory, and each file on the
// command line
//
for (int i=s; i<args.length; i++)
{
parse(new File(args[i]));
}
}
else
{
System.err.println("Usage: java -jar ${artifactId}-${version}-jar-with-dependencies.jar <directory | filename.dmo>");
}
}
catch (Exception ex)
{
System.err.println("ANTLR demo parser threw exception:");
ex.printStackTrace();
}
}
public static void parse(File source) throws Exception
{
// Open the supplied file or directory
//
try
{
// From here, any exceptions are just thrown back up the chain
//
if (source.isDirectory())
{
System.out.println("Directory: " + source.getAbsolutePath());
String files[] = source.list();
for (int i=0; i<files.length; i++)
{
parse(new File(source, files[i]));
}
}
// Else find out if it is an ASP.Net file and parse it if it is
//
else
{
// File without paths etc
//
String sourceFile = source.getName();
if (sourceFile.length() > 3)
{
String suffix = sourceFile.substring(sourceFile.length()-4).toLowerCase();
// Ensure that this is a DEMO script (or seemingly)
//
if (suffix.compareTo(".dmo") == 0)
{
parseSource(source.getAbsolutePath());
}
}
}
}
catch (Exception ex)
{
System.err.println("ANTLR demo parser caught error on file open:");
ex.printStackTrace();
}
}
public static void parseSource(String source) throws Exception
{
// Parse an ANTLR demo file
//
try
{
// First create a file stream using the povided file/path
// and tell the lexer that that is the character source.
// You can also use text that you have already read of course
// by using the string stream.
//
lexer.setCharStream(new ANTLRFileStream(source, "UTF8"));
// Using the lexer as the token source, we create a token
// stream to be consumed by the parser
//
CommonTokenStream tokens = new CommonTokenStream(lexer);
// Now we need an instance of our parser
//
TParser parser = new TParser(tokens);
System.out.println("file: " + source);
// Provide some user feedback
//
System.out.println(" Lexer Start");
long start = System.currentTimeMillis();
// Force token load and lex (don't do this normally,
// it is just for timing the lexer)
//
tokens.LT(1);
long lexerStop = System.currentTimeMillis();
System.out.println(" lexed in " + (lexerStop - start) + "ms.");
// And now we merely invoke the start rule for the parser
//
System.out.println(" Parser Start");
long pStart = System.currentTimeMillis();
a_return psrReturn = parser.a();
long stop = System.currentTimeMillis();
System.out.println(" Parsed in " + (stop - pStart) + "ms.");
// If we got a valid a tree (the syntactic validity of the source code
// was found to be solid), then let's print the tree to show we
// did something; our testing public wants to know!
// We do something fairly cool here and generate a graphviz/dot
// specification for the tree, which will allow the users to visualize
// it :-) we only do that if asked via the -dot option though as not
// all users will hsve installed the graphviz toolset from
// http://www.graphviz.org
//
// Pick up the generic tree
//
Tree t = (Tree)psrReturn.getTree();
// NOw walk it with the generic tree walker, which does nothing but
// verify the tree really.
//
try
{
if (parser.getNumberOfSyntaxErrors() == 0) {
TTree walker = new TTree(new CommonTreeNodeStream(t));
System.out.println(" AST Walk Start${symbol_escape}n");
pStart = System.currentTimeMillis();
walker.a();
stop = System.currentTimeMillis();
System.out.println("${symbol_escape}n AST Walked in " + (stop - pStart) + "ms.");
}
}
catch(Exception w)
{
System.out.println("AST walk caused exception.");
w.printStackTrace();
}
if (makeDot && tokens.size() < 4096)
{
// Now stringify it if you want to...
//
// System.out.println(t.toStringTree());
// Use the ANLTR built in dot generator
//
DOTTreeGenerator gen = new DOTTreeGenerator();
// Which we can cause to generate the DOT specification
// with the input file name suffixed with .dot. You can then use
// the graphviz tools or zgrviewer (Java) to view the graphical
// version of the dot file.
//
source = source.substring(0, source.length()-3);
String outputName = source + "dot";
System.out.println(" Producing AST dot (graphviz) file");
// It produces a jguru string template.
//
StringTemplate st = gen.toDOT(t, new CommonTreeAdaptor());
// Create the output file and write the dot spec to it
//
FileWriter outputStream = new FileWriter(outputName);
outputStream.write(st.toString());
outputStream.close();
// Invoke dot to generate a .png file
//
System.out.println(" Producing png graphic for tree");
pStart = System.currentTimeMillis();
Process proc = Runtime.getRuntime().exec("dot -Tpng -o" + source + "png " + outputName);
proc.waitFor();
stop = System.currentTimeMillis();
System.out.println(" PNG graphic produced in " + (stop - pStart) + "ms.");
}
}
catch (FileNotFoundException ex)
{
// The file we tried to parse does not exist
//
System.err.println("${symbol_escape}n !!The file " + source + " does not exist!!${symbol_escape}n");
}
catch (Exception ex)
{
// Something went wrong in the parser, report this
//
System.err.println("Parser threw an exception:${symbol_escape}n${symbol_escape}n");
ex.printStackTrace();
}
}
}

View File

@ -0,0 +1,234 @@
<!--
[The "BSD license"]
ANTLR - Copyright (c) 2005-2010 Terence Parr
Maven Plugin - Copyright (c) 2009 Jim Idle
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<!-- Maven model we are inheriting from
-->
<modelVersion>4.0.0</modelVersion>
<!--
Now that the ANTLR project has adopted Maven with a vengence,
all ANTLR tools will be grouped under org.antlr and will be
controlled by a project member.
-->
<groupId>org.antlr</groupId>
<!--
This is the ANTLR plugin for ANTLR version 3.1.3 and above. It might
have been best to change the name of the plugin as the 3.1.2 plugins
behave a little differently, however for the sake of one transitional
phase to a much better plugin, it was decided that the name should
remain the same.
-->
<artifactId>antlr3-maven-plugin</artifactId>
<packaging>maven-plugin</packaging>
<parent>
<groupId>org.antlr</groupId>
<artifactId>antlr-master</artifactId>
<version>3.5</version>
</parent>
<name>ANTLR 3 Maven plugin</name>
<prerequisites>
<maven>2.0</maven>
</prerequisites>
<!--
Where does our actual project live on the interwebs.
-->
<url>http://antlr.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<description>
This is the brand new, re-written from scratch plugin for ANTLR v3.
Previous valiant efforts all suffered from being unable to modify the ANTLR Tool
itself to provide support not just for Maven oriented things but any other tool
that might wish to invoke ANTLR without resorting to the command line interface.
Rather than try to shoe-horn new code into the existing Mojo (in fact I think that
by incorporating a patch supplied by someone I ended up with tow versions of the
Mojo, I elected to rewrite everything from scratch, including the documentation, so
that we might end up with a perfect Mojo that can do everything that ANTLR v3 supports
such as imported grammar processing, proper support for library directories and
locating token files from generated sources, and so on.
In the end I decided to also change the the ANTLR Tool.java code so that it
would be the provider of all the things that a build tool needs, rather than
delegating things to 5 different tools. So, things like dependencies, dependency
sorting, option tracking, generating sources and so on are all folded back
in to ANTLR's Tool.java code, where they belong, and they now provide a
public interface to anyone that might want to interface with them.
One other goal of this rewrite was to completely document the whole thing
to death. Hence even this pom has more comments than funcitonal elements,
in case I get run over by a bus or fall off a cliff while skiing.
Jim Idle - March 2009
</description>
<developers>
<developer>
<name>Jim Idle</name>
<url>http://www.temporal-wave.com</url>
<roles>
<role>Originator, version 3.1.3</role>
</roles>
</developer>
<developer>
<name>Terence Parr</name>
<url>http://antlr.org/wiki/display/~admin/Home</url>
<roles>
<role>Project lead - ANTLR</role>
</roles>
</developer>
<developer>
<name>David Holroyd</name>
<url>http://david.holroyd.me.uk/</url>
<roles>
<role>Originator - prior version</role>
</roles>
</developer>
<developer>
<name>Kenny MacDermid</name>
<url>mailto:kenny "at" kmdconsulting.ca</url>
<roles>
<role>Contributor - prior versions</role>
</roles>
</developer>
</developers>
<!-- ============================================================================= -->
<!--
What are we depedent on for the Mojos to execute? We need the
plugin API itself and of course we need the ANTLR Tool and runtime
and any of their dependencies, which we inherit. The Tool itself provides
us with all the dependencies, so we need only name it here.
-->
<dependencies>
<!--
The things we need to build the target language recognizer
-->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>2.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-compiler-api</artifactId>
<version>2.0</version>
</dependency>
<!--
The version of ANTLR tool that this version of the plugin controls.
We have decided that this should be in lockstep with ANTLR itself, other
than -1 -2 -3 etc patch releases.
-->
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-plugin-testing-harness</artifactId>
<version>1.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>2.6</version>
<configuration>
<dependencyLocationsEnabled>false</dependencyLocationsEnabled>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.2</version>
</plugin>
</plugins>
</reporting>
</project>

View File

@ -0,0 +1,90 @@
/**
[The "BSD licence"]
ANTLR - Copyright (c) 2005-2008 Terence Parr
Maven Plugin - Copyright (c) 2009 Jim Idle
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.mojo.antlr3;
import org.antlr.tool.ANTLRErrorListener;
import org.antlr.tool.Message;
import org.antlr.tool.ToolMessage;
import org.apache.maven.plugin.logging.Log;
/**
* The Maven plexus container gives us a Log logging provider
* which we can use to install an error listener for the ANTLR
* tool to report errors by.
*/
public class Antlr3ErrorLog implements ANTLRErrorListener {
private Log log;
/**
* Instantiate an ANTLR ErrorListner that communicates any messages
* it receives to the Maven error sink.
*
* @param log The Maven Error Log
*/
public Antlr3ErrorLog(Log log) {
this.log = log;
}
/**
* Sends an informational message to the Maven log sink.
* @param s The message to send to Maven
*/
public void info(String message) {
log.info(message);
}
/**
* Sends an error message from ANTLR analysis to the Maven Log sink.
*
* @param message The message to send to Maven.
*/
public void error(Message message) {
log.error(message.toString());
}
/**
* Sends a warning message to the Maven log sink.
*
* @param message
*/
public void warning(Message message) {
log.warn(message.toString());
}
/**
* Sends an error message from the ANTLR tool to the Maven Log sink.
* @param toolMessage
*/
public void error(ToolMessage toolMessage) {
log.error(toolMessage.toString());
}
}

View File

@ -0,0 +1,501 @@
/**
[The "BSD licence"]
ANTLR - Copyright (c) 2005-2008 Terence Parr
Maven Plugin - Copyright (c) 2009 Jim Idle
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.
*/
/* ========================================================================
* This is the definitive ANTLR3 Mojo set. All other sets are belong to us.
*/
package org.antlr.mojo.antlr3;
import antlr.RecognitionException;
import antlr.TokenStreamException;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.antlr.Tool;
import org.apache.maven.plugin.logging.Log;
import org.codehaus.plexus.compiler.util.scan.InclusionScanException;
import org.codehaus.plexus.compiler.util.scan.SimpleSourceInclusionScanner;
import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
import org.codehaus.plexus.compiler.util.scan.mapping.SourceMapping;
import org.codehaus.plexus.compiler.util.scan.mapping.SuffixMapping;
/**
* Parses ANTLR grammar files {@code *.g} and transforms them into Java source
* files.
*
* @goal antlr
* @phase generate-sources
* @requiresDependencyResolution compile
* @requiresProject true
*
* @author <a href="mailto:jimi@temporal-wave.com">Jim Idle</a>
*/
public class Antlr3Mojo
extends AbstractMojo {
// First, let's deal with the options that the ANTLR tool itself
// can be configured by.
//
/**
* If set to true, then after the tool has processed an input grammar file
* it will report various statistics about the parser, such as information
* on cyclic DFAs, which rules may use backtracking, and so on.
*
* @parameter default-value="false"
*/
protected boolean report;
/**
* If set to true, then the ANTLR tool will print a version of the input
* grammar(s) which are stripped of any embedded actions.
*
* @parameter default-value="false"
*/
protected boolean printGrammar;
/**
* If set to true, then the code generated by the ANTLR code generator will
* be set to debug mode. This means that when run, the code will 'hang' and
* wait for a debug connection on a TCP port (49100 by default).
*
* @parameter default-value="false"
*/
protected boolean debug;
/**
* If set to true, then the generated parser will compute and report profile
* information at runtime.
*
* @parameter default-value="false"
*/
protected boolean profile;
/**
* If set to true, then the ANTLR tool will generate a description of the
* NFA for each rule in <a href="http://www.graphviz.org">Dot format</a>
*
* @parameter default-value="false"
*/
protected boolean nfa;
/**
* If set to true, then the ANTLR tool will generate a description of the
* DFA for each decision in the grammar in
* <a href="http://www.graphviz.org">Dot format</a>.
*
* @parameter default-value="false"
*/
protected boolean dfa;
/**
* If set to true, the generated parser code will log rule entry and exit
* points to stdout ({@link System#out} for the Java target) as an aid to
* debugging.
*
* @parameter default-value="false"
*/
protected boolean trace;
/**
* If this parameter is set, it indicates that any warning or error messages
* returned by ANLTR, should be formatted in the specified way. Currently,
* ANTLR supports the built-in formats {@code antlr}, {@code gnu} and
* {@code vs2005}.
*
* @parameter default-value="antlr"
*/
protected String messageFormat;
/**
* If set to true, then ANTLR will report verbose messages during the code
* generation process. This includes the names of files, the version of
* ANTLR, and more.
*
* @parameter default-value="true"
*/
protected boolean verbose;
/**
* The maximum number of alternatives allowed in an inline switch statement.
* Beyond this, ANTLR will not generate a switch statement for the DFA.
*
* @parameter default-value="300"
*/
private int maxSwitchCaseLabels;
/**
* The minimum number of alternatives for ANTLR to generate a switch
* statement. For decisions with fewer alternatives, an if/else if/else
* statement will be used instead.
*
* @parameter default-value="3"
*/
private int minSwitchAlts;
/* --------------------------------------------------------------------
* The following are Maven specific parameters, rather than specific
* options that the ANTLR tool can use.
*/
/**
* Provides an explicit list of all the grammars that should be included in
* the generate phase of the plugin. Note that the plugin is smart enough to
* realize that imported grammars should be included but not acted upon
* directly by the ANTLR Tool.
* <p/>
* A set of Ant-like inclusion patterns used to select files from the source
* directory for processing. By default, the pattern <code>**&#47;*.g</code>
* is used to select grammar files.
*
* @parameter
*/
protected Set<String> includes = new HashSet<String>();
/**
* A set of Ant-like exclusion patterns used to prevent certain files from
* being processed. By default, this set is empty such that no files are
* excluded.
*
* @parameter
*/
protected Set<String> excludes = new HashSet<String>();
/**
* The current Maven project.
*
* @parameter expression="${project}"
* @required
* @readonly
*/
protected MavenProject project;
/**
* The directory where the ANTLR grammar files ({@code *.g}) are located.
*
* @parameter default-value="${basedir}/src/main/antlr3"
*/
private File sourceDirectory;
/**
* The directory where the parser files generated by ANTLR will be stored.
* The directory will be registered as a compile source root of the project
* such that the generated files will participate in later build phases like
* compiling and packaging.
*
* @parameter default-value="${project.build.directory}/generated-sources/antlr3"
* @required
*/
private File outputDirectory;
/**
* Location for imported token files, e.g. {@code *.tokens} and imported
* grammars. Note that ANTLR will not try to process grammars that it finds
* to be imported into other grammars (in the same processing session).
*
* @parameter default-value="${basedir}/src/main/antlr3/imports"
*/
private File libDirectory;
public File getSourceDirectory() {
return sourceDirectory;
}
public File getOutputDirectory() {
return outputDirectory;
}
public File getLibDirectory() {
return libDirectory;
}
void addSourceRoot(File outputDir) {
project.addCompileSourceRoot(outputDir.getPath());
}
/**
* An instance of the ANTLR tool build.
*/
protected Tool tool;
/**
* The main entry point for this Mojo, it is responsible for converting
* ANTLR 3.x grammars into the target language specified by the grammar.
*
* @throws MojoExecutionException if a configuration or grammar error causes
* the code generation process to fail
* @throws MojoFailureException if an instance of the ANTLR 3 {@link Tool}
* cannot be created
*/
public void execute()
throws MojoExecutionException, MojoFailureException {
Log log = getLog();
// Check to see if the user asked for debug information, then dump all the
// parameters we have picked up if they did.
//
if (log.isDebugEnabled()) {
// Excludes
//
for (String e : excludes) {
log.debug("ANTLR: Exclude: " + e);
}
// Includes
//
for (String e : includes) {
log.debug("ANTLR: Include: " + e);
}
// Output location
//
log.debug("ANTLR: Output: " + outputDirectory);
// Library directory
//
log.debug("ANTLR: Library: " + libDirectory);
// Flags
//
log.debug("ANTLR: report : " + report);
log.debug("ANTLR: printGrammar : " + printGrammar);
log.debug("ANTLR: debug : " + debug);
log.debug("ANTLR: profile : " + profile);
log.debug("ANTLR: nfa : " + nfa);
log.debug("ANTLR: dfa : " + dfa);
log.debug("ANTLR: trace : " + trace);
log.debug("ANTLR: messageFormat : " + messageFormat);
log.debug("ANTLR: maxSwitchCaseLabels : " + maxSwitchCaseLabels);
log.debug("ANTLR: minSwitchAlts : " + minSwitchAlts);
log.debug("ANTLR: verbose : " + verbose);
}
// Ensure that the output directory path is all in tact so that
// ANTLR can just write into it.
//
File outputDir = getOutputDirectory();
if (!outputDir.exists()) {
outputDir.mkdirs();
}
// First thing we need is an instance of the ANTLR 3.1 build tool
//
try {
// ANTLR Tool buld interface
//
tool = new Tool();
} catch (Exception e) {
log.error("The attempt to create the ANTLR build tool failed, see exception report for details");
throw new MojoFailureException("Jim failed you!");
}
// Next we need to set the options given to us in the pom into the
// tool instance we have created.
//
tool.setDebug(debug);
tool.setGenerate_DFA_dot(dfa);
tool.setGenerate_NFA_dot(nfa);
tool.setProfile(profile);
tool.setReport(report);
tool.setPrintGrammar(printGrammar);
tool.setTrace(trace);
tool.setVerbose(verbose);
tool.setMessageFormat(messageFormat);
tool.setMaxSwitchCaseLabels(maxSwitchCaseLabels);
tool.setMinSwitchAlts(minSwitchAlts);
// Where do we want ANTLR to produce its output? (Base directory)
//
if (log.isDebugEnabled())
{
log.debug("Output directory base will be " + outputDirectory.getAbsolutePath());
}
tool.setOutputDirectory(outputDirectory.getAbsolutePath());
// Tell ANTLR that we always want the output files to be produced in the output directory
// using the same relative path as the input file was to the input directory.
//
tool.setForceRelativeOutput(true);
// Where do we want ANTLR to look for .tokens and import grammars?
//
tool.setLibDirectory(libDirectory.getAbsolutePath());
if (!sourceDirectory.exists()) {
if (log.isInfoEnabled()) {
log.info("No ANTLR grammars to compile in " + sourceDirectory.getAbsolutePath());
}
return;
} else {
if (log.isInfoEnabled()) {
log.info("ANTLR: Processing source directory " + sourceDirectory.getAbsolutePath());
}
}
// Set working directory for ANTLR to be the base source directory
//
tool.setInputDirectory(sourceDirectory.getAbsolutePath());
try {
// Now pick up all the files and process them with the Tool
//
processGrammarFiles(sourceDirectory, outputDirectory);
} catch (InclusionScanException ie) {
log.error(ie);
throw new MojoExecutionException("Fatal error occured while evaluating the names of the grammar files to analyze");
} catch (Exception e) {
getLog().error(e);
throw new MojoExecutionException(e.getMessage());
}
tool.process();
// If any of the grammar files caused errors but did nto throw exceptions
// then we should have accumulated errors in the counts
//
if (tool.getNumErrors() > 0) {
throw new MojoExecutionException("ANTLR caught " + tool.getNumErrors() + " build errors.");
}
// All looks good, so we need to tel Maven about the sources that
// we just created.
//
if (project != null) {
// Tell Maven that there are some new source files underneath
// the output directory.
//
addSourceRoot(this.getOutputDirectory());
}
}
/**
*
* @param sourceDirectory
* @param outputDirectory
* @throws TokenStreamException
* @throws RecognitionException
* @throws IOException
* @throws InclusionScanException
*/
private void processGrammarFiles(File sourceDirectory, File outputDirectory)
throws TokenStreamException, RecognitionException, IOException, InclusionScanException {
// Which files under the source set should we be looking for as grammar files
//
SourceMapping mapping = new SuffixMapping("g", Collections.<String>emptySet());
// What are the sets of includes (defaulted or otherwise).
//
Set<String> includes = getIncludesPatterns();
// Now, to the excludes, we need to add the imports directory
// as this is autoscanned for importd grammars and so is auto-excluded from the
// set of gramamr fiels we shuold be analyzing.
//
excludes.add("imports/**");
SourceInclusionScanner scan = new SimpleSourceInclusionScanner(includes, excludes);
scan.addSourceMapping(mapping);
Set<File> grammarFiles = scan.getIncludedSources(sourceDirectory, null);
if (grammarFiles.isEmpty()) {
if (getLog().isInfoEnabled()) {
getLog().info("No grammars to process");
}
} else {
// Tell the ANTLR tool that we want sorted build mode
//
tool.setMake(true);
// Iterate each grammar file we were given and add it into the tool's list of
// grammars to process.
//
for (File grammar : grammarFiles) {
if (getLog().isDebugEnabled()) {
getLog().debug("Grammar file '" + grammar.getPath() + "' detected.");
}
String relPath = findSourceSubdir(sourceDirectory, grammar.getPath()) + grammar.getName();
if (getLog().isDebugEnabled()) {
getLog().debug(" ... relative path is: " + relPath);
}
tool.addGrammarFile(relPath);
}
}
}
public Set<String> getIncludesPatterns() {
if (includes == null || includes.isEmpty()) {
return Collections.singleton("**/*.g");
}
return includes;
}
/**
* Given the source directory File object and the full PATH to a
* grammar, produce the path to the named grammar file in relative
* terms to the {@code sourceDirectory}. This will then allow ANTLR to
* produce output relative to the base of the output directory and
* reflect the input organization of the grammar files.
*
* @param sourceDirectory The source directory {@link File} object
* @param grammarFileName The full path to the input grammar file
* @return The path to the grammar file relative to the source directory
*/
private String findSourceSubdir(File sourceDirectory, String grammarFileName) {
String srcPath = sourceDirectory.getPath() + File.separator;
if (!grammarFileName.startsWith(srcPath)) {
throw new IllegalArgumentException("expected " + grammarFileName + " to be prefixed with " + sourceDirectory);
}
File unprefixedGrammarFileName = new File(grammarFileName.substring(srcPath.length()));
if ( unprefixedGrammarFileName.getParent()!=null ) {
return unprefixedGrammarFileName.getParent() + File.separator;
}
else {
return "";
}
}
}

View File

@ -0,0 +1,8 @@
Imported Grammar Files
In order to have the ANTLR plugin automatically locate and use grammars used
as imports in your main <<<.g>>> files, you need to place the imported grammar
files in the imports directory beneath the root directory of your grammar
files (which is <<<src/main/antlr3>>> by default of course).
For a default layout, place your import grammars in the directory: <<<src/main/antlr3/imports>>>

View File

@ -0,0 +1,46 @@
Libraries
The introduction of the import directive in a grammar allows reuse of common grammar files
as well as the ability to divide up functional components of large grammars. However it has
caused some confusion in regard to the fact that generated vocabulary files (<<<*.tokens>>>) can also
be searched for with the <<<<libDirectory>>>> directive.
This has confused two separate functions and imposes a structure upon the layout of
your grammar files in certain cases. If you have grammars that both use the import
directive and also require the use of a vocabulary file then you will need to locate
the grammar that generates the <<<.tokens>>> file alongside the grammar that uses it. This
is because you will need to use the <<<<libDirectory>>>> directive to specify the
location of your imported grammars and ANTLR will not find any vocabulary files in
this directory.
The <<<.tokens>>> files for any grammars are generated within the same output directory structure
as the <<<.java>>> files. So, wherever the <<<.java>>> files are generated, you will also find the <<<.tokens>>>
files. ANTLR looks for <<<.tokens>>> files in both the <<<<libDirectory>>>> and the output directory
where it is placing the generated <<<.java>>> files. Hence when you locate the grammars that generate
<<<.tokens>>> files in the same source directory as the ones that use the <<<.tokens>>> files, then
the Maven plugin will find the expected <<<.tokens>>> files.
The <<<<libDirectory>>>> is specified like any other directory parameter in Maven. Here is an
example:
+--
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr3-maven-plugin</artifactId>
<version>${plugin.version}</version>
<executions>
<execution>
<configuration>
<goals>
<goal>antlr</goal>
</goals>
<libDirectory>src/main/antlr_imports</libDirectory>
</configuration>
</execution>
</executions>
</plugin>
+--

View File

@ -0,0 +1,40 @@
Simple configuration
If your grammar files are organized into the default locations as described in the {{{../index.html}introduction}},
then configuring the <<<pom.xml>>> file for your project is as simple as adding this to it
+--
<plugins>
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr3-maven-plugin</artifactId>
<version>3.1.3-1</version>
<executions>
<execution>
<goals>
<goal>antlr</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
+--
When the <<<mvn>>> command is executed all grammar files under <<<src/main/antlr3>>>, except any
import grammars under <<<src/main/antlr3/imports>>> will be analyzed and converted to
Java source code in the output directory <<<target/generated-sources/antlr3>>>.
Your input files under <<<antlr3>>> should be stored in sub directories that
reflect the package structure of your java parsers. If your grammar file <<<parser.g>>> contains:
+---
@header {
package org.jimi.themuss;
}
+---
Then the <<<.g>>> file should be stored in: <<<src/main/antlr3/org/jimi/themuss/parser.g>>>. This way
the generated <<<.java>>> files will correctly reflect the package structure in which they will
finally rest as classes.

View File

@ -0,0 +1,62 @@
-------------
ANTLR v3 Maven Plugin
-------------
Jim Idle
-------------
March 2009
-------------
ANTLR v3 Maven plugin
The ANTLR v3 Maven plugin is completely re-written as of version 3.1.3; if you are familiar
with prior versions, you should note that there are some behavioral differences that make
it worthwhile reading this documentation.
The job of the plugin is essentially to tell the standard ANTLR parser generator where the
input grammar files are and where the output files should be generated. As with all Maven
plugins, there are defaults, which you are advised to comply to, but are not forced to
comply to.
This version of the plugin allows full control over ANTLR and allows configuration of all
options that are useful for a build system. The code required to calculate dependencies,
check the build order, and otherwise work with your grammar files is built into the ANTLR
tool as of version 3.1.3 of ANTLR and this plugin.
* Plugin Versioning
The plugin version tracks the version of the ANTLR tool that it controls. Hence if you
use version 3.1.3 of the plugin, you will build your grammars using version 3.1.3 of the
ANTLR tool, version 3.2 of the plugin will use version 3.2 of the ANTLR tool and so on.
You may also find that there are patch versions of the plugin such as 3.1.3-1 3.1.3-2 and
so on. Use the latest patch release of the plugin.
The current version of the plugin is shown at the top of this page after the <<Last Deployed>> date.
* Default directories
As with all Maven plugins, this plugin will automatically default to standard locations
for your grammar and import files. Organizing your source code to reflect this standard
layout will greatly reduce the configuration effort required. The standard layout lookd
like this:
+--
src/main/
|
+--- antlr3/... .g files organized in the required package structure
|
+--- imports/ .g files that are imported by other grammars.
+--
If your grammar is intended to be part of a package called <<<org.foo.bar>>> then you would
place it in the directory <<<src/main/antlr3/org/foo/bar>>>. The plugin will then produce
<<<.java>>> and <<<.tokens>>> files in the output directory <<<target/generated-sources/antlr3/org/foo/bar>>>
When the Java files are compiled they will be in the correct location for the Javac
compiler without any special configuration. The generated java files are automatically
submitted for compilation by the plugin.
The <<<src/main/antlr3/imports>>> directory is treated in a special way. It should contain
any grammar files that are imported by other grammar files (do not make subdirectories here.)
Such files are never built on their own, but the plugin will automatically tell the ANTLR
tool to look in this directory for library files.

View File

@ -0,0 +1,59 @@
Usage
The ANTLR 3 plugin for Maven can generate parsers for any number of grammars in
your project.
* Compiling Grammars into Parsers
By default, the <<<{{{./antlr-mojo.html}antlr}}>>> goal will search for grammar
files in the directory <<<$\{basedir\}/src/main/antlr3>>> and any additional
<<<.tokens>>> files in the directory <<<$\{basedir\}/src/main/antlr3/imports>>>.
This can be configured to search other directories using the plugin configuration
parameters as described in the <<<{{{./antlr-mojo.html}antlr}}>>> goal
documentation.
The following figure shows the expected layout of files for the default
configuration of this plugin.
+--
src/main/
|
+--- antlr3/... .g files organized in the required package structure
|
+--- imports/ user-created .tokens files and .g files that are imported by other grammars
+--
The next step is to configure your POM to call the plugin. The goals will
normally run during the generate-sources phase of the build. Examples of how to
configure your POM can be found on the various examples pages, reachable via
the page menu. If you stick with the default values, the snippet below will
suffice:
+--
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr3-maven-plugin</artifactId>
<version>${project.version}</version>
<executions>
<execution>
<id>antlr</id>
<goals>
<goal>antlr</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
...
</build>
...
</project>
+--
Note that you can create multiple executions, and thus build some grammars with
different options to others (such as setting the <<<debug>>> option for
instance).

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="ANTLR v3 Maven plugin">
<publishDate position="left"/>
<version position="left"/>
<poweredBy>
<logo name="ANTLR Web Site" href="http://antlr3.org/"
img="http://www.antlr.org/wiki/download/attachments/292/ANTLR3"/>
</poweredBy>
<body>
<links>
<item name="Antlr Web Site" href="http://www.antlr3.org/"/>
</links>
<menu name="Overview">
<item name="Introduction" href="index.html"/>
<item name="Goals" href="plugin-info.html"/>
<item name="Usage" href="usage.html"/>
<item name="FAQ" href="faq.html"/>
</menu>
<menu name="Examples">
<item name="Simple configurations" href="examples/simple.html"/>
<item name="Using library directories" href="examples/libraries.html"/>
<item name="Using imported grammars" href="examples/import.html"/>
</menu>
<menu ref="reports" />
<menu ref="modules" />
</body>
</project>

View File

@ -0,0 +1,56 @@
ANTLR Project Contributors Certification of Origin and Rights
All contributors to ANTLR v3 must formally agree to abide by this
certificate of origin by signing on the bottom with their github
userid, full name, email address (you can obscure your e-mail, but it
must be computable by human), and date.
By signing this agreement, you are warranting and representing that
you have the right to release code contributions or other content free
of any obligations to third parties and are granting Terence Parr and
ANTLR project contributors, henceforth referred to as The ANTLR
Project, a license to incorporate it into The ANTLR Project tools
(such as ANTLRWorks and StringTemplate) or related works under the BSD
license. You understand that The ANTLR Project may or may not
incorporate your contribution and you warrant and represent the
following:
1. I am the creator of all my contributions. I am the author of all
contributed work submitted and further warrant and represent that
such work is my original creation and I have the right to license
it to The ANTLR Project for release under the 3-clause BSD
license. I hereby grant The ANTLR Project a nonexclusive,
irrevocable, royalty-free, worldwide license to reproduce,
distribute, prepare derivative works, and otherwise use this
contribution as part of the ANTLR project, associated
documentation, books, and tools at no cost to The ANTLR Project.
2. I have the right to submit. This submission does not violate the
rights of any person or entity and that I have legal authority over
this submission and to make this certification.
3. If I violate another's rights, liability lies with me. I agree to
defend, indemnify, and hold The ANTLR Project and ANTLR users
harmless from any claim or demand, including reasonable attorney
fees, made by any third party due to or arising out of my violation
of these terms and conditions or my violation of the rights of
another person or entity.
4. I understand and agree that this project and the contribution are
public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license indicated in the file.
I have read this agreement and do so certify by adding my signoff to
the end of the following contributors list.
CONTRIBUTORS:
YYYY/MM/DD, github id, Full name, email
2012/07/12, parrt, Terence Parr, parrt@antlr.org
2012/08/08, Zannick, Benjamin S Wolf, jokeserver@gmail.com
2012/09/15, martint, Martin Traverso, mtraverso@gmail.com
2012/09/16, qmx, Douglas Campos, qmx@qmx.me
2012/09/17, ksgokul, Gokulakannan Somasundaram, gokul007@gmail.com
2012/11/22, sharwell, Sam Harwell, sam@tunnelvisionlabs.com

View File

@ -0,0 +1,144 @@
<!--
[The "BSD license"]
ANTLR - Copyright (c) 2005-2010 Terence Parr
Maven Plugin - Copyright (c) 2009 Jim Idle
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<prerequisites>
<maven>2.0</maven>
</prerequisites>
<groupId>org.antlr</groupId>
<artifactId>maven-gunit-plugin</artifactId>
<packaging>maven-plugin</packaging>
<name>ANTLR 3 gUnit Maven plugin</name>
<description>A Maven plugin for incorporating gUnit testing of grammars</description>
<url>http://antlr.org</url>
<!--
Inherit from the ANTLR master pom, which tells us what
version we are and allows us to inherit dependencies
and so on.
-->
<parent>
<groupId>org.antlr</groupId>
<artifactId>antlr-master</artifactId>
<version>3.5</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!--
What are we depedent on for the Mojos to execute? We need the
plugin API itself and of course we need the ANTLR Tool and runtime
and any of their dependencies, which we inherit. The Tool itself provides
us with all the dependencies, so we need only name it here.
-->
<dependencies>
<!--
The things we need to build the target language recognizer
-->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>2.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-compiler-api</artifactId>
<version>2.0</version>
</dependency>
<!--
The version of ANTLR tool that this version of the plugin controls.
We have decided that this should be in lockstep with ANTLR itself, other
than -1 -2 -3 etc patch releases.
-->
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr</artifactId>
<version>${project.version}</version>
</dependency>
<!--
Dependency on the gUnit artifact.
-->
<dependency>
<groupId>org.antlr</groupId>
<artifactId>gunit</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-plugin-testing-harness</artifactId>
<version>1.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>2.6</version>
<configuration>
<dependencyLocationsEnabled>false</dependencyLocationsEnabled>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,397 @@
package org.antlr.mojo.antlr3;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
import java.util.ArrayList;
import java.util.Collections;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.io.FileWriter;
import java.io.BufferedWriter;
import java.net.URL;
import java.net.MalformedURLException;
import java.net.URLClassLoader;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.compiler.util.scan.mapping.SourceMapping;
import org.codehaus.plexus.compiler.util.scan.mapping.SuffixMapping;
import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
import org.codehaus.plexus.compiler.util.scan.SimpleSourceInclusionScanner;
import org.codehaus.plexus.compiler.util.scan.InclusionScanException;
import org.antlr.runtime.ANTLRFileStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.gunit.GrammarInfo;
import org.antlr.gunit.gUnitExecutor;
import org.antlr.gunit.AbstractTest;
import org.antlr.gunit.Interp;
/**
* Takes gUnit scripts and directly performs testing.
*
* @goal gunit
*
* @phase test
* @requiresDependencyResolution test
* @requiresProject true
*
* @author Steve Ebersole
*/
public class GUnitExecuteMojo extends AbstractMojo {
public static final String ANTLR_GROUP_ID = "org.antlr";
public static final String ANTLR_ARTIFACT_NAME = "antlr";
public static final String ANTLR_RUNTIME_ARTIFACT_NAME = "antlr-runtime";
/**
* INTERNAL : The Maven Project to which we are attached
*
* @parameter expression="${project}"
* @required
*/
private MavenProject project;
/**
* INTERNAL : The artifacts associated to the dependencies defined as part
* of our configuration within the project to which we are being attached.
*
* @parameter expression="${plugin.artifacts}"
* @required
* @readonly
*/
private List<Artifact> pluginArtifacts;
/**
* Specifies the directory containing the gUnit testing files.
*
* @parameter expression="${basedir}/src/test/gunit"
* @required
*/
private File sourceDirectory;
/**
* A set of patterns for matching files from the sourceDirectory that
* should be included as gUnit source files.
*
* @parameter
*/
private Set<String> includes;
/**
* A set of exclude patterns.
*
* @parameter
*/
private Set<String> excludes;
/**
* Specifies directory to which gUnit reports should get written.
*
* @parameter expression="${basedir}/target/gunit-report"
* @required
*/
private File reportDirectory;
/**
* Should gUnit functionality be completely by-passed?
* <p/>
* By default we skip gUnit tests if the user requested that all testing be skipped using 'maven.test.skip'
*
* @parameter expression="${maven.test.skip}"
*/
private boolean skip;
public Set<String> getIncludePatterns() {
return includes == null || includes.isEmpty()
? Collections.singleton( "**/*.testsuite" )
: includes;
}
public Set<String> getExcludePatterns() {
return excludes == null
? Collections.<String>emptySet()
: excludes;
}
public final void execute() throws MojoExecutionException, MojoFailureException {
if ( skip ) {
getLog().info( "Skipping gUnit processing" );
return;
}
Artifact pluginAntlrArtifact = determinePluginAntlrArtifact();
validateProjectsAntlrVersion( determineArtifactVersion( pluginAntlrArtifact ) );
performExecution( determineProjectCompileScopeClassLoader( pluginAntlrArtifact ) );
}
private Artifact determinePluginAntlrArtifact() throws MojoExecutionException {
for ( Artifact artifact : pluginArtifacts ) {
boolean match = ANTLR_GROUP_ID.equals( artifact.getGroupId() )
&& ANTLR_ARTIFACT_NAME.equals( artifact.getArtifactId() );
if ( match ) {
return artifact;
}
}
throw new MojoExecutionException(
"Unexpected state : could not locate " + ANTLR_GROUP_ID + ':' + ANTLR_ARTIFACT_NAME +
" in plugin dependencies"
);
}
private ArtifactVersion determineArtifactVersion(Artifact artifact) throws MojoExecutionException {
try {
return artifact.getVersion() != null
? new DefaultArtifactVersion( artifact.getVersion() )
: artifact.getSelectedVersion();
}
catch ( OverConstrainedVersionException e ) {
throw new MojoExecutionException( "artifact [" + artifact.getId() + "] defined an overly constrained version range" );
}
}
private void validateProjectsAntlrVersion(ArtifactVersion pluginAntlrVersion) throws MojoExecutionException {
Artifact antlrArtifact = null;
Artifact antlrRuntimeArtifact = null;
if ( project.getCompileArtifacts() != null ) {
for ( Object o : project.getCompileArtifacts() ) {
final Artifact artifact = ( Artifact ) o;
if ( ANTLR_GROUP_ID.equals( artifact.getGroupId() ) ) {
if ( ANTLR_ARTIFACT_NAME.equals( artifact.getArtifactId() ) ) {
antlrArtifact = artifact;
break;
}
if ( ANTLR_RUNTIME_ARTIFACT_NAME.equals( artifact.getArtifactId() ) ) {
antlrRuntimeArtifact = artifact;
}
}
}
}
validateBuildTimeArtifact( antlrArtifact, pluginAntlrVersion );
validateRunTimeArtifact( antlrRuntimeArtifact, pluginAntlrVersion );
}
@SuppressWarnings(value = "unchecked")
protected void validateBuildTimeArtifact(Artifact antlrArtifact, ArtifactVersion pluginAntlrVersion)
throws MojoExecutionException {
if ( antlrArtifact == null ) {
validateMissingBuildtimeArtifact();
return;
}
// otherwise, lets make sure they match...
ArtifactVersion projectAntlrVersion = determineArtifactVersion( antlrArtifact );
if ( pluginAntlrVersion.compareTo( projectAntlrVersion ) != 0 ) {
getLog().warn(
"Encountered " + ANTLR_GROUP_ID + ':' + ANTLR_ARTIFACT_NAME + ':' + projectAntlrVersion.toString() +
" which did not match Antlr version used by plugin [" + pluginAntlrVersion.toString() + "]"
);
}
}
protected void validateMissingBuildtimeArtifact() {
// generally speaking, its ok for the project to not define a dep on the build-time artifact...
}
@SuppressWarnings(value = "unchecked")
protected void validateRunTimeArtifact(Artifact antlrRuntimeArtifact, ArtifactVersion pluginAntlrVersion)
throws MojoExecutionException {
if ( antlrRuntimeArtifact == null ) {
// its possible, if the project instead depends on the build-time (or full) artifact.
return;
}
ArtifactVersion projectAntlrVersion = determineArtifactVersion( antlrRuntimeArtifact );
if ( pluginAntlrVersion.compareTo( projectAntlrVersion ) != 0 ) {
getLog().warn(
"Encountered " + ANTLR_GROUP_ID + ':' + ANTLR_RUNTIME_ARTIFACT_NAME + ':' + projectAntlrVersion.toString() +
" which did not match Antlr version used by plugin [" + pluginAntlrVersion.toString() + "]"
);
}
}
/**
* Builds the classloader to pass to gUnit.
*
* @param antlrArtifact The plugin's (our) Antlr dependency artifact.
*
* @return The classloader for gUnit to use
*
* @throws MojoExecutionException Problem resolving artifacts to {@link java.net.URL urls}.
*/
private ClassLoader determineProjectCompileScopeClassLoader(Artifact antlrArtifact)
throws MojoExecutionException {
ArrayList<URL> classPathUrls = new ArrayList<URL>();
getLog().info( "Adding Antlr artifact : " + antlrArtifact.getId() );
classPathUrls.add( resolveLocalURL( antlrArtifact ) );
for ( String path : classpathElements() ) {
try {
getLog().info( "Adding project compile classpath element : " + path );
classPathUrls.add( new File( path ).toURI().toURL() );
}
catch ( MalformedURLException e ) {
throw new MojoExecutionException( "Unable to build path URL [" + path + "]" );
}
}
return new URLClassLoader( classPathUrls.toArray( new URL[classPathUrls.size()] ), getClass().getClassLoader() );
}
protected static URL resolveLocalURL(Artifact artifact) throws MojoExecutionException {
try {
return artifact.getFile().toURI().toURL();
}
catch ( MalformedURLException e ) {
throw new MojoExecutionException( "Unable to resolve artifact url : " + artifact.getId(), e );
}
}
@SuppressWarnings( "unchecked" )
private List<String> classpathElements() throws MojoExecutionException {
try {
// todo : should we combine both compile and test scoped elements?
return ( List<String> ) project.getTestClasspathElements();
}
catch ( DependencyResolutionRequiredException e ) {
throw new MojoExecutionException( "Call to Project#getCompileClasspathElements required dependency resolution" );
}
}
private void performExecution(ClassLoader projectCompileScopeClassLoader) throws MojoExecutionException {
getLog().info( "gUnit report directory : " + reportDirectory.getAbsolutePath() );
if ( !reportDirectory.exists() ) {
boolean directoryCreated = reportDirectory.mkdirs();
if ( !directoryCreated ) {
getLog().warn( "mkdirs() reported problem creating report directory" );
}
}
Result runningResults = new Result();
ArrayList<String> failureNames = new ArrayList<String>();
System.out.println();
System.out.println( "-----------------------------------------------------------" );
System.out.println( " G U N I T R E S U L T S" );
System.out.println( "-----------------------------------------------------------" );
for ( File script : collectIncludedSourceGrammars() ) {
final String scriptPath = script.getAbsolutePath();
System.out.println( "Executing script " + scriptPath );
try {
String scriptBaseName = StringUtils.chompLast( FileUtils.basename( script.getName() ), "." );
ANTLRFileStream antlrStream = new ANTLRFileStream( scriptPath );
GrammarInfo grammarInfo = Interp.parse( antlrStream );
gUnitExecutor executor = new gUnitExecutor(
grammarInfo,
projectCompileScopeClassLoader,
script.getParentFile().getAbsolutePath()
);
String report = executor.execTest();
writeReportFile( new File( reportDirectory, scriptBaseName + ".txt" ), report );
Result testResult = new Result();
testResult.tests = executor.numOfTest;
testResult.failures = executor.numOfFailure;
testResult.invalids = executor.numOfInvalidInput;
System.out.println( testResult.render() );
runningResults.add( testResult );
for ( AbstractTest test : executor.failures ) {
failureNames.add( scriptBaseName + "#" + test.getHeader() );
}
}
catch ( IOException e ) {
throw new MojoExecutionException( "Could not open specified script file", e );
}
catch ( RecognitionException e ) {
throw new MojoExecutionException( "Could not parse gUnit script", e );
}
}
System.out.println();
System.out.println( "Summary :" );
if ( ! failureNames.isEmpty() ) {
System.out.println( " Found " + failureNames.size() + " failures" );
for ( String name : failureNames ) {
System.out.println( " - " + name );
}
}
System.out.println( runningResults.render() );
System.out.println();
if ( runningResults.failures > 0 ) {
throw new MojoExecutionException( "Found gUnit test failures" );
}
if ( runningResults.invalids > 0 ) {
throw new MojoExecutionException( "Found invalid gUnit tests" );
}
}
private Set<File> collectIncludedSourceGrammars() throws MojoExecutionException {
SourceMapping mapping = new SuffixMapping( "g", Collections.<String>emptySet() );
SourceInclusionScanner scan = new SimpleSourceInclusionScanner( getIncludePatterns(), getExcludePatterns() );
scan.addSourceMapping( mapping );
try {
return scan.getIncludedSources( sourceDirectory, null );
}
catch ( InclusionScanException e ) {
throw new MojoExecutionException( "Error determining gUnit sources", e );
}
}
private void writeReportFile(File reportFile, String results) {
try {
Writer writer = new FileWriter( reportFile );
writer = new BufferedWriter( writer );
try {
writer.write( results );
writer.flush();
}
finally {
try {
writer.close();
}
catch ( IOException ignore ) {
}
}
}
catch ( IOException e ) {
getLog().warn( "Error writing gUnit report file", e );
}
}
private static class Result {
private int tests = 0;
private int failures = 0;
private int invalids = 0;
public String render() {
return String.format( "Tests run: %d, Failures: %d, Invalid: %d", tests, failures, invalids );
}
public void add(Result result) {
this.tests += result.tests;
this.failures += result.failures;
this.invalids += result.invalids;
}
}
}

View File

@ -0,0 +1,127 @@
gUnit
Leon, Jen-Yuan Su
leonsu at mac com
CHANGES
December 15, 2010
* TJP gunit checked tokens.index() != size instead of size()-1
November 26, 2010
* Added -p package-name-for-junit-output since test package is usually
different than parser package. TJP
January 24, 2009
* Improved error messages
* stderr was being ignored upon exception.
* generated junit test code indicate lines in gunit script. also error
messages during parse time show line number from gunit script.
* refactored template generation / parameters
* Allow strings in AST descriptions (gUnit.g). E.g., it handle this rule
(I had to quote the "^(" root tokens):
block:
"( ^(A B) | ^(b C) )" -> (BLOCK (ALT ("^(" A B)) (ALT ("^(" b C)))
January 23, 2009
* whitespace ignored and "x y z" turned into x y z now in expected tree p\
attern. E.g.,
rule:
"a : A<X,Y=a.b.c>;" -> (RULE a (BLOCK (ALT
(A (ELEMENT_OPTIONS X (= Y "a.b.c")))))
)
July 31, 2009
* support custom tree adaptor in gUnit script
March 21, 2009
* gUnitTestSuite.java: use lexer rule name if parser rule name is null (by Shaoting)
* add gunit/swingui package for gUnitEditor GUI
Feb 17, 2009
* added new interfaces for GUI editor
* recognizes invalid input as a FAIL case instead of throwing an exception
Steve Ebersole provided a patch for the following two fixes.
* allows setting an output directory (for JUnitCodeGen)
* allows providing a classloader (for both JUnitCodeGen and gUnitExecutor)
Nov 25, 2008
* fixed external test file path issue. if an input test file is not found under the current dir, then try to look for it under the package dir also.
* fixed multiple-line input indentation issue.
* fixed bug: FileNotFoundException terminated gUnit tests due to any non-existent input test file.
* display escaped text for newline characters in the test result for comparing expected and actual string.
Nov 20, 2008
* added new functionality of testing lexical rules
* fixed bug of using PipedInput/Output Stream and changed to ByteArrayOutputStream. Jared Bunting provided a patch on this issue.
* improved jUnit translation mode and moved supporting codes into gUnitBaseTest.
Oct 31, 2008
* fixed bug of testing a tree grammar's template output
July 9, 2008
* fixed bug: program exited upon InvocationTargetException
Sumanto Biswas pointed out the issue and provided suggestions.
* Better handle on test rule's StringTemplate output
May 10, 2008
* added exit code functionality
* fixed string escaping bug for junit generator
1.0.2 - Apr 01, 2008
* fixed grammar bug: multiple-line input, AST output
* adjusted the output of test result
Mar 20, 2008
* moved test result to string template (gUnitTestResult.stg)
* added the display of line of test in the test result
Feb 19, 2008
* fixed bug of displaying test sequence and error message from ANTLR
Feb 8, 2008
* made compatible with ANTLR 3.1b1
1.0.1 - Jan 11, 2008
* Kenny MacDermid helps with code refactoring
1.0 - Aug 20, 2007
Initial early access release

View File

@ -0,0 +1,26 @@
[The "BSD licence"]
Copyright (c) 2007-2008 Leon, Jen-Yuan Su
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.

View File

@ -0,0 +1,56 @@
gUnit
Feb 21, 2009
Leon, Jen-Yuan Su
leonsu at mac com
INTRODUCTION
Welcome to gUnit! I've been working on gUnit from 2007 summer and
this is a project in USF CS, sponsored by professor Terence Parr.
You should use the latest ANTLR v3 with gUnit:
http://www.antlr.org/download.html
See the wiki document:
http://www.antlr.org/wiki/display/ANTLR3/gUnit+-+Grammar+Unit+Testing
Per the license in LICENSE.txt, this software is not guaranteed to
work and might even destroy all life on this planet:
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.
----------------------------------------------------------------------
EXAMPLES
See the wiki tutorial of gUnit:
http://www.antlr.org/wiki/display/ANTLR3/gUnit+-+Grammar+Unit+Testing
----------------------------------------------------------------------
What is gUnit?
gUnit is an unit testing framework for ANTLR grammars. It provides a
simple way to write and run automated tests for grammars in a manner
similar to what jUnit does for unit testing.
----------------------------------------------------------------------
How do I install gUnit?
It is included in ANTLR v3; it needs both ANTLR and StringTemplate jars
in CLASSPATH.

View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>gunit</artifactId>
<packaging>jar</packaging>
<name>ANTLR 3 gUnit</name>
<description>gUnit grammar testing tool for ANTLR 3</description>
<!--
Inherit from the ANTLR master pom, which tells us what
version we are and allows us to inherit dependencies
and so on.
-->
<parent>
<groupId>org.antlr</groupId>
<artifactId>antlr-master</artifactId>
<version>3.5</version>
</parent>
<url>http://www.antlr.org/wiki/display/ANTLR3/gUnit+-+Grammar+Unit+Testing</url>
<!--
Tell Maven which other artifacts we need in order to
build, run and test the ANTLR Tool. The ANTLR Tool uses earlier versions
of ANTLR at runtime (for the moment), uses the current
released version of ANTLR String template, but obviously is
reliant on the latest snapshot of the runtime, which will either be
taken from the antlr-snapshot repository, or your local .m2
repository if you built and installed that locally.
-->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>stringtemplate</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr3-maven-plugin</artifactId>
<version>${project.version}</version>
<configuration></configuration>
<executions>
<execution>
<goals>
<goal>antlr</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,352 @@
/*
[The "BSD licence"]
Copyright (c) 2007-2008 Leon Jen-Yuan Su
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.
*/
grammar gUnit;
options {language=Java;}
tokens {
OK = 'OK';
FAIL = 'FAIL';
DOC_COMMENT;
}
@header {package org.antlr.gunit;}
@lexer::header {
package org.antlr.gunit;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
}
@members {
public GrammarInfo grammarInfo;
public gUnitParser(TokenStream input, GrammarInfo grammarInfo) {
super(input);
this.grammarInfo = grammarInfo;
}
}
gUnitDef: 'gunit' g1=id ('walks' g2=id)? ';'
{
if ( $g2.text!=null ) {
grammarInfo.setGrammarName($g2.text);
grammarInfo.setTreeGrammarName($g1.text);
}
else {
grammarInfo.setGrammarName($g1.text);
}
}
optionsSpec? header? testsuite*
;
optionsSpec
: OPTIONS (option ';')+ '}'
;
// Note: currently, this is the only valid option for setting customized tree adaptor
option : id '=' treeAdaptor
{
if ( $id.text.equals("TreeAdaptor") ) {
grammarInfo.setAdaptor($treeAdaptor.text);
}
// TODO: need a better error logging strategy
else System.err.println("Invalid option detected: "+$text);
}
;
treeAdaptor
: id EXT*
;
header : '@header' ACTION
{
int pos1, pos2;
if ( (pos1=$ACTION.text.indexOf("package"))!=-1 && (pos2=$ACTION.text.indexOf(';'))!=-1 ) {
grammarInfo.setGrammarPackage($ACTION.text.substring(pos1+8, pos2).trim()); // substring the package path
}
else {
System.err.println("error(line "+$ACTION.getLine()+"): invalid header");
}
}
;
testsuite // gUnit test suite based on individual rule
scope {
boolean isLexicalRule;
}
@init {
gUnitTestSuite ts = null;
$testsuite::isLexicalRule = false;
}
: ( r1=RULE_REF ('walks' r2=RULE_REF)?
{
if ( $r2==null ) ts = new gUnitTestSuite($r1.text);
else ts = new gUnitTestSuite($r1.text, $r2.text);
}
| t=TOKEN_REF
{
ts = new gUnitTestSuite();
ts.setLexicalRuleName($t.text);
$testsuite::isLexicalRule = true;
}
)
':'
testcase[ts]+ {grammarInfo.addRuleTestSuite(ts);}
;
// TODO : currently gUnit just ignores illegal test for lexer rule, but should also emit a reminding message
testcase[gUnitTestSuite ts] // individual test within a (rule)testsuite
: input expect {$ts.addTestCase($input.in, $expect.out);}
;
input returns [gUnitTestInput in]
@init {
String testInput = null;
boolean inputIsFile = false;
int line = -1;
}
@after {
in = new gUnitTestInput(testInput, inputIsFile, line);
}
: STRING
{
testInput = $STRING.text.replace("\\n", "\n").replace("\\r", "\r").replace("\\t", "\t")
.replace("\\b", "\b").replace("\\f", "\f").replace("\\\"", "\"").replace("\\'", "\'").replace("\\\\", "\\");
line = $STRING.line;
}
| ML_STRING
{
testInput = $ML_STRING.text;
line = $ML_STRING.line;
}
| file
{
testInput = $file.text;
inputIsFile = true;
line = $file.line;
}
;
expect returns [AbstractTest out]
: OK {$out = new BooleanTest(true);}
| FAIL {$out = new BooleanTest(false);}
| 'returns' RETVAL {if ( !$testsuite::isLexicalRule ) $out = new ReturnTest($RETVAL);}
| '->' output {if ( !$testsuite::isLexicalRule ) $out = new OutputTest($output.token);}
;
output returns [Token token]
: STRING
{
$STRING.setText($STRING.text.replace("\\n", "\n").replace("\\r", "\r").replace("\\t", "\t")
.replace("\\b", "\b").replace("\\f", "\f").replace("\\\"", "\"").replace("\\'", "\'").replace("\\\\", "\\"));
$token = $STRING;
}
| ML_STRING {$token = $ML_STRING;}
| AST {$token = $AST;}
| ACTION {$token = $ACTION;}
;
file returns [int line]
: id EXT? {$line = $id.line;}
;
id returns [int line]
: TOKEN_REF {$line = $TOKEN_REF.line;}
| RULE_REF {$line = $RULE_REF.line;}
;
// L E X I C A L R U L E S
SL_COMMENT
: '//' ~('\r'|'\n')* '\r'? '\n' {$channel=HIDDEN;}
;
ML_COMMENT
: '/*' {$channel=HIDDEN;} .* '*/'
;
STRING : '"' ( ESC | ~('\\'|'"') )* '"' {setText(getText().substring(1, getText().length()-1));}
;
ML_STRING
: {// we need to determine the number of spaces or tabs (indentation) for multi-line input
StringBuffer buf = new StringBuffer();
int i = -1;
int c = input.LA(-1);
while ( c==' ' || c=='\t' ) {
buf.append((char)c);
c = input.LA(--i);
}
String indentation = buf.reverse().toString();
}
'<<' .* '>>'
{// also determine the appropriate newline separator and get info of the first and last 2 characters (exclude '<<' and '>>')
String newline = System.getProperty("line.separator");
String front, end;
int oldFrontIndex = 2;
int oldEndIndex = getText().length()-2;
int newFrontIndex, newEndIndex;
if ( newline.length()==1 ) {
front = getText().substring(2, 3);
end = getText().substring(getText().length()-3, getText().length()-2);
newFrontIndex = 3;
newEndIndex = getText().length()-3;
}
else {// must be 2, e.g. Windows System which uses \r\n as a line separator
front = getText().substring(2, 4);
end = getText().substring(getText().length()-4, getText().length()-2);
newFrontIndex = 4;
newEndIndex = getText().length()-4;
}
// strip unwanted characters, e.g. '<<' (including a newline after it) or '>>' (including a newline before it)
String temp = null;
if ( front.equals(newline) && end.equals(newline) ) {
// need to handle the special case: <<\n>> or <<\r\n>>
if ( newline.length()==1 && getText().length()==5 ) temp = "";
else if ( newline.length()==2 && getText().length()==6 ) temp = "";
else temp = getText().substring(newFrontIndex, newEndIndex);
}
else if ( front.equals(newline) ) {
temp = getText().substring(newFrontIndex, oldEndIndex);
}
else if ( end.equals(newline) ) {
temp = getText().substring(oldFrontIndex, newEndIndex);
}
else {
temp = getText().substring(oldFrontIndex, oldEndIndex);
}
// finally we need to prpcess the indentation line by line
BufferedReader bufReader = new BufferedReader(new StringReader(temp));
buf = new StringBuffer();
String line = null;
int count = 0;
try {
while((line = bufReader.readLine()) != null) {
if ( line.startsWith(indentation) ) line = line.substring(indentation.length());
if ( count>0 ) buf.append(newline);
buf.append(line);
count++;
}
setText(buf.toString());
}
catch (IOException ioe) {
setText(temp);
}
}
;
TOKEN_REF
: 'A'..'Z' ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*
;
RULE_REF
: 'a'..'z' ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*
;
EXT : '.'('a'..'z'|'A'..'Z'|'0'..'9')+;
RETVAL : NESTED_RETVAL {setText(getText().substring(1, getText().length()-1));}
;
fragment
NESTED_RETVAL :
'['
( options {greedy=false;}
: NESTED_RETVAL
| .
)*
']'
;
AST : NESTED_AST (' '? NESTED_AST)*;
fragment
NESTED_AST :
'('
( NESTED_AST
| STRING_LITERAL
| ~('('|')'|'"')
)*
')'
;
OPTIONS : 'options' WS* '{'
;
ACTION
: NESTED_ACTION {setText(getText().substring(1, getText().length()-1));}
;
fragment
NESTED_ACTION :
'{'
( options {greedy=false; k=3;}
: NESTED_ACTION
| STRING_LITERAL
| CHAR_LITERAL
| .
)*
'}'
;
fragment
CHAR_LITERAL
: '\'' ( ESC | ~('\''|'\\') ) '\''
;
fragment
STRING_LITERAL
: '"' ( ESC | ~('\\'|'"') )* '"'
;
fragment
ESC : '\\'
( 'n'
| 'r'
| 't'
| 'b'
| 'f'
| '"'
| '\''
| '\\'
| '>'
| 'u' XDIGIT XDIGIT XDIGIT XDIGIT
| . // unknown, leave as it is
)
;
fragment
XDIGIT :
'0' .. '9'
| 'a' .. 'f'
| 'A' .. 'F'
;
WS : ( ' '
| '\t'
| '\r'? '\n'
)+
{$channel=HIDDEN;}
;

View File

@ -0,0 +1,619 @@
/*
[The "BSD licence"]
Copyright (c) 2005-2007 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.
*/
/** ANTLR v3 grammar written in ANTLR v3 with AST construction */
grammar ANTLRv3;
options {
output=AST;
ASTLabelType=CommonTree;
}
tokens {
DOC_COMMENT;
PARSER;
LEXER;
RULE;
BLOCK;
OPTIONAL;
CLOSURE;
POSITIVE_CLOSURE;
SYNPRED;
RANGE;
CHAR_RANGE;
EPSILON;
ALT;
EOR;
EOB;
EOA; // end of alt
ID;
ARG;
ARGLIST;
RET;
LEXER_GRAMMAR;
PARSER_GRAMMAR;
TREE_GRAMMAR;
COMBINED_GRAMMAR;
INITACTION;
LABEL; // $x used in rewrite rules
TEMPLATE;
SCOPE='scope';
SEMPRED;
GATED_SEMPRED; // {p}? =>
SYN_SEMPRED; // (...) => it's a manually-specified synpred converted to sempred
BACKTRACK_SEMPRED; // auto backtracking mode syn pred converted to sempred
FRAGMENT='fragment';
TREE_BEGIN='^(';
ROOT='^';
BANG='!';
RANGE='..';
REWRITE='->';
}
@members {
int gtype;
public List<String> rules;
}
@header {
package org.antlr.gunit.swingui.parsers;
import java.util.List;
}
@lexer::header {
package org.antlr.gunit.swingui.parsers;
}
grammarDef
: DOC_COMMENT?
( 'lexer' {gtype=LEXER_GRAMMAR;} // pure lexer
| 'parser' {gtype=PARSER_GRAMMAR;} // pure parser
| 'tree' {gtype=TREE_GRAMMAR;} // a tree parser
| {gtype=COMBINED_GRAMMAR;} // merged parser/lexer
)
g='grammar' id ';' optionsSpec? tokensSpec? attrScope* action*
rule+
EOF
-> ^( {adaptor.create(gtype,$g)}
id DOC_COMMENT? optionsSpec? tokensSpec? attrScope* action* rule+
)
;
tokensSpec
: TOKENS tokenSpec+ '}' -> ^(TOKENS tokenSpec+)
;
tokenSpec
: TOKEN_REF
( '=' (lit=STRING_LITERAL|lit=CHAR_LITERAL) -> ^('=' TOKEN_REF $lit)
| -> TOKEN_REF
)
';'
;
attrScope
: 'scope' id ACTION -> ^('scope' id ACTION)
;
/** Match stuff like @parser::members {int i;} */
action
: '@' (actionScopeName '::')? id ACTION -> ^('@' actionScopeName? id ACTION)
;
/** Sometimes the scope names will collide with keywords; allow them as
* ids for action scopes.
*/
actionScopeName
: id
| l='lexer' -> ID[$l]
| p='parser' -> ID[$p]
;
optionsSpec
: OPTIONS (option ';')+ '}' -> ^(OPTIONS option+)
;
option
: id '=' optionValue -> ^('=' id optionValue)
;
optionValue
: id
| STRING_LITERAL
| CHAR_LITERAL
| INT
| s='*' -> STRING_LITERAL[$s] // used for k=*
;
rule
scope {
String name;
}
@after{
this.rules.add($rule::name);
}
: DOC_COMMENT?
( modifier=('protected'|'public'|'private'|'fragment') )?
id {$rule::name = $id.text;}
'!'?
( arg=ARG_ACTION )?
( 'returns' rt=ARG_ACTION )?
throwsSpec? optionsSpec? ruleScopeSpec? ruleAction*
':' altList ';'
exceptionGroup?
-> ^( RULE id {modifier!=null?adaptor.create(modifier):null} ^(ARG $arg)? ^(RET $rt)?
optionsSpec? ruleScopeSpec? ruleAction*
altList
exceptionGroup?
EOR["EOR"]
)
;
/** Match stuff like @init {int i;} */
ruleAction
: '@' id ACTION -> ^('@' id ACTION)
;
throwsSpec
: 'throws' id ( ',' id )* -> ^('throws' id+)
;
ruleScopeSpec
: 'scope' ACTION -> ^('scope' ACTION)
| 'scope' id (',' id)* ';' -> ^('scope' id+)
| 'scope' ACTION
'scope' id (',' id)* ';'
-> ^('scope' ACTION id+ )
;
block
: lp='('
( (opts=optionsSpec)? ':' )?
a1=alternative rewrite ( '|' a2=alternative rewrite )*
rp=')'
-> ^( BLOCK[$lp,"BLOCK"] optionsSpec? (alternative rewrite?)+ EOB[$rp,"EOB"] )
;
altList
@init {
// must create root manually as it's used by invoked rules in real antlr tool.
// leave here to demonstrate use of {...} in rewrite rule
// it's really BLOCK[firstToken,"BLOCK"]; set line/col to previous ( or : token.
CommonTree blkRoot = (CommonTree)adaptor.create(BLOCK,input.LT(-1),"BLOCK");
}
: a1=alternative rewrite ( '|' a2=alternative rewrite )*
-> ^( {blkRoot} (alternative rewrite?)+ EOB["EOB"] )
;
alternative
@init {
Token firstToken = input.LT(1);
Token prevToken = input.LT(-1); // either : or | I think
}
: element+ -> ^(ALT[firstToken,"ALT"] element+ EOA["EOA"])
| -> ^(ALT[prevToken,"ALT"] EPSILON[prevToken,"EPSILON"] EOA["EOA"])
;
exceptionGroup
: ( exceptionHandler )+ ( finallyClause )?
| finallyClause
;
exceptionHandler
: 'catch' ARG_ACTION ACTION -> ^('catch' ARG_ACTION ACTION)
;
finallyClause
: 'finally' ACTION -> ^('finally' ACTION)
;
element
: elementNoOptionSpec
;
elementNoOptionSpec
: id (labelOp='='|labelOp='+=') atom
( ebnfSuffix -> ^( ebnfSuffix ^(BLOCK["BLOCK"] ^(ALT["ALT"] ^($labelOp id atom) EOA["EOA"]) EOB["EOB"]))
| -> ^($labelOp id atom)
)
| id (labelOp='='|labelOp='+=') block
( ebnfSuffix -> ^( ebnfSuffix ^(BLOCK["BLOCK"] ^(ALT["ALT"] ^($labelOp id block) EOA["EOA"]) EOB["EOB"]))
| -> ^($labelOp id block)
)
| atom
( ebnfSuffix -> ^( ebnfSuffix ^(BLOCK["BLOCK"] ^(ALT["ALT"] atom EOA["EOA"]) EOB["EOB"]) )
| -> atom
)
| ebnf
| ACTION
| SEMPRED ( '=>' -> GATED_SEMPRED | -> SEMPRED )
| treeSpec
( ebnfSuffix -> ^( ebnfSuffix ^(BLOCK["BLOCK"] ^(ALT["ALT"] treeSpec EOA["EOA"]) EOB["EOB"]) )
| -> treeSpec
)
;
atom: range ( (op='^'|op='!') -> ^($op range) | -> range )
| terminal
| notSet ( (op='^'|op='!') -> ^($op notSet) | -> notSet )
| RULE_REF ( arg=ARG_ACTION )? ( (op='^'|op='!') )?
-> {$arg!=null&&op!=null}? ^($op RULE_REF $arg)
-> {$arg!=null}? ^(RULE_REF $arg)
-> {$op!=null}? ^($op RULE_REF)
-> RULE_REF
;
notSet
: '~'
( notTerminal -> ^('~' notTerminal)
| block -> ^('~' block)
)
;
treeSpec
: '^(' element ( element )+ ')' -> ^(TREE_BEGIN element+)
;
/** Matches ENBF blocks (and token sets via block rule) */
ebnf
@init {
Token firstToken = input.LT(1);
}
@after {
$ebnf.tree.getToken().setLine(firstToken.getLine());
$ebnf.tree.getToken().setCharPositionInLine(firstToken.getCharPositionInLine());
}
: block
( op='?' -> ^(OPTIONAL[op] block)
| op='*' -> ^(CLOSURE[op] block)
| op='+' -> ^(POSITIVE_CLOSURE[op] block)
| '=>' // syntactic predicate
-> {gtype==COMBINED_GRAMMAR &&
Character.isUpperCase($rule::name.charAt(0))}?
// if lexer rule in combined, leave as pred for lexer
^(SYNPRED["=>"] block)
// in real antlr tool, text for SYN_SEMPRED is predname
-> SYN_SEMPRED
| -> block
)
;
range!
: c1=CHAR_LITERAL RANGE c2=CHAR_LITERAL -> ^(CHAR_RANGE[$c1,".."] $c1 $c2)
;
terminal
: ( CHAR_LITERAL -> CHAR_LITERAL
// Args are only valid for lexer rules
| TOKEN_REF
( ARG_ACTION -> ^(TOKEN_REF ARG_ACTION)
| -> TOKEN_REF
)
| STRING_LITERAL -> STRING_LITERAL
| '.' -> '.'
)
( '^' -> ^('^' $terminal)
| '!' -> ^('!' $terminal)
)?
;
notTerminal
: CHAR_LITERAL
| TOKEN_REF
| STRING_LITERAL
;
ebnfSuffix
@init {
Token op = input.LT(1);
}
: '?' -> OPTIONAL[op]
| '*' -> CLOSURE[op]
| '+' -> POSITIVE_CLOSURE[op]
;
// R E W R I T E S Y N T A X
rewrite
@init {
Token firstToken = input.LT(1);
}
: (rew+='->' preds+=SEMPRED predicated+=rewrite_alternative)*
rew2='->' last=rewrite_alternative
-> ^($rew $preds $predicated)* ^($rew2 $last)
|
;
rewrite_alternative
options {backtrack=true;}
: rewrite_template
| rewrite_tree_alternative
| /* empty rewrite */ -> ^(ALT["ALT"] EPSILON["EPSILON"] EOA["EOA"])
;
rewrite_tree_block
: lp='(' rewrite_tree_alternative ')'
-> ^(BLOCK[$lp,"BLOCK"] rewrite_tree_alternative EOB[$lp,"EOB"])
;
rewrite_tree_alternative
: rewrite_tree_element+ -> ^(ALT["ALT"] rewrite_tree_element+ EOA["EOA"])
;
rewrite_tree_element
: rewrite_tree_atom
| rewrite_tree_atom ebnfSuffix
-> ^( ebnfSuffix ^(BLOCK["BLOCK"] ^(ALT["ALT"] rewrite_tree_atom EOA["EOA"]) EOB["EOB"]))
| rewrite_tree
( ebnfSuffix
-> ^(ebnfSuffix ^(BLOCK["BLOCK"] ^(ALT["ALT"] rewrite_tree EOA["EOA"]) EOB["EOB"]))
| -> rewrite_tree
)
| rewrite_tree_ebnf
;
rewrite_tree_atom
: CHAR_LITERAL
| TOKEN_REF ARG_ACTION? -> ^(TOKEN_REF ARG_ACTION?) // for imaginary nodes
| RULE_REF
| STRING_LITERAL
| d='$' id -> LABEL[$d,$id.text] // reference to a label in a rewrite rule
| ACTION
;
rewrite_tree_ebnf
@init {
Token firstToken = input.LT(1);
}
@after {
$rewrite_tree_ebnf.tree.getToken().setLine(firstToken.getLine());
$rewrite_tree_ebnf.tree.getToken().setCharPositionInLine(firstToken.getCharPositionInLine());
}
: rewrite_tree_block ebnfSuffix -> ^(ebnfSuffix rewrite_tree_block)
;
rewrite_tree
: '^(' rewrite_tree_atom rewrite_tree_element* ')'
-> ^(TREE_BEGIN rewrite_tree_atom rewrite_tree_element* )
;
/** Build a tree for a template rewrite:
^(TEMPLATE (ID|ACTION) ^(ARGLIST ^(ARG ID ACTION) ...) )
where ARGLIST is always there even if no args exist.
ID can be "template" keyword. If first child is ACTION then it's
an indirect template ref
-> foo(a={...}, b={...})
-> ({string-e})(a={...}, b={...}) // e evaluates to template name
-> {%{$ID.text}} // create literal template from string (done in ActionTranslator)
-> {st-expr} // st-expr evaluates to ST
*/
rewrite_template
: // -> template(a={...},...) "..." inline template
id lp='(' rewrite_template_args ')'
( str=DOUBLE_QUOTE_STRING_LITERAL | str=DOUBLE_ANGLE_STRING_LITERAL )
-> ^(TEMPLATE[$lp,"TEMPLATE"] id rewrite_template_args $str)
| // -> foo(a={...}, ...)
rewrite_template_ref
| // -> ({expr})(a={...}, ...)
rewrite_indirect_template_head
| // -> {...}
ACTION
;
/** -> foo(a={...}, ...) */
rewrite_template_ref
: id lp='(' rewrite_template_args ')'
-> ^(TEMPLATE[$lp,"TEMPLATE"] id rewrite_template_args)
;
/** -> ({expr})(a={...}, ...) */
rewrite_indirect_template_head
: lp='(' ACTION ')' '(' rewrite_template_args ')'
-> ^(TEMPLATE[$lp,"TEMPLATE"] ACTION rewrite_template_args)
;
rewrite_template_args
: rewrite_template_arg (',' rewrite_template_arg)*
-> ^(ARGLIST rewrite_template_arg+)
| -> ARGLIST
;
rewrite_template_arg
: id '=' ACTION -> ^(ARG[$id.start] id ACTION)
;
id : TOKEN_REF -> ID[$TOKEN_REF]
| RULE_REF -> ID[$RULE_REF]
;
// L E X I C A L R U L E S
SL_COMMENT
: '//'
( ' $ANTLR ' SRC // src directive
| ~('\r'|'\n')*
)
'\r'? '\n'
{$channel=HIDDEN;}
;
ML_COMMENT
: '/*' {if (input.LA(1)=='*') $type=DOC_COMMENT; else $channel=HIDDEN;} .* '*/'
;
CHAR_LITERAL
: '\'' LITERAL_CHAR '\''
;
STRING_LITERAL
: '\'' LITERAL_CHAR LITERAL_CHAR* '\''
;
fragment
LITERAL_CHAR
: ESC
| ~('\''|'\\')
;
DOUBLE_QUOTE_STRING_LITERAL
: '"' (ESC | ~('\\'|'"'))* '"'
;
DOUBLE_ANGLE_STRING_LITERAL
: '<<' .* '>>'
;
fragment
ESC : '\\'
( 'n'
| 'r'
| 't'
| 'b'
| 'f'
| '"'
| '\''
| '\\'
| '>'
| 'u' XDIGIT XDIGIT XDIGIT XDIGIT
| . // unknown, leave as it is
)
;
fragment
XDIGIT :
'0' .. '9'
| 'a' .. 'f'
| 'A' .. 'F'
;
INT : '0'..'9'+
;
ARG_ACTION
: NESTED_ARG_ACTION
;
fragment
NESTED_ARG_ACTION :
'['
( options {greedy=false; k=1;}
: NESTED_ARG_ACTION
| ACTION_STRING_LITERAL
| ACTION_CHAR_LITERAL
| .
)*
']'
{setText(getText().substring(1, getText().length()-1));}
;
ACTION
: NESTED_ACTION ( '?' {$type = SEMPRED;} )?
;
fragment
NESTED_ACTION :
'{'
( options {greedy=false; k=2;}
: NESTED_ACTION
| SL_COMMENT
| ML_COMMENT
| ACTION_STRING_LITERAL
| ACTION_CHAR_LITERAL
| .
)*
'}'
;
fragment
ACTION_CHAR_LITERAL
: '\'' (ACTION_ESC|~('\\'|'\'')) '\''
;
fragment
ACTION_STRING_LITERAL
: '"' (ACTION_ESC|~('\\'|'"'))* '"'
;
fragment
ACTION_ESC
: '\\\''
| '\\' '"' // ANTLR doesn't like: '\\"'
| '\\' ~('\''|'"')
;
TOKEN_REF
: 'A'..'Z' ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*
;
RULE_REF
: 'a'..'z' ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*
;
/** Match the start of an options section. Don't allow normal
* action processing on the {...} as it's not a action.
*/
OPTIONS
: 'options' WS_LOOP '{'
;
TOKENS
: 'tokens' WS_LOOP '{'
;
/** Reset the file and line information; useful when the grammar
* has been generated so that errors are shown relative to the
* original file like the old C preprocessor used to do.
*/
fragment
SRC : 'src' ' ' file=ACTION_STRING_LITERAL ' ' line=INT
;
WS : ( ' '
| '\t'
| '\r'? '\n'
)+
{$channel=HIDDEN;}
;
fragment
WS_LOOP
: ( WS
| SL_COMMENT
| ML_COMMENT
)*
;

View File

@ -0,0 +1,213 @@
/*
[The "BSD licence"]
Copyright (c) 2007-2008 Leon Jen-Yuan Su
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.
*/
grammar StGUnit;
options {language=Java;}
tokens {
OK = 'OK';
FAIL = 'FAIL';
DOC_COMMENT;
}
@header {
package org.antlr.gunit.swingui.parsers;
import org.antlr.gunit.swingui.model.*;
import org.antlr.gunit.swingui.runner.*;
}
@lexer::header {package org.antlr.gunit.swingui.parsers;}
@members {
public TestSuiteAdapter adapter ;;
}
gUnitDef
: 'gunit' name=id {adapter.setGrammarName($name.text);}
('walks' id)? ';'
header? suite*
;
header
: '@header' ACTION
;
suite
: ( parserRule=RULE_REF ('walks' RULE_REF)?
{adapter.startRule($parserRule.text);}
| lexerRule=TOKEN_REF
{adapter.startRule($lexerRule.text);}
)
':'
test+
{adapter.endRule();}
;
test
: input expect
{adapter.addTestCase($input.in, $expect.out);}
;
expect returns [ITestCaseOutput out]
: OK {$out = TestSuiteAdapter.createBoolOutput(true);}
| FAIL {$out = TestSuiteAdapter.createBoolOutput(false);}
| 'returns' RETVAL {$out = TestSuiteAdapter.createReturnOutput($RETVAL.text);}
| '->' output {$out = TestSuiteAdapter.createStdOutput($output.text);}
| '->' AST {$out = TestSuiteAdapter.createAstOutput($AST.text);}
;
input returns [ITestCaseInput in]
: STRING {$in = TestSuiteAdapter.createStringInput($STRING.text);}
| ML_STRING {$in = TestSuiteAdapter.createMultiInput($ML_STRING.text);}
| fileInput {$in = TestSuiteAdapter.createFileInput($fileInput.path);}
;
output
: STRING
| ML_STRING
| ACTION
;
fileInput returns [String path]
: id {$path = $id.text;} (EXT {$path += $EXT.text;})?
;
id : TOKEN_REF
| RULE_REF
;
// L E X I C A L R U L E S
SL_COMMENT
: '//' ~('\r'|'\n')* '\r'? '\n' {$channel=HIDDEN;}
;
ML_COMMENT
: '/*' {$channel=HIDDEN;} .* '*/'
;
STRING
: '"' ( ESC | ~('\\'|'"') )* '"'
;
ML_STRING
: '<<' .* '>>'
;
TOKEN_REF
: 'A'..'Z' ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*
;
RULE_REF
: 'a'..'z' ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*
;
EXT : '.'('a'..'z'|'A'..'Z'|'0'..'9')+;
RETVAL : NESTED_RETVAL
;
fragment
NESTED_RETVAL :
'['
( options {greedy=false;}
: NESTED_RETVAL
| .
)*
']'
;
AST : NESTED_AST (' '? NESTED_AST)*;
fragment
NESTED_AST :
'('
( options {greedy=false;}
: NESTED_AST
| .
)*
')'
;
ACTION
: NESTED_ACTION
;
fragment
NESTED_ACTION :
'{'
( options {greedy=false; k=3;}
: NESTED_ACTION
| STRING_LITERAL
| CHAR_LITERAL
| .
)*
'}'
;
fragment
CHAR_LITERAL
: '\'' ( ESC | ~('\''|'\\') ) '\''
;
fragment
STRING_LITERAL
: '"' ( ESC | ~('\\'|'"') )* '"'
;
fragment
ESC : '\\'
( 'n'
| 'r'
| 't'
| 'b'
| 'f'
| '"'
| '\''
| '\\'
| '>'
| 'u' XDIGIT XDIGIT XDIGIT XDIGIT
| . // unknown, leave as it is
)
;
fragment
XDIGIT :
'0' .. '9'
| 'a' .. 'f'
| 'A' .. 'F'
;
WS : ( ' '
| '\t'
| '\r'? '\n'
)+
{$channel=HIDDEN;}
;

View File

@ -0,0 +1,86 @@
/*
[The "BSD license"]
Copyright (c) 2007 Kenny MacDermid
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.gunit;
public abstract class AbstractTest implements ITestCase {
// store essential individual test result for string template
protected String header;
protected String actual;
protected boolean hasErrorMsg;
private String testedRuleName;
private int testCaseIndex;
// TODO: remove these. They're only used as part of a refactor to keep the
// code cleaner. It is a mock-instanceOf() replacement.
public abstract int getType();
public abstract String getText();
public abstract String getExpected();
// return an escaped string of the expected result
public String getExpectedResult() {
String expected = getExpected();
if ( expected!=null ) expected = JUnitCodeGen.escapeForJava(expected);
return expected;
}
public abstract String getResult(gUnitTestResult testResult);
public String getHeader() { return this.header; }
public String getActual() { return this.actual; }
// return an escaped string of the actual result
public String getActualResult() {
String actual = getActual();
// there is no need to escape the error message from ANTLR
if ( actual!=null && !hasErrorMsg ) actual = JUnitCodeGen.escapeForJava(actual);
return actual;
}
public String getTestedRuleName() { return this.testedRuleName; }
public int getTestCaseIndex() { return this.testCaseIndex; }
public void setHeader(String rule, String lexicalRule, String treeRule, int numOfTest, int line, String input) {
StringBuffer buf = new StringBuffer();
buf.append("test" + numOfTest + " (");
if ( treeRule!=null ) {
buf.append(treeRule+" walks ");
}
if ( lexicalRule!=null ) {
buf.append(lexicalRule + ", line"+line+")" + " - ");
}
else buf.append(rule + ", line"+line+")" + " - ");
buf.append( "\"" );
buf.append( input );
buf.append( "\"" );
this.header = buf.toString();
}
public void setActual(String actual) { this.actual = actual; }
public void setTestedRuleName(String testedRuleName) { this.testedRuleName = testedRuleName; }
public void setTestCaseIndex(int testCaseIndex) { this.testCaseIndex = testCaseIndex; }
}

View File

@ -0,0 +1,63 @@
/*
[The "BSD license"]
Copyright (c) 2007 Kenny MacDermid
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.gunit;
public class BooleanTest extends AbstractTest {
private boolean ok;
public BooleanTest(boolean ok) {
this.ok = ok;
}
@Override
public String getText() {
return (ok)? "OK" : "FAIL";
}
@Override
public int getType() {
return (ok)? gUnitParser.OK : gUnitParser.FAIL;
}
@Override
public String getResult(gUnitTestResult testResult) {
if ( testResult.isLexerTest() ) {
if ( testResult.isSuccess() ) return "OK";
else {
hasErrorMsg = true; // return error message for boolean test of lexer
return testResult.getError();
}
}
return (testResult.isSuccess())? "OK" : "FAIL";
}
@Override
public String getExpected() {
return (ok)? "OK" : "FAIL";
}
}

View File

@ -0,0 +1,105 @@
/*
[The "BSD licence"]
Copyright (c) 2007-2008 Leon, Jen-Yuan Su
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.gunit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class GrammarInfo {
private String grammarName; // targeted grammar for unit test
private String treeGrammarName = null; // optional, required for testing tree grammar
private String grammarPackage = null; // optional, package parser lives in
private String testPackage = null; // optional, package of junit code
private String adaptor = null; // optional, required if using customized tree adaptor
private List<gUnitTestSuite> ruleTestSuites = new ArrayList<gUnitTestSuite>(); // testsuites for each testing rule
private StringBuffer unitTestResult = new StringBuffer();
public String getGrammarName() {
return grammarName;
}
public void setGrammarName(String grammarName) {
this.grammarName = grammarName;
}
public String getTreeGrammarName() {
return treeGrammarName;
}
public void setTreeGrammarName(String treeGrammarName) {
this.treeGrammarName = treeGrammarName;
}
public String getTestPackage() {
return testPackage;
}
public void setTestPackage(String testPackage) {
this.testPackage = testPackage;
}
public String getGrammarPackage() {
return grammarPackage;
}
public void setGrammarPackage(String grammarPackage) {
this.grammarPackage = grammarPackage;
}
public String getAdaptor() {
return adaptor;
}
public void setAdaptor(String adaptor) {
this.adaptor = adaptor;
}
public List<gUnitTestSuite> getRuleTestSuites() {
// Make this list unmodifiable so that we can refactor knowing it's not changed.
return Collections.unmodifiableList(ruleTestSuites);
}
public void addRuleTestSuite(gUnitTestSuite testSuite) {
this.ruleTestSuites.add(testSuite);
}
public void appendUnitTestResult(String result) {
this.unitTestResult.append(result);
}
// We don't want people messing with the string buffer here, so don't return it.
public String getUnitTestResult() {
return unitTestResult.toString();
}
public void setUnitTestResult(StringBuffer unitTestResult) {
this.unitTestResult = unitTestResult;
}
}

View File

@ -0,0 +1,63 @@
/*
[The "BSD license"]
Copyright (c) 2009 Shaoting Cai
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.gunit;
/**
* ITestCase object locates one test case in a gUnit script by specifying the
* tested rule and the index number of the test case in that group.
*
* For example:
* ----------------------
* ...
* varDef:
* "int i;" OK
* "float 2f;" FAIL
* ...
* ----------------------
* The "testedRuleName" for these two test cases will be "varDef".
* The "index" for the "int"-test will be 0.
* The "index" for the "float"-test will be 1. And so on.
*
* @see ITestSuite
*/
public interface ITestCase {
/**
* Get the name of the rule that is tested by this test case.
* @return name of the tested rule.
*/
public String getTestedRuleName();
/**
* Get the index of the test case in the test group for a rule. Starting
* from 0.
* @return index number of the test case.
*/
public int getTestCaseIndex();
}

View File

@ -0,0 +1,45 @@
/*
[The "BSD license"]
Copyright (c) 2009 Shaoting Cai
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.gunit;
/**
* A gUnit script file is an Antlr "test suite". The interface is defined to
* allow the Swing GUI test runner be notified when gUnit interpreter runner
* runs a passed/failed test case.
*
* CHANGES:
* 2009-03-01: SHAOTING
* - change method return void, parameter test object.
*/
public interface ITestSuite {
public void onPass(ITestCase passTest);
public void onFail(ITestCase failTest);
}

View File

@ -0,0 +1,113 @@
/*
[The "BSD licence"]
Copyright (c) 2007-2008 Leon Jen-Yuan Su
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.gunit;
import java.io.File;
import java.io.IOException;
import org.antlr.runtime.*;
/** The main gUnit interpreter entry point.
* Read a gUnit script, run unit tests or generate a junit file.
*/
public class Interp {
static String testPackage;
static boolean genJUnit;
static String gunitFile;
public static void main(String[] args) throws IOException, ClassNotFoundException, RecognitionException {
/** Pull char from where? */
CharStream input = null;
/** If the input source is a testsuite file, where is it? */
String testsuiteDir = System.getProperty("user.dir");
processArgs(args);
if ( genJUnit ) {
if ( gunitFile!=null ) {
input = new ANTLRFileStream(gunitFile);
File f = new File(gunitFile);
testsuiteDir = getTestsuiteDir(f.getCanonicalPath(), f.getName());
}
else {
input = new ANTLRInputStream(System.in);
}
GrammarInfo grammarInfo = parse(input);
grammarInfo.setTestPackage(testPackage);
JUnitCodeGen generater = new JUnitCodeGen(grammarInfo, testsuiteDir);
generater.compile();
return;
}
if ( gunitFile!=null ) {
input = new ANTLRFileStream(gunitFile);
File f = new File(gunitFile);
testsuiteDir = getTestsuiteDir(f.getCanonicalPath(), f.getName());
}
else
input = new ANTLRInputStream(System.in);
gUnitExecutor executer = new gUnitExecutor(parse(input), testsuiteDir);
System.out.print(executer.execTest()); // unit test result
//return an error code of the number of failures
System.exit(executer.failures.size() + executer.invalids.size());
}
public static void processArgs(String[] args) {
if (args == null || args.length == 0) return;
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-p")) {
if (i + 1 >= args.length) {
System.err.println("missing library directory with -lib option; ignoring");
}
else {
i++;
testPackage = args[i];
}
}
else if (args[i].equals("-o")) genJUnit = true;
else gunitFile = args[i]; // Must be the gunit file
}
}
public static GrammarInfo parse(CharStream input) throws RecognitionException {
gUnitLexer lexer = new gUnitLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
GrammarInfo grammarInfo = new GrammarInfo();
gUnitParser parser = new gUnitParser(tokens, grammarInfo);
parser.gUnitDef(); // parse gunit script and save elements to grammarInfo
return grammarInfo;
}
public static String getTestsuiteDir(String fullPath, String fileName) {
return fullPath.substring(0, fullPath.length()-fileName.length());
}
}

View File

@ -0,0 +1,34 @@
/*
[The "BSD license"]
Copyright (c) 2007 Kenny MacDermid
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.gunit;
public class InvalidInputException extends Exception {
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,407 @@
/*
[The "BSD licence"]
Copyright (c) 2007-2008 Leon Jen-Yuan Su
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.gunit;
import org.antlr.stringtemplate.StringTemplate;
import org.antlr.stringtemplate.StringTemplateGroup;
import org.antlr.stringtemplate.StringTemplateGroupLoader;
import org.antlr.stringtemplate.CommonGroupLoader;
import org.antlr.stringtemplate.language.AngleBracketTemplateLexer;
import java.io.*;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JUnitCodeGen {
public GrammarInfo grammarInfo;
public Map<String, String> ruleWithReturn;
private final String testsuiteDir;
private String outputDirectoryPath = ".";
private final static Handler console = new ConsoleHandler();
private static final Logger logger = Logger.getLogger(JUnitCodeGen.class.getName());
static {
logger.addHandler(console);
}
public JUnitCodeGen(GrammarInfo grammarInfo, String testsuiteDir) throws ClassNotFoundException {
this( grammarInfo, determineClassLoader(), testsuiteDir);
}
private static ClassLoader determineClassLoader() {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if ( classLoader == null ) {
classLoader = JUnitCodeGen.class.getClassLoader();
}
return classLoader;
}
public JUnitCodeGen(GrammarInfo grammarInfo, ClassLoader classLoader, String testsuiteDir) throws ClassNotFoundException {
this.grammarInfo = grammarInfo;
this.testsuiteDir = testsuiteDir;
/** Map the name of rules having return value to its return type */
ruleWithReturn = new HashMap<String, String>();
Class<?> parserClass = locateParserClass( grammarInfo, classLoader );
Method[] methods = parserClass.getDeclaredMethods();
for(Method method : methods) {
if ( !method.getReturnType().getName().equals("void") ) {
ruleWithReturn.put(method.getName(), method.getReturnType().getName().replace('$', '.'));
}
}
}
private Class<?> locateParserClass(GrammarInfo grammarInfo, ClassLoader classLoader) throws ClassNotFoundException {
String parserClassName = grammarInfo.getGrammarName() + "Parser";
if ( grammarInfo.getGrammarPackage() != null ) {
parserClassName = grammarInfo.getGrammarPackage()+ "." + parserClassName;
}
return classLoader.loadClass( parserClassName );
}
public String getOutputDirectoryPath() {
return outputDirectoryPath;
}
public void setOutputDirectoryPath(String outputDirectoryPath) {
this.outputDirectoryPath = outputDirectoryPath;
}
public void compile() throws IOException{
String junitFileName;
if ( grammarInfo.getTreeGrammarName()!=null ) {
junitFileName = "Test"+grammarInfo.getTreeGrammarName();
}
else {
junitFileName = "Test"+grammarInfo.getGrammarName();
}
String lexerName = grammarInfo.getGrammarName()+"Lexer";
String parserName = grammarInfo.getGrammarName()+"Parser";
StringTemplateGroupLoader loader = new CommonGroupLoader("org/antlr/gunit", null);
StringTemplateGroup.registerGroupLoader(loader);
StringTemplateGroup.registerDefaultLexer(AngleBracketTemplateLexer.class);
StringBuffer buf = compileToBuffer(junitFileName, lexerName, parserName);
writeTestFile(".", junitFileName+".java", buf.toString());
}
public StringBuffer compileToBuffer(String className, String lexerName, String parserName) {
StringTemplateGroup group = StringTemplateGroup.loadGroup("junit");
StringBuffer buf = new StringBuffer();
buf.append(genClassHeader(group, className, lexerName, parserName));
buf.append(genTestRuleMethods(group));
buf.append("\n\n}");
return buf;
}
protected String genClassHeader(StringTemplateGroup group, String junitFileName, String lexerName, String parserName) {
StringTemplate classHeaderST = group.getInstanceOf("classHeader");
if ( grammarInfo.getTestPackage()!=null ) { // Set up class package if there is
classHeaderST.setAttribute("header", "package "+grammarInfo.getTestPackage()+";");
}
classHeaderST.setAttribute("junitFileName", junitFileName);
String lexerPath = null;
String parserPath = null;
String treeParserPath = null;
String packagePath = null;
boolean isTreeGrammar = false;
boolean hasPackage = false;
/** Set up appropriate class path for parser/tree parser if using package */
if ( grammarInfo.getGrammarPackage()!=null ) {
hasPackage = true;
packagePath = "./"+grammarInfo.getGrammarPackage().replace('.', '/');
lexerPath = grammarInfo.getGrammarPackage()+"."+lexerName;
parserPath = grammarInfo.getGrammarPackage()+"."+parserName;
if ( grammarInfo.getTreeGrammarName()!=null ) {
treeParserPath = grammarInfo.getGrammarPackage()+"."+grammarInfo.getTreeGrammarName();
isTreeGrammar = true;
}
}
else {
lexerPath = lexerName;
parserPath = parserName;
if ( grammarInfo.getTreeGrammarName()!=null ) {
treeParserPath = grammarInfo.getTreeGrammarName();
isTreeGrammar = true;
}
}
// also set up custom tree adaptor if necessary
String treeAdaptorPath = null;
boolean hasTreeAdaptor = false;
if ( grammarInfo.getAdaptor()!=null ) {
hasTreeAdaptor = true;
treeAdaptorPath = grammarInfo.getAdaptor();
}
classHeaderST.setAttribute("hasTreeAdaptor", hasTreeAdaptor);
classHeaderST.setAttribute("treeAdaptorPath", treeAdaptorPath);
classHeaderST.setAttribute("hasPackage", hasPackage);
classHeaderST.setAttribute("packagePath", packagePath);
classHeaderST.setAttribute("lexerPath", lexerPath);
classHeaderST.setAttribute("parserPath", parserPath);
classHeaderST.setAttribute("treeParserPath", treeParserPath);
classHeaderST.setAttribute("isTreeGrammar", isTreeGrammar);
return classHeaderST.toString();
}
protected String genTestRuleMethods(StringTemplateGroup group) {
StringBuffer buf = new StringBuffer();
if ( grammarInfo.getTreeGrammarName()!=null ) { // Generate junit codes of for tree grammar rule
genTreeMethods(group, buf);
}
else { // Generate junit codes of for grammar rule
genParserMethods(group, buf);
}
return buf.toString();
}
private void genParserMethods(StringTemplateGroup group, StringBuffer buf) {
for ( gUnitTestSuite ts: grammarInfo.getRuleTestSuites() ) {
int i = 0;
for ( Map.Entry<gUnitTestInput, AbstractTest> entry : ts.testSuites.entrySet() ) { // each rule may contain multiple tests
gUnitTestInput input = entry.getKey();
i++;
StringTemplate testRuleMethodST;
/** If rule has multiple return values or ast*/
if ( entry.getValue().getType()== gUnitParser.ACTION && ruleWithReturn.containsKey(ts.getRuleName()) ) {
testRuleMethodST = group.getInstanceOf("testRuleMethod2");
String outputString = entry.getValue().getText();
testRuleMethodST.setAttribute("methodName", "test"+changeFirstCapital(ts.getRuleName())+i);
testRuleMethodST.setAttribute("testRuleName", '"'+ts.getRuleName()+'"');
testRuleMethodST.setAttribute("test", input);
testRuleMethodST.setAttribute("returnType", ruleWithReturn.get(ts.getRuleName()));
testRuleMethodST.setAttribute("expecting", outputString);
}
else {
String testRuleName;
// need to determine whether it's a test for parser rule or lexer rule
if ( ts.isLexicalRule() ) testRuleName = ts.getLexicalRuleName();
else testRuleName = ts.getRuleName();
testRuleMethodST = group.getInstanceOf("testRuleMethod");
String outputString = entry.getValue().getText();
testRuleMethodST.setAttribute("isLexicalRule", ts.isLexicalRule());
testRuleMethodST.setAttribute("methodName", "test"+changeFirstCapital(testRuleName)+i);
testRuleMethodST.setAttribute("testRuleName", '"'+testRuleName+'"');
testRuleMethodST.setAttribute("test", input);
testRuleMethodST.setAttribute("tokenType", getTypeString(entry.getValue().getType()));
// normalize whitespace
outputString = normalizeTreeSpec(outputString);
if ( entry.getValue().getType()==gUnitParser.ACTION ) { // trim ';' at the end of ACTION if there is...
//testRuleMethodST.setAttribute("expecting", outputString.substring(0, outputString.length()-1));
testRuleMethodST.setAttribute("expecting", outputString);
}
else if ( entry.getValue().getType()==gUnitParser.RETVAL ) { // Expected: RETVAL
testRuleMethodST.setAttribute("expecting", outputString);
}
else { // Attach "" to expected STRING or AST
// strip newlines for (...) tree stuff
outputString = outputString.replaceAll("\n", "");
testRuleMethodST.setAttribute("expecting", '"'+escapeForJava(outputString)+'"');
}
}
buf.append(testRuleMethodST.toString());
}
}
}
private void genTreeMethods(StringTemplateGroup group, StringBuffer buf) {
for ( gUnitTestSuite ts: grammarInfo.getRuleTestSuites() ) {
int i = 0;
for ( Map.Entry<gUnitTestInput, AbstractTest> entry : ts.testSuites.entrySet() ) { // each rule may contain multiple tests
gUnitTestInput input = entry.getKey();
i++;
StringTemplate testRuleMethodST;
/** If rule has multiple return values or ast*/
if ( entry.getValue().getType()== gUnitParser.ACTION && ruleWithReturn.containsKey(ts.getTreeRuleName()) ) {
testRuleMethodST = group.getInstanceOf("testTreeRuleMethod2");
String outputString = entry.getValue().getText();
testRuleMethodST.setAttribute("methodName", "test"+changeFirstCapital(ts.getTreeRuleName())+"_walks_"+
changeFirstCapital(ts.getRuleName())+i);
testRuleMethodST.setAttribute("testTreeRuleName", '"'+ts.getTreeRuleName()+'"');
testRuleMethodST.setAttribute("testRuleName", '"'+ts.getRuleName()+'"');
testRuleMethodST.setAttribute("test", input);
testRuleMethodST.setAttribute("returnType", ruleWithReturn.get(ts.getTreeRuleName()));
testRuleMethodST.setAttribute("expecting", outputString);
}
else {
testRuleMethodST = group.getInstanceOf("testTreeRuleMethod");
String outputString = entry.getValue().getText();
testRuleMethodST.setAttribute("methodName", "test"+changeFirstCapital(ts.getTreeRuleName())+"_walks_"+
changeFirstCapital(ts.getRuleName())+i);
testRuleMethodST.setAttribute("testTreeRuleName", '"'+ts.getTreeRuleName()+'"');
testRuleMethodST.setAttribute("testRuleName", '"'+ts.getRuleName()+'"');
testRuleMethodST.setAttribute("test", input);
testRuleMethodST.setAttribute("tokenType", getTypeString(entry.getValue().getType()));
if ( entry.getValue().getType()==gUnitParser.ACTION ) { // trim ';' at the end of ACTION if there is...
//testRuleMethodST.setAttribute("expecting", outputString.substring(0, outputString.length()-1));
testRuleMethodST.setAttribute("expecting", outputString);
}
else if ( entry.getValue().getType()==gUnitParser.RETVAL ) { // Expected: RETVAL
testRuleMethodST.setAttribute("expecting", outputString);
}
else { // Attach "" to expected STRING or AST
testRuleMethodST.setAttribute("expecting", '"'+escapeForJava(outputString)+'"');
}
}
buf.append(testRuleMethodST.toString());
}
}
}
// return a meaningful gUnit token type name instead of using the magic number
public String getTypeString(int type) {
String typeText;
switch (type) {
case gUnitParser.OK :
typeText = "org.antlr.gunit.gUnitParser.OK";
break;
case gUnitParser.FAIL :
typeText = "org.antlr.gunit.gUnitParser.FAIL";
break;
case gUnitParser.STRING :
typeText = "org.antlr.gunit.gUnitParser.STRING";
break;
case gUnitParser.ML_STRING :
typeText = "org.antlr.gunit.gUnitParser.ML_STRING";
break;
case gUnitParser.RETVAL :
typeText = "org.antlr.gunit.gUnitParser.RETVAL";
break;
case gUnitParser.AST :
typeText = "org.antlr.gunit.gUnitParser.AST";
break;
default :
typeText = "org.antlr.gunit.gUnitParser.EOF";
break;
}
return typeText;
}
protected void writeTestFile(String dir, String fileName, String content) {
try {
File f = new File(dir, fileName);
FileWriter w = new FileWriter(f);
BufferedWriter bw = new BufferedWriter(w);
bw.write(content);
bw.close();
w.close();
}
catch (IOException ioe) {
logger.log(Level.SEVERE, "can't write file", ioe);
}
}
public static String escapeForJava(String inputString) {
// Gotta escape literal backslash before putting in specials that use escape.
inputString = inputString.replace("\\", "\\\\");
// Then double quotes need escaping (singles are OK of course).
inputString = inputString.replace("\"", "\\\"");
// note: replace newline to String ".\n", replace tab to String ".\t"
inputString = inputString.replace("\n", "\\n").replace("\t", "\\t").replace("\r", "\\r").replace("\b", "\\b").replace("\f", "\\f");
return inputString;
}
protected String changeFirstCapital(String ruleName) {
String firstChar = String.valueOf(ruleName.charAt(0));
return firstChar.toUpperCase()+ruleName.substring(1);
}
public static String normalizeTreeSpec(String t) {
List<String> words = new ArrayList<String>();
int i = 0;
StringBuilder word = new StringBuilder();
while ( i<t.length() ) {
if ( t.charAt(i)=='(' || t.charAt(i)==')' ) {
if ( word.length()>0 ) {
words.add(word.toString());
word.setLength(0);
}
words.add(String.valueOf(t.charAt(i)));
i++;
continue;
}
if ( Character.isWhitespace(t.charAt(i)) ) {
// upon WS, save word
if ( word.length()>0 ) {
words.add(word.toString());
word.setLength(0);
}
i++;
continue;
}
// ... "x" or ...("x"
if ( t.charAt(i)=='"' && (i-1)>=0 &&
(t.charAt(i-1)=='(' || Character.isWhitespace(t.charAt(i-1))) )
{
i++;
while ( i<t.length() && t.charAt(i)!='"' ) {
if ( t.charAt(i)=='\\' &&
(i+1)<t.length() && t.charAt(i+1)=='"' ) // handle \"
{
word.append('"');
i+=2;
continue;
}
word.append(t.charAt(i));
i++;
}
i++; // skip final "
words.add(word.toString());
word.setLength(0);
continue;
}
word.append(t.charAt(i));
i++;
}
if ( word.length()>0 ) {
words.add(word.toString());
}
//System.out.println("words="+words);
StringBuilder buf = new StringBuilder();
for (int j=0; j<words.size(); j++) {
if ( j>0 && !words.get(j).equals(")") &&
!words.get(j-1).equals("(") ) {
buf.append(' ');
}
buf.append(words.get(j));
}
return buf.toString();
}
}

View File

@ -0,0 +1,67 @@
/*
[The "BSD license"]
Copyright (c) 2007 Kenny MacDermid
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.gunit;
import org.antlr.runtime.Token;
/** OutputTest represents a test for not only standard output string,
* but also AST output which is actually a return value from a parser.
*/
public class OutputTest extends AbstractTest {
private final Token token;
public OutputTest(Token token) {
this.token = token;
}
@Override
public String getText() {
return token.getText();
}
@Override
public int getType() {
return token.getType();
}
@Override
// return ANTLR error msg if test failed
public String getResult(gUnitTestResult testResult) {
// Note: we treat the standard output string as a return value also
if ( testResult.isSuccess() ) return testResult.getReturned();
else {
hasErrorMsg = true;
return testResult.getError();
}
}
@Override
public String getExpected() {
return token.getText();
}
}

View File

@ -0,0 +1,69 @@
/*
[The "BSD license"]
Copyright (c) 2007 Kenny MacDermid
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.gunit;
import org.antlr.runtime.Token;
public class ReturnTest extends AbstractTest {
private final Token retval;
public ReturnTest(Token retval) {
this.retval = retval;
}
@Override
public String getText() {
return retval.getText();
}
@Override
public int getType() {
return retval.getType();
}
@Override
// return ANTLR error msg if test failed
public String getResult(gUnitTestResult testResult) {
if ( testResult.isSuccess() ) return testResult.getReturned();
else {
hasErrorMsg = true;
return testResult.getError();
}
}
@Override
public String getExpected() {
String expect = retval.getText();
if ( expect.charAt(0)=='"' && expect.charAt(expect.length()-1)=='"' ) {
expect = expect.substring(1, expect.length()-1);
}
return expect;
}
}

View File

@ -0,0 +1,475 @@
/*
[The "BSD licence"]
Copyright (c) 2007-2008 Leon, Jen-Yuan Su
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.gunit;
import junit.framework.TestCase;
import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.stringtemplate.StringTemplate;
import java.io.*;
import java.lang.reflect.*;
/** All gUnit-generated JUnit class should extend this class
* which implements the essential methods for triggering
* ANTLR parser/tree walker
*/
public abstract class gUnitBaseTest extends TestCase {
public String treeAdaptorPath;
public String packagePath;
public String lexerPath;
public String parserPath;
public String treeParserPath;
protected String stdout;
protected String stderr;
private PrintStream console = System.out;
private PrintStream consoleErr = System.err;
// Invoke target lexer.rule
public String execLexer(String testRuleName, int line, String testInput, boolean isFile) throws Exception {
CharStream input;
/** Set up ANTLR input stream based on input source, file or String */
if ( isFile ) {
String filePath = testInput;
File testInputFile = new File(filePath);
// if input test file is not found under the current dir, also try to look for it under the package dir
if ( !testInputFile.exists() && packagePath!=null ) {
testInputFile = new File(packagePath, filePath);
if ( testInputFile.exists() ) filePath = testInputFile.getCanonicalPath();
}
input = new ANTLRFileStream(filePath);
}
else {
input = new ANTLRStringStream(testInput);
}
Class<? extends Lexer> lexer;
PrintStream ps = null; // for redirecting stdout later
PrintStream ps2 = null; // for redirecting stderr later
try {
/** Use Reflection to create instances of lexer and parser */
lexer = Class.forName(lexerPath).asSubclass(Lexer.class);
Constructor<? extends Lexer> lexConstructor = lexer.getConstructor(CharStream.class);
Lexer lexObj = lexConstructor.newInstance(input); // makes new instance of lexer
input.setLine(line);
Method ruleName = lexer.getMethod("m"+testRuleName);
/** Start of I/O Redirecting */
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream err = new ByteArrayOutputStream();
ps = new PrintStream(out);
ps2 = new PrintStream(err);
System.setOut(ps);
System.setErr(ps2);
/** End of redirecting */
/** Invoke lexer rule, and get the current index in CharStream */
ruleName.invoke(lexObj, new Object[0]);
Method ruleName2 = lexer.getMethod("getCharIndex");
int currentIndex = (Integer) ruleName2.invoke(lexObj, new Object[0]);
if ( currentIndex!=input.size() ) {
ps2.println("extra text found, '"+input.substring(currentIndex, input.size()-1)+"'");
}
this.stdout = null;
this.stderr = null;
if ( err.toString().length()>0 ) {
this.stderr = err.toString();
return this.stderr;
}
if ( out.toString().length()>0 ) {
this.stdout = out.toString();
}
if ( err.toString().length()==0 && out.toString().length()==0 ) {
return null;
}
} catch (ClassNotFoundException e) {
handleUnexpectedException(e);
} catch (SecurityException e) {
handleUnexpectedException(e);
} catch (NoSuchMethodException e) {
handleUnexpectedException(e);
} catch (IllegalArgumentException e) {
handleUnexpectedException(e);
} catch (InstantiationException e) {
handleUnexpectedException(e);
} catch (IllegalAccessException e) {
handleUnexpectedException(e);
} catch (InvocationTargetException e) { // This exception could be caused from ANTLR Runtime Exception, e.g. MismatchedTokenException
if ( e.getCause()!=null ) this.stderr = e.getCause().toString();
else this.stderr = e.toString();
return this.stderr;
} finally {
try {
if ( ps!=null ) ps.close();
if ( ps2!=null ) ps2.close();
System.setOut(console); // Reset standard output
System.setErr(consoleErr); // Reset standard err out
} catch (Exception e) {
e.printStackTrace();
}
}
return this.stdout;
}
// Invoke target parser.rule
public Object execParser(String testRuleName, int line, String testInput, boolean isFile) throws Exception {
CharStream input;
/** Set up ANTLR input stream based on input source, file or String */
if ( isFile ) {
String filePath = testInput;
File testInputFile = new File(filePath);
// if input test file is not found under the current dir, also try to look for it under the package dir
if ( !testInputFile.exists() && packagePath!=null ) {
testInputFile = new File(packagePath, filePath);
if ( testInputFile.exists() ) filePath = testInputFile.getCanonicalPath();
}
input = new ANTLRFileStream(filePath);
}
else {
input = new ANTLRStringStream(testInput);
}
Class<? extends Lexer> lexer;
Class<? extends Parser> parser;
PrintStream ps = null; // for redirecting stdout later
PrintStream ps2 = null; // for redirecting stderr later
ByteArrayOutputStream out = null;
ByteArrayOutputStream err = null;
try {
/** Use Reflection to create instances of lexer and parser */
lexer = Class.forName(lexerPath).asSubclass(Lexer.class);
Constructor<? extends Lexer> lexConstructor = lexer.getConstructor(CharStream.class);
Lexer lexObj = lexConstructor.newInstance(input); // makes new instance of lexer
input.setLine(line);
CommonTokenStream tokens = new CommonTokenStream(lexObj);
parser = Class.forName(parserPath).asSubclass(Parser.class);
Constructor<? extends Parser> parConstructor = parser.getConstructor(TokenStream.class);
Parser parObj = parConstructor.newInstance(tokens); // makes new instance of parser
// set up customized tree adaptor if necessary
if ( treeAdaptorPath!=null ) {
Method _setTreeAdaptor = parser.getMethod("setTreeAdaptor", TreeAdaptor.class);
Class<? extends TreeAdaptor> _treeAdaptor = Class.forName(treeAdaptorPath).asSubclass(TreeAdaptor.class);
_setTreeAdaptor.invoke(parObj, _treeAdaptor.newInstance());
}
Method ruleName = parser.getMethod(testRuleName);
/** Start of I/O Redirecting */
out = new ByteArrayOutputStream();
err = new ByteArrayOutputStream();
ps = new PrintStream(out);
ps2 = new PrintStream(err);
System.setOut(ps);
System.setErr(ps2);
/** End of redirecting */
/** Invoke grammar rule, and store if there is a return value */
Object ruleReturn = ruleName.invoke(parObj);
String astString = null;
String stString = null;
/** If rule has return value, determine if it contains an AST or a ST */
if ( ruleReturn!=null ) {
if ( ruleReturn.getClass().toString().indexOf(testRuleName+"_return")>0 ) {
try { // NullPointerException may happen here...
Class<?> _return = Class.forName(parserPath+"$"+testRuleName+"_return");
Method[] methods = _return.getDeclaredMethods();
for(Method method : methods) {
if ( method.getName().equals("getTree") ) {
Method returnName = _return.getMethod("getTree");
CommonTree tree = (CommonTree) returnName.invoke(ruleReturn);
astString = tree.toStringTree();
}
else if ( method.getName().equals("getTemplate") ) {
Method returnName = _return.getMethod("getTemplate");
StringTemplate st = (StringTemplate) returnName.invoke(ruleReturn);
stString = st.toString();
}
}
}
catch(Exception e) {
System.err.println(e); // Note: If any exception occurs, the test is viewed as failed.
}
}
}
this.stdout = "";
this.stderr = "";
/** Invalid input */
if ( tokens.index()!=tokens.size()-1 ) {
//throw new InvalidInputException();
this.stderr += "Stopped parsing at token index "+tokens.index()+": ";
}
// retVal could be actual return object from rule, stderr or stdout
this.stdout += out.toString();
this.stderr += err.toString();
if ( err.toString().length()>0 ) return this.stderr;
if ( out.toString().length()>0 ) return this.stdout;
if ( astString!=null ) { // Return toStringTree of AST
return astString;
}
else if ( stString!=null ) {// Return toString of ST
return stString;
}
if ( ruleReturn!=null ) {
return ruleReturn;
}
if ( err.toString().length()==0 && out.toString().length()==0 ) {
return null;
}
}
catch (ClassNotFoundException e) {
handleUnexpectedException(e);
}
catch (SecurityException e) {
handleUnexpectedException(e);
}
catch (NoSuchMethodException e) {
handleUnexpectedException(e);
}
catch (IllegalAccessException e) {
handleUnexpectedException(e);
}
catch (InvocationTargetException e) {
this.stdout = out.toString();
this.stderr = err.toString();
if ( e.getCause()!=null ) this.stderr += e.getCause().toString();
else this.stderr += e.toString();
return this.stderr;
} finally {
try {
if ( ps!=null ) ps.close();
if ( ps2!=null ) ps2.close();
System.setOut(console); // Reset standard output
System.setErr(consoleErr); // Reset standard err out
} catch (Exception e) {
e.printStackTrace();
}
}
return this.stdout;
}
// Invoke target parser.rule
public Object execTreeParser(String testTreeRuleName, String testRuleName, String testInput, boolean isFile) throws Exception {
CharStream input;
if ( isFile ) {
String filePath = testInput;
File testInputFile = new File(filePath);
// if input test file is not found under the current dir, also try to look for it under the package dir
if ( !testInputFile.exists() && packagePath!=null ) {
testInputFile = new File(packagePath, filePath);
if ( testInputFile.exists() ) filePath = testInputFile.getCanonicalPath();
}
input = new ANTLRFileStream(filePath);
}
else {
input = new ANTLRStringStream(testInput);
}
Class<? extends Lexer> lexer;
Class<? extends Parser> parser;
Class<? extends TreeParser> treeParser;
PrintStream ps = null; // for redirecting stdout later
PrintStream ps2 = null; // for redirecting stderr later
try {
/** Use Reflection to create instances of lexer and parser */
lexer = Class.forName(lexerPath).asSubclass(Lexer.class);
Constructor<? extends Lexer> lexConstructor = lexer.getConstructor(CharStream.class);
Lexer lexObj = lexConstructor.newInstance(input); // makes new instance of lexer
CommonTokenStream tokens = new CommonTokenStream(lexObj);
parser = Class.forName(parserPath).asSubclass(Parser.class);
Constructor<? extends Parser> parConstructor = parser.getConstructor(TokenStream.class);
Parser parObj = parConstructor.newInstance(tokens); // makes new instance of parser
// set up customized tree adaptor if necessary
TreeAdaptor customTreeAdaptor = null;
if ( treeAdaptorPath!=null ) {
Method _setTreeAdaptor = parser.getMethod("setTreeAdaptor", TreeAdaptor.class);
Class<? extends TreeAdaptor> _treeAdaptor = Class.forName(treeAdaptorPath).asSubclass(TreeAdaptor.class);
customTreeAdaptor = _treeAdaptor.newInstance();
_setTreeAdaptor.invoke(parObj, customTreeAdaptor);
}
Method ruleName = parser.getMethod(testRuleName);
/** Start of I/O Redirecting */
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream err = new ByteArrayOutputStream();
ps = new PrintStream(out);
ps2 = new PrintStream(err);
System.setOut(ps);
System.setErr(ps2);
/** End of redirecting */
/** Invoke grammar rule, and get the return value */
Object ruleReturn = ruleName.invoke(parObj);
Class<?> _return = Class.forName(parserPath+"$"+testRuleName+"_return");
Method returnName = _return.getMethod("getTree");
CommonTree tree = (CommonTree) returnName.invoke(ruleReturn);
// Walk resulting tree; create tree nodes stream first
CommonTreeNodeStream nodes;
if ( customTreeAdaptor!=null ) {
nodes = new CommonTreeNodeStream(customTreeAdaptor, tree);
}
else {
nodes = new CommonTreeNodeStream(tree);
}
// AST nodes have payload that point into token stream
nodes.setTokenStream(tokens);
// Create a tree walker attached to the nodes stream
treeParser = Class.forName(treeParserPath).asSubclass(TreeParser.class);
Constructor<? extends TreeParser> treeParConstructor = treeParser.getConstructor(TreeNodeStream.class);
TreeParser treeParObj = treeParConstructor.newInstance(nodes); // makes new instance of tree parser
// Invoke the tree rule, and store the return value if there is
Method treeRuleName = treeParser.getMethod(testTreeRuleName);
Object treeRuleReturn = treeRuleName.invoke(treeParObj);
String astString = null;
String stString = null;
/** If tree rule has return value, determine if it contains an AST or a ST */
if ( treeRuleReturn!=null ) {
if ( treeRuleReturn.getClass().toString().indexOf(testTreeRuleName+"_return")>0 ) {
try { // NullPointerException may happen here...
Class<?> _treeReturn = Class.forName(treeParserPath+"$"+testTreeRuleName+"_return");
Method[] methods = _treeReturn.getDeclaredMethods();
for(Method method : methods) {
if ( method.getName().equals("getTree") ) {
Method treeReturnName = _treeReturn.getMethod("getTree");
CommonTree returnTree = (CommonTree) treeReturnName.invoke(treeRuleReturn);
astString = returnTree.toStringTree();
}
else if ( method.getName().equals("getTemplate") ) {
Method treeReturnName = _return.getMethod("getTemplate");
StringTemplate st = (StringTemplate) treeReturnName.invoke(treeRuleReturn);
stString = st.toString();
}
}
}
catch(Exception e) {
System.err.println(e); // Note: If any exception occurs, the test is viewed as failed.
}
}
}
this.stdout = null;
this.stderr = null;
/** Invalid input */
if ( tokens.index()!=tokens.size()-1 ) {
throw new InvalidInputException();
}
// retVal could be actual return object from rule, stderr or stdout
if ( err.toString().length()>0 ) {
this.stderr = err.toString();
return this.stderr;
}
if ( out.toString().length()>0 ) {
this.stdout = out.toString();
}
if ( astString!=null ) { // Return toStringTree of AST
return astString;
}
else if ( stString!=null ) {// Return toString of ST
return stString;
}
if ( treeRuleReturn!=null ) {
return treeRuleReturn;
}
if ( err.toString().length()==0 && out.toString().length()==0 ) {
return null;
}
} catch (ClassNotFoundException e) {
handleUnexpectedException(e);
} catch (SecurityException e) {
handleUnexpectedException(e);
} catch (NoSuchMethodException e) {
handleUnexpectedException(e);
} catch (IllegalAccessException e) {
handleUnexpectedException(e);
} catch (InvocationTargetException e) {
if ( e.getCause()!=null ) this.stderr = e.getCause().toString();
else this.stderr = e.toString();
return this.stderr;
} finally {
try {
if ( ps!=null ) ps.close();
if ( ps2!=null ) ps2.close();
System.setOut(console); // Reset standard output
System.setErr(consoleErr); // Reset standard err out
} catch (Exception e) {
e.printStackTrace();
}
}
return stdout;
}
// Modify the return value if the expected token type is OK or FAIL
public Object examineExecResult(int tokenType, Object retVal) {
System.out.println("expect "+(tokenType==gUnitParser.OK?"OK":"FAIL")+
"stderr=="+stderr);
if ( tokenType==gUnitParser.OK ) { // expected Token: OK
if ( this.stderr==null || this.stderr.length()==0 ) {
return "OK";
}
else {
return "FAIL, "+this.stderr;
}
}
else if ( tokenType==gUnitParser.FAIL ) { // expected Token: FAIL
if ( this.stderr!=null && this.stderr.length()>0 ) {
return "FAIL";
}
else {
return "OK";
}
}
else { // return the same object for the other token types
return retVal;
}
}
protected void handleUnexpectedException(Exception e) {
e.printStackTrace();
System.exit(1);
}
}

View File

@ -0,0 +1,655 @@
/*
[The "BSD licence"]
Copyright (c) 2007-2008 Leon Jen-Yuan Su
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.gunit;
import org.antlr.runtime.*;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.CommonTreeNodeStream;
import org.antlr.runtime.tree.TreeAdaptor;
import org.antlr.runtime.tree.TreeNodeStream;
import org.antlr.stringtemplate.CommonGroupLoader;
import org.antlr.stringtemplate.StringTemplate;
import org.antlr.stringtemplate.StringTemplateGroup;
import org.antlr.stringtemplate.StringTemplateGroupLoader;
import org.antlr.stringtemplate.language.AngleBracketTemplateLexer;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.antlr.runtime.tree.TreeParser;
public class gUnitExecutor implements ITestSuite {
public GrammarInfo grammarInfo;
private final ClassLoader grammarClassLoader;
private final String testsuiteDir;
public int numOfTest;
public int numOfSuccess;
public int numOfFailure;
private String title;
public int numOfInvalidInput;
private String parserName;
private String lexerName;
public List<AbstractTest> failures;
public List<AbstractTest> invalids;
private PrintStream console = System.out;
private PrintStream consoleErr = System.err;
public gUnitExecutor(GrammarInfo grammarInfo, String testsuiteDir) {
this( grammarInfo, determineClassLoader(), testsuiteDir);
}
private static ClassLoader determineClassLoader() {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if ( classLoader == null ) {
classLoader = gUnitExecutor.class.getClassLoader();
}
return classLoader;
}
public gUnitExecutor(GrammarInfo grammarInfo, ClassLoader grammarClassLoader, String testsuiteDir) {
this.grammarInfo = grammarInfo;
this.grammarClassLoader = grammarClassLoader;
this.testsuiteDir = testsuiteDir;
numOfTest = 0;
numOfSuccess = 0;
numOfFailure = 0;
numOfInvalidInput = 0;
failures = new ArrayList<AbstractTest>();
invalids = new ArrayList<AbstractTest>();
}
protected ClassLoader getGrammarClassLoader() {
return grammarClassLoader;
}
protected final Class<?> classForName(String name) throws ClassNotFoundException {
return getGrammarClassLoader().loadClass( name );
}
public String execTest() throws IOException{
// Set up string template for testing result
StringTemplate testResultST = getTemplateGroup().getInstanceOf("testResult");
try {
/** Set up appropriate path for parser/lexer if using package */
if (grammarInfo.getGrammarPackage()!=null ) {
parserName = grammarInfo.getGrammarPackage()+"."+grammarInfo.getGrammarName()+"Parser";
lexerName = grammarInfo.getGrammarPackage()+"."+grammarInfo.getGrammarName()+"Lexer";
}
else {
parserName = grammarInfo.getGrammarName()+"Parser";
lexerName = grammarInfo.getGrammarName()+"Lexer";
}
/*** Start Unit/Functional Testing ***/
// Execute unit test of for parser, lexer and tree grammar
if ( grammarInfo.getTreeGrammarName()!=null ) {
title = "executing testsuite for tree grammar:"+grammarInfo.getTreeGrammarName()+" walks "+parserName;
}
else {
title = "executing testsuite for grammar:"+grammarInfo.getGrammarName();
}
executeTests();
// End of exection of unit testing
// Fill in the template holes with the test results
testResultST.setAttribute("title", title);
testResultST.setAttribute("num_of_test", numOfTest);
testResultST.setAttribute("num_of_failure", numOfFailure);
if ( numOfFailure>0 ) {
testResultST.setAttribute("failure", failures);
}
if ( numOfInvalidInput>0 ) {
testResultST.setAttribute("has_invalid", true);
testResultST.setAttribute("num_of_invalid", numOfInvalidInput);
testResultST.setAttribute("invalid", invalids);
}
}
catch (Exception e) {
handleUnexpectedException(e);
}
return testResultST.toString();
}
private StringTemplateGroup getTemplateGroup() {
StringTemplateGroupLoader loader = new CommonGroupLoader("org/antlr/gunit", null);
StringTemplateGroup.registerGroupLoader(loader);
StringTemplateGroup.registerDefaultLexer(AngleBracketTemplateLexer.class);
StringTemplateGroup group = StringTemplateGroup.loadGroup("gUnitTestResult");
return group;
}
// TODO: throw more specific exceptions
private gUnitTestResult runCorrectParser(String parserName, String lexerName, String rule, String lexicalRule, String treeRule, gUnitTestInput input) throws Exception
{
if ( lexicalRule!=null ) return runLexer(lexerName, lexicalRule, input);
else if ( treeRule!=null ) return runTreeParser(parserName, lexerName, rule, treeRule, input);
else return runParser(parserName, lexerName, rule, input);
}
private void executeTests() throws Exception {
for ( gUnitTestSuite ts: grammarInfo.getRuleTestSuites() ) {
String rule = ts.getRuleName();
String lexicalRule = ts.getLexicalRuleName();
String treeRule = ts.getTreeRuleName();
for ( Map.Entry<gUnitTestInput, AbstractTest> entry : ts.testSuites.entrySet() ) { // each rule may contain multiple tests
gUnitTestInput input = entry.getKey();
numOfTest++;
// Run parser, and get the return value or stdout or stderr if there is
gUnitTestResult result = null;
AbstractTest test = entry.getValue();
try {
// TODO: create a -debug option to turn on logging, which shows progress of running tests
//System.out.print(numOfTest + ". Running rule: " + rule + "; input: '" + input.testInput + "'");
result = runCorrectParser(parserName, lexerName, rule, lexicalRule, treeRule, input);
// TODO: create a -debug option to turn on logging, which shows progress of running tests
//System.out.println("; Expecting " + test.getExpected() + "; Success?: " + test.getExpected().equals(test.getResult(result)));
} catch ( InvalidInputException e) {
numOfInvalidInput++;
test.setHeader(rule, lexicalRule, treeRule, numOfTest, input.line, input.input);
test.setActual(input.input);
invalids.add(test);
continue;
} // TODO: ensure there's no other exceptions required to be handled here...
String expected = test.getExpected();
String actual = test.getResult(result);
test.setActual(actual);
if (actual == null) {
numOfFailure++;
test.setHeader(rule, lexicalRule, treeRule, numOfTest, input.line, input.input);
test.setActual("null");
failures.add(test);
onFail(test);
}
// the 2nd condition is used for the assertFAIL test of lexer rule because BooleanTest return err msg instead of 'FAIL' if isLexerTest
else if ( expected.equals(actual) || (expected.equals("FAIL")&&!actual.equals("OK") ) ) {
numOfSuccess++;
onPass(test);
}
// TODO: something with ACTIONS - at least create action test type and throw exception.
else if ( ts.testSuites.get(input).getType()==gUnitParser.ACTION ) { // expected Token: ACTION
numOfFailure++;
test.setHeader(rule, lexicalRule, treeRule, numOfTest, input.line, input.input);
test.setActual("\t"+"{ACTION} is not supported in the grammarInfo yet...");
failures.add(test);
onFail(test);
}
else {
numOfFailure++;
test.setHeader(rule, lexicalRule, treeRule, numOfTest, input.line, input.input);
failures.add(test);
onFail(test);
}
} // end of 2nd for-loop: tests for individual rule
} // end of 1st for-loop: testsuites for grammar
}
// TODO: throw proper exceptions
protected gUnitTestResult runLexer(String lexerName, String testRuleName, gUnitTestInput testInput) throws Exception {
CharStream input;
Class<? extends Lexer> lexer;
PrintStream ps = null; // for redirecting stdout later
PrintStream ps2 = null; // for redirecting stderr later
try {
/** Set up ANTLR input stream based on input source, file or String */
input = getANTLRInputStream(testInput);
/** Use Reflection to create instances of lexer and parser */
lexer = classForName(lexerName).asSubclass(Lexer.class);
Constructor<? extends Lexer> lexConstructor = lexer.getConstructor(CharStream.class);
Lexer lexObj = lexConstructor.newInstance(input); // makes new instance of lexer
Method ruleName = lexer.getMethod("m"+testRuleName);
/** Start of I/O Redirecting */
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream err = new ByteArrayOutputStream();
ps = new PrintStream(out);
ps2 = new PrintStream(err);
System.setOut(ps);
System.setErr(ps2);
/** End of redirecting */
/** Invoke lexer rule, and get the current index in CharStream */
ruleName.invoke(lexObj);
Method ruleName2 = lexer.getMethod("getCharIndex");
int currentIndex = (Integer) ruleName2.invoke(lexObj);
if ( currentIndex!=input.size() ) {
ps2.print("extra text found, '"+input.substring(currentIndex, input.size()-1)+"'");
}
if ( err.toString().length()>0 ) {
gUnitTestResult testResult = new gUnitTestResult(false, err.toString(), true);
testResult.setError(err.toString());
return testResult;
}
String stdout = null;
if ( out.toString().length()>0 ) {
stdout = out.toString();
}
return new gUnitTestResult(true, stdout, true);
} catch (IOException e) {
return getTestExceptionResult(e);
} catch (ClassNotFoundException e) {
handleUnexpectedException( e );
} catch (SecurityException e) {
handleUnexpectedException( e );
} catch (NoSuchMethodException e) {
handleUnexpectedException( e );
} catch (IllegalArgumentException e) {
handleUnexpectedException( e );
} catch (InstantiationException e) {
handleUnexpectedException( e );
} catch (IllegalAccessException e) {
handleUnexpectedException( e );
} catch (InvocationTargetException e) { // This exception could be caused from ANTLR Runtime Exception, e.g. MismatchedTokenException
return getTestExceptionResult(e);
} finally {
try {
if ( ps!=null ) ps.close();
if ( ps2!=null ) ps2.close();
System.setOut(console); // Reset standard output
System.setErr(consoleErr); // Reset standard err out
} catch (Exception e) {
e.printStackTrace();
}
}
// TODO: verify this:
throw new Exception("This should be unreachable?");
}
// TODO: throw proper exceptions
protected gUnitTestResult runParser(String parserName, String lexerName, String testRuleName, gUnitTestInput testInput) throws Exception {
CharStream input;
Class<? extends Lexer> lexer;
Class<? extends Parser> parser;
PrintStream ps = null; // for redirecting stdout later
PrintStream ps2 = null; // for redirecting stderr later
try {
/** Set up ANTLR input stream based on input source, file or String */
input = getANTLRInputStream(testInput);
/** Use Reflection to create instances of lexer and parser */
lexer = classForName(lexerName).asSubclass(Lexer.class);
Constructor<? extends Lexer> lexConstructor = lexer.getConstructor(CharStream.class);
Lexer lexObj = lexConstructor.newInstance(input); // makes new instance of lexer
CommonTokenStream tokens = new CommonTokenStream(lexObj);
parser = classForName(parserName).asSubclass(Parser.class);
Constructor<? extends Parser> parConstructor = parser.getConstructor(TokenStream.class);
Parser parObj = parConstructor.newInstance(tokens); // makes new instance of parser
// set up customized tree adaptor if necessary
if ( grammarInfo.getAdaptor()!=null ) {
Method _setTreeAdaptor = parser.getMethod("setTreeAdaptor", TreeAdaptor.class);
Class<? extends TreeAdaptor> _treeAdaptor = classForName(grammarInfo.getAdaptor()).asSubclass(TreeAdaptor.class);
_setTreeAdaptor.invoke(parObj, _treeAdaptor.newInstance());
}
Method ruleName = parser.getMethod(testRuleName);
/** Start of I/O Redirecting */
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream err = new ByteArrayOutputStream();
ps = new PrintStream(out);
ps2 = new PrintStream(err);
System.setOut(ps);
System.setErr(ps2);
/** End of redirecting */
/** Invoke grammar rule, and store if there is a return value */
Object ruleReturn = ruleName.invoke(parObj);
String astString = null;
String stString = null;
/** If rule has return value, determine if it contains an AST or a ST */
if ( ruleReturn!=null ) {
if ( ruleReturn.getClass().toString().indexOf(testRuleName+"_return")>0 ) {
try { // NullPointerException may happen here...
Class<?> _return = classForName(parserName+"$"+testRuleName+"_return");
Method[] methods = _return.getDeclaredMethods();
for(Method method : methods) {
if ( method.getName().equals("getTree") ) {
Method returnName = _return.getMethod("getTree");
CommonTree tree = (CommonTree) returnName.invoke(ruleReturn);
astString = tree.toStringTree();
}
else if ( method.getName().equals("getTemplate") ) {
Method returnName = _return.getMethod("getTemplate");
StringTemplate st = (StringTemplate) returnName.invoke(ruleReturn);
stString = st.toString();
}
}
}
catch(Exception e) {
System.err.println(e); // Note: If any exception occurs, the test is viewed as failed.
}
}
}
checkForValidInput(tokens, ps2);
if ( err.toString().length()>0 ) {
gUnitTestResult testResult = new gUnitTestResult(false, err.toString());
testResult.setError(err.toString());
return testResult;
}
String stdout = null;
// TODO: need to deal with the case which has both ST return value and stdout
if ( out.toString().length()>0 ) {
stdout = out.toString();
}
if ( astString!=null ) { // Return toStringTree of AST
return new gUnitTestResult(true, stdout, astString);
}
else if ( stString!=null ) {// Return toString of ST
return new gUnitTestResult(true, stdout, stString);
}
if ( ruleReturn!=null ) {
// TODO: currently only works for a single return with int or String value
return new gUnitTestResult(true, stdout, String.valueOf(ruleReturn));
}
return new gUnitTestResult(true, stdout, stdout);
} catch (IOException e) {
return getTestExceptionResult(e);
} catch (ClassNotFoundException e) {
handleUnexpectedException( e );
} catch (SecurityException e) {
handleUnexpectedException( e );
} catch (NoSuchMethodException e) {
handleUnexpectedException( e );
} catch (IllegalArgumentException e) {
handleUnexpectedException( e );
} catch (InstantiationException e) {
handleUnexpectedException( e );
} catch (IllegalAccessException e) {
handleUnexpectedException( e );
} catch (InvocationTargetException e) { // This exception could be caused from ANTLR Runtime Exception, e.g. MismatchedTokenException
return getTestExceptionResult(e);
} finally {
try {
if ( ps!=null ) ps.close();
if ( ps2!=null ) ps2.close();
System.setOut(console); // Reset standard output
System.setErr(consoleErr); // Reset standard err out
} catch (Exception e) {
e.printStackTrace();
}
}
// TODO: verify this:
throw new Exception("This should be unreachable?");
}
protected gUnitTestResult runTreeParser(String parserName, String lexerName, String testRuleName, String testTreeRuleName, gUnitTestInput testInput) throws Exception {
CharStream input;
String treeParserPath;
Class<? extends Lexer> lexer;
Class<? extends Parser> parser;
Class<? extends TreeParser> treeParser;
PrintStream ps = null; // for redirecting stdout later
PrintStream ps2 = null; // for redirecting stderr later
try {
/** Set up ANTLR input stream based on input source, file or String */
input = getANTLRInputStream(testInput);
/** Set up appropriate path for tree parser if using package */
if ( grammarInfo.getGrammarPackage()!=null ) {
treeParserPath = grammarInfo.getGrammarPackage()+"."+grammarInfo.getTreeGrammarName();
}
else {
treeParserPath = grammarInfo.getTreeGrammarName();
}
/** Use Reflection to create instances of lexer and parser */
lexer = classForName(lexerName).asSubclass(Lexer.class);
Constructor<? extends Lexer> lexConstructor = lexer.getConstructor(CharStream.class);
Lexer lexObj = lexConstructor.newInstance(input); // makes new instance of lexer
CommonTokenStream tokens = new CommonTokenStream(lexObj);
parser = classForName(parserName).asSubclass(Parser.class);
Constructor<? extends Parser> parConstructor = parser.getConstructor(TokenStream.class);
Parser parObj = parConstructor.newInstance(tokens); // makes new instance of parser
// set up customized tree adaptor if necessary
TreeAdaptor customTreeAdaptor = null;
if ( grammarInfo.getAdaptor()!=null ) {
Method _setTreeAdaptor = parser.getMethod("setTreeAdaptor", TreeAdaptor.class);
Class<? extends TreeAdaptor> _treeAdaptor = classForName(grammarInfo.getAdaptor()).asSubclass(TreeAdaptor.class);
customTreeAdaptor = _treeAdaptor.newInstance();
_setTreeAdaptor.invoke(parObj, customTreeAdaptor);
}
Method ruleName = parser.getMethod(testRuleName);
/** Start of I/O Redirecting */
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream err = new ByteArrayOutputStream();
ps = new PrintStream(out);
ps2 = new PrintStream(err);
System.setOut(ps);
System.setErr(ps2);
/** End of redirecting */
/** Invoke grammar rule, and get the return value */
Object ruleReturn = ruleName.invoke(parObj);
Class<?> _return = classForName(parserName+"$"+testRuleName+"_return");
Method returnName = _return.getMethod("getTree");
CommonTree tree = (CommonTree) returnName.invoke(ruleReturn);
// Walk resulting tree; create tree nodes stream first
CommonTreeNodeStream nodes;
if ( customTreeAdaptor!=null ) {
nodes = new CommonTreeNodeStream(customTreeAdaptor, tree);
}
else {
nodes = new CommonTreeNodeStream(tree);
}
// AST nodes have payload that point into token stream
nodes.setTokenStream(tokens);
// Create a tree walker attached to the nodes stream
treeParser = classForName(treeParserPath).asSubclass(TreeParser.class);
Constructor<? extends TreeParser> treeParConstructor = treeParser.getConstructor(TreeNodeStream.class);
TreeParser treeParObj = treeParConstructor.newInstance(nodes); // makes new instance of tree parser
// Invoke the tree rule, and store the return value if there is
Method treeRuleName = treeParser.getMethod(testTreeRuleName);
Object treeRuleReturn = treeRuleName.invoke(treeParObj);
String astString = null;
String stString = null;
/** If tree rule has return value, determine if it contains an AST or a ST */
if ( treeRuleReturn!=null ) {
if ( treeRuleReturn.getClass().toString().indexOf(testTreeRuleName+"_return")>0 ) {
try { // NullPointerException may happen here...
Class<?> _treeReturn = classForName(treeParserPath+"$"+testTreeRuleName+"_return");
Method[] methods = _treeReturn.getDeclaredMethods();
for(Method method : methods) {
if ( method.getName().equals("getTree") ) {
Method treeReturnName = _treeReturn.getMethod("getTree");
CommonTree returnTree = (CommonTree) treeReturnName.invoke(treeRuleReturn);
astString = returnTree.toStringTree();
}
else if ( method.getName().equals("getTemplate") ) {
Method treeReturnName = _return.getMethod("getTemplate");
StringTemplate st = (StringTemplate) treeReturnName.invoke(treeRuleReturn);
stString = st.toString();
}
}
}
catch(Exception e) {
System.err.println(e); // Note: If any exception occurs, the test is viewed as failed.
}
}
}
checkForValidInput( tokens, ps2 );
if ( err.toString().length()>0 ) {
gUnitTestResult testResult = new gUnitTestResult(false, err.toString());
testResult.setError(err.toString());
return testResult;
}
String stdout = null;
// TODO: need to deal with the case which has both ST return value and stdout
if ( out.toString().length()>0 ) {
stdout = out.toString();
}
if ( astString!=null ) { // Return toStringTree of AST
return new gUnitTestResult(true, stdout, astString);
}
else if ( stString!=null ) {// Return toString of ST
return new gUnitTestResult(true, stdout, stString);
}
if ( treeRuleReturn!=null ) {
// TODO: again, currently only works for a single return with int or String value
return new gUnitTestResult(true, stdout, String.valueOf(treeRuleReturn));
}
return new gUnitTestResult(true, stdout, stdout);
} catch (IOException e) {
return getTestExceptionResult(e);
} catch (ClassNotFoundException e) {
handleUnexpectedException( e );
} catch (SecurityException e) {
handleUnexpectedException( e );
} catch (NoSuchMethodException e) {
handleUnexpectedException( e );
} catch (IllegalArgumentException e) {
handleUnexpectedException( e );
} catch (InstantiationException e) {
handleUnexpectedException( e );
} catch (IllegalAccessException e) {
handleUnexpectedException( e );
} catch (InvocationTargetException e) { // note: This exception could be caused from ANTLR Runtime Exception...
return getTestExceptionResult(e);
} finally {
try {
if ( ps!=null ) ps.close();
if ( ps2!=null ) ps2.close();
System.setOut(console); // Reset standard output
System.setErr(consoleErr); // Reset standard err out
} catch (Exception e) {
e.printStackTrace();
}
}
// TODO: verify this:
throw new Exception("Should not be reachable?");
}
// Create ANTLR input stream based on input source, file or String
private CharStream getANTLRInputStream(gUnitTestInput testInput) throws IOException {
CharStream input;
if ( testInput.isFile) {
String filePath = testInput.input;
File testInputFile = new File(filePath);
// if input test file is not found under the current dir, try to look for it from dir where the testsuite file locates
if ( !testInputFile.exists() ) {
testInputFile = new File(this.testsuiteDir, filePath);
if ( testInputFile.exists() ) filePath = testInputFile.getCanonicalPath();
// if still not found, also try to look for it under the package dir
else if ( grammarInfo.getGrammarPackage()!=null ) {
testInputFile = new File("."+File.separator+grammarInfo.getGrammarPackage().replace(".", File.separator), filePath);
if ( testInputFile.exists() ) filePath = testInputFile.getCanonicalPath();
}
}
input = new ANTLRFileStream(filePath);
}
else {
input = new ANTLRStringStream(testInput.input);
}
return input;
}
// set up the cause of exception or the exception name into a gUnitTestResult instance
private gUnitTestResult getTestExceptionResult(Exception e) {
gUnitTestResult testResult;
if ( e.getCause()!=null ) {
testResult = new gUnitTestResult(false, e.getCause().toString(), true);
testResult.setError(e.getCause().toString());
}
else {
testResult = new gUnitTestResult(false, e.toString(), true);
testResult.setError(e.toString());
}
return testResult;
}
/**
* Verify the input has been properly consumed
*/
protected void checkForValidInput(CommonTokenStream tokens, PrintStream ps2) {
if ( tokens.index() != tokens.size() - 1 ) {
//At this point we need to check for redundant EOF tokens
//which might have been added by the Parser:
List<? extends Token> endingTokens = tokens.getTokens(tokens.index(), tokens.size() -1);
for (Token endToken : endingTokens) {
if (! "<EOF>".equals(endToken.getText())) {
//writing to ps2 will mark the test as failed:
ps2.print( "Invalid input" );
return;
}
}
}
}
public void onPass(ITestCase passTest) {
}
public void onFail(ITestCase failTest) {
}
protected void handleUnexpectedException(Exception e) {
e.printStackTrace();
System.exit(1);
}
}

View File

@ -0,0 +1,46 @@
/*
[The "BSD licence"]
Copyright (c) 2007-2008 Leon, Jen-Yuan Su
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.gunit;
/** A class which contains input information of an individual testuite */
public class gUnitTestInput {
public String input; // a test input string for a testsuite
public boolean isFile; // if true, the input represents a filename
public int line; // line number in the script
public gUnitTestInput(String input, boolean isFile, int line) {
this.input = input;
this.isFile = isFile;
this.line = line;
}
public String getInputEscaped() {
return JUnitCodeGen.escapeForJava(input);
}
}

View File

@ -0,0 +1,77 @@
/*
[The "BSD license"]
Copyright (c) 2007 Kenny MacDermid
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.gunit;
public class gUnitTestResult {
private boolean success;
private String output; // stdout
private String error; // stderr
private String returned; // AST (toStringTree) or ST (toString)
private boolean isLexerTest;
public gUnitTestResult(boolean success, String output) {
this.success = success;
this.output = output;
}
public gUnitTestResult(boolean success, String output, boolean isLexerTest) {
this(success, output);
this.isLexerTest = isLexerTest;
}
public gUnitTestResult(boolean success, String output, String returned) {
this(success, output);
this.returned = returned;
}
public boolean isSuccess() {
return success;
}
public String getOutput() {
return output;
}
public String getError() {
return error;
}
public String getReturned() {
return returned;
}
public boolean isLexerTest() {
return isLexerTest;
}
public void setError(String error) {
this.error = error;
}
}

View File

@ -0,0 +1,80 @@
/*
[The "BSD licence"]
Copyright (c) 2007-2008 Leon Jen-Yuan Su
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.gunit;
/** A class which wraps all testsuites for an individual rule */
import java.util.Map;
import java.util.LinkedHashMap;
public class gUnitTestSuite {
protected String rule = null; // paeser rule name for unit testing
protected String lexicalRule = null; // lexical rule name
protected String treeRule = null; // optional, required for testing tree grammar rule
protected boolean isLexicalRule = false;
/** A map which stores input/output pairs (individual testsuites).
* In other words, it maps input data for unit test (gUnitTestInput object)
* to an expected output (Token object).
*/
protected Map<gUnitTestInput, AbstractTest> testSuites = new LinkedHashMap<gUnitTestInput, AbstractTest>();
public gUnitTestSuite() {
}
public gUnitTestSuite(String rule) {
this.rule = rule;
}
public gUnitTestSuite(String treeRule, String rule) {
this.rule = rule;
this.treeRule = treeRule;
}
public void setRuleName(String ruleName) { this.rule = ruleName; }
public void setLexicalRuleName(String lexicalRule) { this.lexicalRule = lexicalRule; this.isLexicalRule = true; }
public void setTreeRuleName(String treeRuleName) { this.treeRule = treeRuleName; }
public String getRuleName() { return this.rule; }
public String getLexicalRuleName() { return this.lexicalRule; }
public String getTreeRuleName() { return this.treeRule; }
public boolean isLexicalRule() { return this.isLexicalRule; }
public void addTestCase(gUnitTestInput input, AbstractTest expect) {
if ( input!=null && expect!=null ) {
/*
* modified by shaoting
* if rule is null, use lexRule name
*/
//expect.setTestedRuleName(this.rule);
expect.setTestedRuleName(this.rule ==null ? this.lexicalRule : this.rule);
expect.setTestCaseIndex(this.testSuites.size());
this.testSuites.put(input, expect);
}
}
}

View File

@ -0,0 +1,51 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui;
import org.antlr.gunit.swingui.model.ITestCaseInput;
import java.awt.event.ActionListener;
import javax.swing.JComponent;
/**
*
* @author scai
*/
public abstract class AbstractInputEditor {
protected ITestCaseInput input;
public void setInput(ITestCaseInput input) {
this.input = input;
}
protected JComponent comp;
public JComponent getControl() { return comp; }
abstract public void addActionListener(ActionListener l) ;
}

View File

@ -0,0 +1,35 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui;
import java.awt.Component;
public interface IController {
public Object getModel() ;
public Component getView() ;
}

View File

@ -0,0 +1,83 @@
package org.antlr.gunit.swingui;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
public class ImageFactory {
private static ImageFactory singleton ;
public static ImageFactory getSingleton() {
if(singleton == null) singleton = new ImageFactory();
return singleton;
}
private ImageFactory() {
ACCEPT = getImage("accept.png");
ADD = getImage("add.png");
DELETE = getImage("delete24.png");
TEXTFILE = getImage("textfile24.png");
TEXTFILE16 = getImage("textfile16.png");
ADDFILE = getImage("addfile24.png");
WINDOW16 = getImage("windowb16.png");
FAV16 = getImage("favb16.png");
SAVE = getImage("floppy24.png");
OPEN = getImage("folder24.png");
EDIT16 = getImage("edit16.png");
FILE16 = getImage("file16.png");
RUN_PASS = getImage("runpass.png");
RUN_FAIL = getImage("runfail.png");
TESTSUITE = getImage("testsuite.png");
TESTGROUP = getImage("testgroup.png");
TESTGROUPX = getImage("testgroupx.png");
NEXT = getImage("next24.png");
}
private ImageIcon getImage(String name) {
name = IMG_DIR + name;
try {
final ClassLoader loader = ImageFactory.class.getClassLoader();
final InputStream in = loader.getResourceAsStream(name);
final byte[] data = new byte[in.available()];
in.read(data);
in.close();
return new ImageIcon(data);
} catch (IOException ex) {
System.err.println("Can't load image file: " + name);
System.exit(1);
} catch(RuntimeException e) {
System.err.println("Can't load image file: " + name);
System.exit(1);
}
return null;
}
private static final String IMG_DIR = "org/antlr/gunit/swingui/images/";
public ImageIcon ACCEPT;
public ImageIcon ADD;
public ImageIcon DELETE;
public ImageIcon TEXTFILE ;
public ImageIcon ADDFILE;
public ImageIcon TEXTFILE16 ;
public ImageIcon WINDOW16;
public ImageIcon FAV16;
public ImageIcon SAVE ;
public ImageIcon OPEN ;
public ImageIcon EDIT16;
public ImageIcon FILE16;
public ImageIcon NEXT;
public ImageIcon RUN_PASS;
public ImageIcon RUN_FAIL;
public ImageIcon TESTSUITE;
public ImageIcon TESTGROUP ;
public ImageIcon TESTGROUPX;
}

View File

@ -0,0 +1,160 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui;
import javax.swing.event.ListDataListener;
import org.antlr.gunit.swingui.model.Rule;
import org.antlr.gunit.swingui.ImageFactory;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionListener;
import org.antlr.gunit.swingui.model.TestSuite;
public class RuleListController implements IController {
/* Sub-controls */
private final JList list = new JList();
private final JScrollPane scroll = new JScrollPane( list,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
/* Model */
private ListModel model = null;
private TestSuite testSuite = null;
public RuleListController() {
this.initComponents();
}
public JScrollPane getView() {
return scroll;
}
private void setTestSuite(TestSuite newTestSuite) {
testSuite = newTestSuite;
model = new RuleListModel();
list.setModel(model);
}
public void initialize(TestSuite ts) {
setTestSuite(ts);
if(model.getSize() > 0) list.setSelectedIndex(0);
list.updateUI();
}
/**
* Initialize view.
*/
private void initComponents() {
scroll.setViewportBorder(BorderFactory.createEtchedBorder());
scroll.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEmptyBorder(), "Rules"));
scroll.setOpaque(false);
list.setOpaque(false);
list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
list.setLayoutOrientation(JList.VERTICAL);
list.setCellRenderer(new RuleListItemRenderer());
}
public void setListSelectionListener(ListSelectionListener l) {
this.list.addListSelectionListener(l);
}
public Object getModel() {
return model;
}
/* ITEM RENDERER */
private class RuleListItemRenderer extends JLabel implements ListCellRenderer{
public RuleListItemRenderer() {
this.setPreferredSize(new Dimension(50, 18));
}
public Component getListCellRendererComponent(
JList list, Object value, int index,
boolean isSelected, boolean hasFocus) {
if(value instanceof Rule) {
final Rule item = (Rule) value;
setText(item.toString());
setForeground(list.getForeground());
setIcon(item.getNotEmpty() ? ImageFactory.getSingleton().FAV16 : null);
if(list.getSelectedValue() == item ) {
setBackground(Color.LIGHT_GRAY);
setOpaque(true);
} else {
setOpaque(false);
}
} else {
this.setText("Error!");
}
return this;
}
}
private class RuleListModel implements ListModel {
public RuleListModel() {
if(testSuite == null)
throw new NullPointerException("Null test suite");
}
public int getSize() {
return testSuite.getRuleCount();
}
public Object getElementAt(int index) {
return testSuite.getRule(index);
}
public void addListDataListener(ListDataListener l) {}
public void removeListDataListener(ListDataListener l) {}
}
}

View File

@ -0,0 +1,240 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTree;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import org.antlr.gunit.swingui.ImageFactory;
import org.antlr.gunit.swingui.model.*;
/**
*
* @author scai
*/
public class RunnerController implements IController {
/* MODEL */
//private TestSuite testSuite;
/* VIEW */
private RunnerView view = new RunnerView();
public class RunnerView extends JPanel {
private JTextArea textArea = new JTextArea();
private JTree tree = new JTree();
private JScrollPane scroll = new JScrollPane(tree,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
public void initComponents() {
//textArea.setOpaque(false);
tree.setOpaque(false);
scroll.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
scroll.setOpaque(false);
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
this.add(scroll);
this.setBorder(BorderFactory.createEmptyBorder());
this.setOpaque(false);
}
};
public RunnerController() {
}
public Object getModel() {
return null;
}
public Component getView() {
return view;
}
public void update() {
view.initComponents();
}
public void OnShowSuiteResult(TestSuite suite) {
update();
view.tree.setModel(new RunnerTreeModel(suite));
view.tree.setCellRenderer(new RunnerTreeRenderer());
}
public void OnShowRuleResult(Rule rule) {
update();
/*
StringBuffer result = new StringBuffer();
result.append("Testing result for rule: " + rule.getName());
result.append("\n--------------------\n\n");
for(TestCase testCase: rule.getTestCases()){
result.append(testCase.isPass() ? "PASS" : "FAIL");
result.append("\n");
}
result.append("\n--------------------\n");
view.textArea.setText(result.toString());
*/
}
private class TestSuiteTreeNode extends DefaultMutableTreeNode {
private TestSuite data ;
public TestSuiteTreeNode(TestSuite suite) {
super(suite.getGrammarName());
for(int i=0; i<suite.getRuleCount(); ++i) {
final Rule rule = suite.getRule(i);
if(rule.getNotEmpty()) this.add(new TestGroupTreeNode(rule));
}
data = suite;
}
@Override
public String toString() {
return String.format("%s (%d test groups)",
data.getGrammarName(),
this.getChildCount());
}
} ;
private class TestGroupTreeNode extends DefaultMutableTreeNode {
private Rule data;
private boolean hasFail = false;
private TestGroupTreeNode(Rule rule) {
super(rule.getName());
for(TestCase tc: rule.getTestCases()) {
this.add(new TestCaseTreeNode(tc));
}
data = rule;
}
@Override
public String toString() {
int iPass = 0;
int iFail = 0;
for(TestCase tc: data.getTestCases()) {
if(tc.isPass())
++iPass;
else
++iFail;
}
hasFail = iFail > 0;
return String.format("%s (pass %d, fail %d)",
data.getName(), iPass, iFail);
}
} ;
private class TestCaseTreeNode extends DefaultMutableTreeNode {
private TestCase data;
private TestCaseTreeNode(TestCase tc) {
super(tc.toString());
data = tc;
}
} ;
private class RunnerTreeModel extends DefaultTreeModel {
public RunnerTreeModel(TestSuite testSuite) {
super(new TestSuiteTreeNode(testSuite));
}
}
private class RunnerTreeRenderer implements TreeCellRenderer {
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
JLabel label = new JLabel();
if(value instanceof TestSuiteTreeNode) {
label.setText(value.toString());
label.setIcon(ImageFactory.getSingleton().TESTSUITE);
} else if(value instanceof TestGroupTreeNode) {
TestGroupTreeNode node = (TestGroupTreeNode) value;
label.setText(value.toString());
label.setIcon( node.hasFail ?
ImageFactory.getSingleton().TESTGROUPX :
ImageFactory.getSingleton().TESTGROUP);
} else if(value instanceof TestCaseTreeNode) {
TestCaseTreeNode node = (TestCaseTreeNode) value;
label.setIcon( (node.data.isPass())?
ImageFactory.getSingleton().RUN_PASS :
ImageFactory.getSingleton().RUN_FAIL);
label.setText(value.toString());
} else {
throw new IllegalArgumentException(
"Invalide tree node type + " + value.getClass().getName());
}
return label;
}
}
}

View File

@ -0,0 +1,93 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
public class StatusBarController implements IController {
private final JPanel panel = new JPanel();
private final JLabel labelText = new JLabel("Ready");
private final JLabel labelRuleName = new JLabel("");
private final JProgressBar progress = new JProgressBar();
public StatusBarController() {
initComponents();
}
private void initComponents() {
labelText.setPreferredSize(new Dimension(300, 20));
labelText.setHorizontalTextPosition(JLabel.LEFT);
progress.setPreferredSize(new Dimension(100, 15));
final JLabel labRuleHint = new JLabel("Rule: ");
FlowLayout layout = new FlowLayout();
layout.setAlignment(FlowLayout.LEFT);
panel.setLayout(layout);
panel.add(labelText);
panel.add(progress);
panel.add(labRuleHint);
panel.add(labelRuleName);
panel.setOpaque(false);
panel.setBorder(javax.swing.BorderFactory.createEmptyBorder());
}
public void setText(String text) {
labelText.setText(text);
}
public void setRule(String name) {
this.labelRuleName.setText(name);
}
public Object getModel() {
throw new UnsupportedOperationException("Not supported yet.");
}
public JPanel getView() {
return panel;
}
public void setProgressIndetermined(boolean value) {
this.progress.setIndeterminate(value);
}
public void setProgress(int value) {
this.progress.setIndeterminate(false);
this.progress.setValue(value);
}
}

View File

@ -0,0 +1,633 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui;
import org.antlr.gunit.swingui.model.*;
import org.antlr.gunit.swingui.ImageFactory;
import java.awt.*;
import java.awt.event.*;
import java.util.HashMap;
import javax.swing.*;
import javax.swing.event.*;
/**
*
* @author scai
*/
public class TestCaseEditController implements IController {
private JPanel view = new JPanel();
private JScrollPane scroll;
private JPanel paneDetail;
private AbstractEditorPane paneDetailInput, paneDetailOutput;
private JToolBar toolbar;
private JList listCases;
private ListModel listModel ;
public ActionListener onTestCaseNumberChange;
/* EDITORS */
private InputFileEditor editInputFile;
private InputStringEditor editInputString;
private InputMultiEditor editInputMulti;
private OutputResultEditor editOutputResult;
private OutputAstEditor editOutputAST;
private OutputStdEditor editOutputStd;
private OutputReturnEditor editOutputReturn;
private JComboBox comboInputType, comboOutputType;
/* TYPE NAME */
private static final String IN_TYPE_STRING = "Single-line Text";
private static final String IN_TYPE_MULTI = "Multi-line Text";
private static final String IN_TYPE_FILE = "Disk File";
private static final String OUT_TYPE_BOOL = "OK or Fail";
private static final String OUT_TYPE_AST = "AST";
private static final String OUT_TYPE_STD = "Standard Output";
private static final String OUT_TYPE_RET = "Return Value";
private static final String DEFAULT_IN_SCRIPT = "";
private static final String DEFAULT_OUT_SCRIPT = "";
private static final Object[] INPUT_TYPE = {
IN_TYPE_STRING, IN_TYPE_MULTI, IN_TYPE_FILE
};
private static final Object[] OUTPUT_TYPE = {
OUT_TYPE_BOOL, OUT_TYPE_AST, OUT_TYPE_STD, OUT_TYPE_RET
};
/* SIZE */
private static final int TEST_CASE_DETAIL_WIDTH = 300;
private static final int TEST_EDITOR_WIDTH = 280;
private static final int TEST_CASE_DETAIL_HEIGHT = 250;
private static final int TEST_EDITOR_HEIGHT = 120;
/* MODEL */
private Rule currentRule = null;
private TestCase currentTestCase = null;
/* END OF MODEL*/
private static final HashMap<Class<?>, String> TypeNameTable;
static {
TypeNameTable = new HashMap<Class<?>, String> ();
TypeNameTable.put(TestCaseInputString.class, IN_TYPE_STRING);
TypeNameTable.put(TestCaseInputMultiString.class, IN_TYPE_MULTI);
TypeNameTable.put(TestCaseInputFile.class, IN_TYPE_FILE);
TypeNameTable.put(TestCaseOutputResult.class, OUT_TYPE_BOOL);
TypeNameTable.put(TestCaseOutputAST.class, OUT_TYPE_AST);
TypeNameTable.put(TestCaseOutputStdOut.class, OUT_TYPE_STD);
TypeNameTable.put(TestCaseOutputReturn.class, OUT_TYPE_RET);
}
//private WorkSpaceView owner;
public TestCaseEditController(WorkSpaceView workspace) {
//this.owner = workspace;
initComponents();
}
public TestCaseEditController() {
initComponents();
}
public void OnLoadRule(Rule rule) {
if(rule == null) throw new IllegalArgumentException("Null");
this.currentRule = rule;
this.currentTestCase = null;
this.listModel = rule;
this.listCases.setModel(this.listModel);
}
public void setCurrentTestCase(TestCase testCase) {
if(testCase == null) throw new IllegalArgumentException("Null");
this.listCases.setSelectedValue(testCase, true);
this.currentTestCase = testCase;
}
public Rule getCurrentRule() {
return this.currentRule;
}
private void initComponents() {
/* CASE LIST */
listCases = new JList();
listCases.addListSelectionListener(new TestCaseListSelectionListener());
listCases.setCellRenderer(listRenderer);
listCases.setOpaque(false);
scroll = new JScrollPane(listCases);
scroll.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEmptyBorder(), "Test Cases"));
scroll.setOpaque(false);
scroll.setViewportBorder(BorderFactory.createEtchedBorder());
/* CASE DETAIL */
editInputString = new InputStringEditor();
editInputMulti = new InputMultiEditor();
editInputFile = new InputFileEditor();
editOutputResult = new OutputResultEditor();
editOutputAST = new OutputAstEditor();
editOutputStd = new OutputStdEditor();
editOutputReturn = new OutputReturnEditor();
paneDetail = new JPanel();
paneDetail.setBorder(BorderFactory.createEmptyBorder());
paneDetail.setOpaque(false);
comboInputType = new JComboBox(INPUT_TYPE);
comboInputType.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
OnInputTestCaseTypeChanged(comboInputType.getSelectedItem());
}
});
comboOutputType = new JComboBox(OUTPUT_TYPE);
comboOutputType.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
OnOutputTestCaseTypeChanged(comboOutputType.getSelectedItem());
}
});
paneDetailInput = new InputEditorPane(comboInputType);
paneDetailOutput = new OutputEditorPane(comboOutputType);
BoxLayout layout = new BoxLayout(paneDetail, BoxLayout.PAGE_AXIS);
paneDetail.setLayout(layout);
paneDetail.add(this.paneDetailInput);
paneDetail.add(this.paneDetailOutput);
/* TOOLBAR */
toolbar = new JToolBar("Edit TestCases", JToolBar.VERTICAL);
toolbar.setFloatable(false);
toolbar.add(new AddTestCaseAction());
toolbar.add(new RemoveTestCaseAction());
/* COMPOSITE */
view.setLayout(new BorderLayout());
view.setBorder(BorderFactory.createEmptyBorder());
view.setOpaque(false);
view.add(toolbar, BorderLayout.WEST);
view.add(scroll, BorderLayout.CENTER);
view.add(paneDetail, BorderLayout.EAST);
}
private void updateInputEditor() {
JComponent editor = null;
if(currentTestCase != null ) {
ITestCaseInput input = this.currentTestCase.getInput();
if(input instanceof TestCaseInputString) {
this.editInputString.setText(input.getScript());
editor = this.editInputString;
comboInputType.setSelectedItem(IN_TYPE_STRING);
} else if(input instanceof TestCaseInputMultiString) {
this.editInputMulti.setText(input.getScript());
editor = this.editInputMulti.getView();
comboInputType.setSelectedItem(IN_TYPE_MULTI);
} else if(input instanceof TestCaseInputFile) {
this.editInputFile.setText(input.getScript());
editor = this.editInputFile;
comboInputType.setSelectedItem(IN_TYPE_FILE);
} else {
throw new Error("Wrong type");
}
}
paneDetailInput.setEditor(editor);
}
private void updateOutputEditor() {
JComponent editor = null;
if(currentTestCase != null) {
ITestCaseOutput output = this.currentTestCase.getOutput();
if(output instanceof TestCaseOutputAST) {
this.editOutputAST.setText(output.getScript());
editor = this.editOutputAST.getView();
comboOutputType.setSelectedItem(OUT_TYPE_AST);
} else if(output instanceof TestCaseOutputResult) {
this.editOutputResult.setValue(output.getScript());
editor = this.editOutputResult;
comboOutputType.setSelectedItem(OUT_TYPE_BOOL);
} else if(output instanceof TestCaseOutputStdOut) {
this.editOutputStd.setText(output.getScript());
editor = this.editOutputStd.getView();
comboOutputType.setSelectedItem(OUT_TYPE_STD);
} else if(output instanceof TestCaseOutputReturn) {
this.editOutputReturn.setText(output.getScript());
editor = this.editOutputReturn.getView();
comboOutputType.setSelectedItem(OUT_TYPE_RET);
} else {
throw new Error("Wrong type");
}
}
this.paneDetailOutput.setEditor(editor);
}
private void OnInputTestCaseTypeChanged(Object inputTypeStr) {
if(this.currentTestCase != null) {
ITestCaseInput input ;
if(inputTypeStr == IN_TYPE_STRING) {
input = new TestCaseInputString(DEFAULT_IN_SCRIPT);
} else if(inputTypeStr == IN_TYPE_MULTI) {
input = new TestCaseInputMultiString(DEFAULT_IN_SCRIPT);
} else if(inputTypeStr == IN_TYPE_FILE) {
input = new TestCaseInputFile(DEFAULT_IN_SCRIPT);
} else {
throw new Error("Wrong Type");
}
if(input.getClass().equals(this.currentTestCase.getInput().getClass()))
return ;
this.currentTestCase.setInput(input);
}
this.updateInputEditor();
}
private void OnOutputTestCaseTypeChanged(Object outputTypeStr) {
if(this.currentTestCase != null) {
ITestCaseOutput output ;
if(outputTypeStr == OUT_TYPE_AST) {
output = new TestCaseOutputAST(DEFAULT_OUT_SCRIPT);
} else if(outputTypeStr == OUT_TYPE_BOOL) {
output = new TestCaseOutputResult(false);
} else if(outputTypeStr == OUT_TYPE_STD) {
output = new TestCaseOutputStdOut(DEFAULT_OUT_SCRIPT);
} else if(outputTypeStr == OUT_TYPE_RET) {
output = new TestCaseOutputReturn(DEFAULT_OUT_SCRIPT);
} else {
throw new Error("Wrong Type");
}
if(output.getClass().equals(this.currentTestCase.getOutput().getClass()))
return ;
this.currentTestCase.setOutput(output);
}
this.updateOutputEditor();
}
private void OnTestCaseSelected(TestCase testCase) {
//if(testCase == null) throw new RuntimeException("Null TestCase");
this.currentTestCase = testCase;
updateInputEditor();
updateOutputEditor();
}
private void OnAddTestCase() {
if(currentRule == null) return;
final TestCase newCase = new TestCase(
new TestCaseInputString(""),
new TestCaseOutputResult(true));
this.currentRule.addTestCase(newCase);
setCurrentTestCase(newCase);
this.listCases.setSelectedValue(newCase, true);
this.listCases.updateUI();
this.OnTestCaseSelected(newCase);
this.onTestCaseNumberChange.actionPerformed(null);
}
private void OnRemoveTestCase() {
if(currentTestCase == null) return;
currentRule.removeElement(currentTestCase);
listCases.updateUI();
final TestCase nextActiveCase = listCases.isSelectionEmpty() ?
null : (TestCase) listCases.getSelectedValue() ;
OnTestCaseSelected(nextActiveCase);
this.onTestCaseNumberChange.actionPerformed(null);
}
public Object getModel() {
return currentRule;
}
public Component getView() {
return view;
}
/* EDITOR CONTAINER */
abstract public class AbstractEditorPane extends JPanel {
private JComboBox combo;
private JComponent editor;
private String title;
private JLabel placeHolder = new JLabel();
public AbstractEditorPane(JComboBox comboBox, String title) {
this.combo = comboBox;
this.editor = placeHolder;
this.title = title;
this.initComponents();
}
private void initComponents() {
placeHolder.setPreferredSize(new Dimension(
TEST_CASE_DETAIL_WIDTH, TEST_CASE_DETAIL_HEIGHT));
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
this.add(combo, BorderLayout.NORTH);
this.add(editor, BorderLayout.CENTER);
this.setOpaque(false);
this.setBorder(BorderFactory.createTitledBorder(title));
this.setPreferredSize(new Dimension(
TEST_CASE_DETAIL_WIDTH, TEST_CASE_DETAIL_HEIGHT));
}
public void setEditor(JComponent newEditor) {
if(newEditor == null) newEditor = placeHolder;
this.remove(editor);
this.add(newEditor);
this.editor = newEditor;
this.updateUI();
}
}
public class InputEditorPane extends AbstractEditorPane {
public InputEditorPane(JComboBox comboBox) {
super(comboBox, "Input");
}
}
public class OutputEditorPane extends AbstractEditorPane {
public OutputEditorPane(JComboBox comboBox) {
super(comboBox, "Output");
}
}
/* INPUT EDITORS */
public class InputStringEditor extends JTextField implements CaretListener {
public InputStringEditor() {
super();
this.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
this.addCaretListener(this);
}
public void caretUpdate(CaretEvent arg0) {
currentTestCase.getInput().setScript(getText());
listCases.updateUI();
}
}
public class InputMultiEditor implements CaretListener {
private JTextArea textArea = new JTextArea(20, 30);
private JScrollPane scroll = new JScrollPane(textArea,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
public InputMultiEditor() {
super();
scroll.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
textArea.addCaretListener(this);
}
public void caretUpdate(CaretEvent arg0) {
currentTestCase.getInput().setScript(getText());
listCases.updateUI();
}
public String getText() {
return textArea.getText();
}
public void setText(String text) {
textArea.setText(text);
}
public JComponent getView() {
return scroll;
}
}
public class InputFileEditor extends InputStringEditor {};
public class OutputResultEditor extends JPanel implements ActionListener {
private JToggleButton tbFail, tbOk;
public OutputResultEditor() {
super();
tbFail = new JToggleButton("Fail");
tbOk = new JToggleButton("OK");
ButtonGroup group = new ButtonGroup();
group.add(tbFail);
group.add(tbOk);
this.add(tbFail);
this.add(tbOk);
this.tbFail.addActionListener(this);
this.tbOk.addActionListener(this);
this.setPreferredSize(
new Dimension(TEST_EDITOR_WIDTH, 100));
}
public void actionPerformed(ActionEvent e) {
TestCaseOutputResult output =
(TestCaseOutputResult) currentTestCase.getOutput();
if(e.getSource() == tbFail) {
output.setScript(false);
} else {
output.setScript(true);
}
listCases.updateUI();
}
public void setValue(String value) {
if(TestCaseOutputResult.OK.equals(value)) {
this.tbOk.setSelected(true);
} else {
this.tbFail.setSelected(true);
}
}
}
public class OutputAstEditor implements CaretListener {
private JTextArea textArea = new JTextArea(20, 30);
private JScrollPane scroll = new JScrollPane(textArea,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
public OutputAstEditor() {
super();
scroll.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
textArea.addCaretListener(this);
}
public void caretUpdate(CaretEvent arg0) {
currentTestCase.getOutput().setScript(getText());
listCases.updateUI();
}
public void setText(String text) {
this.textArea.setText(text);
}
public String getText() {
return this.textArea.getText();
}
public JScrollPane getView() {
return this.scroll;
}
}
public class OutputStdEditor extends OutputAstEditor {}
public class OutputReturnEditor extends OutputAstEditor {}
/* EVENT HANDLERS */
private class TestCaseListSelectionListener implements ListSelectionListener {
public void valueChanged(ListSelectionEvent e) {
if(e.getValueIsAdjusting()) return;
final JList list = (JList) e.getSource();
final TestCase value = (TestCase) list.getSelectedValue();
if(value != null) OnTestCaseSelected(value);
}
}
/* ACTIONS */
private class AddTestCaseAction extends AbstractAction {
public AddTestCaseAction() {
super("Add", ImageFactory.getSingleton().ADD);
putValue(SHORT_DESCRIPTION, "Add a gUnit test case.");
}
public void actionPerformed(ActionEvent e) {
OnAddTestCase();
}
}
private class RemoveTestCaseAction extends AbstractAction {
public RemoveTestCaseAction() {
super("Remove", ImageFactory.getSingleton().DELETE);
putValue(SHORT_DESCRIPTION, "Remove a gUnit test case.");
}
public void actionPerformed(ActionEvent e) {
OnRemoveTestCase();
}
}
/* CELL RENDERERS */
private static TestCaseListRenderer listRenderer
= new TestCaseListRenderer();
private static class TestCaseListRenderer implements ListCellRenderer {
private static Font IN_FONT = new Font("mono", Font.PLAIN, 12);
private static Font OUT_FONT = new Font("default", Font.BOLD, 12);
public static String clamp(String text, int len) {
if(text.length() > len) {
return text.substring(0, len - 3).concat("...");
} else {
return text;
}
}
public static String clampAtNewLine(String text) {
int pos = text.indexOf('\n');
if(pos >= 0) {
return text.substring(0, pos).concat("...");
} else {
return text;
}
}
public Component getListCellRendererComponent(
JList list, Object value, int index,
boolean isSelected, boolean hasFocus) {
final JPanel pane = new JPanel();
if (value instanceof TestCase) {
final TestCase item = (TestCase) value;
// create components
final JLabel labIn = new JLabel(
clamp(clampAtNewLine(item.getInput().getScript()), 18));
final JLabel labOut = new JLabel(
clamp(clampAtNewLine(item.getOutput().getScript()), 18));
labOut.setFont(OUT_FONT);
labIn.setFont(IN_FONT);
labIn.setIcon(item.getInput() instanceof TestCaseInputFile ?
ImageFactory.getSingleton().FILE16 :
ImageFactory.getSingleton().EDIT16);
pane.setBorder(BorderFactory.createEtchedBorder());
pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
pane.add(labIn);
pane.add(labOut);
pane.setBackground(isSelected ? Color.LIGHT_GRAY : Color.WHITE);
}
return pane;
}
}
}

View File

@ -0,0 +1,61 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui;
import java.io.IOException;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
public class Tool {
public static void main(String[] args) throws IOException {
if(args.length == 1 && "-version".equals(args[0])) {
System.out.println("gUnitEditor Swing GUI\nby Shaoting Cai\n");
} else {
showUI();
}
}
private static void showUI() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
WorkSpaceController control = new WorkSpaceController();
control.show();
}
});
}
}

View File

@ -0,0 +1,288 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.antlr.gunit.swingui.runner.gUnitAdapter;
import java.awt.*;
import java.io.IOException;
import org.antlr.gunit.swingui.model.*;
import org.antlr.gunit.swingui.ImageFactory;
import java.awt.event.*;
import java.io.File;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.filechooser.FileFilter;
/**
*
* @author scai
*/
public class WorkSpaceController implements IController{
/* MODEL */
private TestSuite currentTestSuite;
private String testSuiteFileName = null; // path + file
/* VIEW */
private final WorkSpaceView view = new WorkSpaceView();
/* SUB-CONTROL */
private final RunnerController runner = new RunnerController();
public WorkSpaceController() {
view.resultPane = (JPanel) runner.getView();
view.initComponents();
this.initEventHandlers();
this.initToolbar();
}
public void show() {
this.view.setTitle("gUnitEditor");
this.view.setVisible(true);
this.view.pack();
}
public Component getEmbeddedView() {
return view.paneEditor.getView();
}
private void initEventHandlers() {
this.view.tabEditors.addChangeListener(new TabChangeListener());
this.view.listRules.setListSelectionListener(new RuleListSelectionListener());
this.view.paneEditor.onTestCaseNumberChange = new ActionListener() {
public void actionPerformed(ActionEvent e) {
view.listRules.getView().updateUI();
}
};
}
private void OnCreateTest() {
JFileChooser jfc = new JFileChooser();
jfc.setDialogTitle("Create test suite from grammar");
jfc.setDialogType(JFileChooser.OPEN_DIALOG);
jfc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() || f.getName().toLowerCase().endsWith(TestSuiteFactory.GRAMMAR_EXT);
}
@Override
public String getDescription() {
return "ANTLR grammar file (*.g)";
}
});
if( jfc.showOpenDialog(view) != JFileChooser.APPROVE_OPTION ) return;
view.paneStatus.setProgressIndetermined(true);
final File grammarFile = jfc.getSelectedFile();
currentTestSuite = TestSuiteFactory.createTestSuite(grammarFile);
view.listRules.initialize(currentTestSuite);
view.tabEditors.setSelectedIndex(0);
view.paneStatus.setText("Grammar: " + currentTestSuite.getGrammarName());
view.paneStatus.setProgressIndetermined(false);
testSuiteFileName = null;
}
private void OnSaveTest() {
TestSuiteFactory.saveTestSuite(currentTestSuite);
JOptionPane.showMessageDialog(view, "Testsuite saved to:\n" + currentTestSuite.getTestSuiteFile().getAbsolutePath());
}
private void OnOpenTest() {
JFileChooser jfc = new JFileChooser();
jfc.setDialogTitle("Open existing gUnit test suite");
jfc.setDialogType(JFileChooser.OPEN_DIALOG);
jfc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() || f.getName().toLowerCase().endsWith(TestSuiteFactory.TEST_SUITE_EXT);
}
@Override
public String getDescription() {
return "ANTLR unit test file (*.gunit)";
}
});
if( jfc.showOpenDialog(view) != JFileChooser.APPROVE_OPTION ) return;
final File testSuiteFile = jfc.getSelectedFile();
try {
testSuiteFileName = testSuiteFile.getCanonicalPath();
} catch (IOException e) {
throw new RuntimeException(e);
}
view.paneStatus.setProgressIndetermined(true);
currentTestSuite = TestSuiteFactory.loadTestSuite(testSuiteFile);
view.listRules.initialize(currentTestSuite);
view.paneStatus.setText(currentTestSuite.getGrammarName());
view.tabEditors.setSelectedIndex(0);
view.paneStatus.setProgressIndetermined(false);
}
private void OnSelectRule(Rule rule) {
if(rule == null) throw new IllegalArgumentException("Null");
this.view.paneEditor.OnLoadRule(rule);
this.view.paneStatus.setRule(rule.getName());
// run result
this.runner.OnShowRuleResult(rule);
}
private void OnSelectTextPane() {
Thread worker = new Thread () {
@Override
public void run() {
view.paneStatus.setProgressIndetermined(true);
view.txtEditor.setText(
TestSuiteFactory.getScript(currentTestSuite));
view.paneStatus.setProgressIndetermined(false);
}
};
worker.start();
}
private void OnRunTest() {
// save before run
TestSuiteFactory.saveTestSuite(currentTestSuite);
// run
try {
final gUnitAdapter adapter = new gUnitAdapter(currentTestSuite);
if(currentTestSuite == null) return;
adapter.run();
runner.OnShowSuiteResult(currentTestSuite);
view.tabEditors.addTab("Test Result", ImageFactory.getSingleton().FILE16, runner.getView());
view.tabEditors.setSelectedComponent(runner.getView());
} catch (Exception ex) {
JOptionPane.showMessageDialog(view, "Fail to run test:\n" + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
private void initToolbar() {
view.toolbar.add(new JButton(new CreateAction()));
view.toolbar.add(new JButton(new OpenAction()));
view.toolbar.add(new JButton(new SaveAction()));
view.toolbar.add(new JButton(new RunAction()));
}
public Object getModel() {
throw new UnsupportedOperationException("Not supported yet.");
}
public Component getView() {
return view;
}
/** Event handler for rule list selection. */
private class RuleListSelectionListener implements ListSelectionListener {
public void valueChanged(ListSelectionEvent event) {
if(event.getValueIsAdjusting()) return;
final JList list = (JList) event.getSource();
final Rule rule = (Rule) list.getSelectedValue();
if(rule != null) OnSelectRule(rule);
}
}
/** Event handler for switching between editor view and script view. */
public class TabChangeListener implements ChangeListener {
public void stateChanged(ChangeEvent evt) {
if(view.tabEditors.getSelectedIndex() == 1) {
OnSelectTextPane();
}
}
}
/** Create test suite action. */
private class CreateAction extends AbstractAction {
public CreateAction() {
super("Create", ImageFactory.getSingleton().ADDFILE);
putValue(SHORT_DESCRIPTION, "Create a test suite from an ANTLR grammar");
}
public void actionPerformed(ActionEvent e) {
OnCreateTest();
}
}
/** Save test suite action. */
private class SaveAction extends AbstractAction {
public SaveAction() {
super("Save", ImageFactory.getSingleton().SAVE);
putValue(SHORT_DESCRIPTION, "Save the test suite");
}
public void actionPerformed(ActionEvent e) {
OnSaveTest();
}
}
/** Open test suite action. */
private class OpenAction extends AbstractAction {
public OpenAction() {
super("Open", ImageFactory.getSingleton().OPEN);
putValue(SHORT_DESCRIPTION, "Open an existing test suite");
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(
KeyEvent.VK_O, InputEvent.CTRL_MASK));
}
public void actionPerformed(ActionEvent e) {
OnOpenTest();
}
}
/** Run test suite action. */
private class RunAction extends AbstractAction {
public RunAction() {
super("Run", ImageFactory.getSingleton().NEXT);
putValue(SHORT_DESCRIPTION, "Run the current test suite");
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(
KeyEvent.VK_R, InputEvent.CTRL_MASK));
}
public void actionPerformed(ActionEvent e) {
OnRunTest();
}
}
}

View File

@ -0,0 +1,103 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.
*/
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.antlr.gunit.swingui;
import org.antlr.gunit.swingui.ImageFactory;
import java.awt.*;
import javax.swing.*;
/**
*
* @author scai
*/
public class WorkSpaceView extends JFrame {
protected JSplitPane splitListClient ;
protected JTabbedPane tabEditors;
protected JPanel paneToolBar;
protected StatusBarController paneStatus;
protected TestCaseEditController paneEditor;
protected JToolBar toolbar;
protected JTextArea txtEditor;
protected RuleListController listRules;
protected JMenuBar menuBar;
protected JScrollPane scrollCode;
protected JPanel resultPane;
protected JButton btnOpenGrammar;
public WorkSpaceView() {
super();
}
protected void initComponents() {
this.paneEditor = new TestCaseEditController(this);
this.paneStatus = new StatusBarController();
this.toolbar = new JToolBar();
this.toolbar.setBorder(BorderFactory.createEmptyBorder());
this.toolbar.setFloatable(false);
this.toolbar.setBorder(BorderFactory.createEmptyBorder());
this.txtEditor = new JTextArea();
this.txtEditor.setLineWrap(false);
this.txtEditor.setFont(new Font("Courier New", Font.PLAIN, 13));
this.scrollCode = new JScrollPane(txtEditor,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
this.scrollCode.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
this.tabEditors = new JTabbedPane();
this.tabEditors.addTab("Case Editor", ImageFactory.getSingleton().TEXTFILE16, this.paneEditor.getView());
this.tabEditors.addTab("Script Source", ImageFactory.getSingleton().WINDOW16, this.scrollCode);
this.listRules = new RuleListController();
this.splitListClient = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT,
this.listRules.getView(), this.tabEditors);
this.splitListClient.setResizeWeight(0.4);
this.splitListClient.setBorder(BorderFactory.createEmptyBorder());
this.getContentPane().add(this.toolbar, BorderLayout.NORTH);
this.getContentPane().add(this.splitListClient, BorderLayout.CENTER);
this.getContentPane().add(this.paneStatus.getView(), BorderLayout.SOUTH);
// self
this.setPreferredSize(new Dimension(900, 500));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

View File

@ -0,0 +1,34 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui.model;
public interface ITestCaseInput {
public void setScript(String script);
public String getScript();
}

View File

@ -0,0 +1,42 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.
*/
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.antlr.gunit.swingui.model;
/**
*
* @author scai
*/
public interface ITestCaseOutput {
public void setScript(String script);
public String getScript() ;
}

View File

@ -0,0 +1,71 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui.model;
import java.util.ArrayList;
import java.util.List;
import javax.swing.DefaultListModel;
/**
* ANTLR v3 Rule Information.
* @author scai
*/
public class Rule extends DefaultListModel {
private String name;
public Rule(String name) {
this.name = name;
}
public String getName() { return name; }
public boolean getNotEmpty() {
return !this.isEmpty();
}
@Override
public String toString() {
return this.name;
}
public void addTestCase(TestCase newItem) {
this.addElement(newItem);
}
// for string template
public List<TestCase> getTestCases() {
List<TestCase> result = new ArrayList<TestCase>();
for(int i=0; i<this.size(); i++) {
result.add((TestCase)this.getElementAt(i));
}
return result;
}
}

View File

@ -0,0 +1,75 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui.model;
public class TestCase {
private ITestCaseInput input;
private ITestCaseOutput output;
private boolean pass;
public boolean isPass() {
return pass;
}
public void setPass(boolean value) {
pass = value;
}
public ITestCaseInput getInput() {
return this.input;
}
public ITestCaseOutput getOutput() {
return this.output;
}
public TestCase(ITestCaseInput input, ITestCaseOutput output) {
this.input = input;
this.output = output;
}
@Override
public String toString() {
return String.format("[%s]->[%s]", input.getScript(), output.getScript());
}
public void setInput(ITestCaseInput in) {
this.input = in;
}
public void setOutput(ITestCaseOutput out) {
this.output = out;
}
public static String convertPreservedChars(String input) {
//return input.replace("\"", "\\\"");
return input;
}
}

View File

@ -0,0 +1,62 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui.model;
import javax.swing.JComponent;
import javax.swing.JLabel;
/**
*
* @author scai
*/
public class TestCaseInputFile implements ITestCaseInput {
private String fileName;
public TestCaseInputFile(String file) {
this.fileName = file;
}
public String getLabel() {
return "FILE:" + fileName;
}
public void setScript(String script) {
this.fileName = script;
}
@Override
public String toString() {
return fileName;
}
public String getScript() {
return this.fileName;
}
}

View File

@ -0,0 +1,57 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui.model;
/**
*
* @author scai
*/
public class TestCaseInputMultiString implements ITestCaseInput {
private String script;
public TestCaseInputMultiString(String text) {
this.script = text;
}
@Override
public String toString() {
return "<<" + TestCase.convertPreservedChars(script) + ">>";
}
public void setScript(String script) {
this.script = script;
}
public String getScript() {
return this.script;
}
}

View File

@ -0,0 +1,59 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui.model;
/**
*
* @author scai
*/
public class TestCaseInputString implements ITestCaseInput {
private String script;
public TestCaseInputString(String text) {
this.script = text;
}
@Override
public String toString() {
return '"' + TestCase.convertPreservedChars(script) + '"';
}
public void setScript(String script) {
this.script = script;
}
public String getScript() {
return this.script;
}
}

View File

@ -0,0 +1,57 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui.model;
/**
*
* @author scai
*/
public class TestCaseOutputAST implements ITestCaseOutput {
private String treeString;
public TestCaseOutputAST(String script) {
this.treeString = script;
}
public void setScript(String script) {
this.treeString = script;
}
public String getScript() {
return this.treeString;
}
@Override
public String toString() {
return String.format(" -> %s", treeString);
}
}

View File

@ -0,0 +1,63 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui.model;
/**
*
* @author scai
*/
public class TestCaseOutputResult implements ITestCaseOutput {
public static String OK = "OK";
public static String FAIL = "FAIL";
private boolean success ;
public TestCaseOutputResult(boolean result) {
this.success = result;
}
@Override
public String toString() {
return getScript();
}
public String getScript() {
return success ? OK : FAIL;
}
public void setScript(boolean value) {
this.success = value;
}
public void setScript(String script) {
success = Boolean.parseBoolean(script);
}
}

View File

@ -0,0 +1,49 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui.model;
public class TestCaseOutputReturn implements ITestCaseOutput {
private String script;
public TestCaseOutputReturn(String text) {
this.script = text;
}
@Override
public String toString() {
return String.format(" returns [%s]", script);
}
public void setScript(String script) {
this.script = script;
}
public String getScript() {
return this.script;
}
}

View File

@ -0,0 +1,53 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui.model;
/**
*
* @author scai
*/
public class TestCaseOutputStdOut implements ITestCaseOutput {
private String script;
public TestCaseOutputStdOut(String text) {
this.script = text;
}
@Override
public String toString() {
return String.format(" -> \"%s\"", script);
}
public void setScript(String script) {
this.script = script;
}
public String getScript() {
return this.script;
}
}

View File

@ -0,0 +1,100 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui.model;
import java.io.*;
import java.util.*;
import org.antlr.runtime.*;
public class TestSuite {
protected List<Rule> rules ;
protected String grammarName ;
protected CommonTokenStream tokens;
protected File testSuiteFile;
protected TestSuite(String gname, File testFile) {
grammarName = gname;
testSuiteFile = testFile;
rules = new ArrayList<Rule>();
}
/* Get the gUnit test suite file name. */
public File getTestSuiteFile() {
return testSuiteFile;
}
public void addRule(Rule currentRule) {
if(currentRule == null) throw new IllegalArgumentException("Null rule");
rules.add(currentRule);
}
// test rule name
public boolean hasRule(Rule rule) {
for(Rule r: rules) {
if(r.getName().equals(rule.getName())) {
return true;
}
}
return false;
}
public int getRuleCount() {
return rules.size();
}
public void setRules(List<Rule> newRules) {
rules.clear();
rules.addAll(newRules);
}
/* GETTERS AND SETTERS */
public void setGrammarName(String name) { grammarName = name;}
public String getGrammarName() { return grammarName; }
public Rule getRule(int index) { return rules.get(index); }
public CommonTokenStream getTokens() { return tokens; }
public void setTokens(CommonTokenStream ts) { tokens = ts; }
public Rule getRule(String name) {
for(Rule rule: rules) {
if(rule.getName().equals(name)) {
return rule;
}
}
return null;
}
// only for stringtemplate use
public List<Rule> getRulesForStringTemplate() {return rules;}
}

View File

@ -0,0 +1,209 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui.model;
import org.antlr.gunit.swingui.parsers.ANTLRv3Lexer;
import org.antlr.gunit.swingui.parsers.ANTLRv3Parser;
import org.antlr.gunit.swingui.parsers.StGUnitLexer;
import org.antlr.gunit.swingui.parsers.StGUnitParser;
import org.antlr.gunit.swingui.runner.TestSuiteAdapter;
import org.antlr.runtime.ANTLRReaderStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.stringtemplate.StringTemplate;
import org.antlr.stringtemplate.StringTemplateGroup;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class TestSuiteFactory {
private static String TEMPLATE_FILE = "org/antlr/gunit/swingui/gunit.stg";
private static StringTemplateGroup templates;
public static final String TEST_SUITE_EXT = ".gunit";
public static final String GRAMMAR_EXT = ".g";
static {
ClassLoader loader = TestSuiteFactory.class.getClassLoader();
InputStream in = loader.getResourceAsStream(TEMPLATE_FILE);
if ( in == null ) {
throw new RuntimeException("internal error: Can't find templates "+TEMPLATE_FILE);
}
Reader rd = new InputStreamReader(in);
templates = new StringTemplateGroup(rd);
}
/**
* Factory method: create a testsuite from ANTLR grammar. Save the test
* suite file in the same directory of the grammar file.
* @param grammarFile ANTLRv3 grammar file.
* @return test suite object
*/
public static TestSuite createTestSuite(File grammarFile) {
if(grammarFile != null && grammarFile.exists() && grammarFile.isFile()) {
final String fileName = grammarFile.getName();
final String grammarName = fileName.substring(0, fileName.lastIndexOf('.'));
final String grammarDir = grammarFile.getParent();
final File testFile = new File(grammarDir + File.separator + grammarName + TEST_SUITE_EXT);
final TestSuite result = new TestSuite(grammarName, testFile);
result.rules = loadRulesFromGrammar(grammarFile);
if(saveTestSuite(result)) {
return result;
} else {
throw new RuntimeException("Can't save test suite file.");
}
} else {
throw new RuntimeException("Invalid grammar file.");
}
}
/* Load rules from an ANTLR grammar file. */
private static List<Rule> loadRulesFromGrammar(File grammarFile) {
// get all the rule names
final List<String> ruleNames = new ArrayList<String>();
try {
final Reader reader = new BufferedReader(new FileReader(grammarFile));
final ANTLRv3Lexer lexer = new ANTLRv3Lexer(new ANTLRReaderStream(reader));
final CommonTokenStream tokens = new CommonTokenStream(lexer);
final ANTLRv3Parser parser = new ANTLRv3Parser(tokens);
parser.rules = ruleNames;
parser.grammarDef();
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
// convert to rule object
final List<Rule> ruleList = new ArrayList<Rule>();
for(String str: ruleNames) {
ruleList.add(new Rule(str));
}
return ruleList;
}
/* Save testsuite to *.gunit file. */
public static boolean saveTestSuite(TestSuite testSuite) {
final String data = getScript(testSuite);
try {
FileWriter fw = new FileWriter(testSuite.getTestSuiteFile());
fw.write(data);
fw.flush();
fw.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* Get the text script from the testSuite.
* @param testSuite
* @return test script
*/
public static String getScript(TestSuite testSuite) {
if(testSuite == null) return null;
StringTemplate gUnitScript = templates.getInstanceOf("gUnitFile");
gUnitScript.setAttribute("testSuite", testSuite);
return gUnitScript.toString();
}
/**
* From textual script to program model.
* @param file testsuite file (.gunit)
* @return test suite object
*/
public static TestSuite loadTestSuite(File file) {
if ( file.getName().endsWith(GRAMMAR_EXT) ) {
throw new RuntimeException(file.getName()+" is a grammar file not a gunit file");
}
// check grammar file
final File grammarFile = getGrammarFile(file);
if(grammarFile == null)
throw new RuntimeException("Can't find grammar file associated with gunit file: "+file.getAbsoluteFile());
TestSuite result = new TestSuite("", file);
// read in test suite
try {
final Reader reader = new BufferedReader(new FileReader(file));
final StGUnitLexer lexer = new StGUnitLexer(new ANTLRReaderStream(reader));
final CommonTokenStream tokens = new CommonTokenStream(lexer);
final StGUnitParser parser = new StGUnitParser(tokens);
final TestSuiteAdapter adapter = new TestSuiteAdapter(result);
parser.adapter = adapter;
parser.gUnitDef();
result.setTokens(tokens);
reader.close();
} catch (Exception ex) {
throw new RuntimeException("Error reading test suite file.\n" + ex.getMessage());
}
// load un-tested rules from grammar
final List<Rule> completeRuleList = loadRulesFromGrammar(grammarFile);
for(Rule rule: completeRuleList) {
if(!result.hasRule(rule)) {
result.addRule(rule);
//System.out.println("Add rule:" + rule);
}
}
return result;
}
/**
* Get the grammar file of the testsuite file in the same directory.
* @param testsuiteFile
* @return grammar file or null
*/
private static File getGrammarFile(File testsuiteFile) {
String sTestFile;
try {
sTestFile = testsuiteFile.getCanonicalPath();
}
catch (IOException e) {
return null;
}
// Try Foo.g from Foo.gunit
String fname =
sTestFile.substring(0, sTestFile.lastIndexOf('.')) + GRAMMAR_EXT;
File fileGrammar = new File(fname);
if(fileGrammar.exists() && fileGrammar.isFile()) return fileGrammar;
// Try FooParser.g from Foo.gunit
fname = sTestFile.substring(0, sTestFile.lastIndexOf('.'))+"Parser"+GRAMMAR_EXT;
if(fileGrammar.exists() && fileGrammar.isFile()) return fileGrammar;
return fileGrammar;
}
}

View File

@ -0,0 +1,73 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui.runner;
import org.antlr.gunit.*;
import org.antlr.gunit.swingui.model.*;
/**
* The gUnit test executer that will respond to the fail/pass event during the
* execution. The executer is passed into gUnit Interp for execution.
* @author scai
*/
public class NotifiedTestExecuter extends gUnitExecutor {
private TestSuite testSuite ;
public NotifiedTestExecuter(GrammarInfo grammarInfo, ClassLoader loader, String testsuiteDir, TestSuite suite) {
super(grammarInfo, loader, testsuiteDir);
testSuite = suite;
}
@Override
public void onFail(ITestCase failTest) {
if(failTest == null) throw new IllegalArgumentException("Null fail test");
final String ruleName = failTest.getTestedRuleName();
if(ruleName == null) throw new NullPointerException("Null rule name");
final Rule rule = testSuite.getRule(ruleName);
final TestCase failCase = (TestCase) rule.getElementAt(failTest.getTestCaseIndex());
failCase.setPass(false);
//System.out.println(String.format("[FAIL] %s (%d) ", failTest.getTestedRuleName(), failTest.getTestCaseIndex()));
}
@Override
public void onPass(ITestCase passTest) {
if(passTest == null) throw new IllegalArgumentException("Null pass test");
final String ruleName = passTest.getTestedRuleName();
if(ruleName == null) throw new NullPointerException("Null rule name");
final Rule rule = testSuite.getRule(ruleName);
final TestCase passCase = (TestCase) rule.getElementAt(passTest.getTestCaseIndex());
passCase.setPass(true);
//System.out.println(String.format("[PASS] %s (%d) ", passTest.getTestedRuleName(), passTest.getTestCaseIndex()));
}
}

View File

@ -0,0 +1,124 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui.runner;
import java.io.*;
import java.util.HashMap;
/**
* Class loader for parser & lexer generated by antlr.
* @author Shaoting
*/
public class ParserLoader extends ClassLoader {
private HashMap<String, Class<?>> classList;
private String grammar;
/**
* Create a class loader for antlr parser/lexer.
* @param grammarName
* @param classDir
*/
public ParserLoader(String grammarName, String classDir) throws IOException, ClassNotFoundException {
final String lexerName = grammarName + "Lexer";
// load all the class files in the "classDir" related to the grammarName
File dir = new File(classDir);
if(dir.isDirectory()) {
classList = new HashMap<String, Class<?>>();
grammar = grammarName;
File[] files = dir.listFiles(new ClassFilenameFilter(grammarName));
for(File f : files) {
// load class data
final InputStream in = new BufferedInputStream(new FileInputStream(f));
final byte[] classData = new byte[in.available()];
in.read(classData);
in.close();
// define class
final Class<?> newClass = defineClass(null, classData, 0, classData.length);
assert(newClass != null);
resolveClass(newClass);
// save to hashtable
final String fileName = f.getName();
final String className = fileName.substring(0, fileName.lastIndexOf("."));
classList.put(className, newClass);
//System.out.println("adding: " + className);
}
} else {
throw new IOException(classDir + " is not a directory.");
}
if(classList.isEmpty() || !classList.containsKey(lexerName)) {
throw new ClassNotFoundException(lexerName + " not found.");
}
}
@Override
public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
//System.out.print("loading: " + name);
if(name.startsWith(grammar)) {
if(classList.containsKey(name)) {
//System.out.println(" .... found");
return classList.get(name);
} else {
//System.out.println(" .... not found");
throw new ClassNotFoundException(name);
}
} else {
final Class<?> c = findSystemClass(name);
//System.out.println(" .... system found " + c.getName());
return c;
}
}
/**
* Accepts grammarname...($...)?.class
*/
protected static class ClassFilenameFilter implements FilenameFilter {
private String grammarName;
protected ClassFilenameFilter(String name) {
grammarName = name;
}
public boolean accept(File dir, String name) {
return name.startsWith(grammarName) && name.endsWith(".class");
}
}
}

View File

@ -0,0 +1,104 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui.runner;
import org.antlr.gunit.swingui.model.*;
/**
* Adapter class for gunit parser to save information into testsuite object.
* @author Shaoting
*/
public class TestSuiteAdapter {
private TestSuite model ;
private Rule currentRule;
public TestSuiteAdapter(TestSuite testSuite) {
model = testSuite;
}
public void setGrammarName(String name) {
model.setGrammarName(name);
}
public void startRule(String name) {
currentRule = new Rule(name);
}
public void endRule() {
model.addRule(currentRule);
currentRule = null;
}
public void addTestCase(ITestCaseInput in, ITestCaseOutput out) {
TestCase testCase = new TestCase(in, out);
currentRule.addTestCase(testCase);
}
private static String trimChars(String text, int numOfChars) {
return text.substring(numOfChars, text.length() - numOfChars);
}
public static ITestCaseInput createFileInput(String fileName) {
if(fileName == null) throw new IllegalArgumentException("null");
return new TestCaseInputFile(fileName);
}
public static ITestCaseInput createStringInput(String line) {
if(line == null) throw new IllegalArgumentException("null");
// trim double quotes
return new TestCaseInputString(trimChars(line, 1));
}
public static ITestCaseInput createMultiInput(String text) {
if(text == null) throw new IllegalArgumentException("null");
// trim << and >>
return new TestCaseInputMultiString(trimChars(text, 2));
}
public static ITestCaseOutput createBoolOutput(boolean bool) {
return new TestCaseOutputResult(bool);
}
public static ITestCaseOutput createAstOutput(String ast) {
if(ast == null) throw new IllegalArgumentException("null");
return new TestCaseOutputAST(ast);
}
public static ITestCaseOutput createStdOutput(String text) {
if(text == null) throw new IllegalArgumentException("null");
// trim double quotes
return new TestCaseOutputStdOut(trimChars(text, 1));
}
public static ITestCaseOutput createReturnOutput(String text) {
if(text == null) throw new IllegalArgumentException("null");
// trim square brackets
return new TestCaseOutputReturn(trimChars(text, 1));
}
}

View File

@ -0,0 +1,80 @@
/*
[The "BSD licence"]
Copyright (c) 2009 Shaoting Cai
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.gunit.swingui.runner;
import java.io.File;
import java.io.IOException;
import org.antlr.runtime.*;
import org.antlr.runtime.CharStream;
import org.antlr.gunit.*;
import org.antlr.gunit.swingui.model.TestSuite;
/**
* Adapter between gUnitEditor Swing GUI and gUnit command-line tool.
* @author scai
*/
public class gUnitAdapter {
private ParserLoader loader ;
private TestSuite testSuite;
public gUnitAdapter(TestSuite suite) throws IOException, ClassNotFoundException {
int i = 3;
loader = new ParserLoader(suite.getGrammarName(),
suite.getTestSuiteFile().getParent());
testSuite = suite;
}
public void run() {
if (testSuite == null)
throw new IllegalArgumentException("Null testsuite.");
try {
// Parse gUnit test suite file
final CharStream input = new ANTLRFileStream(testSuite.getTestSuiteFile().getCanonicalPath());
final gUnitLexer lexer = new gUnitLexer(input);
final CommonTokenStream tokens = new CommonTokenStream(lexer);
final GrammarInfo grammarInfo = new GrammarInfo();
final gUnitParser parser = new gUnitParser(tokens, grammarInfo);
parser.gUnitDef(); // parse gunit script and save elements to grammarInfo
// Execute test suite
final gUnitExecutor executer = new NotifiedTestExecuter(
grammarInfo, loader,
testSuite.getTestSuiteFile().getParent(), testSuite);
executer.execTest();
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,49 @@
/*
[The "BSD licence"]
Copyright (c) 2007-2008 Leon, Jen-Yuan Su
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.
*/
group gUnitTestResult;
testResult(title, num_of_test, num_of_failure, failure, has_invalid, num_of_invalid, invalid) ::= <<
-----------------------------------------------------------------------
<title> with <num_of_test> tests
-----------------------------------------------------------------------
<num_of_failure> failures found:
<failure:{<it.header>
expected: <it.expectedResult>
actual: <it.actualResult>
}>
<if(has_invalid)>
<num_of_invalid> invalid inputs found:
<invalid:{<it.header>
invalid input: <it.actual>
}>
<endif>
Tests run: <num_of_test>, Failures: <num_of_failure>
>>

View File

@ -0,0 +1,85 @@
/*
[The "BSD licence"]
Copyright (c) 2007-2008 Leon, Jen-Yuan Su
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.
*/
group junit;
classHeader(header,junitFileName,hasTreeAdaptor,treeAdaptorPath,
hasPackage,packagePath,lexerPath,parserPath,treeParserPath,isTreeGrammar) ::= <<
<header>
import org.antlr.gunit.gUnitBaseTest;
public class <junitFileName> extends gUnitBaseTest {
public void setUp() {
<if(hasTreeAdaptor)><\t><\t>this.treeAdaptorPath = "<treeAdaptorPath>";<endif>
<if(hasPackage)><\t><\t>this.packagePath = "<packagePath>";<endif>
this.lexerPath = "<lexerPath>";
this.parserPath = "<parserPath>";
<if(isTreeGrammar)><\t><\t>this.treeParserPath = "<treeParserPath>";<endif>
}<\n><\n>
>>
testTreeRuleMethod(methodName,testTreeRuleName,testRuleName,test,tokenType,expecting) ::= <<
public void <methodName>() throws Exception {
// gunit test on line <test.line>
Object retval = execTreeParser(<testTreeRuleName>, <testRuleName>, "<test.inputEscaped>", <test.isFile>);
Object actual = examineExecResult(<tokenType>, retval);
Object expecting = <expecting>;
assertEquals("testing rule "+<testTreeRuleName>, expecting, actual);
}<\n><\n>
>>
testTreeRuleMethod2(methodName,testTreeRuleName,testRuleName,test,returnType,expecting) ::= <<
public void <methodName>() throws Exception {
// gunit test on line <test.line>
<returnType> retval = (<returnType>)execTreeParser(<testTreeRuleName>, <testRuleName>, "<test.inputEscaped>", <test.isFile>);
assertTrue("testing rule "+<testTreeRuleName>, <expecting>);
}<\n><\n>
>>
testRuleMethod(isLexicalRule,methodName,testRuleName,test,tokenType,expecting) ::= <<
public void <methodName>() throws Exception {
// gunit test on line <test.line>
Object retval = <if(isLexicalRule)>execLexer<else>execParser<endif>(<testRuleName>, <test.line>, "<test.inputEscaped>", <test.isFile>);
Object actual = examineExecResult(<tokenType>, retval);
Object expecting = <expecting>;
assertEquals("testing rule "+<testRuleName>, expecting, actual);
}<\n><\n>
>>
testRuleMethod2(methodName,testRuleName,test,returnType,expecting) ::= <<
public void <methodName>() throws Exception {
// gunit test on line <test.line>
<returnType> retval = (<returnType>)execParser(<testRuleName>, <test.line>, "<test.inputEscaped>", <test.isFile>);
assertTrue("testing rule "+<testRuleName>, <expecting>);
}<\n><\n>
>>

View File

@ -0,0 +1,19 @@
group gunit;
gUnitFile(testSuite) ::= <<gunit <testSuite.grammarName>;
<testSuite.rulesForStringTemplate:testGroup()>
>>
testGroup() ::= <<
<if(it.notEmpty)>
//------------------- <it.name>
<it.name>:
<it.testCases: testCase(); separator="\n\n">
<endif>
>>
testCase() ::= "<it.input> <it.output>"

Some files were not shown because too many files have changed in this diff Show More