Syntax: Diff

jQuery.Syntax Diff

diff --git a/ex.ruby.html b/ex.ruby.html
index a02a04e..7d42db0 100644
--- a/ex.ruby.html
+++ b/ex.ruby.html
@@ -21,6 +21,8 @@
 	<body>
 		<h1>Syntax: Ruby</h1>

+		<h2>Ruby Script #1</h2>
+		
 		<pre class="ruby">#!/usr/bin/env ruby

 # Copyright (c) 2009 Samuel Williams. Released under the GNU GPLv3.
@@ -104,5 +106,257 @@ end
 # RExec daemon runner
 Server.daemonize</pre>

+	<h2>Ruby Script #2</h2>
+	
+	<pre class="ruby">#!/usr/bin/env ruby
+# Simple Operator Expression Parser
+
+require &#x27;set&#x27;
+
+DEBUG = false
+
+class Array
+  def map_to(with)
+    r = {}
+
+    each_with_index { |v,i| r[v] = with[i] }
+
+    return r
+  end
+end
+
+module Expression
+  class Context
+    def initialize(operators, values)
+      @values = values
+      @operators = operators
+    end
+
+    def value_of (key)
+      @values[key]
+    end
+
+    def call (op, args)
+      @operators.call(op, args)
+    end
+  end
+
+  class Constant
+    def initialize(value)
+      @value = value
+    end
+
+    def evaluate (ctx)
+      return @value
+    end
+  end
+
+  class Identifier
+    def initialize(name)
+      @name = name
+    end
+
+    def evaluate (ctx)
+      ctx.value_of(@name)
+    end
+  end
+
+  class Operator
+    def initialize(name, args = [])
+      @name = name
+      @args = args
+    end
+
+    def name
+      @name
+    end
+
+    def evaluate (ctx)
+      ctx.call(@name, @args.collect { |a| a.evaluate(ctx) })
+    end
+
+    def args
+      @args
+    end
+  end
+
+  class Brackets
+    def initialize(node)
+      @node = node
+    end
+
+    def evaluate (ctx)
+      @node.evaluate(ctx)
+    end
+  end
+
+  class Parser
+    def initialize(ops, expr)
+      @identifiers = []
+      @operators = ops
+
+      # Tokens and expressions line up
+      @expressions = []
+      @tokens = []
+
+      @top = nil
+
+      parse(expr)
+    end
+
+    def evaluate (ctx)
+      @expressions.collect do |expr|
+        expr != nil ? expr.evaluate(ctx) : nil
+      end
+    end
+
+    def identifiers
+      @identifiers
+    end
+
+    def tokens
+      @tokens
+    end
+  private
+    def parse(expr)
+      symbols = @operators.keys + [&quot;(&quot;, &quot;)&quot;]
+      tokenizer = Regexp.union(Regexp.union(*symbols), /[A-Z]+/)
+
+      @tokens = expr.scan(tokenizer)
+      @expressions = [nil] * @tokens.size
+
+      @identifiers = Set.new(expr.scan(/[A-Z]+/))
+
+      @top, i = process_expression
+
+      if DEBUG
+        puts &quot;Processed #{i} tokens...&quot;
+        puts &quot;Tokens: &quot; + @tokens.join(&quot; &quot;)
+        puts @top.inspect
+        puts @expressions.inspect
+      end
+    end
+
+    def process_expression(i = 0)
+      ast = []
+      ops = {}
+      while i &lt; @tokens.size
+        t = @tokens[i]
+
+        if t == &quot;(&quot;
+          result, i = process_expression(i+1)
+          ast += result
+        elsif t == &quot;)&quot;
+          break
+        else        
+          result = process_token(i)
+          ast &lt;&lt; result
+        end
+
+        if result.class == Operator
+          ops[result.name] ||= []
+          # Store the index
+          ops[result.name] &lt;&lt; (ast.size - 1)
+        end
+
+        i += 1
+      end
+
+      #puts ast.inspect
+
+      # We need to sort the list of operators now
+      # [c, infix, prefix, c]
+
+      @operators.order.each do |name|
+        op_kind = @operators.kind(name)
+        next unless ops[name]
+
+        ops[name].each do |loc|
+          op = ast[loc]
+
+          if op_kind == :prefix
+            rhs = find_subexpression(ast, loc, RHS_SEARCH)
+            op.args &lt;&lt; ast[rhs]
+            ast[rhs] = nil
+          elsif op_kind == :infix
+            lhs = find_subexpression(ast, loc, LHS_SEARCH)
+            rhs = find_subexpression(ast, loc, RHS_SEARCH)
+            op.args &lt;&lt; ast[lhs]
+            op.args &lt;&lt; ast[rhs]
+            ast[lhs] = ast[rhs] = nil
+          elsif op_kind == :postfix
+            lhs = find_subexpression(ast, loc, LHS_SEARCH)
+            op.args &lt;&lt; ast[lhs]
+            ast[rhs] = nil
+          end
+        end
+      end
+
+      return [ast.uniq, i]
+    end
+
+    RHS_SEARCH = 1
+    LHS_SEARCH = -1
+
+    def find_subexpression(ast, loc, dir)
+      while loc &gt;= 0 &amp;&amp; loc &lt; ast.size
+        loc += dir
+        return loc if ast[loc] != nil
+      end
+
+      return nil
+    end
+
+    def process_token(i) 
+      t = @tokens[i]
+
+      if @operators.key? t
+        tok = Operator.new(t)
+      elsif t.match /[A-Z]+/
+        tok = Identifier.new(t)
+      else
+        tok = Constant.new(t)
+      end
+
+      @expressions[i] = tok
+      return tok
+    end
+  end
+
+  TYPE = 0
+  FUNC = 1
+  class Operators    
+    def initialize
+      @operators = {}
+      @order = []
+    end
+
+    def add(sym, kind, &amp;block)
+      @operators[sym] = [kind, block]
+      @order &lt;&lt; sym
+    end
+
+    def order
+      @order
+    end
+
+    def keys
+      @operators.keys
+    end
+
+    def key? k
+      @operators.key? k
+    end
+
+    def kind k
+      @operators[k][0]
+    end
+
+    def call(k, args)
+      @operators[k][1].call(*args)
+    end
+  end
+end</pre>
+
 	</body>
 </html>
