[sikuli] 192/385: added a syntax highlighter based on the Jygments project

Gilles Filippini pini at moszumanska.debian.org
Sun Jun 29 19:26:11 UTC 2014


This is an automated email from the git hooks/post-receive script.

pini pushed a commit to tag upstream/1.1.0_beta1
in repository sikuli.

commit 17b6184b5ffb40bcb421004c28e5813104c58176
Author: Raimund Hocke <info at its-me-raiman.de>
Date:   Sat Feb 1 08:49:30 2014 +0100

    added a syntax highlighter based on the Jygments project
---
 Jygments4Sikuli/pom.xml                            |  36 ++
 .../main/java/org/sikuli/syntaxhighlight/Def.java  |  56 +++
 .../java/org/sikuli/syntaxhighlight/Filter.java    |  23 ++
 .../java/org/sikuli/syntaxhighlight/Jygments.java  |  53 +++
 .../java/org/sikuli/syntaxhighlight/NestedDef.java |  83 +++++
 .../syntaxhighlight/ResolutionException.java       |  42 +++
 .../main/java/org/sikuli/syntaxhighlight/Run.java  |  18 +
 .../main/java/org/sikuli/syntaxhighlight/Util.java | 153 ++++++++
 .../sikuli/syntaxhighlight/contrib/Css2Lexer.java  | 109 ++++++
 .../syntaxhighlight/contrib/DebugFormatter.java    |  51 +++
 .../syntaxhighlight/contrib/Default2Style.java     |  81 +++++
 .../syntaxhighlight/contrib/HtmlFormatter.java     | 209 +++++++++++
 .../sikuli/syntaxhighlight/format/Formatter.java   | 105 ++++++
 .../syntaxhighlight/grammar/DelegatedLexer.java    | 122 +++++++
 .../sikuli/syntaxhighlight/grammar/Grammar.java    | 154 ++++++++
 .../org/sikuli/syntaxhighlight/grammar/Lexer.java  | 361 +++++++++++++++++++
 .../syntaxhighlight/grammar/PatternRule.java       |  43 +++
 .../sikuli/syntaxhighlight/grammar/RegexLexer.java | 391 +++++++++++++++++++++
 .../syntaxhighlight/grammar/RelativeState.java     |  50 +++
 .../org/sikuli/syntaxhighlight/grammar/Rule.java   |  20 ++
 .../sikuli/syntaxhighlight/grammar/SaveRule.java   |  42 +++
 .../org/sikuli/syntaxhighlight/grammar/State.java  |  85 +++++
 .../org/sikuli/syntaxhighlight/grammar/Token.java  |  57 +++
 .../sikuli/syntaxhighlight/grammar/TokenRule.java  |  58 +++
 .../sikuli/syntaxhighlight/grammar/TokenType.java  | 274 +++++++++++++++
 .../sikuli/syntaxhighlight/grammar/UsingRule.java  |  44 +++
 .../grammar/def/ChangeStateTokenRuleDef.java       |  84 +++++
 .../syntaxhighlight/grammar/def/IncludeDef.java    |  86 +++++
 .../syntaxhighlight/grammar/def/SaveDef.java       |  80 +++++
 .../syntaxhighlight/grammar/def/StateDef.java      |  43 +++
 .../syntaxhighlight/grammar/def/TokenRuleDef.java  | 135 +++++++
 .../syntaxhighlight/grammar/def/UsingRuleDef.java  |  98 ++++++
 .../org/sikuli/syntaxhighlight/scan/EndOfText.java |  20 ++
 .../org/sikuli/syntaxhighlight/scan/Scanner.java   | 121 +++++++
 .../syntaxhighlight/style/ColorStyleElement.java   |  86 +++++
 .../syntaxhighlight/style/EffectStyleElement.java  |  49 +++
 .../syntaxhighlight/style/FontStyleElement.java    |  43 +++
 .../org/sikuli/syntaxhighlight/style/Style.java    | 235 +++++++++++++
 .../sikuli/syntaxhighlight/style/StyleElement.java |  83 +++++
 .../syntaxhighlight/style/def/StyleElementDef.java |  73 ++++
 .../src/main/resources/contrib/ClojureLexer.jso    | 132 +++++++
 .../src/main/resources/contrib/CssLexer.jso        | 160 +++++++++
 .../src/main/resources/contrib/DefaultStyle.jso    |  51 +++
 .../src/main/resources/contrib/HtmlLexer.jso       |  64 ++++
 .../src/main/resources/contrib/JavaLexer.jso       |  48 +++
 .../src/main/resources/contrib/JavascriptLexer.jso |  84 +++++
 .../src/main/resources/contrib/PythonLexer.jso     | 214 +++++++++++
 .../src/main/resources/contrib/XmlLexer.jso        |  38 ++
 pom.xml                                            |   1 +
 49 files changed, 4748 insertions(+)