\ No newline at end of file
diff --git a/example.css b/example.css
index e69de29..4ece63a 100644
--- a/example.css
+++ b/example.css
@@ -0,0 +1,3 @@
+body {
+	font-size: 70%;
+}
\ No newline at end of file
diff --git a/example.html b/example.html
deleted file mode 100644
index 7ac7cd5..0000000
--- a/example.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!DOCTYPE html>
-<html>
-	<head>
-		
-		<link rel="stylesheet" href="jquery.syntax.css" type="text/css" media="screen" />
-		<link rel="stylesheet" href="example.css" type="text/css" media="screen" />
-	
-		<script src="jquery-1.4.1.js" type="text/javascript" charset="utf-8"></script>
-		
-		<script src="jquery.syntax.js" type="text/javascript" charset="utf-8"></script>
-		<script src="jquery.syntax.cache.js" type="text/javascript" charset='utf-8'></script>
-		
-		<script type="text/javascript" language="JavaScript">
-		//<!--
-			$(function() {
-				$('#highlight').click(function() {
-					$('pre.clang').syntax({brush: 'clang', layout: 'table', replace: true}, function(options, html, container) {
-						//$('.function', html).replaceWith(function(){
-						//	return $('<a>').addClass('function').attr('href', '#' + $(this).text()).html($(this).html());
-						//})
-					});
-				});
-			});
-		//-->
-		</script>
- 
-	</head>
-	<body>
-		<h1>Syntax Highlighting Example</h1>
-		
-		<button id="highlight">Highlight</button>
-		
-		<pre class="clang">#include &lt;iostream&gt;
-/* Multi line c comment
-
-	Hello World
-*/
-
-	const char * s = "My name is computer!\n\n"
-int main (int argv, char ** argv) {
-	return EXIT_SUCCESS;
-}
-</pre>
-
-	</body>
-</html>
\ No newline at end of file
diff --git a/example2.html b/example2.html
deleted file mode 100644
index f5f1d8c..0000000
--- a/example2.html
+++ /dev/null
@@ -1,81 +0,0 @@
-<!DOCTYPE html>
-<html>
-	<head>
-		
-		<link rel="stylesheet" href="jquery.syntax.css" type="text/css" media="screen" />
-		<link rel="stylesheet" href="example.css" type="text/css" media="screen" />
-	
-		<script src="jquery-1.4.1.js" type="text/javascript"></script>
-		
-		<script src="jquery.syntax.js" type="text/javascript"></script>
-		<script src="jquery.syntax.cache.js" type="text/javascript" charset='utf-8'></script>
-		
-		<script src="jquery.syntax.brush.clang.js" type="text/javascript"></script>
-		<script src="jquery.syntax.layout.table.js" type="text/javascript"></script>
-		
-		<link rel="stylesheet" href="jquery.syntax.layout.table.css" type="text/css" media="screen">
-		
-		<script type="text/javascript" language="JavaScript">
-		//<!--
-			$(function() {
-				$('#highlight').click(function() {
-					window.console.profile("Syntax Highlighting");
-					start = new Date();
-					
-					$('pre.clang').syntax({brush: 'clang', layout: 'table', replace: true}, function(options, html, container) {
-						//$('.function', html).replaceWith(function(){
-						//	return $('<a>').addClass('function').attr('href', '#' + $(this).text()).html($(this).html());
-						//})
-						
-						finish = new Date();
-						window.console.profileEnd();
-						window.console.log(finish - start);
-					});
-				});
-			});
-		//-->
-		</script>
- 
-	</head>
-	<body>
-		<h1>Syntax Highlighting Example</h1>
-		
-		<button id="highlight">Highlight</button>
-		
-		<pre class="clang">#include &lt;iostream&gt;
-
-// If we increase row by 1, the offset will increase by sz (number of elements per row i.e. number of columns)
-// If we increase col by 1, the offset will increase by 1
-unsigned rowMajorOffset(unsigned row, unsigned col, unsigned sz)
-{
-  return col + row * sz;
-}
-
-// If we increase col by 1, the offset will increase by sz (number of elements per column i.e. number of rows)
-// If we increase row by 1, the offset will increase by 1
-unsigned columnMajorOffset(unsigned row, unsigned col, unsigned sz)
-{
-  return row + col * sz;
-}
-
-int main (int argc, char * const argv[]) {
-	Matrix&lt;float, 4, 2, false&gt; rowMajorMatrix;
-	Matrix&lt;float, 4, 2, true&gt; columnMajorMatrix;
-
-	rowMajorMatrix.loadTestPattern();
-	rowMajorMatrix.debug();
-
-	columnMajorMatrix.loadTestPattern();
-	columnMajorMatrix.debug();
-
-	rowMajorMatrix = columnMajorMatrix.transposeStorage();
-	rowMajorMatrix.debug();
-
-	Matrix&lt;float, 2, 4, false&gt; transposedMatrix = columnMajorMatrix.transposeMatrix();
-	transposedMatrix.debug();
-
-	return 0;
-}</pre>
-
-	</body>
-</html>
\ No newline at end of file
diff --git a/example3.html b/example3.html
deleted file mode 100644
index d559b68..0000000
--- a/example3.html
+++ /dev/null
@@ -1,171 +0,0 @@
-<!DOCTYPE html>
-<html>
-	<head>
-		
-		<link rel="stylesheet" href="jquery.syntax.css" type="text/css" media="screen" />
-		<link rel="stylesheet" href="example.css" type="text/css" media="screen" />
-	
-		<script src="jquery-1.4.1.js" type="text/javascript"></script>
-		
-		<script src="jquery.syntax.js" type="text/javascript"></script>
-		<script src="jquery.syntax.cache.js" type="text/javascript" charset='utf-8'></script>
-		
-		<script src="jquery.syntax.brush.clang.js" type="text/javascript"></script>
-		<script src="jquery.syntax.layout.table.js" type="text/javascript"></script>
-		
-		<link rel="stylesheet" href="jquery.syntax.layout.table.css" type="text/css" media="screen">
-		
-		<script type="text/javascript" language="JavaScript">
-		//<!--
-			$(function() {
-				$('#highlight').click(function() {
-					window.console.profile("Syntax Highlighting");
-					start = new Date();
-					
-					$('pre.clang').syntax({brush: 'clang', layout: 'table', replace: true}, function(options, html, container) {
-						//$('.function', html).replaceWith(function(){
-						//	return $('<a>').addClass('function').attr('href', '#' + $(this).text()).html($(this).html());
-						//})
-						
-						finish = new Date();
-						window.console.profileEnd();
-						window.console.log(finish - start);
-					});
-				});
-			});
-		//-->
-		</script>
- 
-	</head>
-	<body>
-		<h1>Syntax Highlighting Example</h1>
-		
-		<button id="highlight">Highlight</button>
-		
-		<pre class="clang">#include &lt;iostream&gt;
-
-// If we increase row by 1, the offset will increase by sz (number of elements per row i.e. number of columns)
-// If we increase col by 1, the offset will increase by 1
-unsigned rowMajorOffset(unsigned row, unsigned col, unsigned sz)
-{
-    return col + row * sz;
-}
-
-// If we increase col by 1, the offset will increase by sz (number of elements per column i.e. number of rows)
-// If we increase row by 1, the offset will increase by 1
-unsigned columnMajorOffset(unsigned row, unsigned col, unsigned sz)
-{
-    return row + col * sz;
-}
-
-template &lt;typename _ValueT, unsigned _R, unsigned _C, bool _ColumnMajor&gt;
-class Matrix {
-protected:
-	enum { ColumnMajor = _ColumnMajor };
-	enum { R = _R };
-	enum { C = _C };
-
-	typedef _ValueT ValueT;
-
-	ValueT m_values[C*R];
-
-public:
-	const ValueT &amp; at (unsigned r, unsigned c) const
-	{
-		if (ColumnMajor)
-			return m_values[columnMajorOffset(r, c, R)];
-		else
-			return m_values[rowMajorOffset(r, c, C)];
-	}
-
-	ValueT &amp; at (unsigned r, unsigned c)
-	{
-		if (ColumnMajor)
-			return m_values[columnMajorOffset(r, c, R)];
-		else
-			return m_values[rowMajorOffset(r, c, C)];
-	}
-
-	void loadTestPattern ()
-	{
-		for (unsigned r = 0; r &lt; R; r += 1)
-			for (unsigned c = 0; c &lt; C; c += 1)
-				at(r, c) = (r+1) * 1000 + (c+1);
-	}
-
-	void debug ()
-	{
-		using namespace std;
-
-		if (ColumnMajor)
-			cout &lt;&lt; &quot;Column-Major Matrix &quot; &lt;&lt; &quot;(&quot; &lt;&lt; R &lt;&lt; &quot;,&quot; &lt;&lt; C &lt;&lt; &quot;)&quot; &lt;&lt; &quot; @ &quot; &lt;&lt; this &lt;&lt; endl;
-		else
-			cout &lt;&lt; &quot;Row-Major Matrix &quot; &lt;&lt; &quot;(&quot; &lt;&lt; R &lt;&lt; &quot;,&quot; &lt;&lt; C &lt;&lt; &quot;)&quot; &lt;&lt; &quot; @ &quot; &lt;&lt; this &lt;&lt; endl;
-
-		cout &lt;&lt; &quot;Memory Offset: &quot;;
-		for (unsigned i = 0; i &lt; (R*C); i += 1)
-			cout &lt;&lt; i &lt;&lt; &quot;    &quot;;
-		cout &lt;&lt; endl;
-
-		cout &lt;&lt; &quot;       Values: &quot;;	
-		for (unsigned i = 0; i &lt; (R*C); i += 1)
-			cout &lt;&lt; m_values[i] &lt;&lt; &quot; &quot;;
-		cout &lt;&lt; endl;
-
-		cout &lt;&lt; &quot;Standard Mathematical Notation:&quot; &lt;&lt; endl;
-		cout &lt;&lt; &quot;      &quot;;
-		for (unsigned c = 0; c &lt; C; c += 1)
-			cout &lt;&lt; &quot;Col &quot; &lt;&lt; c &lt;&lt; &quot; &quot;;
-		cout &lt;&lt; endl;
-
-		for (unsigned r = 0; r &lt; R; r += 1) {
-			cout &lt;&lt; &quot;Row &quot; &lt;&lt; r &lt;&lt; &quot; &quot;;
-			for (unsigned c = 0; c &lt; C; c += 1)
-				cout &lt;&lt; at(r, c) &lt;&lt; &quot;  &quot;;
-			cout &lt;&lt; endl;
-		}
-		cout &lt;&lt; endl;
-	}
-
-	Matrix&lt;ValueT, R, C, !ColumnMajor&gt; transposeStorage () const
-	{
-		Matrix&lt;ValueT, R, C, !ColumnMajor&gt; result;
-
-		for (unsigned r = 0; r &lt; R; r += 1)
-			for (unsigned c = 0; c &lt; C; c += 1)
-				result.at(r, c) = at(r, c);
-
-		return result;
-	}
-
-	Matrix&lt;ValueT, C, R, !ColumnMajor&gt; transposeMatrix () const
-	{
-		Matrix&lt;ValueT, C, R, !ColumnMajor&gt; result;
-
-		memcpy(&amp;result.at(0,0), m_values, sizeof(m_values));
-
-		return result;
-	}
-};
-
-int main (int argc, char * const argv[]) {
-	Matrix&lt;float, 4, 2, false&gt; rowMajorMatrix;
-	Matrix&lt;float, 4, 2, true&gt; columnMajorMatrix;
-
-	rowMajorMatrix.loadTestPattern();
-	rowMajorMatrix.debug();
-
-	columnMajorMatrix.loadTestPattern();
-	columnMajorMatrix.debug();
-
-	rowMajorMatrix = columnMajorMatrix.transposeStorage();
-	rowMajorMatrix.debug();
-
-	Matrix&lt;float, 2, 4, false&gt; transposedMatrix = columnMajorMatrix.transposeMatrix();
-	transposedMatrix.debug();
-
-	return 0;
-}</pre>
-
-	</body>
-</html>
\ No newline at end of file
diff --git a/jquery.syntax.brush.clang.js b/jquery.syntax.brush.clang.js
index dcbebbc..3c10a29 100644
--- a/jquery.syntax.brush.clang.js
+++ b/jquery.syntax.brush.clang.js
@@ -1,16 +1,19 @@
 // brush: "clang" aliases: ["cpp", "c", "objective-c"]

 Syntax.register('clang', function(brush) {
-	var keywords = ["@interface", "@implementation", "@protocol", "@end", "@private", "@protected", "@public", "@try", "@throw", "@catch", "@finally", "@class", "@selector", "@encode", "@synchronized", "struct", "break", "continue", "else", "for", "switch", "case", "default", "enum", "goto", "register", "sizeof", "typedef", "volatile", "do", "extern", "if", "return", "static", "union", "while", "asm", "dynamic_cast", "namespace", "reinterpret_cast", "try", "explicit", "static_cast", "typeid", "catch", "operator", "template", "class", "friend", "private", "using", "const_cast", "inline", "public", "throw", "virtual", "mutable", "protected", "wchar_t"];
+	var keywords = ["@interface", "@implementation", "@protocol", "@end", "@private", "@protected", "@public", "@try", "@throw", "@catch", "@finally", "@class", "@selector", "@encode", "@synchronized", "@property", "struct", "break", "continue", "else", "for", "switch", "case", "default", "enum", "goto", "register", "sizeof", "typedef", "volatile", "do", "extern", "if", "return", "static", "union", "while", "asm", "dynamic_cast", "namespace", "reinterpret_cast", "try", "explicit", "static_cast", "typeid", "catch", "operator", "template", "class", "friend", "private", "using", "const_cast", "inline", "public", "throw", "virtual", "mutable", "protected", "wchar_t"];

-	var types = ["auto", "const", "double", "float", "int", "short", "char", "long", "signed", "unsigned", "bool", "void", "typename"];
-	var operators = ["+", "*", "/", "-", "&", "|", "~", "!", "%", "<", "=", ">", "new", "delete"];
+	var types = ["auto", "const", "double", "float", "int", "short", "char", "long", "signed", "unsigned", "bool", "void", "typename", "id"];
+	var operators = ["+", "*", "/", "-", "&", "|", "~", "!", "%", "<", "=", ">", "[", "]", "new", "delete"];
 	var values = ["this", "true", "false", /[0-9]+(\.[0-9]+)?/g];

-	brush.push(values, {klass: 'constant', children: null});
-	brush.push(types, {klass: 'type', children: null})
-	brush.push(keywords, {klass: 'keyword', children: null})
-	brush.push(operators, {klass: 'operator', children: null})
+	brush.push(values, {klass: 'constant', children: []});
+	brush.push(types, {klass: 'type', children: []})
+	brush.push(keywords, {klass: 'keyword', children: []})
+	brush.push(operators, {klass: 'operator', children: []})
+	
+	// Objective-C classes
+	brush.push({pattern: /\b[A-Z][\w]*\b/g, klass: 'type', children: []})

 	brush.push({
 		pattern: /#.*$/gmi,
@@ -21,6 +24,15 @@ Syntax.register('clang', function(brush) {
 	brush.push(Syntax.lib.cStyleComment);
 	brush.push(Syntax.lib.cppStyleComment);

+	// Objective-C style functions
+	brush.push({pattern: /\w+:/g, klass: 'function', children: []});
+	brush.push({
+		pattern: /[^:\[]\s+(\w+)(?=])/g,
+		klass: 'function', 
+		matches: Syntax.singleMatchFunction(1, {klass: 'function', children: []})
+	})
+	
+	// Strings
 	brush.push({pattern: /".+?"/g, klass: 'string', children: 'escape'});
 	brush.push({pattern: /\\./g, klass: 'escape'});

diff --git a/jquery.syntax.brush.ruby.js b/jquery.syntax.brush.ruby.js
index 71fdeaf..64a4626 100644
--- a/jquery.syntax.brush.ruby.js
+++ b/jquery.syntax.brush.ruby.js
@@ -1,34 +1,30 @@
 // brush: "ruby" aliases: []

-/*
-	# This ruby code will generate a list of classes.
-	classes = []
-
-	ObjectSpace.each_object(Class) do |c|
-		classes << c
-	end
-
-	puts classes.collect{|cls| cls.name}.inspect
-*/
+Syntax.lib.rubyStyleFunction = {pattern: /(?:def\s+|\.)([a-z_][a-z0-9_]+)/gi, matches: Syntax.singleMatchFunction(1, {klass: 'function', children: null})}
+Syntax.lib.rubyStyleSymbol = {pattern: /:[\w]+/, klass: 'constant', children: null}

 Syntax.register('ruby', function(brush) {
 	var keywords = ["alias", "and", "begin", "break", "case", "class", "def", "define_method", "defined", "do", "each", "else", "elsif", "end", "ensure", "false", "for", "if", "in", "module", "new", "next", "nil", "not", "or", "raise", "redo", "rescue", "retry", "return", "self", "super", "then", "throw", "true", "undef", "unless", "until", "when", "while", "yield"];

-	var types = ["Socket", "UNIXServer", "UNIXSocket", "UDPSocket", "TCPServer", "TCPSocket", "IPSocket", "BasicSocket", "SocketError", "Pathname", "StopIteration", "Enumerable::Enumerator", "Binding", "UnboundMethod", "Method", "Proc", "SystemStackError", "LocalJumpError", "Struct::Tms", "Process::Status", "Time", "Dir", "File::Stat", "File", "IO", "EOFError", "IOError", "Range", "MatchData", "Regexp", "RegexpError", "Struct", "Hash", "Array", "Errno::EDQUOT", "Errno::ESTALE", "Errno::EINPROGRESS", "Errno::EALREADY", "Errno::EHOSTUNREACH", "Errno::EHOSTDOWN", "Errno::ECONNREFUSED", "Errno::ETIMEDOUT", "Errno::ETOOMANYREFS", "Errno::ESHUTDOWN", "Errno::ENOTCONN", "Errno::EISCONN", "Errno::ENOBUFS", "Errno::ECONNRESET", "Errno::ECONNABORTED", "Errno::ENETRESET", "Errno::ENETUNREACH", "Errno::ENETDOWN", "Errno::EADDRNOTAVAIL", "Errno::EADDRINUSE", "Errno::EAFNOSUPPORT", "Errno::EPFNOSUPPORT", "Errno::EOPNOTSUPP", "Errno::ESOCKTNOSUPPORT", "Errno::EPROTONOSUPPORT", "Errno::ENOPROTOOPT", "Errno::EPROTOTYPE", "Errno::EMSGSIZE", "Errno::EDESTADDRREQ", "Errno::ENOTSOCK", "Errno::EUSERS", "Errno::EILSEQ", "Errno::EOVERFLOW", "Errno::EBADMSG", "Errno::EMULTIHOP", "Errno::EPROTO", "Errno::ENOLINK", "Errno::EREMOTE", "Errno::ENOSR", "Errno::ETIME", "Errno::ENODATA", "Errno::ENOSTR", "Errno::EIDRM", "Errno::ENOMSG", "Errno::ELOOP", "Errno::ENOTEMPTY", "Errno::ENOSYS", "Errno::ENOLCK", "Errno::ENAMETOOLONG", "Errno::EDEADLK", "Errno::ERANGE", "Errno::EDOM", "Errno::EPIPE", "Errno::EMLINK", "Errno::EROFS", "Errno::ESPIPE", "Errno::ENOSPC", "Errno::EFBIG", "Errno::ETXTBSY", "Errno::ENOTTY", "Errno::EMFILE", "Errno::ENFILE", "Errno::EINVAL", "Errno::EISDIR", "Errno::ENOTDIR", "Errno::ENODEV", "Errno::EXDEV", "Errno::EEXIST", "Errno::EBUSY", "Errno::ENOTBLK", "Errno::EFAULT", "Errno::EACCES", "Errno::ENOMEM", "Errno::EAGAIN", "Errno::ECHILD", "Errno::EBADF", "Errno::ENOEXEC", "Errno::E2BIG", "Errno::ENXIO", "Errno::EIO", "Errno::EINTR", "Errno::ESRCH", "Errno::ENOENT", "Errno::EPERM", "Bignum", "Float", "Fixnum", "Integer", "Numeric", "FloatDomainError", "ZeroDivisionError", "ThreadGroup", "Continuation", "Thread", "ThreadError", "SystemCallError", "NoMemoryError", "SecurityError", "RuntimeError", "NotImplementedError", "LoadError", "SyntaxError", "ScriptError", "NoMethodError", "NameError::message", "NameError", "RangeError", "IndexError", "ArgumentError", "TypeError", "StandardError", "Interrupt", "SignalException", "fatal", "SystemExit", "Exception", "String", "FalseClass", "TrueClass", "Data", "Symbol", "NilClass", "Class", "Module", "Object"];
-	
 	var operators = ["+", "*", "/", "-", "&", "|", "~", "!", "%", "<", "=", ">"];
 	var values = ["this", "true", "false", "nil", /[0-9]+(\.[0-9]+)?/g];

 	brush.push(values, {klass: 'constant', children: null});
-	brush.push(types, {klass: 'type', children: null})
+	
+	brush.push({pattern: /(@+|\$)[\w]+/g, klass: 'variable', children: null})
+	
+	brush.push({pattern: /[A-Z_][\w:]+/g, klass: 'type', children: null})
 	brush.push(keywords, {klass: 'keyword', children: null})
 	brush.push(operators, {klass: 'operator', children: null})

 	brush.push(Syntax.lib.perlStyleComment)
+	brush.push(Syntax.lib.webLink)

 	brush.push({pattern: /".+?"/g, klass: 'string', children: ['escape']});
+	brush.push({pattern: /'.+?'/g, klass: 'string', children: ['escape']});
 	brush.push({pattern: /\\./g, klass: 'escape'});

 	brush.push(Syntax.lib.rubyStyleFunction);
+	brush.push(Syntax.lib.cStyleFunction);
 });

diff --git a/jquery.syntax.css b/jquery.syntax.css
index 0c4d657..2423173 100644
--- a/jquery.syntax.css
+++ b/jquery.syntax.css
@@ -14,12 +14,12 @@

 .syntax .keyword {
 	color: #3c3;
-	font-weight: bold;
 }

 .syntax .type,
 .syntax .template {
-	color: #3c3;
+	color: #191;
+	font-weight: bold;
 }

 .syntax .typename {
@@ -44,7 +44,12 @@
 	color: #c00;
 }

-.syntax .template {
+.syntax .href {
+	color: #00f;
+	text-decoration: underline;
+}
+
+.syntax .variable {
 	font-style: italic;
 }

diff --git a/jquery.syntax.js b/jquery.syntax.js
index 7dfc642..07fc8a1 100644
--- a/jquery.syntax.js
+++ b/jquery.syntax.js
@@ -73,9 +73,8 @@ ResourceLoader.prototype._finish = function (name) {
 	if (!resource)
 		window.console.log("Could not load resource named ", name)
 	else {
-		for (var i in loading) {
+		for (var i = 0; i < loading.length; i += 1)
 			loading[i](resource)
-		}
 	}
 }

@@ -100,11 +99,9 @@ Syntax = {

 		Syntax.getResource('jquery.syntax.brush', name, callback)
 	}),
-	
 	layouts: new ResourceLoader(function(name, callback) {
 		Syntax.getResource('jquery.syntax.layout', name, callback)
-	}), 
-	
+	}),
 	getStyles: function (path) {
 		var link = $('<link>')
 		$("head").append(link)
@@ -115,7 +112,6 @@ Syntax = {
 			href: path
 		})
 	},
-	
 	getScript: function (path, callback) {
 		$.ajax({
 			async: 'true',
@@ -128,7 +124,6 @@ Syntax = {
 			cache: true
 		})
 	},
-	
 	getResource: function (prefix, name, callback) {
 		var basename = prefix + "." + name

@@ -137,25 +132,22 @@ Syntax = {

 		Syntax.getScript(this.root + basename + '.js', callback)
 	},
-	
 	register: function (name, callback) {
-		brush = Syntax.brushes[name] = new Syntax.Brush()
+		var brush = Syntax.brushes[name] = new Syntax.Brush()
 		window.console.log("Registering brush", name, Syntax.brushes)

 		brush.klass = name

 		callback(brush)
 	},
-	
 	alias: function (name, aliases) {
-		for (var i in aliases)
+		for (var i = 0; i < aliases.length; i += 1)
 			Syntax.aliases[aliases[i]] = name
 	},
-	
 	getMatches: function (text, expr) {
 		//window.console.log("getMatches: ", text, expr)

-		var matches = []
+		var matches = [], match = null

 		while((match = expr.pattern.exec(text)) != null) {
 			if (expr.matches)
@@ -168,23 +160,23 @@ Syntax = {
 	}
 }

-// Default layout
 Syntax.layouts.plain = function (options, html, container) {
 	return html
 }

 Syntax.singleMatchFunction = function(index, rule) {
-	return function(token) {
+	return function(match) {
 		return new Syntax.Match(RegExp.indexOf(match, index), match[index].length, rule, match[index])
 	}
 }

-Syntax.lib.cStyleComment = {pattern: /\/\*[\s\S]*?\*\//gm, klass: 'comment', children: null}
-Syntax.lib.cppStyleComment = {pattern: /\/\/.*$/gm, klass: 'comment', children: null}
-Syntax.lib.perlStyleComment = {pattern: /#.*$/gm, klass: 'comment', children: null}
-Syntax.lib.cStyleFunction = {pattern: /([a-z_][a-z0-9_]+)\s*\(/gi, matches: Syntax.singleMatchFunction(1, {klass: 'function', children: null})}
-Syntax.lib.rubyStyleFunction = {pattern: /\.([a-z_][a-z0-9_]+)/gi, matches: Syntax.singleMatchFunction(1, {klass: 'function', children: null})}
-Syntax.lib.rubyStyleSymbol = {pattern: /:[\w]+/, klass: 'constant', children: null}
+Syntax.lib.cStyleComment = {pattern: /\/\*[\s\S]*?\*\//gm, klass: 'comment', children: ['href']}
+Syntax.lib.cppStyleComment = {pattern: /\/\/.*$/gm, klass: 'comment', children: ['href']}
+Syntax.lib.perlStyleComment = {pattern: /#.*$/gm, klass: 'comment', children: ['href']}
+
+Syntax.lib.cStyleFunction = {pattern: /([a-z_][a-z0-9_]+)\s*\(/gi, matches: Syntax.singleMatchFunction(1, {klass: 'function', children: []})}
+
+Syntax.lib.webLink = {pattern: /https?:\/\/([-\w~!$?&#\/+|.,=]|%[a-f\d]{2})+/g, klass: 'href'}

 Syntax.Match = function (offset, length, expr, value) {
 	this.offset = offset
@@ -199,16 +191,11 @@ Syntax.Match = function (offset, length, expr, value) {
 }

 Syntax.Match.sort = function (a,b) {
-	var diff = a.offset - b.offset
-	
-	if (diff != 0)
-		return diff
-	else
-		return b.length - a.length
+	return (a.offset - b.offset) || (b.length - a.length)
 }

 Syntax.Match.prototype.contains = function (match) {
-	return (this.offset <= match.offset) && (match.endOffset <= this.endOffset)
+	return (match.offset >= this.offset) && (match.endOffset <= this.endOffset)
 }

 Syntax.Match.defaultReduceCallback = function (node, container) {
@@ -237,9 +224,8 @@ Syntax.Match.prototype.reduce = function (append) {
 	if (this.expression && this.expression.klass)
 		container.addClass(this.expression.klass)

-	for (var i in this.children) {
+	for (var i = 0; i < this.children.length; i += 1) {
 		//window.console.log(i, this.children[i], text)
-		
 		var child = this.children[i]
 		var end = child.offset

@@ -256,56 +242,59 @@ Syntax.Match.prototype.reduce = function (append) {
 	else if (start < this.endOffset)
 		append(this.value.substr(start - this.offset, this.endOffset - start), container)
 	else if (start > this.endOffset)
-		window.console.log("Start position", start, "exceeds length of value", this)
+		window.console.log("Start position", start, "exceeds length of value", this.offset, this.length)

 	return container
 }

-// This function is by far the biggest overal cost in terms of run-time. This is not because
-// it is slow, but mostly because it is called many times. It already has as much of the logic
-// folded into it (few function calls). I considered making it non-recursive, but after testing,
-// I found that less than 64 out of 500 function calls recursed at all, so it wouldn't be worth
-// it - it might even get slower.
-Syntax.Match.prototype.insert = function (match) {
+// This is not a general tree insertion function. It is optimised to run in almost constant
+// time, but data must be inserted in sorted order, otherwise you will have problems.
+Syntax.Match.prototype.insertAtEnd = function (match) {
+	if (!this.contains(match)) {
+		window.console.log(this, "does not contain", match)
+		return null;
+	}
+	
 	// We have explicitly said: no children
 	if (this.expression.children === null)
 		return null
-	
-	if (this.expression.children) {
-		// window.console.log(this.expression.children, match.expression.klass, $.inArray(match.expression.klass, this.expression.children))
+	else if (this.expression.children) {
 		if ($.inArray(match.expression.klass, this.expression.children) == -1)
 			return null
 	}

-	if (!this.contains(match))
-		return null
-	
 	if (this.children.length > 0) {
-		for (var i in this.children) {
-			var child = this.children[i]
-			
-			if (match.offset < child.offset) {
-				if (match.endOffset <= child.offset) {
-					this.children.splice(i, 0, match)
-					return this
-				} else {
-					window.console.log("Match " + match.toString() + " " + displacement + "  existing child: " + this.children[i].toString())
-					return null
-				}
-			} else if (match.offset < child.endOffset) {
-				if (match.endOffset <= child.endOffset) {
-					// Recursive step
-					return child.insert(match)
-				} else {
-					var parts = match.halfBisect(child.endOffset)
-					child.insert(parts[0])
-					match = parts[1]
-				}
+		var i = this.children.length-1
+		var child = this.children[i]
+		
+		if (match.offset < child.offset) {
+			if (match.endOffset <= child.offset) {
+				// displacement = 'before'
+				this.children.splice(i, 0, match)
+				return this
+			} else {
+				// displacement = 'left-overlap'
+				return null
+			}
+		} else if (match.offset < child.endOffset) {
+			if (match.endOffset <= child.endOffset) {
+				// displacement = 'contains'
+				var result = child.insertAtEnd(match)
+				return result
 			} else {
-				if (i == this.children.length-1) {
-					this.children.splice(i+1, 0, match)
-					return this
-				}
+				// displacement = 'right-overlap'
+				// This code should work, but right now I don't think its useful.
+				window.console.error("Cannot add match", match, "into", this, "it right-overlaps!")
+				//var parts = match.halfBisect(child.endOffset)
+				// child.insertAtEnd(parts[0])
+				// this.insertAtEnd(parts[1])
+				return null;
+			}
+		} else {
+			// displacement = 'after'
+			if (i == this.children.length-1) {
+				this.children.splice(i+1, 0, match)
+				return this
 			}
 		}

@@ -328,7 +317,14 @@ Syntax.Match.prototype.halfBisect = function(offset) {
 Syntax.Match.prototype.bisectAtOffsets = function(splits) {
 	var parts = [], start = this.offset, prev = null, children = $.merge([], this.children)

-	for (var i in splits) {
+	// Copy the array so we can modify it.
+	splits = splits.slice(0)
+	
+	// We need to split including the last part.
+	splits.push(this.endOffset)
+	splits.sort(function(a,b){return a-b})
+	
+	for (var i = 0; i < splits.length; i += 1) {
 		var offset = splits[i]

 		if (offset < this.offset || offset > this.endOffset) {
@@ -336,7 +332,7 @@ Syntax.Match.prototype.bisectAtOffsets = function(splits) {
 		}

 		var match = new Syntax.Match(start, offset - start, this.expression)
-		match.value = this.value.substr(start, match.length)
+		match.value = this.value.substr(start - this.offset, match.length)

 		if (prev)
 			prev.next = match
@@ -351,7 +347,7 @@ Syntax.Match.prototype.bisectAtOffsets = function(splits) {
 	splits.length = parts.length

 	for (var i = 0; i < parts.length; i += 1) {
-		var offset = splits.shift()
+		var offset = splits[0]

 		while (children.length > 0) {
 			if (children[0].endOffset <= parts[i].endOffset)
@@ -362,15 +358,18 @@ Syntax.Match.prototype.bisectAtOffsets = function(splits) {

 		if (children.length) {
 			// We may have an intersection
-			if (children[0].offset < parts[i].offset) {
-				children_parts = children.shift().bisectAtOffsets(splits)
+			if (children[0].offset < parts[i].endOffset) {
+				var children_parts = children.shift().bisectAtOffsets(splits), j = 0

-				for (var j in children_parts)
+				for (; j < children_parts.length; j += 1)
 					parts[i+j].children.push(children_parts[j])

-				// i += (j-1)
+				// Skip any parts which have been populated already
+				// i += (children_parts.length-1)
 			}
 		}
+		
+		splits.shift();
 	}

 	if (children.length)
@@ -380,14 +379,11 @@ Syntax.Match.prototype.bisectAtOffsets = function(splits) {
 }

 Syntax.Match.prototype.split = function(pattern) {
-	var splits = []
+	var splits = [], match

 	while ((match = pattern.exec(this.value)) != null)
 		splits.push(pattern.lastIndex)

-	// We need to have a fake split at the end.
-	splits.push(this.endOffset)
-	
 	return this.bisectAtOffsets(splits)
 }

@@ -400,14 +396,24 @@ Syntax.Brush.prototype.push = function () {
 	if ($.isArray(arguments[0])) {
 		var patterns = arguments[0], rule = arguments[1]

-		for (var i in patterns)
+		for (var i = 0; i < patterns.length; i += 1)
 			this.push($.extend({pattern: patterns[i]}, rule))
 	} else {
 		var rule = arguments[0]

 		if (typeof(rule.pattern) == 'string') {
 			rule.string = rule.pattern
-			rule.pattern = new RegExp(RegExp.escape(rule.pattern), rule.options || 'g')
+			var prefix = "\\b", postfix = "\\b"
+			
+			if (!rule.pattern.match(/^\w/)) {
+				if (!rule.pattern.match(/\w$/))
+					prefix = postfix = ""
+				else
+					prefix = "\\B"
+			}
+			
+			
+			rule.pattern = rule.pattern = new RegExp(prefix + RegExp.escape(rule.pattern) + postfix, rule.options || 'g')
 		}

 		if (rule.pattern.global)
@@ -420,9 +426,8 @@ Syntax.Brush.prototype.push = function () {
 Syntax.Brush.prototype.getMatches = function(text) {
 	var matches = []

-	for (var i in this.rules) {
+	for (var i = 0; i < this.rules.length; i += 1)
 		matches = matches.concat(Syntax.getMatches(text, this.rules[i]))
-	}

 	return matches
 }
@@ -434,15 +439,14 @@ Syntax.Brush.prototype.process = function(text) {
 	// This sort is absolutely key to the functioning of the tree insertion algorithm.
 	matches.sort(Syntax.Match.sort)

-	for (var i in matches) {
-		top.insert(matches[i])
-	}
+	for (var i = 0; i < matches.length; i += 1)
+		top.insertAtEnd(matches[i])

 	var lines = top.split(/\n/g)

 	var html = $('<pre>').addClass('syntax')

-	for (var i in lines) {
+	for (var i = 0; i < lines.length; i += 1) {
 		var line = lines[i].reduce()
 		html.append(line)
 	}
diff --git a/jquery.syntax.layout.table.css b/jquery.syntax.layout.table.css
index 6e12646..043f721 100644
--- a/jquery.syntax.layout.table.css
+++ b/jquery.syntax.layout.table.css
@@ -11,6 +11,10 @@ table.syntax {
 	-webkit-box-shadow: 5px 5px 5px #eee;
 }

+table.syntax tr.alt td.source {
+	background-color: #f6f6f6;
+}
+
 table.syntax td {
 	padding-left: 0.4em;
 	padding-right: 0.4em;
@@ -22,5 +26,5 @@ table.syntax td.number {

 	width: 2.5em;
 	color: #555;
-	background-color: #dadada;
+	background-color: #e0e0e0;
 }
\ No newline at end of file
diff --git a/jquery.syntax.layout.table.js b/jquery.syntax.layout.table.js
index 9cd68c3..14967c4 100644
--- a/jquery.syntax.layout.table.js
+++ b/jquery.syntax.layout.table.js
@@ -1,16 +1,14 @@
-Syntax.layouts.table = function(options, code, container) {
-	var table = $('<table>'), tr = null, td = null;
+Syntax.layouts.tableJQ = function(options, code, container) {
+	var table = $('<table class="syntax">'), tr = null, td = null;
 	var line = 1;

-	table.addClass('syntax');
-	
 	code.children().each(function() {
-		tr = $('<tr>').addClass('line', 'line-' + line);
+		tr = $('<tr class="line ln' + line + '">')

-		td = $('<td>').addClass('number').text(line);
+		td = $('<td class="number">' + line + '</td>')
 		tr.append(td);

-		td = $('<td>').addClass('source');
+		td = $('<td class="source">')
 		td.append(this);
 		tr.append(td);

@@ -19,4 +17,57 @@ Syntax.layouts.table = function(options, code, container) {
 	});

 	return table;
-}
\ No newline at end of file
+}
+
+Syntax.layouts.tableDOM = function(options, code, container) {
+	var table = $('<table>'), tr = null, td = null;
+	var line = 1;
+	
+	table.addClass('syntax');
+	
+	code.children().each(function() {
+		tr = document.createElement('tr')
+		tr.className = "line ln" + line
+		
+		if (line % 2)
+			tr.className += " alt"
+		
+		td = document.createElement('td')
+		td.className = "number"
+		td.innerHTML = line
+		tr.appendChild(td)
+		
+		td = document.createElement('td')
+		td.className = "source"
+		td.innerHTML = this.innerHTML
+		tr.appendChild(td)
+		
+		table[0].appendChild(tr)
+		line = line + 1;
+	});
+	
+	$('span.href', table).each(function(){
+		$(this).replaceWith($('<a>').attr('href', this.innerHTML).text(this.innerHTML))
+	})
+	
+	return table;
+}
+
+Syntax.layouts.tableSTR = function(options, code, container) {
+	var table = "<table class='syntax'>", tr = null, td = null;
+	var line = 1;
+	
+	code.children().each(function() {
+		table += "<tr class='line ln" + line + "'><td class='number'></td><td class='source'>" + this.innerHTML + "</td></tr>"
+
+		line = line + 1;
+	});
+	
+	table += "</table>"
+	
+	window.console.log(table.length)
+	
+	return $(table);
+}
+
+Syntax.layouts.table = Syntax.layouts.tableDOM
\ No newline at end of file