diff --git a/Jygments4Sikuli/pom.xml b/Jygments4Sikuli/pom.xml
new file mode 100644
index 0000000..e289938
--- /dev/null
+++ b/Jygments4Sikuli/pom.xml
@@ -0,0 +1,36 @@
+<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/xsd/maven-4.0.0.xsd">
+
+	<!--
+	* Copyright 2010-2013, Sikuli.org
+	* Released under the MIT License.
+	*
+	* RaiMan 2013
+	-->
+
+	<parent>
+		<groupId>org.sikuli</groupId>
+		<artifactId>SikuliX-Project</artifactId>
+		<version>1.1.0</version>
+		<relativePath>../</relativePath>
+	</parent>
+
+	<modelVersion>4.0.0</modelVersion>
+
+	<groupId>org.sikuli</groupId>
+	<artifactId>Jygments4Sikuli</artifactId>
+	<version>1.1.0</version>
+
+	<dependencies>
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-core</artifactId>
+			<version>2.3.1</version>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-databind</artifactId>
+			<version>2.3.1</version>
+		</dependency>
+	</dependencies>
+</project>
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/Def.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/Def.java
new file mode 100644
index 0000000..b9ee7b1
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/Def.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight;
+
+/**
+ * @author Tal Liron
+ */
+public abstract class Def<C>
+{
+	//
+	// Attributes
+	//
+
+	public boolean isResolved()
+	{
+		return resolved;
+	}
+
+	public Def<C> getCause( C container )
+	{
+		return null;
+	}
+
+	//
+	// Operations
+	//
+
+	public boolean resolve( C container ) throws ResolutionException
+	{
+		return false;
+	}
+
+	//
+	// Object
+	//
+
+	@Override
+	public String toString()
+	{
+		return getClass().getSimpleName();
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Protected
+
+	protected boolean resolved = false;
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/Filter.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/Filter.java
new file mode 100644
index 0000000..a9e7553
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/Filter.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight;
+
+/**
+ * @author Tal Liron
+ */
+public abstract class Filter
+{
+	public static Filter getFilterByName( String name )
+	{
+		return null;
+	}
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/Jygments.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/Jygments.java
new file mode 100644
index 0000000..c31b055
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/Jygments.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+import org.sikuli.syntaxhighlight.format.Formatter;
+import org.sikuli.syntaxhighlight.grammar.Lexer;
+import org.sikuli.syntaxhighlight.grammar.Token;
+
+/**
+ * @author Tal Liron
+ */
+public abstract class Jygments
+{
+	//
+	// Static operations
+	//
+
+	public static Iterable<Token> lex( String code, Lexer lexer )
+	{
+		return lexer.getTokens( code );
+	}
+
+	public static void format( Iterable<Token> tokens, Formatter formatter, Writer writer ) throws IOException
+	{
+		formatter.format( tokens, writer );
+	}
+
+	public static void highlight( String code, Lexer lexer, Formatter formatter, Writer writer ) throws IOException
+	{
+		format( lex( code, lexer ), formatter, writer );
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private Jygments()
+	{
+	}
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/NestedDef.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/NestedDef.java
new file mode 100644
index 0000000..e53c5d0
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/NestedDef.java
@@ -0,0 +1,83 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Tal Liron
+ */
+public class NestedDef<C> extends Def<C>
+{
+	//
+	// Operations
+	//
+
+	public void addDef( Def<C> def )
+	{
+		defs.add( def );
+	}
+
+	//
+	// Def
+	//
+
+	@Override
+	public boolean resolve( C container ) throws ResolutionException
+	{
+		// Keep resolving until done
+		boolean didSomething = false, keepGoing = true;
+		while( keepGoing )
+		{
+			keepGoing = false;
+			for( Def<C> def : new ArrayList<Def<C>>( defs ) )
+			{
+				if( !def.isResolved() )
+				{
+					if( def.resolve( container ) )
+					{
+						keepGoing = true;
+						didSomething = true;
+					}
+				}
+			}
+		}
+
+		// Are we resolved?
+		resolved = true;
+		for( Def<C> def : defs )
+		{
+			if( !def.isResolved() )
+			{
+				resolved = false;
+				break;
+			}
+		}
+
+		return didSomething;
+	}
+
+	@Override
+	public Def<C> getCause( C container )
+	{
+		for( Def<C> def : defs )
+			if( !def.isResolved() )
+				return def;
+		return null;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private final List<Def<C>> defs = new ArrayList<Def<C>>();
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/ResolutionException.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/ResolutionException.java
new file mode 100644
index 0000000..b346736
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/ResolutionException.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight;
+
+/**
+ * @author Tal Liron
+ */
+public class ResolutionException extends Exception
+{
+	//
+	// Construction
+	//
+
+	public ResolutionException( String message )
+	{
+		super( message );
+	}
+
+	public ResolutionException( String message, Throwable cause )
+	{
+		super( message, cause );
+	}
+
+	public ResolutionException( Throwable cause )
+	{
+		super( cause );
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private static final long serialVersionUID = 1L;
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/Run.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/Run.java
new file mode 100644
index 0000000..df64d42
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/Run.java
@@ -0,0 +1,18 @@
+package org.sikuli.syntaxhighlight;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import org.sikuli.syntaxhighlight.format.Formatter;
+import org.sikuli.syntaxhighlight.grammar.Lexer;
+
+public class Run {
+
+	public static void main(String[] args) throws IOException, ResolutionException {
+		String file = System.getProperty("user.dir") + "/org/sikuli/syntaxhighlight/Run.java";
+		Lexer lexer = Lexer.getByName("java");
+		Formatter formatter = Formatter.getByName("html");
+		String code = Util.streamToString(new FileInputStream(file));
+		formatter.format(lexer.getTokens(code), new PrintWriter(System.out));
+	}
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/Util.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/Util.java
new file mode 100644
index 0000000..c941bcf
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/Util.java
@@ -0,0 +1,153 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+package org.sikuli.syntaxhighlight;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.sikuli.syntaxhighlight.grammar.Lexer;
+
+/**
+ * @author Tal Liron
+ */
+public class Util {
+
+	public static String literalRegEx(String expression) {
+		return "\\Q" + expression + "\\E";
+	}
+
+	public static String replace(String string, String occurence, String replacement) {
+		return string.replaceAll(literalRegEx(occurence), replacement);
+	}
+
+	public static String streamToString(InputStream stream) throws IOException {
+		StringBuilder builder = new StringBuilder();
+		String line;
+
+		try {
+			BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
+			while ((line = reader.readLine()) != null) {
+				builder.append(line).append("\n");
+			}
+		} finally {
+			stream.close();
+		}
+
+		return builder.toString();
+	}
+
+	public static String rejsonToJson(InputStream stream) throws IOException {
+		String rejson = streamToString(stream);
+		String json = rejsonToJson(rejson, true);
+		json = rejsonToJson(json, false);
+		return json;
+	}
+
+	public static String rejsonToJson(String rejson, boolean doubleQuote) {
+		Matcher matcher = doubleQuote ? DOUBLE_QUOTED_STRING.matcher(rejson) : SINGLE_QUOTED_STRING.matcher(rejson);
+		StringBuilder json = new StringBuilder();
+		int start = 0, end = 0, lastEnd = 0;
+		while (matcher.find()) {
+			lastEnd = end;
+			start = matcher.start();
+			end = matcher.end();
+			if ((start > 0) && (rejson.charAt(start - 1) == 'r')) {
+				// Convert Python-style r"" string to Java-compatible pattern
+				String string = rejson.substring(start + 1, end - 1);
+				json.append(rejson.substring(lastEnd, start - 1));
+				json.append('\"');
+				json.append(pythonRegExToJavaPattern(string, doubleQuote));
+				json.append('\"');
+			} /*
+			 * else if( !doubleQuote ) { // From single quote to double quote
+			 * String string = rejson.substring( start + 1, end - 1 );
+			 * json.append( rejson.substring( lastEnd, start - 1 ) );
+			 * json.append( '\"' ); json.append( string.replaceAll( "\"",
+			 * "\\\\\"" ) ); json.append( '\"' ); }
+			 */ else {
+				// As is
+				json.append(rejson.substring(lastEnd, end));
+			}
+		}
+		json.append(rejson.substring(end));
+		// System.out.println( json );
+		return json.toString();
+	}
+
+	public static String pythonRegExToJavaPattern(String pattern, boolean doubleQuote) {
+		pattern = pattern.replaceAll("\\\\", "\\\\\\\\");
+		pattern = pattern.replaceAll("\\{", "\\\\\\\\{");
+		pattern = pattern.replaceAll("\\}", "\\\\\\\\}");
+		if (!doubleQuote) {
+			pattern = pattern.replaceAll("\"", "\\\\\"");
+		}
+		// System.out.println( pattern );
+		return pattern;
+	}
+
+	public static String escapeHtml(String text) {
+		text = text.replace("&", "&");
+		text = text.replace("<", "<");
+		text = text.replace(">", ">");
+		text = text.replace("\"", """);
+		text = text.replace("'", "'");
+		return text;
+	}
+
+	public static String asHtml(String text) {
+		text = escapeHtml(text);
+		text = text.replace(" ", " ");
+		return text;
+	}
+
+	private static final Pattern DOUBLE_QUOTED_STRING = Pattern.compile("\"(?>\\\\.|.)*?\"");
+
+	private static final Pattern SINGLE_QUOTED_STRING = Pattern.compile("'(?>\\\\.|.)*?'");
+
+	public static String extJSON = ".jso";
+
+	public static InputStream getJsonFile(String filename) {
+		URI jarFileURI = null;
+		File jarFile;
+		InputStream stream = null;
+		filename = filename.replace('.', '/') + extJSON;
+		try {
+			jarFileURI = Jygments.class.getProtectionDomain().getCodeSource().getLocation().toURI();
+		} catch (URISyntaxException ex) {
+			//TODO error message
+		}
+		if (jarFileURI != null) {
+			if (jarFileURI.getScheme().equals("file")) {
+				jarFile = new File(jarFileURI.getPath(), filename);
+				if (jarFile.exists()) {
+					try {
+						stream = new FileInputStream(jarFile);
+					} catch (FileNotFoundException ex) {
+						//TODO error message
+					}
+				}
+			} else {
+				stream = Jygments.class.getClassLoader().getResourceAsStream(filename);
+			}
+		}
+		return stream;
+	}
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/contrib/Css2Lexer.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/contrib/Css2Lexer.java
new file mode 100644
index 0000000..a85ca27
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/contrib/Css2Lexer.java
@@ -0,0 +1,109 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.contrib;
+
+import java.util.regex.Pattern;
+
+import org.sikuli.syntaxhighlight.ResolutionException;
+import org.sikuli.syntaxhighlight.grammar.RegexLexer;
+
+/**
+ * @author Tal Liron
+ */
+public class Css2Lexer extends RegexLexer
+{
+	public Css2Lexer()
+	{
+		super();
+
+		addAlias( "css" );
+		addFilename( "*.css" );
+		addMimeType( "text/css" );
+
+		include( "root", "basics" );
+
+		int flags = Pattern.MULTILINE;
+		rule( "basics", "\\s+", flags, "Text" );
+		rule( "basics", "/\\*(?:.|\\n)*?\\*/", flags, "Comment" );
+		rule( "basics", "\\{", flags, "Punctuation", "content" );
+		rule( "basics", "\\:[a-zA-Z0-9_-]+", flags, "Name.Decorator" );
+		rule( "basics", "\\.[a-zA-Z0-9_-]+", flags, "Name.Class" );
+		rule( "basics", "\\#[a-zA-Z0-9_-]+", flags, "Name.Function" );
+		rule( "basics", "@[a-zA-Z0-9_-]+", flags, "Keyword", "atrule" );
+		rule( "basics", "[a-zA-Z0-9_-]+", flags, "Name.Tag" );
+		rule( "basics", "[~\\^\\*!%&\\[\\]\\(\\)<>\\|+=@:;,./?-]", flags, "Operator" );
+		rule( "basics", "\"(\\\\\\\\|\\\\\"|[^\"])*\"", flags, "String.Double" );
+		rule( "basics", "'(\\\\\\\\|\\\\'|[^'])*'\"", flags, "String.Single" );
+
+		rule( "atrule", "\\{", flags, "Punctuation", "atcontent" );
+		rule( "atrule", ";", flags, "Punctuation", "#pop" );
+		include( "atrule", "basics" );
+
+		include( "atcontent", "basics" );
+		rule( "atcontent", "}", flags, "Punctuation", "#pop:2" );
+
+		rule( "content", "\\s+", flags, "Text" );
+		rule( "content", "}", flags, "Punctuation", "#pop" );
+		rule( "content", "url\\(.*?\\)", flags, "String.Other" );
+		rule( "content", "^@.*?$", flags, "Comment.Preproc" );
+
+		rule( "content", "(azimuth|background-attachment|background-color|" + "background-image|background-position|background-repeat|" + "background|border-bottom-color|border-bottom-style|"
+			+ "border-bottom-width|border-left-color|border-left-style|" + "border-left-width|border-right|border-right-color|" + "border-right-style|border-right-width|border-top-color|"
+			+ "border-top-style|border-top-width|border-bottom|" + "border-collapse|border-left|border-width|border-color|" + "border-spacing|border-style|border-top|border|caption-side|"
+			+ "clear|clip|color|content|counter-increment|counter-reset|" + "cue-after|cue-before|cue|cursor|direction|display|" + "elevation|empty-cells|float|font-family|font-size|"
+			+ "font-size-adjust|font-stretch|font-style|font-variant|" + "font-weight|font|height|letter-spacing|line-height|" + "list-style-type|list-style-image|list-style-position|"
+			+ "list-style|margin-bottom|margin-left|margin-right|" + "margin-top|margin|marker-offset|marks|max-height|max-width|" + "min-height|min-width|opacity|orphans|outline|outline-color|"
+			+ "outline-style|outline-width|overflow|padding-bottom|" + "padding-left|padding-right|padding-top|padding|page|" + "page-break-after|page-break-before|page-break-inside|"
+			+ "pause-after|pause-before|pause|pitch|pitch-range|" + "play-during|position|quotes|richness|right|size|" + "speak-header|speak-numeral|speak-punctuation|speak|"
+			+ "speech-rate|stress|table-layout|text-align|text-decoration|" + "text-indent|text-shadow|text-transform|top|unicode-bidi|" + "vertical-align|visibility|voice-family|volume|white-space|"
+			+ "widows|width|word-spacing|z-index|bottom|left|" + "above|absolute|always|armenian|aural|auto|avoid|baseline|" + "behind|below|bidi-override|blink|block|bold|bolder|both|"
+			+ "capitalize|center-left|center-right|center|circle|" + "cjk-ideographic|close-quote|collapse|condensed|continuous|" + "crop|crosshair|cross|cursive|dashed|decimal-leading-zero|"
+			+ "decimal|default|digits|disc|dotted|double|e-resize|embed|" + "extra-condensed|extra-expanded|expanded|fantasy|far-left|" + "far-right|faster|fast|fixed|georgian|groove|hebrew|help|"
+			+ "hidden|hide|higher|high|hiragana-iroha|hiragana|icon|" + "inherit|inline-table|inline|inset|inside|invert|italic|" + "justify|katakana-iroha|katakana|landscape|larger|large|"
+			+ "left-side|leftwards|level|lighter|line-through|list-item|" + "loud|lower-alpha|lower-greek|lower-roman|lowercase|ltr|" + "lower|low|medium|message-box|middle|mix|monospace|"
+			+ "n-resize|narrower|ne-resize|no-close-quote|no-open-quote|" + "no-repeat|none|normal|nowrap|nw-resize|oblique|once|" + "open-quote|outset|outside|overline|pointer|portrait|px|"
+			+ "relative|repeat-x|repeat-y|repeat|rgb|ridge|right-side|" + "rightwards|s-resize|sans-serif|scroll|se-resize|" + "semi-condensed|semi-expanded|separate|serif|show|silent|"
+			+ "slow|slower|small-caps|small-caption|smaller|soft|solid|" + "spell-out|square|static|status-bar|super|sw-resize|" + "table-caption|table-cell|table-column|table-column-group|"
+			+ "table-footer-group|table-header-group|table-row|" + "table-row-group|text|text-bottom|text-top|thick|thin|" + "transparent|ultra-condensed|ultra-expanded|underline|"
+			+ "upper-alpha|upper-latin|upper-roman|uppercase|url|" + "visible|w-resize|wait|wider|x-fast|x-high|x-large|x-loud|" + "x-low|x-small|x-soft|xx-large|xx-small|yes)\\b", flags, "Keyword" );
+
+		rule( "content", "(indigo|gold|firebrick|indianred|yellow|darkolivegreen|" + "darkseagreen|mediumvioletred|mediumorchid|chartreuse|" + "mediumslateblue|black|springgreen|crimson|lightsalmon|brown|"
+			+ "turquoise|olivedrab|cyan|silver|skyblue|gray|darkturquoise|" + "goldenrod|darkgreen|darkviolet|darkgray|lightpink|teal|" + "arkmagenta|lightgoldenrodyellow|lavender|yellowgreen|thistle|"
+			+ "violet|navy|orchid|blue|ghostwhite|honeydew|cornflowerblue|" + "darkblue|darkkhaki|mediumpurple|cornsilk|red|bisque|slategray|" + "darkcyan|khaki|wheat|deepskyblue|darkred|steelblue|aliceblue|"
+			+ "gainsboro|mediumturquoise|floralwhite|coral|purple|lightgrey|" + "lightcyan|darksalmon|beige|azure|lightsteelblue|oldlace|" + "greenyellow|royalblue|lightseagreen|mistyrose|sienna|"
+			+ "lightcoral|orangered|navajowhite|lime|palegreen|burlywood|" + "seashell|mediumspringgreen|fuchsia|papayawhip|blanchedalmond|" + "peru|aquamarine|white|darkslategray|ivory|dodgerblue|"
+			+ "lemonchiffon|chocolate|orange|forestgreen|slateblue|olive|" + "mintcream|antiquewhite|darkorange|cadetblue|moccasin|" + "limegreen|saddlebrown|darkslateblue|lightskyblue|deeppink|"
+			+ "plum|aqua|darkgoldenrod|maroon|sandybrown|magenta|tan|" + "rosybrown|pink|lightblue|palevioletred|mediumseagreen|" + "dimgray|powderblue|seagreen|snow|mediumblue|midnightblue|"
+			+ "paleturquoise|palegoldenrod|whitesmoke|darkorchid|salmon|" + "lightslategray|lawngreen|lightgreen|tomato|hotpink|" + "lightyellow|lavenderblush|linen|mediumaquamarine|green|" + "blueviolet|peachpuff)\\b",
+			flags, "Name.Builtin" );
+
+		rule( "content", "\\!important", flags, "Comment.Preproc" );
+		rule( "content", "/\\*(?:.|\\n)*?\\*/'", flags, "Comment" );
+		rule( "content", "\\#[a-zA-Z0-9]{1,6}", flags, "Number" );
+		rule( "content", "[\\.-]?[0-9]*[\\.]?[0-9]+(em|px|\\%|pt|pc|in|mm|cm|ex)", flags, "Number" );
+		rule( "content", "-?[0-9]+", flags, "Number" );
+		rule( "content", "[~\\^\\*!%&<>\\|+=@:,./?-]+", flags, "Operator" );
+		rule( "content", "[\\[\\]();]+", flags, "Punctuation" );
+		rule( "content", "\"(\\\\\\\\|\\\\\"|[^\"])*\"", flags, "String.Double" );
+		rule( "content", "\"'(\\\\\\\\|\\\\'|[^'])*'", flags, "String.Single" );
+		rule( "content", "[a-zA-Z][a-zA-Z0-9]+", flags, "Name" );
+
+		try
+		{
+			resolve();
+		}
+		catch( ResolutionException x )
+		{
+			throw new RuntimeException( x );
+		}
+	}
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/contrib/DebugFormatter.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/contrib/DebugFormatter.java
new file mode 100644
index 0000000..1878313
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/contrib/DebugFormatter.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.contrib;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.sikuli.syntaxhighlight.format.Formatter;
+import org.sikuli.syntaxhighlight.grammar.Token;
+import org.sikuli.syntaxhighlight.style.Style;
+
+/**
+ * @author Tal Liron
+ */
+public class DebugFormatter extends Formatter
+{
+	//
+	// Construction
+	//
+
+	public DebugFormatter()
+	{
+		this( null, false, null, null );
+	}
+
+	public DebugFormatter( Style style, boolean full, String title, String encoding )
+	{
+		super( style, full, title, encoding );
+	}
+
+	//
+	// Formatter
+	//
+
+	@Override
+	public void format( Iterable<Token> tokenSource, Writer writer ) throws IOException
+	{
+		for( Token token : tokenSource )
+			writer.write( token.getPos() + " " + token.getType() + ": " + token.getValue() + "\n" );
+		writer.flush();
+	}
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/contrib/Default2Style.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/contrib/Default2Style.java
new file mode 100644
index 0000000..63e1bf2
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/contrib/Default2Style.java
@@ -0,0 +1,81 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.contrib;
+
+import org.sikuli.syntaxhighlight.ResolutionException;
+import org.sikuli.syntaxhighlight.style.Style;
+
+/**
+ * @author Tal Liron
+ */
+public class Default2Style extends Style
+{
+	public Default2Style()
+	{
+		super();
+
+		add( "Whitespace", "#bbbbbb" );
+		add( "Comment", "italic", "#408080" );
+		add( "Comment.Preproc", "noitalic", "#BC7A00" );
+
+		add( "Keyword", "bold", "#008000" );
+		add( "Keyword.Pseudo", "nobold" );
+		add( "Keyword.Type", "nobold", "#B00040" );
+
+		add( "Operator", "#666666" );
+		add( "Operator.Word", "bold", "#AA22FF" );
+
+		add( "Name.Builtin", "#008000" );
+		add( "Name.Function", "#0000FF" );
+		add( "Name.Class", "bold", "#0000FF" );
+		add( "Name.Namespace", "bold", "#0000FF" );
+		add( "Name.Exception", "bold", "#D2413A" );
+		add( "Name.Variable", "#19177C" );
+		add( "Name.Constant", "#880000" );
+		add( "Name.Label", "#A0A000" );
+		add( "Name.Entity", "bold", "#999999" );
+		add( "Name.Attribute", "#7D9029" );
+		add( "Name.Tag", "bold", "#008000" );
+		add( "Name.Decorator", "#AA22FF" );
+
+		add( "String", "#BA2121" );
+		add( "String.Doc", "italic" );
+		add( "String.Interpol", "bold", "#BB6688" );
+		add( "String.Escape", "bold", "#BB6622" );
+		add( "String.Regex", "#BB6688" );
+		add( "String.Symbol", "#19177C" );
+		add( "String.Other", "#008000" );
+		add( "Number", "#666666" );
+
+		add( "Generic.Heading", "bold", "#000080" );
+		add( "Generic.Subheading", "bold", "#800080" );
+		add( "Generic.Deleted", "#A00000" );
+		add( "Generic.Inserted", "#00A000" );
+		add( "Generic.Error", "#FF0000" );
+		add( "Generic.Emph", "italic" );
+		add( "Generic.Strong", "bold" );
+		add( "Generic.Prompt", "bold", "#000080" );
+		add( "Generic.Output", "#888" );
+		add( "Generic.Traceback", "#04D" );
+
+		add( "Error", "border", "#FF0000" );
+
+		try
+		{
+			resolve();
+		}
+		catch( ResolutionException x )
+		{
+			throw new RuntimeException( x );
+		}
+	}
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/contrib/HtmlFormatter.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/contrib/HtmlFormatter.java
new file mode 100644
index 0000000..3a63bde
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/contrib/HtmlFormatter.java
@@ -0,0 +1,209 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.contrib;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+import java.util.Map;
+
+import org.sikuli.syntaxhighlight.ResolutionException;
+import org.sikuli.syntaxhighlight.Util;
+import org.sikuli.syntaxhighlight.format.Formatter;
+import org.sikuli.syntaxhighlight.grammar.Token;
+import org.sikuli.syntaxhighlight.grammar.TokenType;
+import org.sikuli.syntaxhighlight.style.ColorStyleElement;
+import org.sikuli.syntaxhighlight.style.EffectStyleElement;
+import org.sikuli.syntaxhighlight.style.FontStyleElement;
+import org.sikuli.syntaxhighlight.style.Style;
+import org.sikuli.syntaxhighlight.style.StyleElement;
+
+/**
+ * @author Tal Liron
+ */
+public class HtmlFormatter extends Formatter
+{
+	//
+	// Construction
+	//
+
+	public HtmlFormatter() throws ResolutionException
+	{
+		this( Style.getByName( "default" ), false, null, null );
+	}
+
+	public HtmlFormatter( Style style, boolean full, String title, String encoding )
+	{
+		super( style, full, title, encoding );
+	}
+
+	//
+	// Formatter
+	//
+
+	@Override
+	public void format( Iterable<Token> tokenSource, Writer writer ) throws IOException
+	{
+		writer.write( getDocHeader1() );
+		writer.write( "  <title>" );
+		writer.write( Util.escapeHtml( getTitle() ) );
+		writer.write( "</title>\n" );
+		writer.write( getDocHeader2() );
+		writer.write( getEncoding() );
+		writer.write( getDocHeader3() );
+		writer.write( getCssFileTemplate() );
+		formatStyleSheet( writer );
+		writer.write( getDocHeader4() );
+		if( getTitle().length() > 0 )
+		{
+			writer.write( "<h2>" );
+			writer.write( Util.escapeHtml( getTitle() ) );
+			writer.write( "</h2>\n" );
+		}
+		writer.write( "<div><pre>\n" );
+		StringBuilder line = new StringBuilder();
+		int line_no = 1;
+		for( Token token : tokenSource )
+		{
+	        String[] toks = token.getValue().split("\n", -1);
+	        for(int i = 0; i < toks.length - 1; i++) {
+	            format_partial_token(token, toks[i], line);
+	            format_line(line.toString(), writer, line_no++);
+	            line = new StringBuilder();
+            }
+            format_partial_token(token, toks[toks.length-1], line);
+		}
+		if(line.length() > 0)
+		    format_line(line.toString(), writer, line_no++);
+
+		writer.write( "</pre></div>\n" );
+		writer.write( getDocFooter() );
+		writer.flush();
+	}
+
+    private void format_partial_token(Token token, String part_tok, StringBuilder line)
+    {	
+		if( token.getType().getShortName().length() > 0 )
+		{
+			line.append( "<span class=\"" );
+			line.append( token.getType().getShortName() );
+			line.append( "\">" );
+			line.append( Util.escapeHtml( part_tok ) );
+			line.append( "</span>" );
+		}
+		else
+			line.append( Util.escapeHtml( part_tok ) );
+    }
+	
+	public void format_line(String line, Writer writer, int line_no) throws IOException
+    {
+        writer.write(line);
+        writer.write("\n");
+    }
+	
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Protected
+
+	protected String getClassPrefix()
+	{
+	    return "";
+	}
+
+	protected String getCssFileTemplate()
+	{
+	    return "    td.linenos { background-color: #f0f0f0; padding-right: 10px; }\n" + "    span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }\n"
+		+ "    pre { line-height: 125%; }\n";
+    }
+    
+	protected String getDocHeader1()
+	{
+	    return "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n" + "<html>\n" + "<head>\n";
+    }
+    
+	protected String getDocHeader2()
+	{
+	    return "  <meta http-equiv=\"content-type\" content=\"text/html; charset=";
+    }
+    
+	protected String getDocHeader3()
+	{
+	    return "\">\n" + "  <style type=\"text/css\">\n";
+    }
+    
+	protected String getDocHeader4()
+	{
+	    return "  </style>\n" + "</head>\n" + "<body>\n";
+    }
+    
+	/*
+	 * private static final String DOC_HEADER_EXTERNAL_CSS =
+	 * "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n" +
+	 * "   \"http://www.w3.org/TR/html4/strict.dtd\">\n" + "\n" + "<html>\n" +
+	 * "<head>\n" + "  <title>%(title)s</title>\n" +
+	 * "  <meta http-equiv=\"content-type\" content=\"text/html; charset=%(encoding)s\">\n"
+	 * + "  <link rel=\"stylesheet\" href=\"%(cssfile)s\" type=\"text/css\">\n"
+	 * + "</head>\n" + "<body>\n" + "<h2>%(title)s</h2>\n";
+	 */
+
+	protected String getDocFooter()
+	{
+	    return "</body>\n" + "</html>\n";
+    }
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private void formatStyleSheet( Writer writer ) throws IOException
+	{
+		for( Map.Entry<TokenType, List<StyleElement>> entry : getStyle().getStyleElements().entrySet() )
+		{
+			TokenType tokenType = entry.getKey();
+			List<StyleElement> styleElementsForTokenType = entry.getValue();
+			writer.write( "    ." );
+			writer.write( getClassPrefix() );
+			writer.write( tokenType.getShortName() );
+			writer.write( " { " );
+			for( StyleElement styleElement : styleElementsForTokenType )
+			{
+				if( styleElement instanceof ColorStyleElement )
+				{
+					ColorStyleElement colorStyleElement = (ColorStyleElement) styleElement;
+					if( colorStyleElement.getType() == ColorStyleElement.Type.Foreground )
+						writer.write( "color: " );
+					else if( colorStyleElement.getType() == ColorStyleElement.Type.Background )
+						writer.write( "background-color: " );
+					else if( colorStyleElement.getType() == ColorStyleElement.Type.Border )
+						writer.write( "border: 1px solid " );
+					writer.write( colorStyleElement.getColor() );
+					writer.write( "; " );
+				}
+				else if( styleElement instanceof EffectStyleElement )
+				{
+					if( styleElement == EffectStyleElement.Bold )
+						writer.write( "font-weight: bold; " );
+					else if( styleElement == EffectStyleElement.Italic )
+						writer.write( "font-style: italic; " );
+					else if( styleElement == EffectStyleElement.Underline )
+						writer.write( "text-decoration: underline; " );
+				}
+				else if( styleElement instanceof FontStyleElement )
+				{
+					// We don't want to set fonts in this formatter
+				}
+			}
+			writer.write( "} /* " );
+			writer.write( tokenType.getName() );
+			writer.write( " */\n" );
+		}
+	}
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/format/Formatter.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/format/Formatter.java
new file mode 100644
index 0000000..769e5f6
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/format/Formatter.java
@@ -0,0 +1,105 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.format;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.sikuli.syntaxhighlight.Jygments;
+import org.sikuli.syntaxhighlight.ResolutionException;
+import org.sikuli.syntaxhighlight.grammar.Token;
+import org.sikuli.syntaxhighlight.style.Style;
+
+/**
+ * @author Tal Liron
+ */
+public abstract class Formatter
+{
+	//
+	// Static operations
+	//
+
+	public static Formatter getByName( String name ) throws ResolutionException
+	{
+		if( Character.isLowerCase( name.charAt( 0 ) ) )
+			name = Character.toUpperCase( name.charAt( 0 ) ) + name.substring( 1 ) + "Formatter";
+
+		Formatter formatter = getByFullName( name );
+		if( formatter != null )
+			return formatter;
+		else
+		{
+			// Try contrib package
+			String pack = Jygments.class.getPackage().getName() + ".contrib";
+			formatter = getByFullName( pack + "." + name );
+			if( formatter == null )
+			{
+				// Try this package
+				pack = Formatter.class.getPackage().getName();
+				formatter = getByFullName( pack + "." + name );
+			}
+			return formatter;
+		}
+	}
+
+	public static Formatter getByFullName( String fullName ) throws ResolutionException
+	{
+		try
+		{
+			return (Formatter) Jygments.class.getClassLoader().loadClass( fullName ).newInstance();
+		}
+		catch( InstantiationException x )
+		{
+		}
+		catch( IllegalAccessException x )
+		{
+		}
+		catch( ClassNotFoundException x )
+		{
+		}
+
+		return null;
+	}
+
+	public Formatter( Style style, boolean full, String title, String encoding )
+	{
+		this.style = style;
+		this.title = title != null ? title : "";
+		this.encoding = encoding != null ? encoding : "utf8";
+	}
+
+	public Style getStyle()
+	{
+		return style;
+	}
+
+	public String getTitle()
+	{
+		return title;
+	}
+
+	public String getEncoding()
+	{
+		return encoding;
+	}
+
+	public abstract void format( Iterable<Token> tokenSource, Writer writer ) throws IOException;
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private final Style style;
+
+	private final String title;
+
+	private final String encoding;
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/DelegatedLexer.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/DelegatedLexer.java
new file mode 100644
index 0000000..f8b63b4
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/DelegatedLexer.java
@@ -0,0 +1,122 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.grammar;
+
+import org.sikuli.syntaxhighlight.ResolutionException;
+import java.util.Map;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.LinkedList;
+
+/**
+ * @author Tal Liron
+ */
+public class DelegatedLexer extends Lexer
+{
+	//
+	// Lexer
+	//
+
+	@Override
+	public Iterable<Token> getTokensUnprocessed( String text )
+	{
+		StringBuilder buffered = new StringBuilder();
+		List<Token> lngBuffer = new LinkedList<Token>();
+		List<Insertion> insertions = new LinkedList<Insertion>();
+
+		Iterable<Token> tokens = languageLexer.getTokensUnprocessed( text );
+
+		for( Token t : tokens )
+		{
+			if( t.getType().getName().equals( "Other" ) )
+			{
+				if( !lngBuffer.isEmpty() )
+				{
+					insertions.add( new Insertion( buffered.length(), lngBuffer ) );
+					lngBuffer = new LinkedList<Token>();
+				}
+				buffered.append( t.getValue() );
+			}
+			else
+				lngBuffer.add( t );
+		}
+
+		if( !lngBuffer.isEmpty() )
+			insertions.add( new Insertion( buffered.length(), lngBuffer ) );
+
+		return doInsertions( insertions, rootLexer.getTokensUnprocessed( buffered.toString() ) );
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Protected
+
+	@Override
+	protected void addJson( Map<String, Object> json ) throws ResolutionException
+	{
+		super.addJson( json );
+		rootLexer = Lexer.getByName( (String) json.get( "root_lexer" ) );
+		languageLexer = Lexer.getByName( (String) json.get( "language_lexer" ) );
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private Lexer rootLexer;
+
+	private Lexer languageLexer;
+
+	private static class Insertion
+	{
+		public int index;
+
+		public List<Token> lngBuffer;
+
+		public Insertion( int index, List<Token> lngBuffer )
+		{
+			super();
+			this.index = index;
+			this.lngBuffer = lngBuffer;
+		}
+	}
+
+	private Iterable<Token> doInsertions( List<Insertion> insertions, Iterable<Token> tokens )
+	{
+		ListIterator<Insertion> li = insertions.listIterator();
+		Insertion next_ins = li.hasNext() ? (Insertion) li.next() : null;
+		int len = 0;
+		LinkedList<Token> rc = new LinkedList<Token>();
+
+		for( Token t : tokens )
+		{
+			len += t.getValue().length();
+			String s = t.getValue();
+			int pos = 0;
+			while( next_ins != null && next_ins.index <= len )
+			{
+				rc.add( new Token( t.getPos(), t.getType(), s.substring( pos, s.length() + ( next_ins.index - len ) ) ) );
+				pos = s.length() + ( next_ins.index - len );
+				for( Token tt : next_ins.lngBuffer )
+					rc.add( tt );
+				next_ins = li.hasNext() ? li.next() : null;
+			}
+			if( pos < s.length() )
+				rc.add( new Token( t.getPos(), t.getType(), s.substring( pos ) ) );
+		}
+
+		// Do remaining tokens
+		while( li.hasNext() )
+			for( Token tt : ( (Insertion) li.next() ).lngBuffer )
+				rc.add( tt );
+
+		return rc;
+	}
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/Grammar.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/Grammar.java
new file mode 100644
index 0000000..0af6127
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/Grammar.java
@@ -0,0 +1,154 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.grammar;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.sikuli.syntaxhighlight.Def;
+import org.sikuli.syntaxhighlight.NestedDef;
+import org.sikuli.syntaxhighlight.ResolutionException;
+
+/**
+ * @author Tal Liron
+ */
+public class Grammar extends NestedDef<Grammar>
+{
+	//
+	// Attributes
+	//
+
+	public State getState( String stateName )
+	{
+		State state = statesByName.get( stateName );
+		if( state == null )
+		{
+			state = new State( stateName );
+			statesByName.put( stateName, state );
+			addDef( state );
+		}
+		return state;
+	}
+
+	public State resolveState( String stateName ) throws ResolutionException
+	{
+		if( stateName.startsWith( "#pop" ) )
+		{
+			int depth = 1;
+			if( stateName.length() > 4 )
+			{
+				String depthString = stateName.substring( 5 );
+				try
+				{
+					depth = Integer.parseInt( depthString );
+				}
+				catch( NumberFormatException x )
+				{
+					throw new ResolutionException( x );
+				}
+			}
+			return new RelativeState( false, depth );
+		}
+		else if( stateName.startsWith( "#push" ) )
+		{
+			int depth = 1;
+			if( stateName.length() > 5 )
+			{
+				String depthString = stateName.substring( 6 );
+				try
+				{
+					depth = Integer.parseInt( depthString );
+				}
+				catch( NumberFormatException x )
+				{
+					throw new ResolutionException( x );
+				}
+			}
+			return new RelativeState( true, depth );
+		}
+
+		State state = getState( stateName );
+		if( state.isResolved() )
+			return state;
+		else
+			return null;
+	}
+
+	public List<State> resolveStates( List<String> stateNames ) throws ResolutionException
+	{
+		ArrayList<State> states = new ArrayList<State>();
+		for( String stateName : stateNames )
+		{
+			String[] combinedStateName = stateName.split( "\\+" );
+			if( combinedStateName.length > 1 )
+			{
+				State combinedState = null;
+
+				for( String singleStateName : combinedStateName )
+				{
+					State state = resolveState( singleStateName );
+					if( state == null )
+						return null;
+
+					if( combinedState == null )
+						combinedState = state;
+					else
+						combinedState = new State( combinedState, state );
+				}
+
+				states.add( combinedState );
+			}
+			else
+			{
+				State state = resolveState( stateName );
+				if( state == null )
+					return null;
+
+				states.add( state );
+			}
+		}
+
+		return states;
+	}
+
+	//
+	// Operations
+	//
+
+	public void resolve() throws ResolutionException
+	{
+		resolve( this );
+
+		// Are we resolved?
+		for( Map.Entry<String, State> entry : statesByName.entrySet() )
+		{
+			if( !entry.getValue().isResolved() )
+			{
+				String message = "Unresolved state: " + entry.getKey();
+				Def<Grammar> cause = entry.getValue().getCause( this );
+				while( cause != null )
+				{
+					message += ", cause: " + cause;
+					cause = cause.getCause( this );
+				}
+				throw new ResolutionException( message );
+			}
+		}
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private final Map<String, State> statesByName = new HashMap<String, State>();
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/Lexer.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/Lexer.java
new file mode 100644
index 0000000..18ac312
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/Lexer.java
@@ -0,0 +1,361 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.grammar;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.sikuli.syntaxhighlight.Filter;
+import org.sikuli.syntaxhighlight.Jygments;
+import org.sikuli.syntaxhighlight.ResolutionException;
+import org.sikuli.syntaxhighlight.Util;
+import org.sikuli.syntaxhighlight.grammar.def.ChangeStateTokenRuleDef;
+import org.sikuli.syntaxhighlight.grammar.def.IncludeDef;
+import org.sikuli.syntaxhighlight.grammar.def.TokenRuleDef;
+
+/**
+ * @author Tal Liron
+ */
+public class Lexer extends Grammar
+{
+	//
+	// Static operations
+	//
+
+	public static Lexer getByName( String name ) throws ResolutionException
+	{
+		if( ( name == null ) || ( name.length() == 0 ) )
+			name = "Lexer";
+		else if( Character.isLowerCase( name.charAt( 0 ) ) )
+			name = Character.toUpperCase( name.charAt( 0 ) ) + name.substring( 1 ) + "Lexer";
+
+		Lexer lexer = getByFullName( name );
+		if( lexer != null )
+			return lexer;
+		else
+		{
+			// Try contrib package
+			String pack = Jygments.class.getPackage().getName() + ".contrib";
+			lexer = getByFullName( pack + "." + name );
+			if( lexer == null )
+			{
+				// Try this package
+				pack = Lexer.class.getPackage().getName();
+				lexer = getByFullName( pack + "." + name );
+			}
+			return lexer;
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	public static Lexer getByFullName( String fullName ) throws ResolutionException
+	{
+		// Try cache
+		Lexer lexer = lexers.get( fullName );
+		if( lexer != null )
+			return lexer;
+
+		try
+		{
+			return (Lexer) Jygments.class.getClassLoader().loadClass( fullName ).newInstance();
+		}
+		catch( InstantiationException x )
+		{
+		}
+		catch( IllegalAccessException x )
+		{
+		}
+		catch( ClassNotFoundException x )
+		{
+		}
+
+		InputStream stream = Util.getJsonFile(fullName);
+		if( stream != null )
+		{
+			try
+			{
+				String converted = Util.rejsonToJson( stream );
+				ObjectMapper objectMapper = new ObjectMapper();
+				objectMapper.getFactory().configure( JsonParser.Feature.ALLOW_COMMENTS, true );
+				Map<String, Object> json = objectMapper.readValue( converted, HashMap.class );
+				Object className = json.get( "class" );
+				if( className == null )
+					className = "";
+
+				lexer = getByName( className.toString() );
+				lexer.addJson( json );
+				lexer.resolve();
+
+				if( lexer != null )
+				{
+					// Cache it
+					Lexer existing = lexers.putIfAbsent( fullName, lexer );
+					if( existing != null )
+						lexer = existing;
+				}
+
+				return lexer;
+			}
+			catch( JsonParseException x )
+			{
+				throw new ResolutionException( x );
+			}
+			catch( JsonMappingException x )
+			{
+				throw new ResolutionException( x );
+			}
+			catch( IOException x )
+			{
+				throw new ResolutionException( x );
+			}
+		}
+
+		return null;
+	}
+
+	public static Lexer getForFileName( String fileName ) throws ResolutionException
+	{
+		if( lexerMap.isEmpty() )
+		{
+			try
+			{
+				File jarFile = new File( Jygments.class.getProtectionDomain().getCodeSource().getLocation().toURI() );
+				JarInputStream jarInputStream = new JarInputStream( new FileInputStream( jarFile ) );
+				try
+				{
+					for( JarEntry jarEntry = jarInputStream.getNextJarEntry(); jarEntry != null; jarEntry = jarInputStream.getNextJarEntry() )
+					{
+						if( jarEntry.getName().endsWith( Util.extJSON ) )
+						{
+							String lexerName = jarEntry.getName();
+							// strip off the JSON file ending
+							lexerName = lexerName.substring( 0, lexerName.length() - Util.extJSON.length() );
+							Lexer lexer = Lexer.getByFullName( lexerName );
+							for( String filename : lexer.filenames )
+								if( filename.startsWith( "*." ) )
+									lexerMap.put( filename.substring( filename.lastIndexOf( '.' ) ), lexer );
+						}
+					}
+				}
+				finally
+				{
+					jarInputStream.close();
+				}
+			}
+			catch( URISyntaxException x )
+			{
+				throw new ResolutionException( x );
+			}
+			catch( FileNotFoundException x )
+			{
+				throw new ResolutionException( x );
+			}
+			catch( IOException x )
+			{
+				throw new ResolutionException( x );
+			}
+		}
+
+		return lexerMap.get( fileName.substring( fileName.lastIndexOf( '.' ) ) );
+	}
+
+	//
+	// Construction
+	//
+
+	public Lexer()
+	{
+		this( false, false, 4, "utf8" );
+	}
+
+	public Lexer( boolean stripNewlines, boolean stripAll, int tabSize, String encoding )
+	{
+		this.stripNewLines = stripNewlines;
+		this.stripAll = stripAll;
+		this.tabSize = tabSize;
+	}
+
+	//
+	// Attributes
+	//
+
+	public List<Filter> getFilters()
+	{
+		return filters;
+	}
+
+	public boolean isStripNewLines()
+	{
+		return stripNewLines;
+	}
+
+	public void setStripNewLines( boolean stripNewLines )
+	{
+		this.stripNewLines = stripNewLines;
+	}
+
+	public boolean isStripAll()
+	{
+		return stripAll;
+	}
+
+	public void setStripAll( boolean stripAll )
+	{
+		this.stripAll = stripAll;
+	}
+
+	public int getTabSize()
+	{
+		return tabSize;
+	}
+
+	public void setTabSize( int tabSize )
+	{
+		this.tabSize = tabSize;
+	}
+
+	public void addFilter( Filter filter )
+	{
+		filters.add( filter );
+	}
+
+	public float analyzeText( String text )
+	{
+		return 0;
+	}
+
+	public Iterable<Token> getTokens( String text )
+	{
+		return getTokens( text, false );
+	}
+
+	public Iterable<Token> getTokens( String text, boolean unfiltered )
+	{
+		// text = text.replace( "\r\n", "\n" ).replace( "\r", "\n" );
+		// if( stripAll )
+		// text = text.trim();
+		// if( stripNewLines )
+		// text = text.replace( "\n", "" );
+		if( tabSize > 0 )
+		{
+			// expand tabs
+		}
+		if( !text.endsWith( "\n" ) )
+			text += "\n";
+		Iterable<Token> tokens = getTokensUnprocessed( text );
+		if( !unfiltered )
+		{
+			// apply filters
+		}
+		return tokens;
+	}
+
+	public Iterable<Token> getTokensUnprocessed( String text )
+	{
+		ArrayList<Token> list = new ArrayList<Token>( 1 );
+		list.add( new Token( 0, TokenType.Text, text ) );
+		return list;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Protected
+
+	protected void addAlias( String alias )
+	{
+		aliases.add( alias );
+	}
+
+	protected void addFilename( String filename )
+	{
+		filenames.add( filename );
+	}
+
+	protected void addMimeType( String mimeType )
+	{
+		mimeTypes.add( mimeType );
+	}
+
+	protected void include( String stateName, String includedStateName )
+	{
+		getState( stateName ).addDef( new IncludeDef( stateName, includedStateName ) );
+	}
+
+	protected void rule( String stateName, String pattern, int flags, String tokenTypeName )
+	{
+		getState( stateName ).addDef( new TokenRuleDef( stateName, pattern, flags, tokenTypeName ) );
+	}
+
+	protected void rule( String stateName, String pattern, int flags, String tokenTypeName, String nextStateName )
+	{
+		getState( stateName ).addDef( new ChangeStateTokenRuleDef( stateName, pattern, flags, new String[]
+		{
+			tokenTypeName
+		}, nextStateName ) );
+	}
+
+	protected void rule( String stateName, String pattern, int flags, String[] tokenTypeNames )
+	{
+		getState( stateName ).addDef( new TokenRuleDef( stateName, pattern, flags, tokenTypeNames ) );
+	}
+
+	protected void rule( String stateName, String pattern, int flags, String[] tokenTypeNames, String... nextStateNames )
+	{
+		getState( stateName ).addDef( new ChangeStateTokenRuleDef( stateName, pattern, flags, tokenTypeNames, nextStateNames ) );
+	}
+
+	protected void addJson( Map<String, Object> json ) throws ResolutionException
+	{
+		@SuppressWarnings("unchecked")
+		List<String> filenames = (List<String>) json.get( "filenames" );
+		if( filenames == null )
+			return;
+		for( String filename : filenames )
+			addFilename( filename );
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private static final ConcurrentMap<String, Lexer> lexers = new ConcurrentHashMap<String, Lexer>();
+
+	private static final ConcurrentMap<String, Lexer> lexerMap = new ConcurrentHashMap<String, Lexer>();
+
+	private final List<Filter> filters = new ArrayList<Filter>();
+
+	private boolean stripNewLines;
+
+	private boolean stripAll;
+
+	private int tabSize;
+
+	private final List<String> aliases = new ArrayList<String>();
+
+	private final List<String> filenames = new ArrayList<String>();
+
+	private final List<String> mimeTypes = new ArrayList<String>();
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/PatternRule.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/PatternRule.java
new file mode 100644
index 0000000..728715a
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/PatternRule.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.grammar;
+
+import java.util.regex.Pattern;
+
+/**
+ * @author Tal Liron
+ */
+public abstract class PatternRule extends Rule
+{
+	//
+	// Construction
+	//
+
+	public PatternRule( Pattern pattern )
+	{
+		this.pattern = pattern;
+	}
+
+	//
+	// Attributes
+	//
+
+	public Pattern getPattern()
+	{
+		return pattern;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private final Pattern pattern;
+}
\ No newline at end of file
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/RegexLexer.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/RegexLexer.java
new file mode 100644
index 0000000..9c3472f
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/RegexLexer.java
@@ -0,0 +1,391 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.grammar;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.sikuli.syntaxhighlight.ResolutionException;
+import org.sikuli.syntaxhighlight.grammar.def.ChangeStateTokenRuleDef;
+import org.sikuli.syntaxhighlight.grammar.def.SaveDef;
+import org.sikuli.syntaxhighlight.grammar.def.TokenRuleDef;
+import org.sikuli.syntaxhighlight.grammar.def.UsingRuleDef;
+
+/**
+ * @author Tal Liron
+ */
+public class RegexLexer extends Lexer
+{
+	// //////////////////////////////////////////////////////////////////////////
+	// Protected
+
+	@Override
+	public List<Token> getTokensUnprocessed( String text )
+	{
+		List<Token> tokens = new ArrayList<Token>();
+
+		// Start at root state
+		LinkedList<State> stateStack = new LinkedList<State>();
+		State state = getState( "root" );
+		stateStack.add( state );
+
+		int pos = 0;
+		int length = text.length();
+		while( pos < length )
+		{
+			int eol = text.indexOf( '\n', pos );
+			// int endRegion = eol >= 0 ? eol + 2 : length;
+			// if( endRegion > length )
+			// endRegion = length;
+			int endRegion = length;
+			boolean matches = false;
+
+			// Does any rule in the current state match at the current position?
+			// System.out.println("Text: " + text.substring( pos ));
+			for( Rule rule : new ArrayList<Rule>( state.getRules() ) )
+			{
+				if( rule instanceof PatternRule )
+				{
+					PatternRule patternRule = (PatternRule) rule;
+					// System.out.println( "Trying pattern: " +
+					// rule.getPattern().pattern() );
+					Matcher matcher = patternRule.getPattern().matcher( text );
+					// From current position to end of line
+					// matcher.useTransparentBounds( true );
+					matcher.region( pos, endRegion );
+					if( matcher.lookingAt() )
+					{
+						// System.out.println( "Match! " + matcher.group() + " "
+						// +
+						// rule );
+
+						// Yes, so apply it!
+						if( rule instanceof TokenRule )
+						{
+							TokenRule tokenRule = (TokenRule) rule;
+							List<TokenType> tokenTypes = tokenRule.getTokenTypes();
+							if( tokenTypes.size() == 1 )
+								// Single token
+								tokens.add( new Token( pos, tokenTypes.get( 0 ), matcher.group() ) );
+							else
+							{
+								if( tokenTypes.size() != matcher.groupCount() )
+									throw new RuntimeException( "The number of token types in the rule does not match the number of groups in the regular expression" );
+
+								// Multiple tokens by group
+								int group = 1;
+								for( TokenType tokenType : tokenTypes )
+								{
+									String value = matcher.group( group );
+									// System.out.println( matcher.pattern() +
+									// " " +
+									// value + " " + tokenType );
+									// pos = matcher.start( group );
+									tokens.add( new Token( pos, tokenType, value ) );
+									// pos = matcher.end( group );
+									group++;
+								}
+							}
+
+							// Change state
+							List<State> nextStates = tokenRule.getNextStates();
+							if( nextStates != null )
+							{
+								for( State nextState : nextStates )
+								{
+									if( nextState instanceof RelativeState )
+									{
+										RelativeState relativeState = (RelativeState) nextState;
+										if( relativeState.isPush() )
+											// Push
+											stateStack.addLast( state );
+										else
+											// Pop
+											for( int depth = relativeState.getDepth(); ( depth > 0 ) && !stateStack.isEmpty(); depth-- )
+												state = stateStack.removeLast();
+									}
+									else
+									{
+										// Push and switch
+										stateStack.addLast( state );
+										state = nextState;
+									}
+								}
+							}
+							/*
+							 * else { // Pop if( stateStack.size() > 1 ) state =
+							 * stateStack.removeLast(); }
+							 */
+						}
+						else if( rule instanceof UsingRule )
+						{
+							UsingRule usingRule = (UsingRule) rule;
+							// System.err.println( "!!!!!!!" +
+							// rule.getPattern().pattern() );
+							// System.err.println( "!!!!!!!!!!!!!!" +
+							// matcher.group().length() );
+							Iterable<Token> usingTokens = usingRule.getLexer().getTokensUnprocessed( matcher.group() );
+							for( Token usingToken : usingTokens )
+								tokens.add( usingToken );
+						}
+
+						pos = matcher.end();
+						// System.out.println( pos );
+						matches = true;
+
+						// Don't process other rules here
+						break;
+					}
+				}
+				else if( rule instanceof SaveRule )
+				{
+					SaveRule saveRule = (SaveRule) rule;
+					State saveState = saveRule.getState();
+					if( saveState != state )
+					{
+						saveState.getRules().clear();
+						saveState.include( state );
+					}
+				}
+			}
+
+			if( !matches )
+			{
+				// tokens.add( new Token( pos, TokenType.Error, state.getName()
+				// ) );
+				if( pos != eol )
+				{
+					// Unmatched character
+					tokens.add( new Token( pos, TokenType.Error, text.substring( pos, pos + 1 ) ) );
+				}
+				else
+				{
+					// Fallback for states that don't explicitly match new
+					// lines.
+
+					tokens.add( new Token( pos, TokenType.Text, "\n" ) );
+
+					// Reset state stack
+					/*
+					 * state = getState( "root" ); stateStack.clear();
+					 * stateStack.addLast( state );
+					 */
+				}
+
+				pos += 1;
+			}
+		}
+
+		return tokens;
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	protected void addJson( Map<String, Object> json ) throws ResolutionException
+	{
+		super.addJson( json );
+
+		// Initialize constants
+		Object constantsObject = json.get( "constants" );
+		Map<String, List<String>> constants = new HashMap<String, List<String>>();
+		if( constantsObject != null )
+		{
+			if( !( constantsObject instanceof Map<?, ?> ) )
+				throw new ResolutionException( "\"constants\" must be a map" );
+
+			for( Map.Entry<String, Object> entry : ( (Map<String, Object>) constantsObject ).entrySet() )
+			{
+				String constantName = entry.getKey();
+				Object constantObject = entry.getValue();
+				ArrayList<String> strings = new ArrayList<String>();
+				constants.put( constantName, strings );
+				if( constantObject instanceof List<?> )
+				{
+					StringBuilder pattern = new StringBuilder();
+					for( String patternElement : (List<String>) constantObject )
+						pattern.append( patternElement );
+					strings.add( pattern.toString() );
+				}
+				else if( constantObject instanceof String )
+					strings.add( (String) constantObject );
+				else
+					throw new ResolutionException( "Unexpected value in \"constants\" map: " + constantObject );
+			}
+		}
+
+		// Flags
+		int defaultFlags = Pattern.MULTILINE;
+		Object flagsObject = json.get( "flags" );
+		if( flagsObject != null )
+		{
+			if( !( flagsObject instanceof List<?> ) )
+				throw new ResolutionException( "\"flags\" must be an array of strings" );
+
+			for( Object flagObject : (List<?>) flagsObject )
+			{
+				if( !( flagObject instanceof String ) )
+					throw new ResolutionException( "\"flags\" must be an array of strings" );
+
+				String flag = (String) flagObject;
+				if( flag.equalsIgnoreCase( "CANON_EQ" ) )
+					defaultFlags |= Pattern.CANON_EQ;
+				else if( flag.equalsIgnoreCase( "CASE_INSENSITIVE" ) || flag.equalsIgnoreCase( "IGNORECASE" ) )
+					defaultFlags |= Pattern.CASE_INSENSITIVE;
+				else if( flag.equalsIgnoreCase( "COMMENTS" ) )
+					defaultFlags |= Pattern.COMMENTS;
+				else if( flag.equalsIgnoreCase( "DOTALL" ) )
+					defaultFlags |= Pattern.DOTALL;
+				else if( flag.equalsIgnoreCase( "LITERAL" ) )
+					defaultFlags |= Pattern.LITERAL;
+				else if( flag.equalsIgnoreCase( "MULTILINE" ) )
+					defaultFlags |= Pattern.MULTILINE;
+				else if( flag.equalsIgnoreCase( "UNICODE_CASE" ) )
+					defaultFlags |= Pattern.UNICODE_CASE;
+				else if( flag.equalsIgnoreCase( "UNIX_LINES" ) )
+					defaultFlags |= Pattern.UNIX_LINES;
+				else
+					throw new ResolutionException( "\"flags\" contains an unrecognized flag: " + flag );
+			}
+		}
+
+		Object statesObject = json.get( "states" );
+		if( statesObject == null )
+			throw new ResolutionException( "Grammar does not contain \"states\" map" );
+
+		if( !( statesObject instanceof Map<?, ?> ) )
+			throw new ResolutionException( "\"states\" must be a map" );
+
+		for( Map.Entry<String, Object> entry : ( (Map<String, Object>) statesObject ).entrySet() )
+		{
+			String stateName = entry.getKey();
+			Object stateObject = entry.getValue();
+			if( !( stateObject instanceof Iterable<?> ) )
+				throw new ResolutionException( "State \"" + stateName + "\" must be an array" );
+
+			for( Iterable<Object> arguments : (Iterable<Iterable<Object>>) stateObject )
+			{
+				List<Object> argumentsList = new ArrayList<Object>();
+				for( Object argument : (List<Object>) arguments )
+					argumentsList.add( argument );
+
+				if( argumentsList.isEmpty() )
+					throw new ResolutionException( "Entry in state \"" + stateName + "\" must have at least one argument" );
+
+				Object command = argumentsList.get( 0 );
+				if( !( command instanceof String ) )
+					throw new ResolutionException( "Entry in state \"" + stateName + "\" must have a string as the first argument" );
+
+				if( command.equals( "#include" ) )
+				{
+					if( argumentsList.size() != 2 )
+						throw new ResolutionException( "\"#include\" command in state \"" + stateName + "\" must have a string as an argument" );
+
+					Object includedState = argumentsList.get( 1 );
+					if( !( includedState instanceof String ) )
+						throw new ResolutionException( "\"#include\" command in state \"" + stateName + "\" must have a string as an argument" );
+
+					include( stateName, (String) includedState );
+				}
+				else if( command.equals( "#using" ) )
+				{
+					if( argumentsList.size() != 3 )
+						throw new ResolutionException( "\"#using\" command in state \"" + stateName + "\" must have two strings as arguments" );
+
+					Object pattern = argumentsList.get( 1 );
+					if( !( pattern instanceof String ) )
+						throw new ResolutionException( "\"#using\" command in state \"" + stateName + "\" must have two strings as arguments" );
+
+					Object usingLexerName = argumentsList.get( 2 );
+					if( !( usingLexerName instanceof String ) )
+						throw new ResolutionException( "\"#using\" command in state \"" + stateName + "\" must have two strings as arguments" );
+
+					getState( stateName ).addDef( new UsingRuleDef( stateName, (String) pattern, (String) usingLexerName ) );
+				}
+				else if( command.equals( "#save" ) )
+				{
+					if( argumentsList.size() != 2 )
+						throw new ResolutionException( "\"#save\" command in state \"" + stateName + "\" must have one string as an argument" );
+
+					Object savedStateName = argumentsList.get( 1 );
+					if( !( savedStateName instanceof String ) )
+						throw new ResolutionException( "\"#save\" command in state \"" + stateName + "\" must have one string as an argument" );
+
+					getState( stateName ).addDef( new SaveDef( stateName, (String) savedStateName ) );
+				}
+				else
+				{
+					// Command is a pattern
+					String pattern = (String) command;
+
+					if( pattern.startsWith( "#constant:" ) )
+					{
+						// Concatenate
+						StringBuilder builder = new StringBuilder();
+						String[] concatArguments = pattern.substring( 10 ).split( "," );
+						for( String concatArgument : concatArguments )
+						{
+							List<String> strings = constants.get( concatArgument );
+							if( strings == null )
+								throw new ResolutionException( "Unknown constant \"" + concatArgument + "\" for #pattern in state \"" + stateName + "\" must have at least a token type as an argument" );
+							for( String string : strings )
+								builder.append( string );
+						}
+						pattern = builder.toString();
+					}
+
+					if( argumentsList.size() < 2 )
+						throw new ResolutionException( "Rule in state \"" + stateName + "\" must have at least a token type as an argument" );
+
+					Object tokenTypeNames = argumentsList.get( 1 );
+					if( tokenTypeNames instanceof String )
+					{
+						ArrayList<String> list = new ArrayList<String>( 1 );
+						list.add( (String) tokenTypeNames );
+						tokenTypeNames = list;
+					}
+
+					if( !( tokenTypeNames instanceof List<?> ) )
+						throw new ResolutionException( "Expected token type name or array of token type names in rule in state \"" + stateName + "\"" );
+
+					if( argumentsList.size() == 2 )
+					{
+						// Token rule
+						getState( stateName ).addDef( new TokenRuleDef( stateName, pattern, defaultFlags, (List<String>) tokenTypeNames ) );
+					}
+					else if( argumentsList.size() == 3 )
+					{
+						// Change state token rule
+						Object nextStateNames = argumentsList.get( 2 );
+						if( nextStateNames instanceof String )
+						{
+							ArrayList<String> list = new ArrayList<String>( 1 );
+							list.add( (String) nextStateNames );
+							nextStateNames = list;
+						}
+
+						if( !( nextStateNames instanceof List<?> ) )
+							throw new ResolutionException( "Expected state name or array of state names in rule in state \"" + stateName + "\"" );
+
+						getState( stateName ).addDef( new ChangeStateTokenRuleDef( stateName, pattern, defaultFlags, (List<String>) tokenTypeNames, (List<String>) nextStateNames ) );
+					}
+					else
+						throw new ResolutionException( "Too many arguments for rule in state \"" + stateName + "\"" );
+				}
+			}
+		}
+	}
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/RelativeState.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/RelativeState.java
new file mode 100644
index 0000000..48f263a
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/RelativeState.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.grammar;
+
+/**
+ * @author Tal Liron
+ */
+public class RelativeState extends State
+{
+	//
+	// Construction
+	//
+
+	public RelativeState( boolean push, int depth )
+	{
+		super( push ? "#push" : "#pop" + ( depth > 1 ? ":" + depth : "" ) );
+		this.push = push;
+		this.depth = depth;
+	}
+
+	//
+	// Attributes
+	//
+
+	public boolean isPush()
+	{
+		return push;
+	}
+
+	public int getDepth()
+	{
+		return depth;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private final boolean push;
+
+	private final int depth;
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/Rule.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/Rule.java
new file mode 100644
index 0000000..63e1f81
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/Rule.java
@@ -0,0 +1,20 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.grammar;
+
+
+/**
+ * @author Tal Liron
+ */
+public class Rule
+{
+}
\ No newline at end of file
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/SaveRule.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/SaveRule.java
new file mode 100644
index 0000000..f14443a
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/SaveRule.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.grammar;
+
+/**
+ * @author Tal Liron
+ */
+public class SaveRule extends Rule
+{
+	//
+	// Construction
+	//
+
+	public SaveRule( State state )
+	{
+		super();
+		this.state = state;
+	}
+
+	//
+	// Attributes
+	//
+
+	public State getState()
+	{
+		return state;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private final State state;
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/State.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/State.java
new file mode 100644
index 0000000..422a2bd
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/State.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.grammar;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.sikuli.syntaxhighlight.NestedDef;
+
+/**
+ * @author Tal Liron
+ */
+public class State extends NestedDef<Grammar>
+{
+	//
+	// Construction
+	//
+
+	public State( String name )
+	{
+		super();
+		this.name = name;
+	}
+
+	public State( State state1, State state2 )
+	{
+		this( state1.getName() + "+" + state2.getName() );
+		include( state1 );
+		include( state2 );
+	}
+
+	//
+	// Attributes
+	//
+
+	public String getName()
+	{
+		return name;
+	}
+
+	public List<Rule> getRules()
+	{
+		return rules;
+	}
+
+	//
+	// Operations
+	//
+
+	public void addRule( Rule rule )
+	{
+		rules.add( rule );
+	}
+
+	public void addRuleAt( int location, Rule rule )
+	{
+		rules.add( location, rule );
+	}
+
+	public void include( State includedState )
+	{
+		rules.addAll( includedState.rules );
+	}
+
+	public void includeAt( int location, State includedState )
+	{
+		rules.addAll( location, includedState.rules );
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private final String name;
+
+	private final List<Rule> rules = new ArrayList<Rule>();
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/Token.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/Token.java
new file mode 100644
index 0000000..1babe5f
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/Token.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.grammar;
+
+/**
+ * @author Tal Liron
+ */
+public class Token
+{
+	//
+	// Construction
+	//
+
+	public Token( int pos, TokenType tokenType, String value )
+	{
+		this.pos = pos;
+		this.tokenType = tokenType;
+		this.value = value;
+	}
+
+	//
+	// Attributes
+	//
+
+	public int getPos()
+	{
+		return pos;
+	}
+
+	public TokenType getType()
+	{
+		return tokenType;
+	}
+
+	public String getValue()
+	{
+		return value;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private final int pos;
+
+	private final TokenType tokenType;
+
+	private final String value;
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/TokenRule.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/TokenRule.java
new file mode 100644
index 0000000..f943aa6
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/TokenRule.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.grammar;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * @author Tal Liron
+ */
+public class TokenRule extends PatternRule
+{
+	//
+	// Construction
+	//
+
+	public TokenRule( Pattern pattern, List<TokenType> tokenTypes )
+	{
+		this( pattern, tokenTypes, (List<State>) null );
+	}
+
+	public TokenRule( Pattern pattern, List<TokenType> tokenTypes, List<State> nextStates )
+	{
+		super( pattern );
+		this.nextStates = nextStates;
+		this.tokenTypes = tokenTypes;
+	}
+
+	//
+	// Attributes
+	//
+
+	public List<TokenType> getTokenTypes()
+	{
+		return tokenTypes;
+	}
+
+	public List<State> getNextStates()
+	{
+		return nextStates;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private final List<TokenType> tokenTypes;
+
+	private final List<State> nextStates;
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/TokenType.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/TokenType.java
new file mode 100644
index 0000000..4d41d89
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/TokenType.java
@@ -0,0 +1,274 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.grammar;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Tal Liron
+ */
+public class TokenType
+{
+	//
+	// Constants
+	//
+
+	public static final TokenType Token = create( "Token", "" );
+
+	// Main
+
+	public static final TokenType Whitespace = create( "Whitespace", "w", Token );
+
+	public static final TokenType Text = create( "Text", "", Token );
+
+	public static final TokenType Error = create( "Error", "err", Token );
+
+	public static final TokenType Other = create( "Other", "x", Token );
+
+	// Keywords
+
+	public static final TokenType Keyword = create( "Keyword", "k", Text );
+
+	public static final TokenType Keyword_Constant = create( "Keyword.Constant", "kc", Keyword );
+
+	public static final TokenType Keyword_Declaration = create( "Keyword.Declaration", "kd", Keyword );
+
+	public static final TokenType Keyword_Namespace = create( "Keyword.Namespace", "kn", Keyword );
+
+	public static final TokenType Keyword_Pseudo = create( "Keyword.Pseudo", "kp", Keyword );
+
+	public static final TokenType Keyword_Reserved = create( "Keyword.Reserved", "kr", Keyword );
+
+	public static final TokenType Keyword_Type = create( "Keyword.Type", "kt", Keyword );
+
+	// Names
+
+	public static final TokenType Name = create( "Name", "n", Text );
+
+	public static final TokenType Name_Attribute = create( "Name.Attribute", "na", Name );
+
+	public static final TokenType Name_Builtin = create( "Name.Builtin", "nb", Name );
+
+	public static final TokenType Name_Builtin_Pseudo = create( "Name.Builtin.Pseudo", "bp", Name_Builtin );
+
+	public static final TokenType Name_Class = create( "Name.Class", "nc", Name );
+
+	public static final TokenType Name_Constant = create( "Name.Constant", "no", Name );
+
+	public static final TokenType Name_Decorator = create( "Name.Decorator", "nd", Name );
+
+	public static final TokenType Name_Entity = create( "Name.Entity", "ni", Name );
+
+	public static final TokenType Name_Exception = create( "Name.Exception", "ne", Name );
+
+	public static final TokenType Name_Function = create( "Name.Function", "nf", Name );
+
+	public static final TokenType Name_Property = create( "Name.Property", "py", Name );
+
+	public static final TokenType Name_Label = create( "Name.Label", "nl", Name );
+
+	public static final TokenType Name_Namespace = create( "Name.Namespace", "nn", Name );
+
+	public static final TokenType Name_Other = create( "Name.Other", "nx", Name );
+
+	public static final TokenType Name_Tag = create( "Name.Tag", "nt", Name );
+
+	public static final TokenType Name_Variable = create( "Name.Variable", "nv", Name );
+
+	public static final TokenType Name_Variable_Class = create( "Name.Variable.Class", "vc", Name_Variable );
+
+	public static final TokenType Name_Variable_Global = create( "Name.Variable.Global", "vg", Name_Variable );
+
+	public static final TokenType Name_Variable_Instance = create( "Name.Variable.Instance", "vi", Name_Variable );
+
+	// Literals
+
+	public static final TokenType Literal = create( "Literal", "l", Text );
+
+	public static final TokenType Literal_Date = create( "Literal.Date", "ld", Literal );
+
+	// Strings
+
+	public static final TokenType String = create( "String", "s", Text );
+
+	public static final TokenType String_Backtick = create( "String.Backtick", "sb", String );
+
+	public static final TokenType String_Char = create( "String.Char", "sc", String );
+
+	public static final TokenType String_Doc = create( "String.Doc", "sd", String );
+
+	public static final TokenType String_Double = create( "String.Double", "s2", String );
+
+	public static final TokenType String_Escape = create( "String.Escape", "se", String );
+
+	public static final TokenType String_Heredoc = create( "String.Heredoc", "sh", String );
+
+	public static final TokenType String_Interpol = create( "String.Interpol", "si", String );
+
+	public static final TokenType String_Other = create( "String.Other", "sx", String );
+
+	public static final TokenType String_Regex = create( "String.Regex", "sr", String );
+
+	public static final TokenType String_Single = create( "String.Single", "s1", String );
+
+	public static final TokenType String_Symbol = create( "String.Symbol", "ss", String );
+
+	// Numbers
+
+	public static final TokenType Number = create( "Number", "m", Text );
+
+	public static final TokenType Number_Float = create( "Number.Float", "mf", Number );
+
+	public static final TokenType Number_Hex = create( "Number.Hex", "mh", Number );
+
+	public static final TokenType Number_Integer = create( "Number.Integer", "mi", Number );
+
+	public static final TokenType Number_Integer_Long = create( "Number.Integer.Long", "il", Number_Integer );
+
+	public static final TokenType Number_Oct = create( "Number.Oct", "mo", Number );
+
+	// Operators
+
+	public static final TokenType Operator = create( "Operator", "o", Text );
+
+	public static final TokenType Operator_Word = create( "Operator.Word", "ow", Operator );
+
+	// Punctuation
+
+	public static final TokenType Punctuation = create( "Punctuation", "p", Text );
+
+	// Comments
+
+	public static final TokenType Comment = create( "Comment", "c", Text );
+
+	public static final TokenType Comment_Multiline = create( "Comment.Multiline", "cm", Comment );
+
+	public static final TokenType Comment_Preproc = create( "Comment.Preproc", "cp", Comment );
+
+	public static final TokenType Comment_Single = create( "Comment.Single", "c1", Comment );
+
+	public static final TokenType Comment_Special = create( "Comment.Special", "cs", Comment );
+
+	// Generics
+
+	public static final TokenType Generic = create( "Generic", "g", Text );
+
+	public static final TokenType Generic_Deleted = create( "Generic.Deleted", "gd", Generic );
+
+	public static final TokenType Generic_Emph = create( "Generic.Emph", "ge", Generic );
+
+	public static final TokenType Generic_Error = create( "Generic.Error", "gr", Generic );
+
+	public static final TokenType Generic_Heading = create( "Generic.Heading", "gh", Generic );
+
+	public static final TokenType Generic_Inserted = create( "Generic.Inserted", "gi", Generic );
+
+	public static final TokenType Generic_Output = create( "Generic.Output", "go", Generic );
+
+	public static final TokenType Generic_Prompt = create( "Generic.Prompt", "gp", Generic );
+
+	public static final TokenType Generic_Strong = create( "Generic.Strong", "gs", Generic );
+
+	public static final TokenType Generic_Subheading = create( "Generic.Subheading", "gu", Generic );
+
+	public static final TokenType Generic_Traceback = create( "Generic.Traceback", "gt", Generic );
+
+	//
+	// Static attributes
+	//
+
+	public static TokenType getTokenTypeByName( String name )
+	{
+		return tokenTypesByName.get( name );
+	}
+
+	public static TokenType getTokenTypeByShortName( String shortName )
+	{
+		return tokenTypesByShortName.get( shortName );
+	}
+
+	public static Collection<TokenType> getTokenTypes()
+	{
+		return tokenTypesByName.values();
+	}
+
+	//
+	// Attributes
+	//
+
+	public String getName()
+	{
+		return name;
+	}
+
+	public String getShortName()
+	{
+		return shortName;
+	}
+
+	public TokenType getParent()
+	{
+		return parent;
+	}
+
+	//
+	// Object
+	//
+
+	@Override
+	public String toString()
+	{
+		return name;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private static Map<String, TokenType> tokenTypesByName;
+
+	private static Map<String, TokenType> tokenTypesByShortName;
+
+	private static final TokenType create( String name, String shortName )
+	{
+		return create( name, shortName, null );
+	}
+
+	private static final TokenType create( String name, String shortName, TokenType parent )
+	{
+		TokenType tokenType = new TokenType( name, shortName, parent );
+
+		if( tokenTypesByName == null )
+			tokenTypesByName = new HashMap<String, TokenType>();
+		if( tokenTypesByShortName == null )
+			tokenTypesByShortName = new HashMap<String, TokenType>();
+
+		tokenTypesByName.put( name, tokenType );
+		tokenTypesByShortName.put( shortName, tokenType );
+
+		return tokenType;
+	}
+
+	private final String name;
+
+	private final String shortName;
+
+	private final TokenType parent;
+
+	private TokenType( String name, String shortName, TokenType parent )
+	{
+		this.name = name;
+		this.shortName = shortName;
+		this.parent = parent;
+	}
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/UsingRule.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/UsingRule.java
new file mode 100644
index 0000000..2c17b25
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/UsingRule.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.grammar;
+
+import java.util.regex.Pattern;
+
+/**
+ * @author Tal Liron
+ */
+public class UsingRule extends PatternRule
+{
+	//
+	// Construction
+	//
+
+	public UsingRule( Pattern pattern, Lexer lexer )
+	{
+		super( pattern );
+		this.lexer = lexer;
+	}
+
+	//
+	// Attributes
+	//
+
+	public Lexer getLexer()
+	{
+		return lexer;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private final Lexer lexer;
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/def/ChangeStateTokenRuleDef.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/def/ChangeStateTokenRuleDef.java
new file mode 100644
index 0000000..36d39cc
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/def/ChangeStateTokenRuleDef.java
@@ -0,0 +1,84 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.grammar.def;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.sikuli.syntaxhighlight.ResolutionException;
+import org.sikuli.syntaxhighlight.grammar.Grammar;
+import org.sikuli.syntaxhighlight.grammar.Rule;
+import org.sikuli.syntaxhighlight.grammar.State;
+import org.sikuli.syntaxhighlight.grammar.TokenRule;
+import org.sikuli.syntaxhighlight.grammar.TokenType;
+
+/**
+ * @author Tal Liron
+ */
+public class ChangeStateTokenRuleDef extends TokenRuleDef
+{
+	//
+	// Construction
+	//
+
+	public ChangeStateTokenRuleDef( String stateName, String pattern, int flags, List<String> tokenTypeNames, List<String> nextStateNames )
+	{
+		super( stateName, pattern, flags, tokenTypeNames );
+		this.nextStateNames = nextStateNames;
+	}
+
+	public ChangeStateTokenRuleDef( String stateName, String pattern, int flags, String[] tokenTypeNames, String... nextStateNames )
+	{
+		super( stateName, pattern, flags, tokenTypeNames );
+		ArrayList<String> list = new ArrayList<String>( nextStateNames.length );
+		for( String nextStateName : nextStateNames )
+			list.add( nextStateName );
+		this.nextStateNames = list;
+	}
+
+	//
+	// Def
+	//
+
+	@Override
+	public boolean resolve( Grammar grammar ) throws ResolutionException
+	{
+		if( grammar.resolveStates( nextStateNames ) != null )
+			return super.resolve( grammar );
+		else
+		{
+			if( placeHolder == null )
+			{
+				placeHolder = new Rule();
+				State state = grammar.getState( stateName );
+				state.addRule( placeHolder );
+			}
+			return false;
+		}
+	}
+
+	//
+	// TokenRuleDef
+	//
+
+	@Override
+	protected TokenRule createTokenRule( Pattern pattern, List<TokenType> tokenTypes, Grammar grammar ) throws ResolutionException
+	{
+		return new TokenRule( pattern, tokenTypes, grammar.resolveStates( nextStateNames ) );
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private final List<String> nextStateNames;
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/def/IncludeDef.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/def/IncludeDef.java
new file mode 100644
index 0000000..96921f5
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/def/IncludeDef.java
@@ -0,0 +1,86 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.grammar.def;
+
+import org.sikuli.syntaxhighlight.Def;
+import org.sikuli.syntaxhighlight.ResolutionException;
+import org.sikuli.syntaxhighlight.grammar.Grammar;
+import org.sikuli.syntaxhighlight.grammar.Rule;
+import org.sikuli.syntaxhighlight.grammar.State;
+
+/**
+ * @author Tal Liron
+ */
+public class IncludeDef extends StateDef
+{
+	public IncludeDef( String stateName, String includedStateName )
+	{
+		super( stateName );
+		this.includedStateName = includedStateName;
+	}
+
+	//
+	// Def
+	//
+
+	@Override
+	public boolean resolve( Grammar grammar ) throws ResolutionException
+	{
+		State state = grammar.getState( stateName );
+		State includedState = grammar.getState( includedStateName );
+
+		// Only include a resolved state
+		if( includedState.isResolved() )
+		{
+			if( placeHolder != null )
+			{
+				int location = state.getRules().indexOf( placeHolder );
+				state.getRules().remove( placeHolder );
+				state.includeAt( location, includedState );
+			}
+			else
+				state.include( includedState );
+
+			resolved = true;
+			return true;
+		}
+		else if( placeHolder == null )
+		{
+			// Remember location
+			placeHolder = new Rule();
+			state.addRule( placeHolder );
+		}
+
+		return false;
+	}
+
+	@Override
+	public Def<Grammar> getCause( Grammar grammar )
+	{
+		return grammar.getState( includedStateName ).getCause( grammar );
+	}
+
+	//
+	// Object
+	//
+
+	@Override
+	public String toString()
+	{
+		return super.toString() + " " + stateName + ", " + includedStateName;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private final String includedStateName;
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/def/SaveDef.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/def/SaveDef.java
new file mode 100644
index 0000000..56c6d60
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/def/SaveDef.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.grammar.def;
+
+import org.sikuli.syntaxhighlight.ResolutionException;
+import org.sikuli.syntaxhighlight.grammar.Grammar;
+import org.sikuli.syntaxhighlight.grammar.Rule;
+import org.sikuli.syntaxhighlight.grammar.SaveRule;
+import org.sikuli.syntaxhighlight.grammar.State;
+
+/**
+ * @author Tal Liron
+ */
+public class SaveDef extends StateDef
+{
+	public SaveDef( String stateName, String savedStateName )
+	{
+		super( stateName );
+		this.savedStateName = savedStateName;
+	}
+
+	//
+	// Def
+	//
+
+	@Override
+	public boolean resolve( Grammar grammar ) throws ResolutionException
+	{
+		State state = grammar.getState( stateName );
+		State savedState = grammar.getState( savedStateName );
+
+		// Only include a resolved state
+		if( savedState.isResolved() )
+		{
+			if( placeHolder != null )
+			{
+				int location = state.getRules().indexOf( placeHolder );
+				state.getRules().remove( placeHolder );
+				state.addRuleAt( location, new SaveRule( savedState ) );
+			}
+			else
+				state.addRule( new SaveRule( savedState ) );
+
+			resolved = true;
+			return true;
+		}
+		else if( placeHolder == null )
+		{
+			// Remember location
+			placeHolder = new Rule();
+			state.addRule( placeHolder );
+		}
+
+		return false;
+	}
+
+	//
+	// Object
+	//
+
+	@Override
+	public String toString()
+	{
+		return super.toString() + " " + stateName + ", " + savedStateName;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private final String savedStateName;
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/def/StateDef.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/def/StateDef.java
new file mode 100644
index 0000000..ff585e2
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/def/StateDef.java
@@ -0,0 +1,43 @@
+package org.sikuli.syntaxhighlight.grammar.def;
+
+import org.sikuli.syntaxhighlight.Def;
+import org.sikuli.syntaxhighlight.grammar.Grammar;
+import org.sikuli.syntaxhighlight.grammar.Rule;
+
+public abstract class StateDef extends Def<Grammar>
+{
+	//
+	// Construction
+	//
+
+	public StateDef( String stateName )
+	{
+		this.stateName = stateName;
+	}
+
+	//
+	// Attributes
+	//
+
+	public String getStateName()
+	{
+		return stateName;
+	}
+
+	//
+	// Object
+	//
+
+	@Override
+	public String toString()
+	{
+		return super.toString() + " " + stateName;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Protected
+
+	protected final String stateName;
+
+	protected Rule placeHolder = null;
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/def/TokenRuleDef.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/def/TokenRuleDef.java
new file mode 100644
index 0000000..7746493
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/def/TokenRuleDef.java
@@ -0,0 +1,135 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.grammar.def;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.sikuli.syntaxhighlight.ResolutionException;
+import org.sikuli.syntaxhighlight.grammar.Grammar;
+import org.sikuli.syntaxhighlight.grammar.State;
+import org.sikuli.syntaxhighlight.grammar.TokenRule;
+import org.sikuli.syntaxhighlight.grammar.TokenType;
+
+/**
+ * @author Tal Liron
+ */
+public class TokenRuleDef extends StateDef
+{
+	//
+	// Construction
+	//
+
+	public TokenRuleDef( String stateName, String pattern, int flags, List<String> tokenTypeNames )
+	{
+		super( stateName );
+		this.pattern = pattern;
+		this.flags = flags;
+		this.tokenTypeNames = tokenTypeNames;
+	}
+
+	public TokenRuleDef( String stateName, String pattern, int flags, String... tokenTypeNames )
+	{
+		super( stateName );
+		this.pattern = pattern;
+		this.flags = flags;
+		ArrayList<String> list = new ArrayList<String>( tokenTypeNames.length );
+		for( String tokenTypeName : tokenTypeNames )
+			list.add( tokenTypeName );
+		this.tokenTypeNames = list;
+	}
+
+	//
+	// Attributes
+	//
+
+	public String getPattern()
+	{
+		return pattern;
+	}
+
+	public List<String> getTokenTypeNames()
+	{
+		return tokenTypeNames;
+	}
+
+	//
+	// Def
+	//
+
+	@Override
+	public boolean resolve( Grammar grammar ) throws ResolutionException
+	{
+		Pattern pattern;
+		try
+		{
+			//pattern = Pattern.compile( this.pattern, Pattern.MULTILINE | Pattern.DOTALL );
+			pattern = Pattern.compile( this.pattern, flags );
+		}
+		catch( PatternSyntaxException x )
+		{
+			throw new ResolutionException( "RegEx syntax error: " + this.pattern, x );
+		}
+
+		ArrayList<TokenType> tokenTypes = new ArrayList<TokenType>();
+		for( String tokenTypeName : tokenTypeNames )
+		{
+			TokenType tokenType = TokenType.getTokenTypeByName( tokenTypeName );
+			if( tokenType == null )
+				throw new ResolutionException( "Unknown token type: " + tokenTypeName );
+			tokenTypes.add( tokenType );
+		}
+
+		TokenRule rule = createTokenRule( pattern, tokenTypes, grammar );
+		State state = grammar.getState( stateName );
+		if( placeHolder != null )
+		{
+			int location = state.getRules().indexOf( placeHolder );
+			state.getRules().remove( placeHolder );
+			state.addRuleAt( location, rule );
+		}
+		else
+			state.addRule( rule );
+
+		resolved = true;
+		return true;
+	}
+
+	//
+	// Object
+	//
+
+	@Override
+	public String toString()
+	{
+		return super.toString() + ", " + pattern + ", " + tokenTypeNames;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Protected
+
+	protected TokenRule createTokenRule( Pattern pattern, List<TokenType> tokenTypes, Grammar grammar ) throws ResolutionException
+	{
+		return new TokenRule( pattern, tokenTypes );
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private final String pattern;
+
+	private final int flags;
+
+	private final List<String> tokenTypeNames;
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/def/UsingRuleDef.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/def/UsingRuleDef.java
new file mode 100644
index 0000000..1d7d444
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/grammar/def/UsingRuleDef.java
@@ -0,0 +1,98 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.grammar.def;
+
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.sikuli.syntaxhighlight.ResolutionException;
+import org.sikuli.syntaxhighlight.grammar.Grammar;
+import org.sikuli.syntaxhighlight.grammar.Lexer;
+import org.sikuli.syntaxhighlight.grammar.State;
+import org.sikuli.syntaxhighlight.grammar.UsingRule;
+
+/**
+ * @author Tal Liron
+ */
+public class UsingRuleDef extends StateDef
+{
+	//
+	// Construction
+	//
+
+	public UsingRuleDef( String stateName, String pattern, String usingLexerName )
+	{
+		super( stateName );
+		this.pattern = pattern;
+		this.usingLexerName = usingLexerName;
+	}
+
+	//
+	// Attributes
+	//
+
+	public String getPattern()
+	{
+		return pattern;
+	}
+
+	public String getUsingLexerName()
+	{
+		return usingLexerName;
+	}
+
+	//
+	// Def
+	//
+
+	@Override
+	public boolean resolve( Grammar grammar ) throws ResolutionException
+	{
+		Pattern pattern;
+		try
+		{
+			pattern = Pattern.compile( this.pattern, Pattern.MULTILINE | Pattern.DOTALL );
+		}
+		catch( PatternSyntaxException x )
+		{
+			throw new ResolutionException( "RegEx syntax error: " + this.pattern, x );
+		}
+
+		Lexer usingLexer = Lexer.getByName( usingLexerName );
+		UsingRule rule = new UsingRule( pattern, usingLexer );
+		State state = grammar.getState( stateName );
+		state.addRule( rule );
+
+		resolved = true;
+		return true;
+	}
+
+	//
+	// Object
+	//
+
+	@Override
+	public String toString()
+	{
+		return super.toString() + ", " + pattern + ", " + usingLexerName;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Protected
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private final String pattern;
+
+	private final String usingLexerName;
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/scan/EndOfText.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/scan/EndOfText.java
new file mode 100644
index 0000000..3267e81
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/scan/EndOfText.java
@@ -0,0 +1,20 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.scan;
+
+/**
+ * @author Tal Liron
+ */
+public class EndOfText extends Exception
+{
+	private static final long serialVersionUID = 1L;
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/scan/Scanner.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/scan/Scanner.java
new file mode 100644
index 0000000..ecb7d13
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/scan/Scanner.java
@@ -0,0 +1,121 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.scan;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author Tal Liron
+ */
+public abstract class Scanner
+{
+	//
+	// Construction
+	//
+
+	public Scanner( String text, int flags )
+	{
+		this.data = text;
+		this.flags = flags;
+		dataLength = data.length();
+	}
+
+	//
+	// Attributes
+	//
+
+	public int getStartPos()
+	{
+		return startPos;
+	}
+
+	public int getPos()
+	{
+		return pos;
+	}
+
+	public String getLast()
+	{
+		return last;
+	}
+
+	public String getMatch()
+	{
+		return match;
+	}
+
+	public boolean isEos()
+	{
+		return pos >= dataLength;
+	}
+
+	public Matcher check( String pattern ) throws EndOfText
+	{
+		if( isEos() )
+			throw new EndOfText();
+		Pattern re = patternCache.get( pattern );
+		if( re == null )
+		{
+			re = Pattern.compile( pattern, flags );
+			patternCache.put( pattern, re );
+		}
+		return re.matcher( data.substring( pos ) );
+	}
+
+	public boolean test( String pattern ) throws EndOfText
+	{
+		return check( pattern ).matches();
+	}
+
+	public boolean scan( String pattern ) throws EndOfText
+	{
+		if( isEos() )
+			throw new EndOfText();
+		Pattern re = patternCache.get( pattern );
+		if( re == null )
+		{
+			re = Pattern.compile( pattern, flags );
+			patternCache.put( pattern, re );
+		}
+		last = match;
+		Matcher matcher = re.matcher( data.substring( pos ) );
+		if( !matcher.matches() )
+			return false;
+		startPos = matcher.start();
+		pos = matcher.end();
+		match = matcher.group();
+		return true;
+	}
+
+	public boolean getChar() throws EndOfText
+	{
+		return scan( "." );
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private static final Map<String, Pattern> patternCache = new HashMap<String, Pattern>();
+
+	private final int flags;
+
+	private final int dataLength;
+
+	private String data;
+
+	private int startPos, pos;
+
+	private String match, last;
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/style/ColorStyleElement.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/style/ColorStyleElement.java
new file mode 100644
index 0000000..94319ea
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/style/ColorStyleElement.java
@@ -0,0 +1,86 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.style;
+
+/**
+ * @author Tal Liron
+ */
+public class ColorStyleElement extends StyleElement
+{
+	//
+	// Types
+	//
+
+	public enum Type
+	{
+		Foreground, Background, Border
+	}
+
+	//
+	// Static attributes
+	//
+
+	public static ColorStyleElement getColorStyleElementByName( String name )
+	{
+		if( name.startsWith( "bg:" ) )
+			return background( name.substring( 3 ) );
+		if( name.startsWith( "border:" ) )
+			return border( name.substring( 7 ) );
+		return foreground( name );
+	}
+
+	public static ColorStyleElement foreground( String color )
+	{
+		return new ColorStyleElement( color, Type.Foreground, color );
+	}
+
+	public static ColorStyleElement background( String color )
+	{
+		return new ColorStyleElement( "bg:" + color, Type.Background, color );
+	}
+
+	public static ColorStyleElement border( String color )
+	{
+		return new ColorStyleElement( "border:" + color, Type.Border, color );
+	}
+
+	//
+	// Attributes
+	//
+
+	public String getColor()
+	{
+		return color;
+	}
+
+	public Type getType()
+	{
+		return type;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Protected
+
+	protected ColorStyleElement( String name, Type type, String color )
+	{
+		super( name );
+		this.type = type;
+		this.color = color;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private final String color;
+
+	private final Type type;
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/style/EffectStyleElement.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/style/EffectStyleElement.java
new file mode 100644
index 0000000..d8d6b38
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/style/EffectStyleElement.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.style;
+
+/**
+ * @author Tal Liron
+ */
+public class EffectStyleElement extends StyleElement
+{
+	//
+	// Constants
+	//
+
+	public static final EffectStyleElement Bold = create( "bold" );
+
+	public static final EffectStyleElement NoBold = create( "nobold" );
+
+	public static final EffectStyleElement Italic = create( "italic" );
+
+	public static final EffectStyleElement NoItalic = create( "noitalic" );
+
+	public static final EffectStyleElement Underline = create( "underline" );
+
+	public static final EffectStyleElement NoUnderline = create( "nounderline" );
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Protected
+
+	protected EffectStyleElement( String name )
+	{
+		super( name );
+	}
+
+	private static EffectStyleElement create( String name )
+	{
+		EffectStyleElement fontStyleElement = new EffectStyleElement( name );
+		add( fontStyleElement );
+		return fontStyleElement;
+	}
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/style/FontStyleElement.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/style/FontStyleElement.java
new file mode 100644
index 0000000..155d3fe
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/style/FontStyleElement.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.style;
+
+/**
+ * @author Tal Liron
+ */
+public class FontStyleElement extends StyleElement
+{
+	//
+	// Constants
+	//
+
+	public static final FontStyleElement Roman = create( "roman" );
+
+	public static final FontStyleElement Sans = create( "sans" );
+
+	public static final FontStyleElement Mono = create( "mono" );
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Protected
+
+	protected FontStyleElement( String name )
+	{
+		super( name );
+	}
+
+	private static FontStyleElement create( String name )
+	{
+		FontStyleElement fontStyleElement = new FontStyleElement( name );
+		add( fontStyleElement );
+		return fontStyleElement;
+	}
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/style/Style.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/style/Style.java
new file mode 100644
index 0000000..3ad8ce8
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/style/Style.java
@@ -0,0 +1,235 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.style;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.sikuli.syntaxhighlight.Jygments;
+import org.sikuli.syntaxhighlight.NestedDef;
+import org.sikuli.syntaxhighlight.ResolutionException;
+import org.sikuli.syntaxhighlight.grammar.TokenType;
+import org.sikuli.syntaxhighlight.style.def.StyleElementDef;
+
+/**
+ * @author Tal Liron
+ */
+public class Style extends NestedDef<Style>
+{
+	static String extJSON = ".jso";
+	//
+	// Static operations
+	//
+
+	public static Style getByName( String name ) throws ResolutionException
+	{
+		if( Character.isLowerCase( name.charAt( 0 ) ) )
+			name = Character.toUpperCase( name.charAt( 0 ) ) + name.substring( 1 ) + "Style";
+
+		Style style = getByFullName( name );
+		if( style != null )
+			return style;
+		else
+		{
+			// Try contrib package
+			String pack = Jygments.class.getPackage().getName() + ".contrib";
+			name = pack + "." + name;
+			return getByFullName( name );
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	public static Style getByFullName( String fullName ) throws ResolutionException
+	{
+		// Try cache
+		Style style = styles.get( fullName );
+		if( style != null )
+			return style;
+
+		try
+		{
+			return (Style) Jygments.class.getClassLoader().loadClass( fullName ).newInstance();
+		}
+		catch( InstantiationException x )
+		{
+		}
+		catch( IllegalAccessException x )
+		{
+		}
+		catch( ClassNotFoundException x )
+		{
+		}
+
+		InputStream stream = Jygments.class.getClassLoader().getResourceAsStream( fullName.replace( '.', '/' ) + extJSON );
+		if( stream != null )
+		{
+			ObjectMapper objectMapper = new ObjectMapper();
+			objectMapper.getFactory().configure( JsonParser.Feature.ALLOW_COMMENTS, true );
+			try
+			{
+				Map<String, Object> json = objectMapper.readValue( stream, HashMap.class );
+				style = new Style();
+				style.addJson( json );
+				style.resolve();
+
+				// Cache it
+				Style existing = styles.putIfAbsent( fullName, style );
+				if( existing != null )
+					style = existing;
+
+				return style;
+			}
+			catch( JsonParseException x )
+			{
+				throw new ResolutionException( x );
+			}
+			catch( JsonMappingException x )
+			{
+				throw new ResolutionException( x );
+			}
+			catch( IOException x )
+			{
+				throw new ResolutionException( x );
+			}
+		}
+
+		return null;
+	}
+
+	//
+	// Attributes
+	//
+
+	public Map<TokenType, List<StyleElement>> getStyleElements()
+	{
+		return styleElements;
+	}
+
+	//
+	// Operations
+	//
+
+	public void addStyleElement( TokenType tokenType, StyleElement styleElement )
+	{
+		List<StyleElement> styleElementsForTokenType = styleElements.get( tokenType );
+		if( styleElementsForTokenType == null )
+		{
+			styleElementsForTokenType = new ArrayList<StyleElement>();
+			styleElements.put( tokenType, styleElementsForTokenType );
+		}
+		styleElementsForTokenType.add( styleElement );
+	}
+
+	public void resolve() throws ResolutionException
+	{
+		resolve( this );
+	}
+
+	//
+	// Def
+	//
+
+	@Override
+	public boolean resolve( Style style ) throws ResolutionException
+	{
+		if( super.resolve( style ) )
+		{
+			boolean done = false;
+			while( !done )
+			{
+				done = true;
+				for( TokenType tokenType : TokenType.getTokenTypes() )
+				{
+					if( tokenType != TokenType.Token )
+					{
+						if( !styleElements.containsKey( tokenType ) )
+						{
+							boolean doneOne = false;
+							TokenType parent = tokenType.getParent();
+							while( parent != null )
+							{
+								if( parent == TokenType.Token )
+								{
+									doneOne = true;
+									break;
+								}
+
+								List<StyleElement> parentElements = styleElements.get( parent );
+								if( parentElements != null )
+								{
+									styleElements.put( tokenType, parentElements );
+									doneOne = true;
+									break;
+								}
+
+								parent = parent.getParent();
+							}
+
+							if( !doneOne )
+								done = false;
+						}
+					}
+				}
+			}
+
+			return true;
+		}
+		else
+			return false;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Protected
+
+	protected void add( String tokenTypeName, String... styleElementNames )
+	{
+		ArrayList<String> list = new ArrayList<String>( styleElementNames.length );
+		for( String styleElementName : styleElementNames )
+			list.add( styleElementName );
+		addDef( new StyleElementDef( tokenTypeName, list ) );
+	}
+
+	@SuppressWarnings("unchecked")
+	protected void addJson( Map<String, Object> json ) throws ResolutionException
+	{
+		for( Map.Entry<String, Object> entry : json.entrySet() )
+		{
+			String tokenTypeName = entry.getKey();
+			if( entry.getValue() instanceof Iterable<?> )
+			{
+				for( String styleElementName : (Iterable<String>) entry.getValue() )
+					add( tokenTypeName, styleElementName );
+			}
+			else if( entry.getValue() instanceof String )
+				add( tokenTypeName, (String) entry.getValue() );
+			else
+				throw new ResolutionException( "Unexpected value in style definition: " + entry.getValue() );
+		}
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private static final ConcurrentMap<String, Style> styles = new ConcurrentHashMap<String, Style>();
+
+	private final Map<TokenType, List<StyleElement>> styleElements = new HashMap<TokenType, List<StyleElement>>();
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/style/StyleElement.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/style/StyleElement.java
new file mode 100644
index 0000000..97fbd64
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/style/StyleElement.java
@@ -0,0 +1,83 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.style;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Tal Liron
+ */
+public class StyleElement
+{
+	static
+	{
+		// Force these classes to load
+		new ColorStyleElement( null, null, null );
+		new EffectStyleElement( null );
+		new FontStyleElement( null );
+	}
+
+	//
+	// Static attributes
+	//
+
+	public static StyleElement getStyleElementByName( String name )
+	{
+		StyleElement styleElement = styleElementsByName.get( name );
+		if( styleElement == null )
+			styleElement = ColorStyleElement.getColorStyleElementByName( name );
+		return styleElement;
+	}
+
+	//
+	// Attributes
+	//
+
+	public String getName()
+	{
+		return name;
+	}
+
+	//
+	// Object
+	//
+
+	@Override
+	public String toString()
+	{
+		return name;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Protected
+
+	protected static final void add( StyleElement styleElement )
+	{
+		if( styleElementsByName == null )
+			styleElementsByName = new HashMap<String, StyleElement>();
+
+		styleElementsByName.put( styleElement.name, styleElement );
+	}
+
+	protected StyleElement( String name )
+	{
+		this.name = name;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private static Map<String, StyleElement> styleElementsByName;
+
+	private final String name;
+}
diff --git a/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/style/def/StyleElementDef.java b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/style/def/StyleElementDef.java
new file mode 100644
index 0000000..230bab9
--- /dev/null
+++ b/Jygments4Sikuli/src/main/java/org/sikuli/syntaxhighlight/style/def/StyleElementDef.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright 2010-2013 Three Crickets LLC.
+ * <p>
+ * The contents of this file are subject to the terms of a BSD license. See
+ * attached license.txt.
+ * <p>
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly from Three Crickets
+ * at http://threecrickets.com/
+ */
+
+package org.sikuli.syntaxhighlight.style.def;
+
+import java.util.List;
+
+import org.sikuli.syntaxhighlight.Def;
+import org.sikuli.syntaxhighlight.ResolutionException;
+import org.sikuli.syntaxhighlight.grammar.TokenType;
+import org.sikuli.syntaxhighlight.style.Style;
+import org.sikuli.syntaxhighlight.style.StyleElement;
+
+/**
+ * @author Tal Liron
+ */
+public class StyleElementDef extends Def<Style>
+{
+	//
+	// Construction
+	//
+
+	public StyleElementDef( String tokenTypeName, List<String> styleElementNames )
+	{
+		this.tokenTypeName = tokenTypeName;
+		this.styleElementNames = styleElementNames;
+	}
+
+	//
+	// Def
+	//
+
+	@Override
+	public boolean resolve( Style style ) throws ResolutionException
+	{
+		TokenType tokenType = TokenType.getTokenTypeByName( tokenTypeName );
+		if( tokenType == null )
+			throw new ResolutionException( "Unknown token type: " + tokenTypeName );
+
+		//TokenType parent = tokenType.getParent();
+		//boolean addToParent = false;
+		//if( ( parent != null ) && ( !style.getStyleElements().containsKey( parent ) ) )
+			//addToParent = true;
+		for( String styleElementName : styleElementNames )
+		{
+			StyleElement styleElement = StyleElement.getStyleElementByName( styleElementName );
+			if( styleElement == null )
+				throw new ResolutionException( "Unknown style element: " + styleElementName );
+
+			style.addStyleElement( tokenType, styleElement );
+			//if( addToParent )
+				//style.addStyleElement( parent, styleElement );
+		}
+
+		resolved = true;
+		return true;
+	}
+
+	// //////////////////////////////////////////////////////////////////////////
+	// Private
+
+	private final String tokenTypeName;
+
+	private final List<String> styleElementNames;
+}
diff --git a/Jygments4Sikuli/src/main/resources/contrib/ClojureLexer.jso b/Jygments4Sikuli/src/main/resources/contrib/ClojureLexer.jso
new file mode 100644
index 0000000..468f28f
--- /dev/null
+++ b/Jygments4Sikuli/src/main/resources/contrib/ClojureLexer.jso
@@ -0,0 +1,132 @@
+/*
+* Clojure Lexer
+*/
+
+{
+	"name": "Clojure",
+	"aliases": ["clojure", "clj"],
+	"filenames": ["*.clj"],
+	"mimetypes": ["text/x-clojure", "application/x-clojure"],
+
+	"class": "regex",
+
+	"states":
+	{
+		"root":
+		[
+			/* the comments - always starting with semicolon
+			and going to the end of the line */
+			[r';.*$', "Comment.Single"],
+			
+			/* whitespaces - usually not relevant */
+			[r'\s+', "Text"],
+			
+			/* numbers */
+			[r'-?\d+\.\d+', "Number.Float"],
+			[r'-?\d+', "Number.Integer"],
+			/* support for uncommon kinds of numbers -
+			have to figure out what the characters mean
+			(r'(#e|#i|#b|#o|#d|#x)[\d.]+', "Number"), */
+			
+			/* strings, symbols and characters */
+			[r'"(\\\\|\\"|[^"])*"', "String"],
+			[r"'[a-zA-Z0-9!$%&*+,/:<=>?@^_~-]+", "String.Symbol"],
+			[r'\\([()/\'\".\'_!§$%& ?;=+-]{1}|[a-zA-Z0-9]+)', "String.Char"],
+			
+			/* constants */
+			[r'(#t|#f)', "Name.Constant"],
+			
+			/* special operators */
+			[r"('|#|`|,@|,|\.)", "Operator"],
+			
+			/* highlight the keywords */
+			["#constant:keywords", "Keyword"],
+			
+			/* first variable in a quoted string like
+			'(this is syntactic sugar) */
+			[r"(?<='\()[a-zA-Z0-9!$%&*+,/:<=>?@^_~-]+", "Name.Variable"],
+			[r"(?<=#\()[a-zA-Z0-9!$%&*+,/:<=>?@^_~-]+", "Name.Variable"],
+			
+			/* highlight the builtins */
+			["#constant:builtins", "Name.Builtin"],
+			
+			/* the remaining functions */
+			[r'(?<=\()[a-zA-Z0-9!$%&*+,/:<=>?@^_~-]+', "Name.Function"],
+			/* find the remaining variables */
+			[r'[a-zA-Z0-9!$%&*+,/:<=>?@^_~-]+', "Name.Variable"],
+			
+			/* Clojure accepts vector notation */
+			[r'(\[|\])', "Punctuation"],
+			
+			/* Clojure accepts map notation */
+			[r'({|})', "Punctuation"],
+			
+			/* the famous parentheses! */
+			[r'(\(|\))', "Punctuation"]
+		]
+	},
+
+
+	"constants":
+	{
+		"keywords":
+		[
+			r'(fn|def|defn|defmacro|defmethod|defmulti|defn-|',
+			r'defstruct|if|cond|let|for)'
+		],
+
+		"builtins":
+		[
+			r'(?<=\()(.|..|',
+			r'\*|\+|-|->|..|/|<|<=|=|==|>|>=|',
+			r'accessor|agent|agent-errors|aget|alength|all-ns|',
+			r'alter|and|append-child|apply|array-map|aset|',
+			r'aset-boolean|aset-byte|aset-char|aset-double|aset-float|',
+			r'aset-int|aset-long|aset-short|assert|assoc|await|',
+			r'await-for|bean|binding|bit-and|bit-not|bit-or|',
+			r'bit-shift-left|bit-shift-right|bit-xor|boolean|branch\?|',
+			r'butlast|byte|cast|char|children|class|',
+			r'clear-agent-errors|comment|commute|comp|comparator|',
+			r'complement|concat|conj|cons|constantly|',
+			r'construct-proxy|contains\?|count|create-ns|create-struct|',
+			r'cycle|dec|deref|difference|disj|dissoc|distinct|',
+			r'doall|doc|dorun|doseq|dosync|dotimes|doto|',
+			r'double|down|drop|drop-while|edit|end\?|ensure|',
+			r'eval|every\?|false\?|ffirst|file-seq|filter|find|',
+			r'find-doc|find-ns|find-var|first|float|flush|',
+			r'fnseq|frest|gensym|get|get-proxy-class|',
+			r'hash-map|hash-set|identical\?|identity|if-let|import|',
+			r'in-ns|inc|index|insert-child|insert-left|insert-right|',
+			r'inspect-table|inspect-tree|instance\?|int|interleave|',
+			r'intersection|into|into-array|iterate|join|key|keys|',
+			r'keyword|keyword\?|last|lazy-cat|lazy-cons|left|',
+			r'lefts|line-seq|list|list\*|load|load-file|',
+			r'locking|long|loop|macroexpand|macroexpand-1|',
+			r'make-array|make-node|map|map-invert|map\?|mapcat|',
+			r'max|max-key|memfn|merge|merge-with|meta|min|',
+			r'min-key|name|namespace|neg\?|new|newline|next|',
+			r'nil\?|node|not|not-any\?|not-every\?|not=|ns-imports|',
+			r'ns-interns|ns-map|ns-name|ns-publics|ns-refers|',
+			r'ns-resolve|ns-unmap|nth|nthrest|or|parse|partial|',
+			r'path|peek|pop|pos\?|pr|pr-str|print|print-str|',
+			r'println|println-str|prn|prn-str|project|proxy|',
+			r'proxy-mappings|quot|rand|rand-int|range|re-find|',
+			r're-groups|re-matcher|re-matches|re-pattern|re-seq|',
+			r'read|read-line|reduce|ref|ref-set|refer|rem|',
+			r'remove|remove-method|remove-ns|rename|rename-keys|',
+			r'repeat|replace|replicate|resolve|rest|resultset-seq|',
+			r'reverse|rfirst|right|rights|root|rrest|rseq|',
+			r'second|select|select-keys|send|send-off|seq|',
+			r'seq-zip|seq\?|set|short|slurp|some|sort|',
+			r'sort-by|sorted-map|sorted-map-by|sorted-set|',
+			r'special-symbol\?|split-at|split-with|str|string\?|',
+			r'struct|struct-map|subs|subvec|symbol|symbol\?|',
+			r'sync|take|take-nth|take-while|test|time|to-array|',
+			r'to-array-2d|tree-seq|true\?|union|up|update-proxy|',
+			r'val|vals|var-get|var-set|var\?|vector|vector-zip|',
+			r'vector\?|when|when-first|when-let|when-not|',
+			r'with-local-vars|with-meta|with-open|with-out-str|',
+			r'xml-seq|xml-zip|zero\?|zipmap|zipper)'
+		]
+	}
+}
diff --git a/Jygments4Sikuli/src/main/resources/contrib/CssLexer.jso b/Jygments4Sikuli/src/main/resources/contrib/CssLexer.jso
new file mode 100644
index 0000000..70f05a5
--- /dev/null
+++ b/Jygments4Sikuli/src/main/resources/contrib/CssLexer.jso
@@ -0,0 +1,160 @@
+/*
+* CSS Lexer
+*/
+
+{
+	"aliases": "css",
+	"filenames": ["*.css"],
+	"mimetypes": "text/css",
+
+	"class": "regex",
+
+	"states":
+	{
+		"root":
+		[
+			["#include", "basics"]
+		],
+		
+		"basics":
+		[
+			[r'\s+', "Text"],
+			[r'/\*(?:.|\n)*?\*/', "Comment"],
+			[r'{', "Punctuation", "content"],
+			[r'\:[a-zA-Z0-9_-]+', "Name.Decorator"],
+			[r'\.[a-zA-Z0-9_-]+', "Name.Class"],
+			[r'\#[a-zA-Z0-9_-]+', "Name.Function"],
+			[r'@[a-zA-Z0-9_-]+', "Keyword", "atrule"],
+			[r'[a-zA-Z0-9_-]+', "Name.Tag"],
+			[r'[~\^\*!%&\[\]\(\)<>\|+=@:;,./?-]', "Operator"],
+			[r'"(\\\\|\\"|[^"])*"', "String.Double"],
+			[r"'(\\\\|\\'|[^'])*'", "String.Single"]
+		],
+		
+		"atrules":
+		[
+			[r'{', "Punctuation", "atcontent"],
+			[r';', "Punctuation", "#pop"],
+			["#include", "basics"]
+		],
+		
+		"atcontent":
+		[
+			["#include", "basics"],
+			[r'}', "Punctuation", "#pop:2"]
+		],
+		
+		"content":
+		[
+			[r'\s+', "Text"],
+			[r'}', "Punctuation", "#pop"],
+			[r'url\(.*?\)', "String.Other"],
+			[r'^@.*?$', "Comment.Preproc"],
+			["#constant:keywords", "Keyword"],
+			["#constant:builtins", "Name.Builtin"],
+			[r'\!important', "Comment.Preproc"],
+			[r'/\*(?:.|\n)*?\*/', "Comment"],
+			[r'\#[a-zA-Z0-9]{1,6}', "Number"],
+			[r'[\.-]?[0-9]*[\.]?[0-9]+(em|px|\%|pt|pc|in|mm|cm|ex)', "Number"],
+			[r'-?[0-9]+', "Number"],
+			[r'[~\^\*!%&<>\|+=@:,./?-]+', "Operator"],
+			[r'[\[\]();]+', "Punctuation"],
+			[r'"(\\\\|\\"|[^"])*"', "String.Double"],
+			[r"'(\\\\|\\'|[^'])*'", "String.Single"],
+			[r'[a-zA-Z][a-zA-Z0-9]+', "Name"]
+		]
+	},
+	
+	"constants":
+	{
+		"keywords":
+		[
+			r"(azimuth|background-attachment|background-color|",
+			r"background-image|background-position|background-repeat|",
+			r"background|border-bottom-color|border-bottom-style|",
+			r"border-bottom-width|border-left-color|border-left-style|",
+			r"border-left-width|border-right|border-right-color|",
+			r"border-right-style|border-right-width|border-top-color|",
+			r"border-top-style|border-top-width|border-bottom|",
+			r"border-collapse|border-left|border-width|border-color|",
+			r"border-spacing|border-style|border-top|border|caption-side|",
+			r"clear|clip|color|content|counter-increment|counter-reset|",
+			r"cue-after|cue-before|cue|cursor|direction|display|",
+			r"elevation|empty-cells|float|font-family|font-size|",
+			r"font-size-adjust|font-stretch|font-style|font-variant|",
+			r"font-weight|font|height|letter-spacing|line-height|",
+			r"list-style-type|list-style-image|list-style-position|",
+			r"list-style|margin-bottom|margin-left|margin-right|",
+			r"margin-top|margin|marker-offset|marks|max-height|max-width|",
+			r"min-height|min-width|opacity|orphans|outline|outline-color|",
+			r"outline-style|outline-width|overflow|padding-bottom|",
+			r"padding-left|padding-right|padding-top|padding|page|",
+			r"page-break-after|page-break-before|page-break-inside|",
+			r"pause-after|pause-before|pause|pitch|pitch-range|",
+			r"play-during|position|quotes|richness|right|size|",
+			r"speak-header|speak-numeral|speak-punctuation|speak|",
+			r"speech-rate|stress|table-layout|text-align|text-decoration|",
+			r"text-indent|text-shadow|text-transform|top|unicode-bidi|",
+			r"vertical-align|visibility|voice-family|volume|white-space|",
+			r"widows|width|word-spacing|z-index|bottom|left|",
+			r"above|absolute|always|armenian|aural|auto|avoid|baseline|",
+			r"behind|below|bidi-override|blink|block|bold|bolder|both|",
+			r"capitalize|center-left|center-right|center|circle|",
+			r"cjk-ideographic|close-quote|collapse|condensed|continuous|",
+			r"crop|crosshair|cross|cursive|dashed|decimal-leading-zero|",
+			r"decimal|default|digits|disc|dotted|double|e-resize|embed|",
+			r"extra-condensed|extra-expanded|expanded|fantasy|far-left|",
+			r"far-right|faster|fast|fixed|georgian|groove|hebrew|help|",
+			r"hidden|hide|higher|high|hiragana-iroha|hiragana|icon|",
+			r"inherit|inline-table|inline|inset|inside|invert|italic|",
+			r"justify|katakana-iroha|katakana|landscape|larger|large|",
+			r"left-side|leftwards|level|lighter|line-through|list-item|",
+			r"loud|lower-alpha|lower-greek|lower-roman|lowercase|ltr|",
+			r"lower|low|medium|message-box|middle|mix|monospace|",
+			r"n-resize|narrower|ne-resize|no-close-quote|no-open-quote|",
+			r"no-repeat|none|normal|nowrap|nw-resize|oblique|once|",
+			r"open-quote|outset|outside|overline|pointer|portrait|px|",
+			r"relative|repeat-x|repeat-y|repeat|rgb|ridge|right-side|",
+			r"rightwards|s-resize|sans-serif|scroll|se-resize|",
+			r"semi-condensed|semi-expanded|separate|serif|show|silent|",
+			r"slow|slower|small-caps|small-caption|smaller|soft|solid|",
+			r"spell-out|square|static|status-bar|super|sw-resize|",
+			r"table-caption|table-cell|table-column|table-column-group|",
+			r"table-footer-group|table-header-group|table-row|",
+			r"table-row-group|text|text-bottom|text-top|thick|thin|",
+			r"transparent|ultra-condensed|ultra-expanded|underline|",
+			r"upper-alpha|upper-latin|upper-roman|uppercase|url|",
+			r"visible|w-resize|wait|wider|x-fast|x-high|x-large|x-loud|",
+			r"x-low|x-small|x-soft|xx-large|xx-small|yes)\b"
+		],
+		
+		"builtins":
+		[
+			r"(indigo|gold|firebrick|indianred|yellow|darkolivegreen|",
+			r"darkseagreen|mediumvioletred|mediumorchid|chartreuse|",
+			r"mediumslateblue|black|springgreen|crimson|lightsalmon|brown|",
+			r"turquoise|olivedrab|cyan|silver|skyblue|gray|darkturquoise|",
+			r"goldenrod|darkgreen|darkviolet|darkgray|lightpink|teal|",
+			r"arkmagenta|lightgoldenrodyellow|lavender|yellowgreen|thistle|",
+			r"violet|navy|orchid|blue|ghostwhite|honeydew|cornflowerblue|",
+			r"darkblue|darkkhaki|mediumpurple|cornsilk|red|bisque|slategray|",
+			r"darkcyan|khaki|wheat|deepskyblue|darkred|steelblue|aliceblue|",
+			r"gainsboro|mediumturquoise|floralwhite|coral|purple|lightgrey|",
+			r"lightcyan|darksalmon|beige|azure|lightsteelblue|oldlace|",
+			r"greenyellow|royalblue|lightseagreen|mistyrose|sienna|",
+			r"lightcoral|orangered|navajowhite|lime|palegreen|burlywood|",
+			r"seashell|mediumspringgreen|fuchsia|papayawhip|blanchedalmond|",
+			r"peru|aquamarine|white|darkslategray|ivory|dodgerblue|",
+			r"lemonchiffon|chocolate|orange|forestgreen|slateblue|olive|",
+			r"mintcream|antiquewhite|darkorange|cadetblue|moccasin|",
+			r"limegreen|saddlebrown|darkslateblue|lightskyblue|deeppink|",
+			r"plum|aqua|darkgoldenrod|maroon|sandybrown|magenta|tan|",
+			r"rosybrown|pink|lightblue|palevioletred|mediumseagreen|",
+			r"dimgray|powderblue|seagreen|snow|mediumblue|midnightblue|",
+			r"paleturquoise|palegoldenrod|whitesmoke|darkorchid|salmon|",
+			r"lightslategray|lawngreen|lightgreen|tomato|hotpink|",
+			r"lightyellow|lavenderblush|linen|mediumaquamarine|green|",
+			r"blueviolet|peachpuff)\b"
+		]
+	}
+}
\ No newline at end of file
diff --git a/Jygments4Sikuli/src/main/resources/contrib/DefaultStyle.jso b/Jygments4Sikuli/src/main/resources/contrib/DefaultStyle.jso
new file mode 100644
index 0000000..ef6ea3b
--- /dev/null
+++ b/Jygments4Sikuli/src/main/resources/contrib/DefaultStyle.jso
@@ -0,0 +1,51 @@
+/*
+* The default style.
+*/
+
+{
+	"Whitespace": "#bbbbbb",
+	"Comment": ["italic", "#408080"],
+	"Comment.Preproc": ["noitalic", "#BC7A00"],
+
+	"Keyword": ["bold", "#008000"],
+	"Keyword.Pseudo": "nobold",
+	"Keyword.Type": ["nobold", "#B00040"],
+
+	"Operator": "#666666",
+	"Operator.Word": ["bold", "#AA22FF"],
+
+	"Name.Builtin": "#008000",
+	"Name.Function": "#0000FF",
+	"Name.Class": ["bold", "#0000FF"],
+	"Name.Namespace": ["bold", "#0000FF"],
+	"Name.Exception": ["bold", "#D2413A"],
+	"Name.Variable": "#19177C",
+	"Name.Constant": "#880000",
+	"Name.Label": "#A0A000",
+	"Name.Entity": ["bold", "#999999"],
+	"Name.Attribute": "#7D9029",
+	"Name.Tag": ["bold", "#008000"],
+	"Name.Decorator": "#AA22FF",
+
+	"String": "#BA2121",
+	"String.Doc": "italic",
+	"String.Interpol": ["bold", "#BB6688"],
+	"String.Escape": ["bold", "#BB6622"],
+	"String.Regex": "#BB6688",
+	"String.Symbol": "#19177C",
+	"String.Other": "#008000",
+	"Number": "#666666",
+
+	"Generic.Heading": ["bold", "#000080"],
+	"Generic.Subheading": ["bold", "#800080"],
+	"Generic.Deleted": "#A00000",
+	"Generic.Inserted": "#00A000",
+	"Generic.Error": "#FF0000",
+	"Generic.Emph": "italic",
+	"Generic.Strong": "bold",
+	"Generic.Prompt": ["bold", "#000080"],
+	"Generic.Output": "#888",
+	"Generic.Traceback": "#04D",
+
+	"Error": ["border", "#FF0000"]
+}
\ No newline at end of file
diff --git a/Jygments4Sikuli/src/main/resources/contrib/HtmlLexer.jso b/Jygments4Sikuli/src/main/resources/contrib/HtmlLexer.jso
new file mode 100644
index 0000000..34d69b3
--- /dev/null
+++ b/Jygments4Sikuli/src/main/resources/contrib/HtmlLexer.jso
@@ -0,0 +1,64 @@
+/*
+* HTML Lexer
+*/
+
+{
+	"name": "HTML",
+	"aliases": ["html"],
+	"filenames": ["*.html", "*.htm", "*.xhtml", "*.xslt"],
+	"mimetypes": ["text/html", "application/xhtml+xml"],
+
+  	"class": "regex",
+	"flags": ["IGNORECASE", "DOTALL"],
+
+	"states":
+	{	
+		"root":
+		[
+			[r'[^<&]+', "Text"],
+			[r'&\S*?;', "Name.Entity"],
+			[r'\<\!\[CDATA\[.*?\]\]\>', "Comment.Preproc"],
+			[r'<!--', "Comment", "comment"],
+			[r'<\?.*?\?>', "Comment.Preproc"],
+			[r'<![^>]*>', "Comment.Preproc"],
+			[r'<\s*script\s*', "Name.Tag", ["script-content", "tag"]],
+			[r'<\s*style\s*', "Name.Tag", ["style-content", "tag"]],
+			[r'<\s*[a-zA-Z0-9:]+', "Name.Tag", "tag"],
+			[r'<\s*/\s*[a-zA-Z0-9:]+\s*>', "Name.Tag"]
+		],
+
+		"comment":
+		[
+			[r'[^-]+', "Comment"],
+			[r'-->', "Comment", "#pop"],
+			[r'-', "Comment"]
+		],
+
+		"tag":
+		[
+			[r'\s+', "Text"],
+			[r'[a-zA-Z0-9_:-]+\s*=', "Name.Attribute", "attr"],
+			[r'[a-zA-Z0-9_:-]+', "Name.Attribute"],
+			[r'/?\s*>', "Name.Tag", "#pop"]
+		],
+
+		"script-content":
+		[
+			[r'<\s*/\s*script\s*>', "Name.Tag", "#pop"],
+            ["#using", r'.+?(?=<\s*/\s*script\s*>)', "javascript"]
+		],
+
+		"style-content":
+		[
+			[r'<\s*/\s*style\s*>', "Name.Tag", "#pop"],
+            ["#using", r'.+?(?=<\s*/\s*style\s*>)', "css"]
+		],
+
+		"attr":
+		[
+			[r'".*?"', "String", "#pop"],
+			[r"'.*?'", "String", "#pop"],
+			[r'[^\s>]+', "String", "#pop"]
+		]
+	}
+}
\ No newline at end of file
diff --git a/Jygments4Sikuli/src/main/resources/contrib/JavaLexer.jso b/Jygments4Sikuli/src/main/resources/contrib/JavaLexer.jso
new file mode 100644
index 0000000..302ae78
--- /dev/null
+++ b/Jygments4Sikuli/src/main/resources/contrib/JavaLexer.jso
@@ -0,0 +1,48 @@
+{
+    "name": "Java",
+    "aliases": ["java"],
+    "filenames": ["*.java"],
+    "mimetypes": ["text/x-java"],
+
+    "class": "regex",
+
+	"flags": ["MULTILINE", "DOTALL"],
+
+    //: optional Comment or Whitespace
+    "_ws": r'(?:\s|//.*?\n|/[*].*?[*]/)+',
+
+    "states": {
+        "root": [
+            [r'^(\s*(?:[a-zA-Z_][a-zA-Z0-9_\.\[\]]*\s+)+?)([a-zA-Z_][a-zA-Z0-9_]*)(\s*)(\()',
+             ["Name.Function", "Name.Function", "Text", "Operator"] ],
+            [r'[^\S\n]+', "Text"],
+            [r'//.*?\n', "Comment.Single"],
+            [r'/\*.*?\*/', "Comment.Multiline"],
+            [r'@[a-zA-Z_][a-zA-Z0-9_\.]*', "Name.Decorator"],
+            [r'(assert|break|case|catch|continue|default|do|else|finally|for|if|goto|instanceof|new|return|switch|this|throw|try|while)\b',
+             "Keyword"],
+            [r'(abstract|const|enum|extends|final|implements|native|private|protected|public|static|strictfp|super|synchronized|throws|transient|volatile)\b', "Keyword.Declaration"],
+            [r'(boolean|byte|char|double|float|int|long|short|void)\b', "Keyword.Type"],
+            [r'(package)(\s+)', ["Keyword.Namespace", "Text"]],
+            [r'(true|false|null)\b', "Keyword.Constant"],
+            [r'(class|interface)(\s+)', ["Keyword.Declaration", "Text"], "class"],
+            [r'(import)(\s+)', ["Keyword.Namespace", "Text"], "import"],
+            [r'"(\\\\|\\"|[^"])*+"', "String"],
+            [r"'\\.'|'[^\\]'|'\\u[0-9a-f]{4}'", "String.Char"],
+            [r'(\.)([a-zA-Z_][a-zA-Z0-9_]*)', ["Operator", "Name.Attribute"]],
+            [r'[a-zA-Z_][a-zA-Z0-9_]*:', "Name.Label"],
+            [r'[a-zA-Z_\$][a-zA-Z0-9_]*', "Name"],
+            [r'[~\^\*!%&\[\]\(\)\{\}<>\|+=:;,./?-]', "Operator"],
+            [r'[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?', "Number.Float"],
+            [r'0x[0-9a-f]+', "Number.Hex"],
+            [r'[0-9]+L?', "Number.Integer"],
+            [r'\n', "Text"]
+        ],
+        "class": [
+            [r'[a-zA-Z_][a-zA-Z0-9_]*', "Name.Class", "#pop"]
+        ],
+        "import": [
+            [r'[a-zA-Z0-9_.]+\*?', "Name.Namespace", "#pop"]
+        ]
+    }    
+}
diff --git a/Jygments4Sikuli/src/main/resources/contrib/JavascriptLexer.jso b/Jygments4Sikuli/src/main/resources/contrib/JavascriptLexer.jso
new file mode 100644
index 0000000..c8b861b
--- /dev/null
+++ b/Jygments4Sikuli/src/main/resources/contrib/JavascriptLexer.jso
@@ -0,0 +1,84 @@
+/*
+* JavaScript Lexer
+*/
+
+{
+	"name": "JavaScript",
+	"aliases": ["js", "javascript"],
+	"filenames": ["*.js"],
+	"mimetypes": ["application/x-javascript", "text/x-javascript", "text/javascript"],
+
+	"class": "regex",
+	"flags": ["DOTALL"],
+
+	"states":
+	{
+		"commentsandwhitespace":
+		[
+			[r'\s+', "Text"],
+			[r'<!--', "Comment"],
+			[r'//.*?\n', "Comment.Single"],
+			[r'/\*.*?\*/', "Comment.Multiline"]
+		],
+
+		"slashstartsregex":
+		[
+			["#include", "commentsandwhitespace"],
+			[r'/(\\.|[^[/\\\n]|\[(\\.|[^\]\\\n])*])+/([gim]+\b|\B)', "String.Regex", "#pop"],
+			[r'(?=/)', "Text", ["#pop", "badregex"]],
+			[r'', "Text", "#pop"]
+		],
+
+		"badregex":
+		[
+			[r'\n', "Text", "#pop"]
+		],
+
+		"root":
+		[
+			[r'^(?=\s|/|<!--)', "Text", "slashstartsregex"],
+			["#include", "commentsandwhitespace"],
+			[r'\+\+|--|~|&&|\?|:|\|\||\\(?=\n)|(<<|>>>?|==?|!=?|[-<>+*%&\|\^/])=?', "Operator", "slashstartsregex"],
+			[r'[{(\[;,]', "Punctuation", "slashstartsregex"],
+			[r'[})\].]', "Punctuation"],
+			["#constant:keywords", "Keyword", "slashstartsregex"],
+			[r'(var|with|function)\b', "Keyword.Declaration", "slashstartsregex"],
+			["#constant:reserved", "Keyword.Reserved"],
+			[r'(true|false|null|NaN|Infinity|undefined)\b', "Keyword.Constant"],
+			["#constant:builtins", "Name.Builtin"],
+			[r'[$a-zA-Z_][a-zA-Z0-9_]*', "Name.Other"],
+			[r'[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?', "Number.Float"],
+			[r'0x[0-9a-fA-F]+', "Number.Hex"],
+			[r'[0-9]+', "Number.Integer"],
+			[r'"(\\\\|\\"|[^"])*"', "String.Double"],
+			[r"'(\\\\|\\'|[^'])*'", "String.Single"]
+		]
+	},
+	
+	"constants":
+	{
+		"keywords":
+		[
+			r'(for|in|while|do|break|return|continue|switch|case|default|if|else|',
+			r'throw|try|catch|finally|new|delete|typeof|instanceof|void|',
+			r'this)\b'
+		],
+
+		"reserved":
+		[
+			r'(abstract|boolean|byte|char|class|const|debugger|double|enum|export|',
+			r'extends|final|float|goto|implements|import|int|interface|long|native|',
+			r'package|private|protected|public|short|static|super|synchronized|throws|',
+			r'transient|volatile)\b'
+		],
+
+		"builtins":
+		[
+			r'(Array|Boolean|Date|Error|Function|Math|netscape|',
+			r'Number|Object|Packages|RegExp|String|sun|decodeURI|',
+			r'decodeURIComponent|encodeURI|encodeURIComponent|',
+			r'Error|eval|isFinite|isNaN|parseFloat|parseInt|document|this|',
+			r'window)\b'
+		]
+	}
+}
diff --git a/Jygments4Sikuli/src/main/resources/contrib/PythonLexer.jso b/Jygments4Sikuli/src/main/resources/contrib/PythonLexer.jso
new file mode 100644
index 0000000..ee6cdb8
--- /dev/null
+++ b/Jygments4Sikuli/src/main/resources/contrib/PythonLexer.jso
@@ -0,0 +1,214 @@
+/*
+* Python Lexer
+*
+*
+* State entries are arrays where the first argument is either a
+* command (beginning with "#") or a regular expression to which
+* a rule is applied.
+*
+* Supported commands:
+*
+*  "#include": The value is the state to include.
+*
+* Rules are literal regular expressions, or functions (beginning
+* with "#") applied to lists of strings in the "constants" map.
+*
+* Regex literals can use Python-style raw notation, prefixed with
+* "r". Both single-quotes and double-quoted raw notations are
+* supported.
+*
+* The second rule argument is the token type to apply to regex's
+* match, or an array of token types to apply to group matches, in
+* ordered sequence.
+*
+* The optional third argument is the next state to switch to
+* after the rule is applied, or an array of states to combine and
+* switch to.
+*
+* States can be either literal names of states, or the relative
+* references "#push" and "#pop" for the state stack. "#pop" can
+* also optionally specify a depth beyond one, for example:
+* "#pop:2".
+*
+*/
+
+{
+    "aliases": ["python", "py"],
+    "filenames": ["*.py", "*.pyw", "*.sc", "SConstruct", "SConscript"],
+    "mimetypes": ["text/x-python", "application/x-python"],
+
+	"class": "regex",
+
+	"states":
+	{
+		"root":
+		[
+			[r'\n', "Text"],
+			[r'^(\s*)("""(?:.|\n)*?""")', ["Text", "String.Doc"]],
+			[r"^(\s*)('''(?:.|\n)*?''')", ["Text", "String.Doc"]],
+			[r'[^\S\n]+', "Text"],
+			[r'#.*$', "Comment"],
+			[r'[\]{}:(),;\[]', "Punctuation"], /* square brackets were escaped for Jygments */
+			[r'\\\n', "Text"],
+			[r'\\', "Text"],
+			[r'(in|is|and|or|not)\b', "Operator.Word"],
+			[r'!=|==|<<|>>|[-~+/*%=<>&^|.]', "Operator"],
+			["#include", "keywords"],
+			[r'(def)((?:\s|\\\s)+)', ["Keyword", "Text"], "funcname"],
+			[r'(class)((?:\s|\\\s)+)', ["Keyword", "Text"], "classname"],
+			[r'(from)((?:\s|\\\s)+)', ["Keyword.Namespace", "Text"], "fromimport"],
+			[r'(import)((?:\s|\\\s)+)', ["Keyword.Namespace", "Text"], "import"],
+			["#include", "builtins"],
+			["#include", "backtick"],
+			[r'(?:[rR]|[uU][rR]|[rR][uU])"""', "String", "tdqs"],
+			[r"(?:[rR]|[uU][rR]|[rR][uU])'''", "String", "tsqs"],
+			[r'(?:[rR]|[uU][rR]|[rR][uU])"', "String", "dqs"],
+			[r"(?:[rR]|[uU][rR]|[rR][uU])'", "String", "sqs"],
+			[r'[2uU]?"""', "String", "stringescape+tdqs"],
+			[r"[uU]?'''", "String", "stringescape+tsqs"],
+			[r'[uU]?"', "String", "stringescape+dqs"],
+			[r"[uU]?'", "String", "stringescape+sqs"],
+			["#include", "name"],
+			["#include", "numbers"]
+		],
+
+		"keywords":
+		[
+			[r"(assert|break|continue|del|elif|else|except|exec|finally|for|global|if|lambda|pass|print|raise|return|try|while|yield|as|with)\b", "Keyword"]
+		],
+
+		"builtins":
+		[
+			["#constant:builtins", "Name.Builtin"],
+			[r'(?<!\.)(self|None|Ellipsis|NotImplemented|False|True)\b', "Name.Builtin.Pseudo"], 
+			["#constant:exceptions", "Name.Exception"]
+		],
+
+		"numbers":
+		[
+			[r'(\d+\.\d*|\d*\.\d+)([eE][+-]?[0-9]+)?', "Number.Float"],
+			[r'\d+[eE][+-]?[0-9]+', "Number.Float"],
+			[r'0\d+', "Number.Oct"],
+			[r'0[xX][a-fA-F0-9]+', "Number.Hex"],
+			[r'\d+L', "Number.Integer.Long"],
+			[r'\d+', "Number.Integer"]
+		],
+
+		"backtick":
+		[
+			[r"`.*?`", "String.Backtick"]
+		],
+
+		"name":
+		[
+			[r'@[a-zA-Z0-9_.]+', "Name.Decorator"],
+			[r'[a-zA-Z_][a-zA-Z0-9_]*', "Name"]
+		],
+
+		"funcname":
+		[
+			[r'[a-zA-Z_][a-zA-Z0-9_]*', "Name.Function", "#pop"]
+		],
+
+		"classname":
+		[
+			[r'[a-zA-Z_][a-zA-Z0-9_]*', "Name.Class", "#pop"]
+		],
+
+		"import":
+		[
+			[r'((?:\s|\\\s)+)(as)((?:\s|\\\s)+)', ["Text", "Keyword.Namespace", "Text"]],
+			[r'[a-zA-Z_][a-zA-Z0-9_.]*', "Name.Namespace"],
+			[r'(\s*)(,)(\s*)', ["Text", "Operator", "Text"]],
+			[r'', "Text", "#pop"] /* all else: go back */
+		],
+
+		"fromimport":
+		[
+			[r'((?:\s|\\\s)+)(import)\b', ["Text", "Keyword.Namespace"], "#pop"],
+			[r'[a-zA-Z_.][a-zA-Z0-9_.]*', "Name.Namespace"]
+		],
+
+		"stringescape":
+		[
+			[r'\\([\\abfnrtv"\']|\n|N{.*?}|u[a-fA-F0-9]{4}|U[a-fA-F0-9]{8}|x[a-fA-F0-9]{2}|[0-7]{1,3})', "String.Escape"]
+		],
+
+		"strings":
+		[
+			[r'%(\([a-zA-Z0-9_]+\))?[-#0 +]*([0-9]+|[*])?(\.([0-9]+|[*]))?[hlL]?[diouxXeEfFgGcrs%]', "String.Interpol"],
+			[r'[^\\\'"%\n]+', "String"],
+			/* quotes, percents and backslashes must be parsed one at a time */
+			[r'[\'"\\]', "String"],
+			/* unhandled string formatting sign */
+			[r'%', "String"]
+			/* newlines are an error (use "nl" state) */
+		],
+
+		"nl":
+		[
+			[r'\n', "String"]
+		],
+
+		"dqs":
+		[
+			[r'"', "String", "#pop"],
+			[r'\\\\|\\"|\\\n', "String.Escape"], /* included here again for raw strings */
+			["#include", "strings"]
+		],
+
+		"sqs":
+		[
+			[r"'", "String", "#pop"],
+			[r"\\\\|\\'|\\\n", "String.Escape"], /* included here again for raw strings */
+			["#include", "strings"]
+		],
+
+		"tdqs":
+		[
+			[r'"""', "String", "#pop"],
+			["#include", "strings"],
+			["#include", "nl"]
+		],
+
+		"tsqs":
+		[
+			[r"'''", "String", "#pop"],
+			["#include", "strings"],
+			["#include", "nl"]
+		]
+	},
+	
+	"constants":
+	{			
+		"builtins":
+		[
+			r"(?<!\.)(__import__|abs|all|any|apply|basestring|bin|bool|buffer|",
+			r"bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|",
+			r"complex|delattr|dict|dir|divmod|enumerate|eval|execfile|exit|",
+			r"file|filter|float|frozenset|getattr|globals|hasattr|hash|hex|id|",
+			r"input|int|intern|isinstance|issubclass|iter|len|list|locals|",
+			r"long|map|max|min|next|object|oct|open|ord|pow|property|range|",
+			r"raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|",
+			r"sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|",
+			r"vars|xrange|zip)\b"
+		],
+
+		"exceptions":
+		[			
+			r"(?<!\.)(ArithmeticError|AssertionError|AttributeError|",
+			r"BaseException|DeprecationWarning|EOFError|EnvironmentError|",
+			r"Exception|FloatingPointError|FutureWarning|GeneratorExit|IOError|",
+			r"ImportError|ImportWarning|IndentationError|IndexError|KeyError|",
+			r"KeyboardInterrupt|LookupError|MemoryError|NameError|",
+			r"NotImplemented|NotImplementedError|OSError|OverflowError|",
+			r"OverflowWarning|PendingDeprecationWarning|ReferenceError|",
+			r"RuntimeError|RuntimeWarning|StandardError|StopIteration|",
+			r"SyntaxError|SyntaxWarning|SystemError|SystemExit|TabError|",
+			r"TypeError|UnboundLocalError|UnicodeDecodeError|",
+			r"UnicodeEncodeError|UnicodeError|UnicodeTranslateError|",
+			r"UnicodeWarning|UserWarning|ValueError|VMSError|Warning|",
+			r"WindowsError|ZeroDivisionError)\b"
+		]
+	}
+}
\ No newline at end of file
diff --git a/Jygments4Sikuli/src/main/resources/contrib/XmlLexer.jso b/Jygments4Sikuli/src/main/resources/contrib/XmlLexer.jso
new file mode 100644
index 0000000..bfaf03d
--- /dev/null
+++ b/Jygments4Sikuli/src/main/resources/contrib/XmlLexer.jso
@@ -0,0 +1,38 @@
+{
+    "name": "XML",
+    "aliases": ["xml"],
+    "filenames": ["*.xml", "*.xsl", "*.rss", "*.xslt", "*.xsd", "*.wsdl"],
+    "mimetypes": ["text/xml", "application/xml", "image/svg+xml",
+                 "application/rss+xml", "application/atom+xml"],
+    "class": "regex",
+	"flags": ["MULTILINE", "DOTALL"],
+
+    "states": {
+        "root": [
+            [r'[^<&]+', "Text"],
+            [r'&\S*?;', "Name.Entity"],
+            [r'\<\!\[CDATA\[.*?\]\]\>', "Comment.Preproc"],
+            [r'<!--', "Comment", "comment"],
+            [r'<\?.*?\?>', "Comment.Preproc"],
+            [r'<![^>]*>', "Comment.Preproc"],
+            [r'<\s*[a-zA-Z0-9:._-]+', "Name.Tag", "tag"],
+            [r'<\s*/\s*[a-zA-Z0-9:._-]+\s*>', "Name.Tag"]
+        ],
+        "comment": [
+            [r'[^-]+', "Comment"],
+            [r'-->', "Comment", "#pop"],
+            [r'-', "Comment"]
+        ],
+        "tag": [
+            [r'\s+', "Text"],
+            [r'[a-zA-Z0-9_.:-]+\s*=', "Name.Attribute", "attr"],
+            [r'/?\s*>', "Name.Tag", "#pop"]
+        ],
+        "attr": [
+            [r'\s+', "Text"],
+            [r'".*?"', "String", "#pop"],
+            ["'.*?'", "String", "#pop"],
+            [r'[^\s>]+', "String", "#pop"]
+        ]
+    }
+}
diff --git a/pom.xml b/pom.xml
index 4e81c68..2d49173 100755
--- a/pom.xml
+++ b/pom.xml
@@ -217,6 +217,7 @@
 	</profiles>
 
 	<modules>
+		<module>Jygments4Sikuli</module>
 		<module>OpenCV4Sikuli</module>
 		<module>Tesseract4Sikuli</module>
 		<module>Libs</module>

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/sikuli.git



More information about the pkg-java-commits mailing list