├── .gitignore
├── LICENSE.txt
├── Makefile
├── README.md
├── app.rb
├── bubble-to-pegjs.rb
├── bubble-to-pegjs_ex.rb
├── config.ru
├── public
├── index.html
├── js
│ ├── peg-0.6.1.js
│ ├── peg-0.6.1.min.js
│ ├── qunit
│ │ ├── qunit.css
│ │ └── qunit.js
│ └── sql.pegjs-head.js
└── test
│ ├── test-head.html
│ ├── test-peg.html
│ └── test-sample.html
├── sql-bubble.rb
├── sql-bubble.txt
├── sql.pegjs
└── views
└── index.erb
/.gitignore:
--------------------------------------------------------------------------------
1 | tmp
2 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | /**
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | all:
2 | ./sql-bubble.rb > tmp/rules.rb
3 | ./bubble-to-pegjs.rb tmp/rules.rb > tmp/sql.pegjs
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SQL parser in JavaScript
2 | ========================
3 |
4 | This project provides a PEG.js grammar for SQL syntax. Why? I wanted
5 | to properly parse SQL in JavaScript, knocking down yet another thing
6 | in that long list of things that will be reimplemented in JavaScript
7 | one day.
8 |
9 | See the ./sql.pegjs file, or...
10 |
11 | * http://github.com/steveyen/sqld3/blob/master/sql.pegjs
12 |
13 | Unlike previous hand-coded attempts at SQL parsing in JavaScript, such
14 | as my previous http://code.google.com/p/trimpath/wiki/TrimQuery, the
15 | parsing here is grammar (PEG) based.
16 |
17 | SQL syntax
18 | ----------
19 |
20 | The SQL syntax follows sqlite 3.7 documented syntax, specifically from...
21 |
22 | * sqlite's art/syntax/bubble-generator-data.tcl
23 | * http://www.sqlite.org/docsrc/artifact?name=a7001f134e2f341c3b46fad9623556260530904b
24 |
25 | See also
26 | --------
27 |
28 | * PEG.js: http://pegjs.majda.cz/
29 | * sqlite: http://www.sqlite.org
30 |
31 | License
32 | -------
33 |
34 | Apache 2.0 -- this was made for you and me.
35 |
36 |
--------------------------------------------------------------------------------
/app.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'sinatra'
3 | require 'erb'
4 |
5 | class App < Sinatra::Base
6 | set :static, true
7 | set :public, 'public'
8 |
9 | ["/", "/index", "/index.html"].each do |path|
10 | get path do
11 | erb :index
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/bubble-to-pegjs.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | $grammar = ARGV[0]
4 | $grammarx = ARGV[1] || 'bubble-to-pegjs_ex.rb'
5 | $keywords = {}
6 | $override = {}
7 |
8 | # ----------------------------------------
9 |
10 | def rule(name, *rest)
11 | print "#{name} =\n"
12 | if $override[name]
13 | print "#{$override[name]}\n"
14 | else
15 | rest.flatten.each do |x|
16 | $keywords[x] = true if x.class == String and x.match(/^[A-Z]+$/)
17 | print "#{x} "
18 | end
19 | print "\n\n"
20 | end
21 | end
22 |
23 | def either(*rest)
24 | r = []
25 | rest.each {|x| r << x; r << '/' }
26 | ["("] + r[0..-2] + [")"]
27 | end
28 |
29 | def stack(*rest)
30 | return rest if rest.length <= 1
31 | return ["("] + rest + [")"]
32 | end
33 |
34 | def line(*rest)
35 | return stack(*rest)
36 | end
37 |
38 | def loop(*rest)
39 | return ["("] + rest + [")+"]
40 | end
41 |
42 | def toploop(*rest)
43 | return loop(*rest)
44 | end
45 |
46 | def tailbranch(*rest) # TODO.
47 | return rest
48 | end
49 |
50 | def opt(*rest)
51 | return ["("] + rest + [")?"]
52 | end
53 |
54 | def optx(*rest)
55 | return opt(*rest)
56 | end
57 |
58 | # ----------------------------------------
59 |
60 | $header = <<-EOS
61 | // generated pegjs, from #{$grammar} and #{$grammarx}
62 |
63 | start = sql_stmt_list
64 | EOS
65 |
66 | # ----------------------------------------
67 |
68 | $extra = <<-EOS
69 | dot = '.'
70 | comma = ','
71 | semicolon = ';'
72 | minusminus = '--'
73 | minus = '-'
74 | plus = '+'
75 | lparen = '('
76 | rparen = ')'
77 | star = '*'
78 | newline = '\\n'
79 | anything_except_newline = [^\\n]*
80 | comment_beg = '/*'
81 | comment_end = '*/'
82 | anything_except_comment_end = .* & '*/'
83 | string_literal = '\"' (escape_char / [^"])* '\"'
84 | escape_char = '\\\\' .
85 | nil = ''
86 |
87 | whitespace = [\\s]*
88 | whitespace1 = [\\s]+
89 |
90 | unary_operator = '-' / '+' / '~' / 'NOT'
91 | binary_operator =
92 | '||'
93 | / '*' / '/' / '%'
94 | / '+' / '-'
95 | / '<<' / '>>' / '&' / '|'
96 | / '<' / '<=' / '>' / '>='
97 | / '=' / '==' / '!=' / '<>'
98 | / 'IS' / 'IS NOT' / 'IN' / 'LIKE' / 'GLOB' / 'MATCH' / 'REGEXP'
99 | / 'AND'
100 | / 'OR'
101 |
102 | digit = [0-9]
103 | decimal_point = dot
104 | equal = '='
105 |
106 | name = [A-Za-z0-9_]+
107 | database_name = name
108 | table_name = name
109 | table_alias = name
110 | table_or_index_name = name
111 | new_table_name = name
112 | index_name = name
113 | column_name = name
114 | column_alias = name
115 | foreign_table = name
116 | savepoint_name = name
117 | collation_name = name
118 | trigger_name = name
119 | view_name = name
120 | module_name = name
121 | module_argument = name
122 | bind_parameter = name
123 | function_name = name
124 | pragma_name = name
125 |
126 | error_message = string_literal
127 |
128 | CURRENT_TIME = 'now'
129 | CURRENT_DATE = 'now'
130 | CURRENT_TIMESTAMP = 'now'
131 |
132 | blob_literal = string_literal
133 |
134 | end_of_input = ''
135 | EOS
136 |
137 | # ----------------------------------------
138 |
139 | load $grammarx
140 |
141 | print "#{$header}\n"
142 |
143 | load $grammar
144 |
145 | print "#{$extra}\n"
146 |
147 | $keywords.keys.sort.each do |keyword|
148 | print "#{keyword} = whitespace1 \"#{keyword}\"\n"
149 | end
150 |
151 |
--------------------------------------------------------------------------------
/bubble-to-pegjs_ex.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | $keywords['EXPLAIN'] = true
4 | $keywords['QUERY'] = true
5 | $keywords['PLAN'] = true
6 |
7 | $override[:sql_stmt] = <<-EOS
8 | ( ( EXPLAIN ( QUERY PLAN )? )? (
9 | // alter_table_stmt
10 | // / analyze_stmt
11 | // / attach_stmt / begin_stmt / commit_stmt
12 | // / create_index_stmt / create_table_stmt / create_trigger_stmt
13 | // / create_view_stmt / create_virtual_table_stmt
14 | // / delete_stmt / delete_stmt_limited
15 | // / detach_stmt / drop_index_stmt / drop_table_stmt / drop_trigger_stmt / drop_view_stmt
16 | // / insert_stmt
17 | // / pragma_stmt / reindex_stmt / release_stmt / rollback_stmt / savepoint_stmt
18 | select_stmt
19 | // / update_stmt / update_stmt_limited
20 | // / vacuum_stmt
21 | ) )
22 | EOS
23 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | require 'app'
2 |
3 | use Rack::ShowExceptions
4 |
5 | run App.new
6 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
28 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
test markup
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/public/js/peg-0.6.1.min.js:
--------------------------------------------------------------------------------
1 | /* PEG.js 0.6.1 (http://pegjs.majda.cz/) */(function(){function buildNodeVisitor(a){return function(b){return a[b.type].apply(null,arguments)}}function quoteForRegexpClass(a){return a.replace(/\\/g,"\\\\").replace(/\0/g,"\\0").replace(/\//g,"\\/").replace(/]/g,"\\]").replace(/-/g,"\\-").replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/[\x80-\uFFFF]/g,escape)}function quote(a){return'"'+a.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/[\x80-\uFFFF]/g,escape)+'"'}function escape(a){var b=a.charCodeAt(0);if(b<=255)var c="x",d=2;else var c="u",d=4;return"\\"+c+padLeft(b.toString(16).toUpperCase(),"0",d)}function padLeft(a,b,c){var d=a,e=c-a.length;for(var f=0;fd){var p=a.charAt(d);d++}else{var p=null;e&&l("any character")}if(p!==null)var q=[o,p];else{var q=null;d=j}}else{var q=null;d=j}while(q!==null){i.push(q);var j=d,k=d,m=e;e=!1;if(a.substr(d,2)==="*/"){var n="*/";d+=2}else{var n=null;e&&l('"*/"')}e=m;if(n===null)var o="";else{var o=null;d=k}if(o!==null){if(a.length>d){var p=a.charAt(d);d++}else{var p=null;e&&l("any character")}if(p!==null)var q=[o,p];else{var q=null;d=j}}else{var q=null;d=j}}if(i!==null){if(a.substr(d,2)==="*/"){var r="*/";d+=2}else{var r=null;e&&l('"*/"')}if(r!==null)var s=[g,i,r];else{var s=null;d=f}}else{var s=null;d=f}}else{var s=null;d=f}h[b]={nextPos:d,result:s};return s}function bi(){var b="singleLineComment@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,2)==="//"){var g="//";d+=2}else{var g=null;e&&l('"//"')}if(g!==null){var i=[],j=d,k=d,m=e;e=!1;var n=bl();e=m;if(n===null)var o="";else{var o=null;d=k}if(o!==null){if(a.length>d){var p=a.charAt(d);d++}else{var p=null;e&&l("any character")}if(p!==null)var q=[o,p];else{var q=null;d=j}}else{var q=null;d=j}while(q!==null){i.push(q);var j=d,k=d,m=e;e=!1;var n=bl();e=m;if(n===null)var o="";else{var o=null;d=k}if(o!==null){if(a.length>d){var p=a.charAt(d);d++}else{var p=null;e&&l("any character")}if(p!==null)var q=[o,p];else{var q=null;d=j}}else{var q=null;d=j}}if(i!==null)var r=[g,i];else{var r=null;d=f}}else{var r=null;d=f}h[b]={nextPos:d,result:r};return r}function bh(){var a="comment@"+d,b=h[a];if(b){d=b.nextPos;return b.result}var c=e;e=!1;var f=bi();if(f!==null)var g=f;else{var i=bj();if(i!==null)var g=i;else var g=null}e=c,e&&g===null&&l("comment"),h[a]={nextPos:d,result:g};return g}function bg(){var a="__@"+d,b=h[a];if(b){d=b.nextPos;return b.result}var c=[],e=bm();if(e!==null)var f=e;else{var g=bk();if(g!==null)var f=g;else{var i=bh();if(i!==null)var f=i;else var f=null}}while(f!==null){c.push(f);var e=bm();if(e!==null)var f=e;else{var g=bk();if(g!==null)var f=g;else{var i=bh();if(i!==null)var f=i;else var f=null}}}h[a]={nextPos:d,result:c};return c}function bf(){var b="upperCaseLetter@"+d,c=h[b];if(c){d=c.nextPos;return c.result}if(a.substr(d).match(/^[A-Z]/)!==null){var f=a.charAt(d);d++}else{var f=null;e&&l("[A-Z]")}h[b]={nextPos:d,result:f};return f}function be(){var b="lowerCaseLetter@"+d,c=h[b];if(c){d=c.nextPos;return c.result}if(a.substr(d).match(/^[a-z]/)!==null){var f=a.charAt(d);d++}else{var f=null;e&&l("[a-z]")}h[b]={nextPos:d,result:f};return f}function bd(){var a="letter@"+d,b=h[a];if(b){d=b.nextPos;return b.result}var c=be();if(c!==null)var e=c;else{var f=bf();if(f!==null)var e=f;else var e=null}h[a]={nextPos:d,result:e};return e}function bc(){var b="hexDigit@"+d,c=h[b];if(c){d=c.nextPos;return c.result}if(a.substr(d).match(/^[0-9a-fA-F]/)!==null){var f=a.charAt(d);d++}else{var f=null;e&&l("[0-9a-fA-F]")}h[b]={nextPos:d,result:f};return f}function bb(){var b="digit@"+d,c=h[b];if(c){d=c.nextPos;return c.result}if(a.substr(d).match(/^[0-9]/)!==null){var f=a.charAt(d);d++}else{var f=null;e&&l("[0-9]")}h[b]={nextPos:d,result:f};return f}function ba(){var b="eolEscapeSequence@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,1)==="\\"){var g="\\";d+=1}else{var g=null;e&&l('"\\\\"')}if(g!==null){var i=bk();if(i!==null)var j=[g,i];else{var j=null;d=f}}else{var j=null;d=f}var k=j!==null?function(a){return a}(j[1]):null;h[b]={nextPos:d,result:k};return k}function _(){var b="unicodeEscapeSequence@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,2)==="\\u"){var g="\\u";d+=2}else{var g=null;e&&l('"\\\\u"')}if(g!==null){var i=bc();if(i!==null){var j=bc();if(j!==null){var k=bc();if(k!==null){var m=bc();if(m!==null)var n=[g,i,j,k,m];else{var n=null;d=f}}else{var n=null;d=f}}else{var n=null;d=f}}else{var n=null;d=f}}else{var n=null;d=f}var o=n!==null?function(a,b,c,d){return String.fromCharCode(parseInt("0x"+a+b+c+d))}(n[1],n[2],n[3],n[4]):null;h[b]={nextPos:d,result:o};return o}function $(){var b="hexEscapeSequence@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,2)==="\\x"){var g="\\x";d+=2}else{var g=null;e&&l('"\\\\x"')}if(g!==null){var i=bc();if(i!==null){var j=bc();if(j!==null)var k=[g,i,j];else{var k=null;d=f}}else{var k=null;d=f}}else{var k=null;d=f}var m=k!==null?function(a,b){return String.fromCharCode(parseInt("0x"+a+b))}(k[1],k[2]):null;h[b]={nextPos:d,result:m};return m}function Z(){var b="zeroEscapeSequence@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,2)==="\\0"){var g="\\0";d+=2}else{var g=null;e&&l('"\\\\0"')}if(g!==null){var i=d,j=e;e=!1;var k=bb();e=j;if(k===null)var m="";else{var m=null;d=i}if(m!==null)var n=[g,m];else{var n=null;d=f}}else{var n=null;d=f}var o=n!==null?function(){return" "}():null;h[b]={nextPos:d,result:o};return o}function Y(){var b="simpleEscapeSequence@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,1)==="\\"){var g="\\";d+=1}else{var g=null;e&&l('"\\\\"')}if(g!==null){var i=d,j=e;e=!1;var k=bb();if(k!==null)var m=k;else{if(a.substr(d,1)==="x"){var n="x";d+=1}else{var n=null;e&&l('"x"')}if(n!==null)var m=n;else{if(a.substr(d,1)==="u"){var o="u";d+=1}else{var o=null;e&&l('"u"')}if(o!==null)var m=o;else{var p=bl();if(p!==null)var m=p;else var m=null}}}e=j;if(m===null)var q="";else{var q=null;d=i}if(q!==null){if(a.length>d){var r=a.charAt(d);d++}else{var r=null;e&&l("any character")}if(r!==null)var s=[g,q,r];else{var s=null;d=f}}else{var s=null;d=f}}else{var s=null;d=f}var t=s!==null?function(a){return a.replace("b","\b").replace("f","\f").replace("n","\n").replace("r","\r").replace("t","\t").replace("v","")}(s[2]):null;h[b]={nextPos:d,result:t};return t}function X(){var b="simpleBracketDelimitedCharacter@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d,g=d,i=e;e=!1;if(a.substr(d,1)==="]"){var j="]";d+=1}else{var j=null;e&&l('"]"')}if(j!==null)var k=j;else{if(a.substr(d,1)==="\\"){var m="\\";d+=1}else{var m=null;e&&l('"\\\\"')}if(m!==null)var k=m;else{var n=bl();if(n!==null)var k=n;else var k=null}}e=i;if(k===null)var o="";else{var o=null;d=g}if(o!==null){if(a.length>d){var p=a.charAt(d);d++}else{var p=null;e&&l("any character")}if(p!==null)var q=[o,p];else{var q=null;d=f}}else{var q=null;d=f}var r=q!==null?function(a){return a}(q[1]):null;h[b]={nextPos:d,result:r};return r}function W(){var a="bracketDelimitedCharacter@"+d,b=h[a];if(b){d=b.nextPos;return b.result}var c=X();if(c!==null)var e=c;else{var f=Y();if(f!==null)var e=f;else{var g=Z();if(g!==null)var e=g;else{var i=$();if(i!==null)var e=i;else{var j=_();if(j!==null)var e=j;else{var k=ba();if(k!==null)var e=k;else var e=null}}}}}h[a]={nextPos:d,result:e};return e}function V(){var a="classCharacter@"+d,b=h[a];if(b){d=b.nextPos;return b.result}var c=W(),e=c!==null?function(a){return{data:a,rawText:quoteForRegexpClass(a)}}(c):null;h[a]={nextPos:d,result:e};return e}function U(){var b="classCharacterRange@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d,g=V();if(g!==null){if(a.substr(d,1)==="-"){var i="-";d+=1}else{var i=null;e&&l('"-"')}if(i!==null){var j=V();if(j!==null)var k=[g,i,j];else{var k=null;d=f}}else{var k=null;d=f}}else{var k=null;d=f}var m=k!==null?function(a,b){if(a.data.charCodeAt(0)>b.data.charCodeAt(0))throw new this.SyntaxError("Invalid character range: "+a.rawText+"-"+b.rawText+".");return{data:[a.data,b.data],rawText:a.rawText+"-"+b.rawText}}(k[0],k[2]):null;h[b]={nextPos:d,result:m};return m}function T(){var b="class@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=e;e=!1;var g=d;if(a.substr(d,1)==="["){var i="[";d+=1}else{var i=null;e&&l('"["')}if(i!==null){if(a.substr(d,1)==="^"){var j="^";d+=1}else{var j=null;e&&l('"^"')}var k=j!==null?j:"";if(k!==null){var m=[],n=U();if(n!==null)var o=n;else{var p=V();if(p!==null)var o=p;else var o=null}while(o!==null){m.push(o);var n=U();if(n!==null)var o=n;else{var p=V();if(p!==null)var o=p;else var o=null}}if(m!==null){if(a.substr(d,1)==="]"){var q="]";d+=1}else{var q=null;e&&l('"]"')}if(q!==null){var r=bg();if(r!==null)var s=[i,k,m,q,r];else{var s=null;d=g}}else{var s=null;d=g}}else{var s=null;d=g}}else{var s=null;d=g}}else{var s=null;d=g}var t=s!==null?function(a,b){var c=map(b,function(a){return a.data}),d="["+a+map(b,function(a){return a.rawText}).join("")+"]";return{type:"class",inverted:a==="^",parts:c,rawText:d}}(s[1],s[2]):null;e=f,e&&t===null&&l("character class"),h[b]={nextPos:d,result:t};return t}function S(){var b="simpleSingleQuotedCharacter@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d,g=d,i=e;e=!1;if(a.substr(d,1)==="'"){var j="'";d+=1}else{var j=null;e&&l('"\'"')}if(j!==null)var k=j;else{if(a.substr(d,1)==="\\"){var m="\\";d+=1}else{var m=null;e&&l('"\\\\"')}if(m!==null)var k=m;else{var n=bl();if(n!==null)var k=n;else var k=null}}e=i;if(k===null)var o="";else{var o=null;d=g}if(o!==null){if(a.length>d){var p=a.charAt(d);d++}else{var p=null;e&&l("any character")}if(p!==null)var q=[o,p];else{var q=null;d=f}}else{var q=null;d=f}var r=q!==null?function(a){return a}(q[1]):null;h[b]={nextPos:d,result:r};return r}function R(){var a="singleQuotedCharacter@"+d,b=h[a];if(b){d=b.nextPos;return b.result}var c=S();if(c!==null)var e=c;else{var f=Y();if(f!==null)var e=f;else{var g=Z();if(g!==null)var e=g;else{var i=$();if(i!==null)var e=i;else{var j=_();if(j!==null)var e=j;else{var k=ba();if(k!==null)var e=k;else var e=null}}}}}h[a]={nextPos:d,result:e};return e}function Q(){var b="singleQuotedLiteral@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,1)==="'"){var g="'";d+=1}else{var g=null;e&&l('"\'"')}if(g!==null){var i=[],j=R();while(j!==null){i.push(j);var j=R()}if(i!==null){if(a.substr(d,1)==="'"){var k="'";d+=1}else{var k=null;e&&l('"\'"')}if(k!==null)var m=[g,i,k];else{var m=null;d=f}}else{var m=null;d=f}}else{var m=null;d=f}var n=m!==null?function(a){return a.join("")}(m[1]):null;h[b]={nextPos:d,result:n};return n}function P(){var b="simpleDoubleQuotedCharacter@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d,g=d,i=e;e=!1;if(a.substr(d,1)==='"'){var j='"';d+=1}else{var j=null;e&&l('"\\""')}if(j!==null)var k=j;else{if(a.substr(d,1)==="\\"){var m="\\";d+=1}else{var m=null;e&&l('"\\\\"')}if(m!==null)var k=m;else{var n=bl();if(n!==null)var k=n;else var k=null}}e=i;if(k===null)var o="";else{var o=null;d=g}if(o!==null){if(a.length>d){var p=a.charAt(d);d++}else{var p=null;e&&l("any character")}if(p!==null)var q=[o,p];else{var q=null;d=f}}else{var q=null;d=f}var r=q!==null?function(a){return a}(q[1]):null;h[b]={nextPos:d,result:r};return r}function O(){var a="doubleQuotedCharacter@"+d,b=h[a];if(b){d=b.nextPos;return b.result}var c=P();if(c!==null)var e=c;else{var f=Y();if(f!==null)var e=f;else{var g=Z();if(g!==null)var e=g;else{var i=$();if(i!==null)var e=i;else{var j=_();if(j!==null)var e=j;else{var k=ba();if(k!==null)var e=k;else var e=null}}}}}h[a]={nextPos:d,result:e};return e}function N(){var b="doubleQuotedLiteral@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,1)==='"'){var g='"';d+=1}else{var g=null;e&&l('"\\""')}if(g!==null){var i=[],j=O();while(j!==null){i.push(j);var j=O()}if(i!==null){if(a.substr(d,1)==='"'){var k='"';d+=1}else{var k=null;e&&l('"\\""')}if(k!==null)var m=[g,i,k];else{var m=null;d=f}}else{var m=null;d=f}}else{var m=null;d=f}var n=m!==null?function(a){return a.join("")}(m[1]):null;h[b]={nextPos:d,result:n};return n}function M(){var a="literal@"+d,b=h[a];if(b){d=b.nextPos;return b.result}var c=e;e=!1;var f=d,g=N();if(g!==null)var i=g;else{var j=Q();if(j!==null)var i=j;else var i=null}if(i!==null){var k=bg();if(k!==null)var m=[i,k];else{var m=null;d=f}}else{var m=null;d=f}var n=m!==null?function(a){return a}(m[0]):null;e=c,e&&n===null&&l("literal"),h[a]={nextPos:d,result:n};return n}function L(){var b="identifier@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=e;e=!1;var g=d,i=bd();if(i!==null)var j=i;else{if(a.substr(d,1)==="_"){var k="_";d+=1}else{var k=null;e&&l('"_"')}if(k!==null)var j=k;else{if(a.substr(d,1)==="$"){var m="$";d+=1}else{var m=null;e&&l('"$"')}if(m!==null)var j=m;else var j=null}}if(j!==null){var n=[],o=bd();if(o!==null)var p=o;else{var q=bb();if(q!==null)var p=q;else{if(a.substr(d,1)==="_"){var r="_";d+=1}else{var r=null;e&&l('"_"')}if(r!==null)var p=r;else{if(a.substr(d,1)==="$"){var s="$";d+=1}else{var s=null;e&&l('"$"')}if(s!==null)var p=s;else var p=null}}}while(p!==null){n.push(p);var o=bd();if(o!==null)var p=o;else{var q=bb();if(q!==null)var p=q;else{if(a.substr(d,1)==="_"){var r="_";d+=1}else{var r=null;e&&l('"_"')}if(r!==null)var p=r;else{if(a.substr(d,1)==="$"){var s="$";d+=1}else{var s=null;e&&l('"$"')}if(s!==null)var p=s;else var p=null}}}}if(n!==null){var t=bg();if(t!==null)var u=[j,n,t];else{var u=null;d=g}}else{var u=null;d=g}}else{var u=null;d=g}var v=u!==null?function(a,b){return a+b.join("")}(u[0],u[1]):null;e=f,e&&v===null&&l("identifier"),h[b]={nextPos:d,result:v};return v}function K(){var b="dot@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,1)==="."){var g=".";d+=1}else{var g=null;e&&l('"."')}if(g!==null){var i=bg();if(i!==null)var j=[g,i];else{var j=null;d=f}}else{var j=null;d=f}var k=j!==null?function(){return"."}():null;h[b]={nextPos:d,result:k};return k}function J(){var b="rparen@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,1)===")"){var g=")";d+=1}else{var g=null;e&&l('")"')}if(g!==null){var i=bg();if(i!==null)var j=[g,i];else{var j=null;d=f}}else{var j=null;d=f}var k=j!==null?function(){return")"}():null;h[b]={nextPos:d,result:k};return k}function I(){var b="lparen@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,1)==="("){var g="(";d+=1}else{var g=null;e&&l('"("')}if(g!==null){var i=bg();if(i!==null)var j=[g,i];else{var j=null;d=f}}else{var j=null;d=f}var k=j!==null?function(){return"("}():null;h[b]={nextPos:d,result:k};return k}function H(){var b="plus@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,1)==="+"){var g="+";d+=1}else{var g=null;e&&l('"+"')}if(g!==null){var i=bg();if(i!==null)var j=[g,i];else{var j=null;d=f}}else{var j=null;d=f}var k=j!==null?function(){return"+"}():null;h[b]={nextPos:d,result:k};return k}function G(){var b="star@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,1)==="*"){var g="*";d+=1}else{var g=null;e&&l('"*"')}if(g!==null){var i=bg();if(i!==null)var j=[g,i];else{var j=null;d=f}}else{var j=null;d=f}var k=j!==null?function(){return"*"}():null;h[b]={nextPos:d,result:k};return k}function F(){var b="question@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,1)==="?"){var g="?";d+=1}else{var g=null;e&&l('"?"')}if(g!==null){var i=bg();if(i!==null)var j=[g,i];else{var j=null;d=f}}else{var j=null;d=f}var k=j!==null?function(){return"?"}():null;h[b]={nextPos:d,result:k};return k}function E(){var b="not@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,1)==="!"){var g="!";d+=1}else{var g=null;e&&l('"!"')}if(g!==null){var i=bg();if(i!==null)var j=[g,i];else{var j=null;d=f}}else{var j=null;d=f}var k=j!==null?function(){return"!"}():null;h[b]={nextPos:d,result:k};return k}function D(){var b="and@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,1)==="&"){var g="&";d+=1}else{var g=null;e&&l('"&"')}if(g!==null){var i=bg();if(i!==null)var j=[g,i];else{var j=null;d=f}}else{var j=null;d=f}var k=j!==null?function(){return"&"}():null;h[b]={nextPos:d,result:k};return k}function C(){var b="slash@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,1)==="/"){var g="/";d+=1}else{var g=null;e&&l('"/"')}if(g!==null){var i=bg();if(i!==null)var j=[g,i];else{var j=null;d=f}}else{var j=null;d=f}var k=j!==null?function(){return"/"}():null;h[b]={nextPos:d,result:k};return k}function B(){var b="semicolon@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,1)===";"){var g=";";d+=1}else{var g=null;e&&l('";"')}if(g!==null){var i=bg();if(i!==null)var j=[g,i];else{var j=null;d=f}}else{var j=null;d=f}var k=j!==null?function(){return";"}():null;h[b]={nextPos:d,result:k};return k}function A(){var b="colon@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,1)===":"){var g=":";d+=1}else{var g=null;e&&l('":"')}if(g!==null){var i=bg();if(i!==null)var j=[g,i];else{var j=null;d=f}}else{var j=null;d=f}var k=j!==null?function(){return":"}():null;h[b]={nextPos:d,result:k};return k}function z(){var b="equals@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,1)==="="){var g="=";d+=1}else{var g=null;e&&l('"="')}if(g!==null){var i=bg();if(i!==null)var j=[g,i];else{var j=null;d=f}}else{var j=null;d=f}var k=j!==null?function(){return"="}():null;h[b]={nextPos:d,result:k};return k}function y(){var b="nonBraceCharacter@"+d,c=h[b];if(c){d=c.nextPos;return c.result}if(a.substr(d).match(/^[^{}]/)!==null){var f=a.charAt(d);d++}else{var f=null;e&&l("[^{}]")}h[b]={nextPos:d,result:f};return f}function x(){var a="nonBraceCharacters@"+d,b=h[a];if(b){d=b.nextPos;return b.result}var c=y();if(c!==null){var e=[];while(c!==null){e.push(c);var c=y()}}else var e=null;var f=e!==null?function(a){return a.join("")}(e):null;h[a]={nextPos:d,result:f};return f}function w(){var b="braced@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d;if(a.substr(d,1)==="{"){var g="{";d+=1}else{var g=null;e&&l('"{"')}if(g!==null){var i=[],j=w();if(j!==null)var k=j;else{var m=y();if(m!==null)var k=m;else var k=null}while(k!==null){i.push(k);var j=w();if(j!==null)var k=j;else{var m=y();if(m!==null)var k=m;else var k=null}}if(i!==null){if(a.substr(d,1)==="}"){var n="}";d+=1}else{var n=null;e&&l('"}"')}if(n!==null)var o=[g,i,n];else{var o=null;d=f}}else{var o=null;d=f}}else{var o=null;d=f}var p=o!==null?function(a){return"{"+a.join("")+"}"}(o[1]):null;h[b]={nextPos:d,result:p};return p}function v(){var a="action@"+d,b=h[a];if(b){d=b.nextPos;return b.result}var c=e;e=!1;var f=d,g=w();if(g!==null){var i=bg();if(i!==null)var j=[g,i];else{var j=null;d=f}}else{var j=null;d=f}var k=j!==null?function(a){return a.substr(1,a.length-2)}(j[0]):null;e=c,e&&k===null&&l("action"),h[a]={nextPos:d,result:k};return k}function u(){var b="primary@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d,g=L();if(g!==null){var i=d,j=e;e=!1;var k=d,m=M();if(m!==null)var n=m;else{if(a.substr(d,0)===""){var o="";d+=0}else{var o=null;e&&l('""')}if(o!==null)var n=o;else var n=null}if(n!==null){var q=z();if(q!==null)var r=[n,q];else{var r=null;d=k}}else{var r=null;d=k}e=j;if(r===null)var s="";else{var s=null;d=i}if(s!==null)var t=[g,s];else{var t=null;d=f}}else{var t=null;d=f}var u=t!==null?function(a){return{type:"rule_ref",name:a}}(t[0]):null;if(u!==null)var v=u;else{var w=M(),x=w!==null?function(a){return{type:"literal",value:a}}(w):null;if(x!==null)var v=x;else{var y=K(),A=y!==null?function(){return{type:"any"}}():null;if(A!==null)var v=A;else{var B=T();if(B!==null)var v=B;else{var C=d,D=I();if(D!==null){var E=p();if(E!==null){var F=J();if(F!==null)var G=[D,E,F];else{var G=null;d=C}}else{var G=null;d=C}}else{var G=null;d=C}var H=G!==null?function(a){return a}(G[1]):null;if(H!==null)var v=H;else var v=null}}}}h[b]={nextPos:d,result:v};return v}function t(){var a="suffixed@"+d,b=h[a];if(b){d=b.nextPos;return b.result}var c=d,e=u();if(e!==null){var f=F();if(f!==null)var g=[e,f];else{var g=null;d=c}}else{var g=null;d=c}var i=g!==null?function(a){return{type:"optional",expression:a}}(g[0]):null;if(i!==null)var j=i;else{var k=d,l=u();if(l!==null){var m=G();if(m!==null)var n=[l,m];else{var n=null;d=k}}else{var n=null;d=k}var o=n!==null?function(a){return{type:"zero_or_more",expression:a}}(n[0]):null;if(o!==null)var j=o;else{var p=d,q=u();if(q!==null){var r=H();if(r!==null)var s=[q,r];else{var s=null;d=p}}else{var s=null;d=p}var t=s!==null?function(a){return{type:"one_or_more",expression:a}}(s[0]):null;if(t!==null)var j=t;else{var v=u();if(v!==null)var j=v;else var j=null}}}h[a]={nextPos:d,result:j};return j}function s(){var a="prefixed@"+d,b=h[a];if(b){d=b.nextPos;return b.result}var c=d,e=D();if(e!==null){var f=v();if(f!==null)var g=[e,f];else{var g=null;d=c}}else{var g=null;d=c}var i=g!==null?function(a){return{type:"semantic_and",code:a}}(g[1]):null;if(i!==null)var j=i;else{var k=d,l=D();if(l!==null){var m=t();if(m!==null)var n=[l,m];else{var n=null;d=k}}else{var n=null;d=k}var o=n!==null?function(a){return{type:"simple_and",expression:a}}(n[1]):null;if(o!==null)var j=o;else{var p=d,q=E();if(q!==null){var r=v();if(r!==null)var s=[q,r];else{var s=null;d=p}}else{var s=null;d=p}var u=s!==null?function(a){return{type:"semantic_not",code:a}}(s[1]):null;if(u!==null)var j=u;else{var w=d,x=E();if(x!==null){var y=t();if(y!==null)var z=[x,y];else{var z=null;d=w}}else{var z=null;d=w}var A=z!==null?function(a){return{type:"simple_not",expression:a}}(z[1]):null;if(A!==null)var j=A;else{var B=t();if(B!==null)var j=B;else var j=null}}}}h[a]={nextPos:d,result:j};return j}function r(){var a="labeled@"+d,b=h[a];if(b){d=b.nextPos;return b.result}var c=d,e=L();if(e!==null){var f=A();if(f!==null){var g=s();if(g!==null)var i=[e,f,g];else{var i=null;d=c}}else{var i=null;d=c}}else{var i=null;d=c}var j=i!==null?function(a,b){return{type:"labeled",label:a,expression:b}}(i[0],i[2]):null;if(j!==null)var k=j;else{var l=s();if(l!==null)var k=l;else var k=null}h[a]={nextPos:d,result:k};return k}function q(){var a="sequence@"+d,b=h[a];if(b){d=b.nextPos;return b.result}var c=d,e=[],f=r();while(f!==null){e.push(f);var f=r()}if(e!==null){var g=v();if(g!==null)var i=[e,g];else{var i=null;d=c}}else{var i=null;d=c}var j=i!==null?function(a,b){var c=a.length!=1?{type:"sequence",elements:a}:a[0];return{type:"action",expression:c,code:b}}(i[0],i[1]):null;if(j!==null)var k=j;else{var l=[],m=r();while(m!==null){l.push(m);var m=r()}var n=l!==null?function(a){return a.length!=1?{type:"sequence",elements:a}:a[0]}(l):null;if(n!==null)var k=n;else var k=null}h[a]={nextPos:d,result:k};return k}function p(){var a="choice@"+d,b=h[a];if(b){d=b.nextPos;return b.result}var c=d,e=q();if(e!==null){var f=[],g=d,i=C();if(i!==null){var j=q();if(j!==null)var k=[i,j];else{var k=null;d=g}}else{var k=null;d=g}while(k!==null){f.push(k);var g=d,i=C();if(i!==null){var j=q();if(j!==null)var k=[i,j];else{var k=null;d=g}}else{var k=null;d=g}}if(f!==null)var l=[e,f];else{var l=null;d=c}}else{var l=null;d=c}var m=l!==null?function(a,b){if(b.length>0){var c=[a].concat(map(b,function(a){return a[1]}));return{type:"choice",alternatives:c}}return a}(l[0],l[1]):null;h[a]={nextPos:d,result:m};return m}function o(){var b="rule@"+d,c=h[b];if(c){d=c.nextPos;return c.result}var f=d,g=L();if(g!==null){var i=M();if(i!==null)var j=i;else{if(a.substr(d,0)===""){var k="";d+=0}else{var k=null;e&&l('""')}if(k!==null)var j=k;else var j=null}if(j!==null){var m=z();if(m!==null){var n=p();if(n!==null){var o=B(),q=o!==null?o:"";if(q!==null)var r=[g,j,m,n,q];else{var r=null;d=f}}else{var r=null;d=f}}else{var r=null;d=f}}else{var r=null;d=f}}else{var r=null;d=f}var s=r!==null?function(a,b,c){return{type:"rule",name:a,displayName:b!==""?b:null,expression:c}}(r[0],r[1],r[3]):null;h[b]={nextPos:d,result:s};return s}function n(){var a="initializer@"+d,b=h[a];if(b){d=b.nextPos;return b.result}var c=d,e=v();if(e!==null){var f=B(),g=f!==null?f:"";if(g!==null)var i=[e,g];else{var i=null;d=c}}else{var i=null;d=c}var j=i!==null?function(a){return{type:"initializer",code:a}}(i[0]):null;h[a]={nextPos:d,result:j};return j}function m(){var a="grammar@"+d,b=h[a];if(b){d=b.nextPos;return b.result}var c=d,e=bg();if(e!==null){var f=n(),g=f!==null?f:"";if(g!==null){var i=o();if(i!==null){var j=[];while(i!==null){j.push(i);var i=o()}}else var j=null;if(j!==null)var k=[e,g,j];else{var k=null;d=c}}else{var k=null;d=c}}else{var k=null;d=c}var l=k!==null?function(a,b){var c={};each(b,function(a){c[a.name]=a});return{type:"grammar",initializer:a!==""?a:null,rules:c,startRule:b[0].name}}(k[1],k[2]):null;h[a]={nextPos:d,result:l};return l}function l(a){df&&(f=d,g=[]),g.push(a))}function k(a){return'"'+a.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/[\x80-\uFFFF]/g,j)+'"'}function j(a){var b=a.charCodeAt(0);if(b<=255)var c="x",d=2;else var c="u",d=4;return"\\"+c+i(b.toString(16).toUpperCase(),"0",d)}function i(a,b,c){var d=a,e=c-a.length;for(var f=0;f0&&d(a.elements[0],b)},labeled:c,simple_and:c,simple_not:c,semantic_and:b,semantic_not:b,optional:c,zero_or_more:c,one_or_more:c,action:c,rule_ref:function(b,c){if(contains(c,b.name))throw new PEG.GrammarError('Left recursion detected for rule "'+b.name+'".');d(a.rules[b.name],c)},literal:b,any:b,"class":b});d(a,[])}},PEG.compiler.passes={proxyRules:function(a){function c(a,b,c){function f(a){return function(b,c,d){each(b[a],function(a){g(a,c,d)})}}function e(a,b,c){g(a.expression,b,c)}function d(){}var g=buildNodeVisitor({grammar:function(a,b,c){for(var d in a.rules)g(a.rules[d],b,c)},rule:e,choice:f("alternatives"),sequence:f("elements"),labeled:e,simple_and:e,simple_not:e,semantic_and:d,semantic_not:d,optional:e,zero_or_more:e,one_or_more:e,action:e,rule_ref:function(a,b,c){a.name===b&&(a.name=c)},literal:d,any:d,"class":d});g(a,b,c)}function b(a){return a.type==="rule"&&a.expression.type==="rule_ref"}for(var d in a.rules)b(a.rules[d])&&(c(a,a.rules[d].name,a.rules[d].expression.name),d===a.startRule&&(a.startRule=a.rules[d].expression.name),delete a.rules[d]);return a}},PEG.compiler.emitter=function(a){function b(){function b(a){return map(a,function(a){if(!/\n/.test(a))return a;var b=a.match(/^\s*/)[0],c=a.split("\n"),d=[c[0]].concat(map(c.slice(1),function(a){return b+a}));return d.join("\n")})}function a(a){return map(a,function(a){return a.replace(/\$\{([a-zA-Z_][a-zA-Z0-9_]*)(\|([a-zA-Z_][a-zA-Z0-9_]*))?\}/g,function(a,b,c,e){var f=d[b];if(f===undefined)throw new Error('Undefined variable: "'+b+'".');if(e!==undefined&&e!=""){if(e==="string")return quote(f);throw new Error('Unrecognized filter: "'+e+'".')}return f})})}var c=Array.prototype.slice.call(arguments),d=c[c.length-1]instanceof Object?c.pop():{};return b(a(c)).join("\n")}var c={_counters:{},next:function(a){this._counters[a]=this._counters[a]||0;return a+this._counters[a]++},reset:function(){this._counters={}}},d=buildNodeVisitor({grammar:function(a){var c=a.initializer!==null?d(a.initializer):"",e=[];for(var f in a.rules)e.push(quote(f)+": parse_"+f);e.sort();var g=[];for(var f in a.rules)g.push(d(a.rules[f]));return b("(function(){"," /* Generated by PEG.js 0.6.1 (http://pegjs.majda.cz/). */"," "," var result = {"," /*"," * Parses the input with a generated parser. If the parsing is successfull,"," * returns a value explicitly or implicitly specified by the grammar from"," * which the parser was generated (see |PEG.buildParser|). If the parsing is"," * unsuccessful, throws |PEG.parser.SyntaxError| describing the error."," */"," parse: function(input, startRule) {"," var parseFunctions = {"," ${parseFunctionTableItems}"," };"," "," if (startRule !== undefined) {"
2 | ," if (parseFunctions[startRule] === undefined) {",' throw new Error("Invalid rule name: " + quote(startRule) + ".");'," }"," } else {"," startRule = ${startRule|string};"," }"," "," var pos = 0;"," var reportMatchFailures = true;"," var rightmostMatchFailuresPos = 0;"," var rightmostMatchFailuresExpected = [];"," var cache = {};"," "," function padLeft(input, padding, length) {"," var result = input;"," "," var padLength = length - input.length;"," for (var i = 0; i < padLength; i++) {"," result = padding + result;"," }"," "," return result;"," }"," "," function escape(ch) {"," var charCode = ch.charCodeAt(0);"," "," if (charCode <= 0xFF) {"," var escapeChar = 'x';"," var length = 2;"," } else {"," var escapeChar = 'u';"," var length = 4;"," }"," "," return '\\\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), '0', length);"," }"," "," function quote(s) {"," /*"," * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a"," * string literal except for the closing quote character, backslash,"," * carriage return, line separator, paragraph separator, and line feed."," * Any character may appear in the form of an escape sequence."," */"," return '\"' + s"," .replace(/\\\\/g, '\\\\\\\\') // backslash"," .replace(/\"/g, '\\\\\"') // closing quote character"," .replace(/\\r/g, '\\\\r') // carriage return"," .replace(/\\n/g, '\\\\n') // line feed"," .replace(/[\\x80-\\uFFFF]/g, escape) // non-ASCII characters"," + '\"';"," }"," "," function matchFailed(failure) {"," if (pos < rightmostMatchFailuresPos) {"," return;"," }"," "," if (pos > rightmostMatchFailuresPos) {"," rightmostMatchFailuresPos = pos;"," rightmostMatchFailuresExpected = [];"," }"," "," rightmostMatchFailuresExpected.push(failure);"," }"," "," ${parseFunctionDefinitions}"," "," function buildErrorMessage() {"," function buildExpected(failuresExpected) {"," failuresExpected.sort();"," "," var lastFailure = null;"," var failuresExpectedUnique = [];"," for (var i = 0; i < failuresExpected.length; i++) {"," if (failuresExpected[i] !== lastFailure) {"," failuresExpectedUnique.push(failuresExpected[i]);"," lastFailure = failuresExpected[i];"," }"," }"," "," switch (failuresExpectedUnique.length) {"," case 0:"," return 'end of input';"," case 1:"," return failuresExpectedUnique[0];"," default:"," return failuresExpectedUnique.slice(0, failuresExpectedUnique.length - 1).join(', ')"," + ' or '"," + failuresExpectedUnique[failuresExpectedUnique.length - 1];"," }"," }"," "," var expected = buildExpected(rightmostMatchFailuresExpected);"," var actualPos = Math.max(pos, rightmostMatchFailuresPos);"," var actual = actualPos < input.length"," ? quote(input.charAt(actualPos))"," : 'end of input';"," "," return 'Expected ' + expected + ' but ' + actual + ' found.';"," }"," "," function computeErrorPosition() {"," /*"," * The first idea was to use |String.split| to break the input up to the"," * error position along newlines and derive the line and column from"," * there. However IE's |split| implementation is so broken that it was"," * enough to prevent it."," */"," "," var line = 1;"," var column = 1;"," var seenCR = false;"," "," for (var i = 0; i < rightmostMatchFailuresPos; i++) {"," var ch = input.charAt(i);"," if (ch === '\\n') {"," if (!seenCR) { line++; }"," column = 1;"," seenCR = false;"," } else if (ch === '\\r' | ch === '\\u2028' || ch === '\\u2029') {"," line++;"," column = 1;"," seenCR = true;"," } else {"," column++;"," seenCR = false;"," }"," }"," "," return { line: line, column: column };"," }"," "," ${initializerCode}"," "," var result = parseFunctions[startRule]();"," "," /*"," * The parser is now in one of the following three states:"," *"," * 1. The parser successfully parsed the whole input."," *"," * - |result !== null|"," * - |pos === input.length|"," * - |rightmostMatchFailuresExpected| may or may not contain something"," *"," * 2. The parser successfully parsed only a part of the input."," *"," * - |result !== null|"," * - |pos < input.length|"," * - |rightmostMatchFailuresExpected| may or may not contain something"," *"," * 3. The parser did not successfully parse any part of the input."," *"," * - |result === null|"," * - |pos === 0|"," * - |rightmostMatchFailuresExpected| contains at least one failure"," *"," * All code following this comment (including called functions) must"," * handle these states."," */"," if (result === null || pos !== input.length) {"," var errorPosition = computeErrorPosition();"," throw new this.SyntaxError("," buildErrorMessage(),"," errorPosition.line,"," errorPosition.column"," );"," }"," "," return result;"," },"," "," /* Returns the parser source code. */"," toSource: function() { return this._source; }"," };"," "," /* Thrown when a parser encounters a syntax error. */"," "," result.SyntaxError = function(message, line, column) {"," this.name = 'SyntaxError';"," this.message = message;"," this.line = line;"," this.column = column;"," };"," "," result.SyntaxError.prototype = Error.prototype;"," "," return result;","})()",{initializerCode:c,parseFunctionTableItems:e.join(",\n"),parseFunctionDefinitions:g.join("\n\n"),startRule:a.startRule})},initializer:function(a){return a.code},rule:function(a){c.reset();var e=c.next("result");if(a.displayName!==null)var f=b("var savedReportMatchFailures = reportMatchFailures;","reportMatchFailures = false;"),g=b("reportMatchFailures = savedReportMatchFailures;"),h=b("if (reportMatchFailures && ${resultVar} === null) {"," matchFailed(${displayName|string});","}",{displayName:a.displayName,resultVar:e});else var f="",g="",h="";return b("function parse_${name}() {"," var cacheKey = '${name}@' + pos;"," var cachedResult = cache[cacheKey];"," if (cachedResult) {"," pos = cachedResult.nextPos;"," return cachedResult.result;"," }"," "," ${setReportMatchFailuresCode}"," ${code}"," ${restoreReportMatchFailuresCode}"," ${reportMatchFailureCode}"," "," cache[cacheKey] = {"," nextPos: pos,"," result: ${resultVar}"," };"," return ${resultVar};","}",{name:a.name,setReportMatchFailuresCode:f,restoreReportMatchFailuresCode:g,reportMatchFailureCode:h,code:d(a.expression,e),resultVar:e})},choice:function(a,e){var f=b("var ${resultVar} = null;",{resultVar:e});for(var g=a.alternatives.length-1;g>=0;g--){var h=c.next("result");f=b("${alternativeCode}","if (${alternativeResultVar} !== null) {"," var ${resultVar} = ${alternativeResultVar};","} else {"," ${code};","}",{alternativeCode:d(a.alternatives[g],h),alternativeResultVar:h,code:f,resultVar:e})}return f},sequence:function(a,e){var f=c.next("savedPos"),g=map(a.elements,function(){return c.next("result")}),h=b("var ${resultVar} = ${elementResultVarArray};",{resultVar:e,elementResultVarArray:"["+g.join(", ")+"]"});for(var i=a.elements.length-1;i>=0;i--)h=b("${elementCode}","if (${elementResultVar} !== null) {"," ${code}","} else {"," var ${resultVar} = null;"," pos = ${savedPosVar};","}",{elementCode:d(a.elements[i],g[i]),elementResultVar:g[i],code:h,savedPosVar:f,resultVar:e});return b("var ${savedPosVar} = pos;","${code}",{code:h,savedPosVar:f})},labeled:function(a,b){return d(a.expression,b)},simple_and:function(a,e){var f=c.next("savedPos"),g=c.next("savedReportMatchFailuresVar"),h=c.next("result");return b("var ${savedPosVar} = pos;","var ${savedReportMatchFailuresVar} = reportMatchFailures;","reportMatchFailures = false;","${expressionCode}","reportMatchFailures = ${savedReportMatchFailuresVar};","if (${expressionResultVar} !== null) {"," var ${resultVar} = '';"," pos = ${savedPosVar};","} else {"," var ${resultVar} = null;","}",{expressionCode:d(a.expression,h),expressionResultVar:h,savedPosVar:f,savedReportMatchFailuresVar:g,resultVar:e})},simple_not:function(a,e){var f=c.next("savedPos"),g=c.next("savedReportMatchFailuresVar"),h=c.next("result");return b("var ${savedPosVar} = pos;","var ${savedReportMatchFailuresVar} = reportMatchFailures;","reportMatchFailures = false;","${expressionCode}","reportMatchFailures = ${savedReportMatchFailuresVar};","if (${expressionResultVar} === null) {"," var ${resultVar} = '';","} else {"," var ${resultVar} = null;"," pos = ${savedPosVar};","}",{expressionCode:d(a.expression,h),expressionResultVar:h,savedPosVar:f,savedReportMatchFailuresVar:g,resultVar:e})},semantic_and:function(a,c){return b("var ${resultVar} = (function() {${actionCode}})() ? '' : null;",{actionCode:a.code,resultVar:c})},semantic_not:function(a,c){return b("var ${resultVar} = (function() {${actionCode}})() ? null : '';",{actionCode:a.code,resultVar:c})},optional:function(a,e){var f=c.next("result");return b("${expressionCode}","var ${resultVar} = ${expressionResultVar} !== null ? ${expressionResultVar} : '';",{expressionCode:d(a.expression,f),expressionResultVar:f,resultVar:e})},zero_or_more:function(a,e){var f=c.next("result");return b("var ${resultVar} = [];","${expressionCode}","while (${expressionResultVar} !== null) {"," ${resultVar}.push(${expressionResultVar});"," ${expressionCode}","}",{expressionCode:d(a.expression,f),expressionResultVar:f,resultVar:e})},one_or_more:function(a,e){var f=c.next("result");return b("${expressionCode}","if (${expressionResultVar} !== null) {"," var ${resultVar} = [];"," while (${expressionResultVar} !== null) {"," ${resultVar}.push(${expressionResultVar});"," ${expressionCode}"," }","} else {"," var ${resultVar} = null;","}",{expressionCode:d(a.expression,f),expressionResultVar:f,resultVar:e})},action:function(a,e){var f=c.next("result");if(a.expression.type==="sequence"){var g=[],h=[],i=a.expression.elements,j=i.length;for(var k=0;k pos) {"," var ${resultVar} = input.charAt(pos);"," pos++;","} else {"," var ${resultVar} = null;"," if (reportMatchFailures) {"," matchFailed('any character');"," }","}",{resultVar:c})},"class":function(a,c){if(a.parts.length>0)var d="/^["+(a.inverted?"^":"")+map(a.parts,function(a){return a instanceof Array?quoteForRegexpClass(a[0])+"-"+quoteForRegexpClass(a[1]):quoteForRegexpClass(a)}).join("")+"]/";else var d=a.inverted?"/^[\\S\\s]/":"/^(?!)/";return b("if (input.substr(pos).match(${regexp}) !== null) {"," var ${resultVar} = input.charAt(pos);"," pos++;","} else {"," var ${resultVar} = null;"," if (reportMatchFailures) {"," matchFailed(${rawText|string});"," }","}",{regexp:d,rawText:a.rawText,resultVar:c})}});return d(a)};if(typeof module=="object")module.exports=PEG;else if(typeof window=="object")window.PEG=PEG;else throw new Error('Can\'t export PEG library (no "module" nor "window" object detected).')})()
--------------------------------------------------------------------------------
/public/js/qunit/qunit.css:
--------------------------------------------------------------------------------
1 | /** Font Family and Sizes */
2 |
3 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
4 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
5 | }
6 |
7 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
8 | #qunit-tests { font-size: smaller; }
9 |
10 |
11 | /** Resets */
12 |
13 | #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
14 | margin: 0;
15 | padding: 0;
16 | }
17 |
18 |
19 | /** Header */
20 |
21 | #qunit-header {
22 | padding: 0.5em 0 0.5em 1em;
23 |
24 | color: #8699a4;
25 | background-color: #0d3349;
26 |
27 | font-size: 1.5em;
28 | line-height: 1em;
29 | font-weight: normal;
30 |
31 | border-radius: 15px 15px 0 0;
32 | -moz-border-radius: 15px 15px 0 0;
33 | -webkit-border-top-right-radius: 15px;
34 | -webkit-border-top-left-radius: 15px;
35 | }
36 |
37 | #qunit-header a {
38 | text-decoration: none;
39 | color: #c2ccd1;
40 | }
41 |
42 | #qunit-header a:hover,
43 | #qunit-header a:focus {
44 | color: #fff;
45 | }
46 |
47 | #qunit-banner {
48 | height: 5px;
49 | }
50 |
51 | #qunit-testrunner-toolbar {
52 | padding: 0.5em 0 0.5em 2em;
53 | color: #5E740B;
54 | background-color: #eee;
55 | }
56 |
57 | #qunit-userAgent {
58 | padding: 0.5em 0 0.5em 2.5em;
59 | background-color: #2b81af;
60 | color: #fff;
61 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
62 | }
63 |
64 |
65 | /** Tests: Pass/Fail */
66 |
67 | #qunit-tests {
68 | list-style-position: inside;
69 | }
70 |
71 | #qunit-tests li {
72 | padding: 0.4em 0.5em 0.4em 2.5em;
73 | border-bottom: 1px solid #fff;
74 | list-style-position: inside;
75 | }
76 |
77 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
78 | display: none;
79 | }
80 |
81 | #qunit-tests li strong {
82 | cursor: pointer;
83 | }
84 |
85 | #qunit-tests li a {
86 | padding: 0.5em;
87 | color: #c2ccd1;
88 | text-decoration: none;
89 | }
90 | #qunit-tests li a:hover,
91 | #qunit-tests li a:focus {
92 | color: #000;
93 | }
94 |
95 | #qunit-tests ol {
96 | margin-top: 0.5em;
97 | padding: 0.5em;
98 |
99 | background-color: #fff;
100 |
101 | border-radius: 15px;
102 | -moz-border-radius: 15px;
103 | -webkit-border-radius: 15px;
104 |
105 | box-shadow: inset 0px 2px 13px #999;
106 | -moz-box-shadow: inset 0px 2px 13px #999;
107 | -webkit-box-shadow: inset 0px 2px 13px #999;
108 | }
109 |
110 | #qunit-tests table {
111 | border-collapse: collapse;
112 | margin-top: .2em;
113 | }
114 |
115 | #qunit-tests th {
116 | text-align: right;
117 | vertical-align: top;
118 | padding: 0 .5em 0 0;
119 | }
120 |
121 | #qunit-tests td {
122 | vertical-align: top;
123 | }
124 |
125 | #qunit-tests pre {
126 | margin: 0;
127 | white-space: pre-wrap;
128 | word-wrap: break-word;
129 | }
130 |
131 | #qunit-tests del {
132 | background-color: #e0f2be;
133 | color: #374e0c;
134 | text-decoration: none;
135 | }
136 |
137 | #qunit-tests ins {
138 | background-color: #ffcaca;
139 | color: #500;
140 | text-decoration: none;
141 | }
142 |
143 | /*** Test Counts */
144 |
145 | #qunit-tests b.counts { color: black; }
146 | #qunit-tests b.passed { color: #5E740B; }
147 | #qunit-tests b.failed { color: #710909; }
148 |
149 | #qunit-tests li li {
150 | margin: 0.5em;
151 | padding: 0.4em 0.5em 0.4em 0.5em;
152 | background-color: #fff;
153 | border-bottom: none;
154 | list-style-position: inside;
155 | }
156 |
157 | /*** Passing Styles */
158 |
159 | #qunit-tests li li.pass {
160 | color: #5E740B;
161 | background-color: #fff;
162 | border-left: 26px solid #C6E746;
163 | }
164 |
165 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
166 | #qunit-tests .pass .test-name { color: #366097; }
167 |
168 | #qunit-tests .pass .test-actual,
169 | #qunit-tests .pass .test-expected { color: #999999; }
170 |
171 | #qunit-banner.qunit-pass { background-color: #C6E746; }
172 |
173 | /*** Failing Styles */
174 |
175 | #qunit-tests li li.fail {
176 | color: #710909;
177 | background-color: #fff;
178 | border-left: 26px solid #EE5757;
179 | }
180 |
181 | #qunit-tests > li:last-child {
182 | border-radius: 0 0 15px 15px;
183 | -moz-border-radius: 0 0 15px 15px;
184 | -webkit-border-bottom-right-radius: 15px;
185 | -webkit-border-bottom-left-radius: 15px;
186 | }
187 |
188 | #qunit-tests .fail { color: #000000; background-color: #EE5757; }
189 | #qunit-tests .fail .test-name,
190 | #qunit-tests .fail .module-name { color: #000000; }
191 |
192 | #qunit-tests .fail .test-actual { color: #EE5757; }
193 | #qunit-tests .fail .test-expected { color: green; }
194 |
195 | #qunit-banner.qunit-fail { background-color: #EE5757; }
196 |
197 |
198 | /** Result */
199 |
200 | #qunit-testresult {
201 | padding: 0.5em 0.5em 0.5em 2.5em;
202 |
203 | color: #2b81af;
204 | background-color: #D2E0E6;
205 |
206 | border-bottom: 1px solid white;
207 | }
208 |
209 | /** Fixture */
210 |
211 | #qunit-fixture {
212 | position: absolute;
213 | top: -10000px;
214 | left: -10000px;
215 | }
216 |
--------------------------------------------------------------------------------
/public/js/qunit/qunit.js:
--------------------------------------------------------------------------------
1 | /*
2 | * QUnit - A JavaScript Unit Testing Framework
3 | *
4 | * http://docs.jquery.com/QUnit
5 | *
6 | * Copyright (c) 2011 John Resig, Jörn Zaefferer
7 | * Dual licensed under the MIT (MIT-LICENSE.txt)
8 | * or GPL (GPL-LICENSE.txt) licenses.
9 | */
10 |
11 | (function(window) {
12 |
13 | var defined = {
14 | setTimeout: typeof window.setTimeout !== "undefined",
15 | sessionStorage: (function() {
16 | try {
17 | return !!sessionStorage.getItem;
18 | } catch(e){
19 | return false;
20 | }
21 | })()
22 | };
23 |
24 | var testId = 0;
25 |
26 | var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {
27 | this.name = name;
28 | this.testName = testName;
29 | this.expected = expected;
30 | this.testEnvironmentArg = testEnvironmentArg;
31 | this.async = async;
32 | this.callback = callback;
33 | this.assertions = [];
34 | };
35 | Test.prototype = {
36 | init: function() {
37 | var tests = id("qunit-tests");
38 | if (tests) {
39 | var b = document.createElement("strong");
40 | b.innerHTML = "Running " + this.name;
41 | var li = document.createElement("li");
42 | li.appendChild( b );
43 | li.className = "running";
44 | li.id = this.id = "test-output" + testId++;
45 | tests.appendChild( li );
46 | }
47 | },
48 | setup: function() {
49 | if (this.module != config.previousModule) {
50 | if ( config.previousModule ) {
51 | QUnit.moduleDone( {
52 | name: config.previousModule,
53 | failed: config.moduleStats.bad,
54 | passed: config.moduleStats.all - config.moduleStats.bad,
55 | total: config.moduleStats.all
56 | } );
57 | }
58 | config.previousModule = this.module;
59 | config.moduleStats = { all: 0, bad: 0 };
60 | QUnit.moduleStart( {
61 | name: this.module
62 | } );
63 | }
64 |
65 | config.current = this;
66 | this.testEnvironment = extend({
67 | setup: function() {},
68 | teardown: function() {}
69 | }, this.moduleTestEnvironment);
70 | if (this.testEnvironmentArg) {
71 | extend(this.testEnvironment, this.testEnvironmentArg);
72 | }
73 |
74 | QUnit.testStart( {
75 | name: this.testName
76 | } );
77 |
78 | // allow utility functions to access the current test environment
79 | // TODO why??
80 | QUnit.current_testEnvironment = this.testEnvironment;
81 |
82 | try {
83 | if ( !config.pollution ) {
84 | saveGlobal();
85 | }
86 |
87 | this.testEnvironment.setup.call(this.testEnvironment);
88 | } catch(e) {
89 | QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message );
90 | }
91 | },
92 | run: function() {
93 | if ( this.async ) {
94 | QUnit.stop();
95 | }
96 |
97 | if ( config.notrycatch ) {
98 | this.callback.call(this.testEnvironment);
99 | return;
100 | }
101 | try {
102 | this.callback.call(this.testEnvironment);
103 | } catch(e) {
104 | fail("Test " + this.testName + " died, exception and test follows", e, this.callback);
105 | QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) );
106 | // else next test will carry the responsibility
107 | saveGlobal();
108 |
109 | // Restart the tests if they're blocking
110 | if ( config.blocking ) {
111 | start();
112 | }
113 | }
114 | },
115 | teardown: function() {
116 | try {
117 | checkPollution();
118 | this.testEnvironment.teardown.call(this.testEnvironment);
119 | } catch(e) {
120 | QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message );
121 | }
122 | },
123 | finish: function() {
124 | if ( this.expected && this.expected != this.assertions.length ) {
125 | QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );
126 | }
127 |
128 | var good = 0, bad = 0,
129 | tests = id("qunit-tests");
130 |
131 | config.stats.all += this.assertions.length;
132 | config.moduleStats.all += this.assertions.length;
133 |
134 | if ( tests ) {
135 | var ol = document.createElement("ol");
136 |
137 | for ( var i = 0; i < this.assertions.length; i++ ) {
138 | var assertion = this.assertions[i];
139 |
140 | var li = document.createElement("li");
141 | li.className = assertion.result ? "pass" : "fail";
142 | li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed");
143 | ol.appendChild( li );
144 |
145 | if ( assertion.result ) {
146 | good++;
147 | } else {
148 | bad++;
149 | config.stats.bad++;
150 | config.moduleStats.bad++;
151 | }
152 | }
153 |
154 | // store result when possible
155 | if ( QUnit.config.reorder && defined.sessionStorage ) {
156 | if (bad) {
157 | sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad)
158 | } else {
159 | sessionStorage.removeItem("qunit-" + this.testName);
160 | }
161 | }
162 |
163 | if (bad == 0) {
164 | ol.style.display = "none";
165 | }
166 |
167 | var b = document.createElement("strong");
168 | b.innerHTML = this.name + " (" + bad + ", " + good + ", " + this.assertions.length + ")";
169 |
170 | var a = document.createElement("a");
171 | a.innerHTML = "Rerun";
172 | a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
173 |
174 | addEvent(b, "click", function() {
175 | var next = b.nextSibling.nextSibling,
176 | display = next.style.display;
177 | next.style.display = display === "none" ? "block" : "none";
178 | });
179 |
180 | addEvent(b, "dblclick", function(e) {
181 | var target = e && e.target ? e.target : window.event.srcElement;
182 | if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
183 | target = target.parentNode;
184 | }
185 | if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
186 | window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
187 | }
188 | });
189 |
190 | var li = id(this.id);
191 | li.className = bad ? "fail" : "pass";
192 | li.removeChild( li.firstChild );
193 | li.appendChild( b );
194 | li.appendChild( a );
195 | li.appendChild( ol );
196 |
197 | } else {
198 | for ( var i = 0; i < this.assertions.length; i++ ) {
199 | if ( !this.assertions[i].result ) {
200 | bad++;
201 | config.stats.bad++;
202 | config.moduleStats.bad++;
203 | }
204 | }
205 | }
206 |
207 | try {
208 | QUnit.reset();
209 | } catch(e) {
210 | fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset);
211 | }
212 |
213 | QUnit.testDone( {
214 | name: this.testName,
215 | failed: bad,
216 | passed: this.assertions.length - bad,
217 | total: this.assertions.length
218 | } );
219 | },
220 |
221 | queue: function() {
222 | var test = this;
223 | synchronize(function() {
224 | test.init();
225 | });
226 | function run() {
227 | // each of these can by async
228 | synchronize(function() {
229 | test.setup();
230 | });
231 | synchronize(function() {
232 | test.run();
233 | });
234 | synchronize(function() {
235 | test.teardown();
236 | });
237 | synchronize(function() {
238 | test.finish();
239 | });
240 | }
241 | // defer when previous test run passed, if storage is available
242 | var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName);
243 | if (bad) {
244 | run();
245 | } else {
246 | synchronize(run);
247 | };
248 | }
249 |
250 | };
251 |
252 | var QUnit = {
253 |
254 | // call on start of module test to prepend name to all tests
255 | module: function(name, testEnvironment) {
256 | config.currentModule = name;
257 | config.currentModuleTestEnviroment = testEnvironment;
258 | },
259 |
260 | asyncTest: function(testName, expected, callback) {
261 | if ( arguments.length === 2 ) {
262 | callback = expected;
263 | expected = 0;
264 | }
265 |
266 | QUnit.test(testName, expected, callback, true);
267 | },
268 |
269 | test: function(testName, expected, callback, async) {
270 | var name = '' + testName + '', testEnvironmentArg;
271 |
272 | if ( arguments.length === 2 ) {
273 | callback = expected;
274 | expected = null;
275 | }
276 | // is 2nd argument a testEnvironment?
277 | if ( expected && typeof expected === 'object') {
278 | testEnvironmentArg = expected;
279 | expected = null;
280 | }
281 |
282 | if ( config.currentModule ) {
283 | name = '' + config.currentModule + ": " + name;
284 | }
285 |
286 | if ( !validTest(config.currentModule + ": " + testName) ) {
287 | return;
288 | }
289 |
290 | var test = new Test(name, testName, expected, testEnvironmentArg, async, callback);
291 | test.module = config.currentModule;
292 | test.moduleTestEnvironment = config.currentModuleTestEnviroment;
293 | test.queue();
294 | },
295 |
296 | /**
297 | * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
298 | */
299 | expect: function(asserts) {
300 | config.current.expected = asserts;
301 | },
302 |
303 | /**
304 | * Asserts true.
305 | * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
306 | */
307 | ok: function(a, msg) {
308 | a = !!a;
309 | var details = {
310 | result: a,
311 | message: msg
312 | };
313 | msg = escapeHtml(msg);
314 | QUnit.log(details);
315 | config.current.assertions.push({
316 | result: a,
317 | message: msg
318 | });
319 | },
320 |
321 | /**
322 | * Checks that the first two arguments are equal, with an optional message.
323 | * Prints out both actual and expected values.
324 | *
325 | * Prefered to ok( actual == expected, message )
326 | *
327 | * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
328 | *
329 | * @param Object actual
330 | * @param Object expected
331 | * @param String message (optional)
332 | */
333 | equal: function(actual, expected, message) {
334 | QUnit.push(expected == actual, actual, expected, message);
335 | },
336 |
337 | notEqual: function(actual, expected, message) {
338 | QUnit.push(expected != actual, actual, expected, message);
339 | },
340 |
341 | deepEqual: function(actual, expected, message) {
342 | QUnit.push(QUnit.equiv(actual, expected), actual, expected, message);
343 | },
344 |
345 | notDeepEqual: function(actual, expected, message) {
346 | QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message);
347 | },
348 |
349 | strictEqual: function(actual, expected, message) {
350 | QUnit.push(expected === actual, actual, expected, message);
351 | },
352 |
353 | notStrictEqual: function(actual, expected, message) {
354 | QUnit.push(expected !== actual, actual, expected, message);
355 | },
356 |
357 | raises: function(block, expected, message) {
358 | var actual, ok = false;
359 |
360 | if (typeof expected === 'string') {
361 | message = expected;
362 | expected = null;
363 | }
364 |
365 | try {
366 | block();
367 | } catch (e) {
368 | actual = e;
369 | }
370 |
371 | if (actual) {
372 | // we don't want to validate thrown error
373 | if (!expected) {
374 | ok = true;
375 | // expected is a regexp
376 | } else if (QUnit.objectType(expected) === "regexp") {
377 | ok = expected.test(actual);
378 | // expected is a constructor
379 | } else if (actual instanceof expected) {
380 | ok = true;
381 | // expected is a validation function which returns true is validation passed
382 | } else if (expected.call({}, actual) === true) {
383 | ok = true;
384 | }
385 | }
386 |
387 | QUnit.ok(ok, message);
388 | },
389 |
390 | start: function() {
391 | config.semaphore--;
392 | if (config.semaphore > 0) {
393 | // don't start until equal number of stop-calls
394 | return;
395 | }
396 | if (config.semaphore < 0) {
397 | // ignore if start is called more often then stop
398 | config.semaphore = 0;
399 | }
400 | // A slight delay, to avoid any current callbacks
401 | if ( defined.setTimeout ) {
402 | window.setTimeout(function() {
403 | if ( config.timeout ) {
404 | clearTimeout(config.timeout);
405 | }
406 |
407 | config.blocking = false;
408 | process();
409 | }, 13);
410 | } else {
411 | config.blocking = false;
412 | process();
413 | }
414 | },
415 |
416 | stop: function(timeout) {
417 | config.semaphore++;
418 | config.blocking = true;
419 |
420 | if ( timeout && defined.setTimeout ) {
421 | clearTimeout(config.timeout);
422 | config.timeout = window.setTimeout(function() {
423 | QUnit.ok( false, "Test timed out" );
424 | QUnit.start();
425 | }, timeout);
426 | }
427 | }
428 | };
429 |
430 | // Backwards compatibility, deprecated
431 | QUnit.equals = QUnit.equal;
432 | QUnit.same = QUnit.deepEqual;
433 |
434 | // Maintain internal state
435 | var config = {
436 | // The queue of tests to run
437 | queue: [],
438 |
439 | // block until document ready
440 | blocking: true,
441 |
442 | // by default, run previously failed tests first
443 | // very useful in combination with "Hide passed tests" checked
444 | reorder: true,
445 |
446 | noglobals: false,
447 | notrycatch: false
448 | };
449 |
450 | // Load paramaters
451 | (function() {
452 | var location = window.location || { search: "", protocol: "file:" },
453 | params = location.search.slice( 1 ).split( "&" ),
454 | length = params.length,
455 | urlParams = {},
456 | current;
457 |
458 | if ( params[ 0 ] ) {
459 | for ( var i = 0; i < length; i++ ) {
460 | current = params[ i ].split( "=" );
461 | current[ 0 ] = decodeURIComponent( current[ 0 ] );
462 | // allow just a key to turn on a flag, e.g., test.html?noglobals
463 | current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
464 | urlParams[ current[ 0 ] ] = current[ 1 ];
465 | if ( current[ 0 ] in config ) {
466 | config[ current[ 0 ] ] = current[ 1 ];
467 | }
468 | }
469 | }
470 |
471 | QUnit.urlParams = urlParams;
472 | config.filter = urlParams.filter;
473 |
474 | // Figure out if we're running the tests from a server or not
475 | QUnit.isLocal = !!(location.protocol === 'file:');
476 | })();
477 |
478 | // Expose the API as global variables, unless an 'exports'
479 | // object exists, in that case we assume we're in CommonJS
480 | if ( typeof exports === "undefined" || typeof require === "undefined" ) {
481 | extend(window, QUnit);
482 | window.QUnit = QUnit;
483 | } else {
484 | extend(exports, QUnit);
485 | exports.QUnit = QUnit;
486 | }
487 |
488 | // define these after exposing globals to keep them in these QUnit namespace only
489 | extend(QUnit, {
490 | config: config,
491 |
492 | // Initialize the configuration options
493 | init: function() {
494 | extend(config, {
495 | stats: { all: 0, bad: 0 },
496 | moduleStats: { all: 0, bad: 0 },
497 | started: +new Date,
498 | updateRate: 1000,
499 | blocking: false,
500 | autostart: true,
501 | autorun: false,
502 | filter: "",
503 | queue: [],
504 | semaphore: 0
505 | });
506 |
507 | var tests = id( "qunit-tests" ),
508 | banner = id( "qunit-banner" ),
509 | result = id( "qunit-testresult" );
510 |
511 | if ( tests ) {
512 | tests.innerHTML = "";
513 | }
514 |
515 | if ( banner ) {
516 | banner.className = "";
517 | }
518 |
519 | if ( result ) {
520 | result.parentNode.removeChild( result );
521 | }
522 |
523 | if ( tests ) {
524 | result = document.createElement( "p" );
525 | result.id = "qunit-testresult";
526 | result.className = "result";
527 | tests.parentNode.insertBefore( result, tests );
528 | result.innerHTML = 'Running...
';
529 | }
530 | },
531 |
532 | /**
533 | * Resets the test setup. Useful for tests that modify the DOM.
534 | *
535 | * If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
536 | */
537 | reset: function() {
538 | if ( window.jQuery ) {
539 | jQuery( "#main, #qunit-fixture" ).html( config.fixture );
540 | } else {
541 | var main = id( 'main' ) || id( 'qunit-fixture' );
542 | if ( main ) {
543 | main.innerHTML = config.fixture;
544 | }
545 | }
546 | },
547 |
548 | /**
549 | * Trigger an event on an element.
550 | *
551 | * @example triggerEvent( document.body, "click" );
552 | *
553 | * @param DOMElement elem
554 | * @param String type
555 | */
556 | triggerEvent: function( elem, type, event ) {
557 | if ( document.createEvent ) {
558 | event = document.createEvent("MouseEvents");
559 | event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
560 | 0, 0, 0, 0, 0, false, false, false, false, 0, null);
561 | elem.dispatchEvent( event );
562 |
563 | } else if ( elem.fireEvent ) {
564 | elem.fireEvent("on"+type);
565 | }
566 | },
567 |
568 | // Safe object type checking
569 | is: function( type, obj ) {
570 | return QUnit.objectType( obj ) == type;
571 | },
572 |
573 | objectType: function( obj ) {
574 | if (typeof obj === "undefined") {
575 | return "undefined";
576 |
577 | // consider: typeof null === object
578 | }
579 | if (obj === null) {
580 | return "null";
581 | }
582 |
583 | var type = Object.prototype.toString.call( obj )
584 | .match(/^\[object\s(.*)\]$/)[1] || '';
585 |
586 | switch (type) {
587 | case 'Number':
588 | if (isNaN(obj)) {
589 | return "nan";
590 | } else {
591 | return "number";
592 | }
593 | case 'String':
594 | case 'Boolean':
595 | case 'Array':
596 | case 'Date':
597 | case 'RegExp':
598 | case 'Function':
599 | return type.toLowerCase();
600 | }
601 | if (typeof obj === "object") {
602 | return "object";
603 | }
604 | return undefined;
605 | },
606 |
607 | push: function(result, actual, expected, message) {
608 | var details = {
609 | result: result,
610 | message: message,
611 | actual: actual,
612 | expected: expected
613 | };
614 |
615 | message = escapeHtml(message) || (result ? "okay" : "failed");
616 | message = '' + message + "";
617 | expected = escapeHtml(QUnit.jsDump.parse(expected));
618 | actual = escapeHtml(QUnit.jsDump.parse(actual));
619 | var output = message + 'Expected: | ' + expected + ' |
';
620 | if (actual != expected) {
621 | output += 'Result: | ' + actual + ' |
';
622 | output += 'Diff: | ' + QUnit.diff(expected, actual) +' |
';
623 | }
624 | if (!result) {
625 | var source = sourceFromStacktrace();
626 | if (source) {
627 | details.source = source;
628 | output += 'Source: | ' + source +' |
';
629 | }
630 | }
631 | output += "
";
632 |
633 | QUnit.log(details);
634 |
635 | config.current.assertions.push({
636 | result: !!result,
637 | message: output
638 | });
639 | },
640 |
641 | url: function( params ) {
642 | params = extend( extend( {}, QUnit.urlParams ), params );
643 | var querystring = "?",
644 | key;
645 | for ( key in params ) {
646 | querystring += encodeURIComponent( key ) + "=" +
647 | encodeURIComponent( params[ key ] ) + "&";
648 | }
649 | return window.location.pathname + querystring.slice( 0, -1 );
650 | },
651 |
652 | // Logging callbacks; all receive a single argument with the listed properties
653 | // run test/logs.html for any related changes
654 | begin: function() {},
655 | // done: { failed, passed, total, runtime }
656 | done: function() {},
657 | // log: { result, actual, expected, message }
658 | log: function() {},
659 | // testStart: { name }
660 | testStart: function() {},
661 | // testDone: { name, failed, passed, total }
662 | testDone: function() {},
663 | // moduleStart: { name }
664 | moduleStart: function() {},
665 | // moduleDone: { name, failed, passed, total }
666 | moduleDone: function() {}
667 | });
668 |
669 | if ( typeof document === "undefined" || document.readyState === "complete" ) {
670 | config.autorun = true;
671 | }
672 |
673 | addEvent(window, "load", function() {
674 | QUnit.begin({});
675 |
676 | // Initialize the config, saving the execution queue
677 | var oldconfig = extend({}, config);
678 | QUnit.init();
679 | extend(config, oldconfig);
680 |
681 | config.blocking = false;
682 |
683 | var userAgent = id("qunit-userAgent");
684 | if ( userAgent ) {
685 | userAgent.innerHTML = navigator.userAgent;
686 | }
687 | var banner = id("qunit-header");
688 | if ( banner ) {
689 | banner.innerHTML = ' ' + banner.innerHTML + ' ' +
690 | '' +
691 | '';
692 | addEvent( banner, "change", function( event ) {
693 | var params = {};
694 | params[ event.target.name ] = event.target.checked ? true : undefined;
695 | window.location = QUnit.url( params );
696 | });
697 | }
698 |
699 | var toolbar = id("qunit-testrunner-toolbar");
700 | if ( toolbar ) {
701 | var filter = document.createElement("input");
702 | filter.type = "checkbox";
703 | filter.id = "qunit-filter-pass";
704 | addEvent( filter, "click", function() {
705 | var ol = document.getElementById("qunit-tests");
706 | if ( filter.checked ) {
707 | ol.className = ol.className + " hidepass";
708 | } else {
709 | var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
710 | ol.className = tmp.replace(/ hidepass /, " ");
711 | }
712 | if ( defined.sessionStorage ) {
713 | if (filter.checked) {
714 | sessionStorage.setItem("qunit-filter-passed-tests", "true");
715 | } else {
716 | sessionStorage.removeItem("qunit-filter-passed-tests");
717 | }
718 | }
719 | });
720 | if ( defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) {
721 | filter.checked = true;
722 | var ol = document.getElementById("qunit-tests");
723 | ol.className = ol.className + " hidepass";
724 | }
725 | toolbar.appendChild( filter );
726 |
727 | var label = document.createElement("label");
728 | label.setAttribute("for", "qunit-filter-pass");
729 | label.innerHTML = "Hide passed tests";
730 | toolbar.appendChild( label );
731 | }
732 |
733 | var main = id('main') || id('qunit-fixture');
734 | if ( main ) {
735 | config.fixture = main.innerHTML;
736 | }
737 |
738 | if (config.autostart) {
739 | QUnit.start();
740 | }
741 | });
742 |
743 | function done() {
744 | config.autorun = true;
745 |
746 | // Log the last module results
747 | if ( config.currentModule ) {
748 | QUnit.moduleDone( {
749 | name: config.currentModule,
750 | failed: config.moduleStats.bad,
751 | passed: config.moduleStats.all - config.moduleStats.bad,
752 | total: config.moduleStats.all
753 | } );
754 | }
755 |
756 | var banner = id("qunit-banner"),
757 | tests = id("qunit-tests"),
758 | runtime = +new Date - config.started,
759 | passed = config.stats.all - config.stats.bad,
760 | html = [
761 | 'Tests completed in ',
762 | runtime,
763 | ' milliseconds.
',
764 | '',
765 | passed,
766 | ' tests of ',
767 | config.stats.all,
768 | ' passed, ',
769 | config.stats.bad,
770 | ' failed.'
771 | ].join('');
772 |
773 | if ( banner ) {
774 | banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
775 | }
776 |
777 | if ( tests ) {
778 | id( "qunit-testresult" ).innerHTML = html;
779 | }
780 |
781 | QUnit.done( {
782 | failed: config.stats.bad,
783 | passed: passed,
784 | total: config.stats.all,
785 | runtime: runtime
786 | } );
787 | }
788 |
789 | function validTest( name ) {
790 | var filter = config.filter,
791 | run = false;
792 |
793 | if ( !filter ) {
794 | return true;
795 | }
796 |
797 | not = filter.charAt( 0 ) === "!";
798 | if ( not ) {
799 | filter = filter.slice( 1 );
800 | }
801 |
802 | if ( name.indexOf( filter ) !== -1 ) {
803 | return !not;
804 | }
805 |
806 | if ( not ) {
807 | run = true;
808 | }
809 |
810 | return run;
811 | }
812 |
813 | // so far supports only Firefox, Chrome and Opera (buggy)
814 | // could be extended in the future to use something like https://github.com/csnover/TraceKit
815 | function sourceFromStacktrace() {
816 | try {
817 | throw new Error();
818 | } catch ( e ) {
819 | if (e.stacktrace) {
820 | // Opera
821 | return e.stacktrace.split("\n")[6];
822 | } else if (e.stack) {
823 | // Firefox, Chrome
824 | return e.stack.split("\n")[4];
825 | }
826 | }
827 | }
828 |
829 | function escapeHtml(s) {
830 | if (!s) {
831 | return "";
832 | }
833 | s = s + "";
834 | return s.replace(/[\&"<>\\]/g, function(s) {
835 | switch(s) {
836 | case "&": return "&";
837 | case "\\": return "\\\\";
838 | case '"': return '\"';
839 | case "<": return "<";
840 | case ">": return ">";
841 | default: return s;
842 | }
843 | });
844 | }
845 |
846 | function synchronize( callback ) {
847 | config.queue.push( callback );
848 |
849 | if ( config.autorun && !config.blocking ) {
850 | process();
851 | }
852 | }
853 |
854 | function process() {
855 | var start = (new Date()).getTime();
856 |
857 | while ( config.queue.length && !config.blocking ) {
858 | if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {
859 | config.queue.shift()();
860 | } else {
861 | window.setTimeout( process, 13 );
862 | break;
863 | }
864 | }
865 | if (!config.blocking && !config.queue.length) {
866 | done();
867 | }
868 | }
869 |
870 | function saveGlobal() {
871 | config.pollution = [];
872 |
873 | if ( config.noglobals ) {
874 | for ( var key in window ) {
875 | config.pollution.push( key );
876 | }
877 | }
878 | }
879 |
880 | function checkPollution( name ) {
881 | var old = config.pollution;
882 | saveGlobal();
883 |
884 | var newGlobals = diff( config.pollution, old );
885 | if ( newGlobals.length > 0 ) {
886 | ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
887 | }
888 |
889 | var deletedGlobals = diff( old, config.pollution );
890 | if ( deletedGlobals.length > 0 ) {
891 | ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
892 | }
893 | }
894 |
895 | // returns a new Array with the elements that are in a but not in b
896 | function diff( a, b ) {
897 | var result = a.slice();
898 | for ( var i = 0; i < result.length; i++ ) {
899 | for ( var j = 0; j < b.length; j++ ) {
900 | if ( result[i] === b[j] ) {
901 | result.splice(i, 1);
902 | i--;
903 | break;
904 | }
905 | }
906 | }
907 | return result;
908 | }
909 |
910 | function fail(message, exception, callback) {
911 | if ( typeof console !== "undefined" && console.error && console.warn ) {
912 | console.error(message);
913 | console.error(exception);
914 | console.warn(callback.toString());
915 |
916 | } else if ( window.opera && opera.postError ) {
917 | opera.postError(message, exception, callback.toString);
918 | }
919 | }
920 |
921 | function extend(a, b) {
922 | for ( var prop in b ) {
923 | if ( b[prop] === undefined ) {
924 | delete a[prop];
925 | } else {
926 | a[prop] = b[prop];
927 | }
928 | }
929 |
930 | return a;
931 | }
932 |
933 | function addEvent(elem, type, fn) {
934 | if ( elem.addEventListener ) {
935 | elem.addEventListener( type, fn, false );
936 | } else if ( elem.attachEvent ) {
937 | elem.attachEvent( "on" + type, fn );
938 | } else {
939 | fn();
940 | }
941 | }
942 |
943 | function id(name) {
944 | return !!(typeof document !== "undefined" && document && document.getElementById) &&
945 | document.getElementById( name );
946 | }
947 |
948 | // Test for equality any JavaScript type.
949 | // Discussions and reference: http://philrathe.com/articles/equiv
950 | // Test suites: http://philrathe.com/tests/equiv
951 | // Author: Philippe Rathé
952 | QUnit.equiv = function () {
953 |
954 | var innerEquiv; // the real equiv function
955 | var callers = []; // stack to decide between skip/abort functions
956 | var parents = []; // stack to avoiding loops from circular referencing
957 |
958 | // Call the o related callback with the given arguments.
959 | function bindCallbacks(o, callbacks, args) {
960 | var prop = QUnit.objectType(o);
961 | if (prop) {
962 | if (QUnit.objectType(callbacks[prop]) === "function") {
963 | return callbacks[prop].apply(callbacks, args);
964 | } else {
965 | return callbacks[prop]; // or undefined
966 | }
967 | }
968 | }
969 |
970 | var callbacks = function () {
971 |
972 | // for string, boolean, number and null
973 | function useStrictEquality(b, a) {
974 | if (b instanceof a.constructor || a instanceof b.constructor) {
975 | // to catch short annotaion VS 'new' annotation of a declaration
976 | // e.g. var i = 1;
977 | // var j = new Number(1);
978 | return a == b;
979 | } else {
980 | return a === b;
981 | }
982 | }
983 |
984 | return {
985 | "string": useStrictEquality,
986 | "boolean": useStrictEquality,
987 | "number": useStrictEquality,
988 | "null": useStrictEquality,
989 | "undefined": useStrictEquality,
990 |
991 | "nan": function (b) {
992 | return isNaN(b);
993 | },
994 |
995 | "date": function (b, a) {
996 | return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf();
997 | },
998 |
999 | "regexp": function (b, a) {
1000 | return QUnit.objectType(b) === "regexp" &&
1001 | a.source === b.source && // the regex itself
1002 | a.global === b.global && // and its modifers (gmi) ...
1003 | a.ignoreCase === b.ignoreCase &&
1004 | a.multiline === b.multiline;
1005 | },
1006 |
1007 | // - skip when the property is a method of an instance (OOP)
1008 | // - abort otherwise,
1009 | // initial === would have catch identical references anyway
1010 | "function": function () {
1011 | var caller = callers[callers.length - 1];
1012 | return caller !== Object &&
1013 | typeof caller !== "undefined";
1014 | },
1015 |
1016 | "array": function (b, a) {
1017 | var i, j, loop;
1018 | var len;
1019 |
1020 | // b could be an object literal here
1021 | if ( ! (QUnit.objectType(b) === "array")) {
1022 | return false;
1023 | }
1024 |
1025 | len = a.length;
1026 | if (len !== b.length) { // safe and faster
1027 | return false;
1028 | }
1029 |
1030 | //track reference to avoid circular references
1031 | parents.push(a);
1032 | for (i = 0; i < len; i++) {
1033 | loop = false;
1034 | for(j=0;j= 0) {
1179 | type = "array";
1180 | } else {
1181 | type = typeof obj;
1182 | }
1183 | return type;
1184 | },
1185 | separator:function() {
1186 | return this.multiline ? this.HTML ? '
' : '\n' : this.HTML ? ' ' : ' ';
1187 | },
1188 | indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
1189 | if ( !this.multiline )
1190 | return '';
1191 | var chr = this.indentChar;
1192 | if ( this.HTML )
1193 | chr = chr.replace(/\t/g,' ').replace(/ /g,' ');
1194 | return Array( this._depth_ + (extra||0) ).join(chr);
1195 | },
1196 | up:function( a ) {
1197 | this._depth_ += a || 1;
1198 | },
1199 | down:function( a ) {
1200 | this._depth_ -= a || 1;
1201 | },
1202 | setParser:function( name, parser ) {
1203 | this.parsers[name] = parser;
1204 | },
1205 | // The next 3 are exposed so you can use them
1206 | quote:quote,
1207 | literal:literal,
1208 | join:join,
1209 | //
1210 | _depth_: 1,
1211 | // This is the list of parsers, to modify them, use jsDump.setParser
1212 | parsers:{
1213 | window: '[Window]',
1214 | document: '[Document]',
1215 | error:'[ERROR]', //when no parser is found, shouldn't happen
1216 | unknown: '[Unknown]',
1217 | 'null':'null',
1218 | 'undefined':'undefined',
1219 | 'function':function( fn ) {
1220 | var ret = 'function',
1221 | name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
1222 | if ( name )
1223 | ret += ' ' + name;
1224 | ret += '(';
1225 |
1226 | ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join('');
1227 | return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' );
1228 | },
1229 | array: array,
1230 | nodelist: array,
1231 | arguments: array,
1232 | object:function( map ) {
1233 | var ret = [ ];
1234 | QUnit.jsDump.up();
1235 | for ( var key in map )
1236 | ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(map[key]) );
1237 | QUnit.jsDump.down();
1238 | return join( '{', ret, '}' );
1239 | },
1240 | node:function( node ) {
1241 | var open = QUnit.jsDump.HTML ? '<' : '<',
1242 | close = QUnit.jsDump.HTML ? '>' : '>';
1243 |
1244 | var tag = node.nodeName.toLowerCase(),
1245 | ret = open + tag;
1246 |
1247 | for ( var a in QUnit.jsDump.DOMAttrs ) {
1248 | var val = node[QUnit.jsDump.DOMAttrs[a]];
1249 | if ( val )
1250 | ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' );
1251 | }
1252 | return ret + close + open + '/' + tag + close;
1253 | },
1254 | functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function
1255 | var l = fn.length;
1256 | if ( !l ) return '';
1257 |
1258 | var args = Array(l);
1259 | while ( l-- )
1260 | args[l] = String.fromCharCode(97+l);//97 is 'a'
1261 | return ' ' + args.join(', ') + ' ';
1262 | },
1263 | key:quote, //object calls it internally, the key part of an item in a map
1264 | functionCode:'[code]', //function calls it internally, it's the content of the function
1265 | attribute:quote, //node calls it internally, it's an html attribute value
1266 | string:quote,
1267 | date:quote,
1268 | regexp:literal, //regex
1269 | number:literal,
1270 | 'boolean':literal
1271 | },
1272 | DOMAttrs:{//attributes to dump from nodes, name=>realName
1273 | id:'id',
1274 | name:'name',
1275 | 'class':'className'
1276 | },
1277 | HTML:false,//if true, entities are escaped ( <, >, \t, space and \n )
1278 | indentChar:' ',//indentation unit
1279 | multiline:true //if true, items in a collection, are separated by a \n, else just a space.
1280 | };
1281 |
1282 | return jsDump;
1283 | })();
1284 |
1285 | // from Sizzle.js
1286 | function getText( elems ) {
1287 | var ret = "", elem;
1288 |
1289 | for ( var i = 0; elems[i]; i++ ) {
1290 | elem = elems[i];
1291 |
1292 | // Get the text from text nodes and CDATA nodes
1293 | if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
1294 | ret += elem.nodeValue;
1295 |
1296 | // Traverse everything else, except comment nodes
1297 | } else if ( elem.nodeType !== 8 ) {
1298 | ret += getText( elem.childNodes );
1299 | }
1300 | }
1301 |
1302 | return ret;
1303 | };
1304 |
1305 | /*
1306 | * Javascript Diff Algorithm
1307 | * By John Resig (http://ejohn.org/)
1308 | * Modified by Chu Alan "sprite"
1309 | *
1310 | * Released under the MIT license.
1311 | *
1312 | * More Info:
1313 | * http://ejohn.org/projects/javascript-diff-algorithm/
1314 | *
1315 | * Usage: QUnit.diff(expected, actual)
1316 | *
1317 | * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick brown fox jumped jumps over"
1318 | */
1319 | QUnit.diff = (function() {
1320 | function diff(o, n){
1321 | var ns = new Object();
1322 | var os = new Object();
1323 |
1324 | for (var i = 0; i < n.length; i++) {
1325 | if (ns[n[i]] == null)
1326 | ns[n[i]] = {
1327 | rows: new Array(),
1328 | o: null
1329 | };
1330 | ns[n[i]].rows.push(i);
1331 | }
1332 |
1333 | for (var i = 0; i < o.length; i++) {
1334 | if (os[o[i]] == null)
1335 | os[o[i]] = {
1336 | rows: new Array(),
1337 | n: null
1338 | };
1339 | os[o[i]].rows.push(i);
1340 | }
1341 |
1342 | for (var i in ns) {
1343 | if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) {
1344 | n[ns[i].rows[0]] = {
1345 | text: n[ns[i].rows[0]],
1346 | row: os[i].rows[0]
1347 | };
1348 | o[os[i].rows[0]] = {
1349 | text: o[os[i].rows[0]],
1350 | row: ns[i].rows[0]
1351 | };
1352 | }
1353 | }
1354 |
1355 | for (var i = 0; i < n.length - 1; i++) {
1356 | if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null &&
1357 | n[i + 1] == o[n[i].row + 1]) {
1358 | n[i + 1] = {
1359 | text: n[i + 1],
1360 | row: n[i].row + 1
1361 | };
1362 | o[n[i].row + 1] = {
1363 | text: o[n[i].row + 1],
1364 | row: i + 1
1365 | };
1366 | }
1367 | }
1368 |
1369 | for (var i = n.length - 1; i > 0; i--) {
1370 | if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null &&
1371 | n[i - 1] == o[n[i].row - 1]) {
1372 | n[i - 1] = {
1373 | text: n[i - 1],
1374 | row: n[i].row - 1
1375 | };
1376 | o[n[i].row - 1] = {
1377 | text: o[n[i].row - 1],
1378 | row: i - 1
1379 | };
1380 | }
1381 | }
1382 |
1383 | return {
1384 | o: o,
1385 | n: n
1386 | };
1387 | }
1388 |
1389 | return function(o, n){
1390 | o = o.replace(/\s+$/, '');
1391 | n = n.replace(/\s+$/, '');
1392 | var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/));
1393 |
1394 | var str = "";
1395 |
1396 | var oSpace = o.match(/\s+/g);
1397 | if (oSpace == null) {
1398 | oSpace = [" "];
1399 | }
1400 | else {
1401 | oSpace.push(" ");
1402 | }
1403 | var nSpace = n.match(/\s+/g);
1404 | if (nSpace == null) {
1405 | nSpace = [" "];
1406 | }
1407 | else {
1408 | nSpace.push(" ");
1409 | }
1410 |
1411 | if (out.n.length == 0) {
1412 | for (var i = 0; i < out.o.length; i++) {
1413 | str += '' + out.o[i] + oSpace[i] + "";
1414 | }
1415 | }
1416 | else {
1417 | if (out.n[0].text == null) {
1418 | for (n = 0; n < out.o.length && out.o[n].text == null; n++) {
1419 | str += '' + out.o[n] + oSpace[n] + "";
1420 | }
1421 | }
1422 |
1423 | for (var i = 0; i < out.n.length; i++) {
1424 | if (out.n[i].text == null) {
1425 | str += '' + out.n[i] + nSpace[i] + "";
1426 | }
1427 | else {
1428 | var pre = "";
1429 |
1430 | for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) {
1431 | pre += '' + out.o[n] + oSpace[n] + "";
1432 | }
1433 | str += " " + out.n[i].text + nSpace[i] + pre;
1434 | }
1435 | }
1436 | }
1437 |
1438 | return str;
1439 | };
1440 | })();
1441 |
1442 | })(this);
1443 |
--------------------------------------------------------------------------------
/public/js/sql.pegjs-head.js:
--------------------------------------------------------------------------------
1 | // Header/utility functions for sql.pegjs grammar match bodies.
2 | //
3 | function append(arr, x) {
4 | arr[arr.length] = x;
5 | return arr;
6 | }
7 |
8 | function flatten(x, rejectSpace, acc) {
9 | acc = acc || [];
10 | if (x == null || x == undefined) {
11 | if (!rejectSpace) {
12 | return append(acc, x);
13 | }
14 | return acc;
15 | }
16 | if (x.length == undefined) { // Just an object, not a string or array.
17 | return append(acc, x);
18 | }
19 | if (rejectSpace &&
20 | ((x.length == 0) ||
21 | (typeof(x) == "string" &&
22 | x.match(/^\s*$/)))) {
23 | return acc;
24 | }
25 | if (typeof(x) == "string") {
26 | return append(acc, x);
27 | }
28 | for (var i = 0; i < x.length; i++) {
29 | flatten(x[i], rejectSpace, acc);
30 | }
31 | return acc;
32 | }
33 |
34 | function flatstr(x, rejectSpace, joinChar) {
35 | return flatten(x, rejectSpace, []).join(joinChar || '');
36 | }
37 |
38 | function filter(arr, x) {
39 | var acc = [];
40 | for (var i = 0; i < arr.length; i++) {
41 | if (arr[i] != x) {
42 | acc[length] = arr[i];
43 | }
44 | }
45 | return acc;
46 | }
47 |
48 | function nonempty(x) { // Ex: nonempty("") == null;
49 | if (x == null || x.length > 0) { // Ex: nonempty(null) == null;
50 | return x;
51 | }
52 | return null;
53 | }
54 |
55 | function put_if_not_null(m, key, val) {
56 | if (val) {
57 | m[key] = val;
58 | }
59 | return m;
60 | }
61 | function merge(src, dst) {
62 | for (var k in src) {
63 | dst[k] = src[k];
64 | }
65 | return dst;
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/public/test/test-head.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
test markup
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/public/test/test-peg.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
28 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
test markup
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/public/test/test-sample.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
test markup
333 |
334 |
335 |
336 |
--------------------------------------------------------------------------------
/sql-bubble.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # Process the sql-bubble.txt into a ruby rules.
4 | #
5 | bubble = ARGV[0] || 'sql-bubble.txt'
6 |
7 | lines = File.open(bubble, "r").readlines
8 | lines = lines.reject {|x| x.match(/^\s*\#/)} # Comments.
9 | lines = lines.reject {|x| x.match(/^\s*$/)} # Whitespace.
10 | lines = lines.map {|x| x.gsub(/^set all_graphs \{/, '')}
11 | lines = lines.map {|x| x.gsub(/^\}/, 'nil')}
12 | lines = lines.map {|x| x.gsub(/\{\}/, 'nil')}
13 | lines = lines.map {|x| x.gsub(/\./, 'dot')}
14 | lines = lines.map {|x| x.gsub(/\=/, 'equal')}
15 | lines = lines.map {|x| x.gsub(/\-\-/, 'minusminus')}
16 | lines = lines.map {|x| x.gsub(/ \-/, ' minus')}
17 | lines = lines.map {|x| x.gsub(/\+/, 'plus')}
18 | lines = lines.map {|x| x.gsub(/\-/, '_')}
19 | lines = lines.map {|x| x.gsub(/,/, 'comma')}
20 | lines = lines.map {|x| x.gsub(/\/\*/, 'comment_beg')}
21 | lines = lines.map {|x| x.gsub(/\*\//, 'comment_end')}
22 | lines = lines.map {|x| x.gsub(/\*/, 'star')}
23 | lines = lines.map {|x| x.gsub(/\;/, 'semicolon')}
24 | lines = lines.map {|x| x.gsub(/\(/, 'lparen')}
25 | lines = lines.map {|x| x.gsub(/\)/, 'rparen')}
26 | lines = lines.map {|x| x.gsub(/\{/, '(')}
27 | lines = lines.map {|x| x.gsub(/\}/, ')')}
28 | lines = lines.map {|x| x.gsub(/\//, '')}
29 | lines = lines.map {|x| x.gsub(/([A-Z][A-Z_]*)/, '"\\1"')}
30 | lines = lines.map {|x| x.gsub(/([a-z][a-z_]+)/, ':\\1')}
31 | lines = lines.map {|x| x.gsub(/^ \)/, ' ))')}
32 |
33 | c = lines.join("")
34 |
35 | if true
36 | c = c.gsub(/\(\s*:toploop ?/, "toploop( ")
37 | c = c.gsub(/\(\s*:tailbranch ?/, "tailbranch( ")
38 | c = c.gsub(/\(\s*:opt(x?) ?/, "opt\\1( ")
39 | c = c.gsub(/\(\s*:or ?/, "either( ")
40 | c = c.gsub(/\(\s*:line ?/, "line( ")
41 | c = c.gsub(/\(\s*:loop ?/, "loop( ")
42 | c = c.gsub(/\(\s*:stack ?/, "stack( ")
43 | c = c.gsub(/\"(\s+)/, "\",\\1")
44 | c = c.gsub(/([a-z])(\s+)/, "\\1,\\2")
45 | c = c.gsub(/\)/, "),\\1")
46 | c = c.gsub(/,(\s*)\)/, "\\1)")
47 | c = c.gsub(/ \)\),/, " ))")
48 | c = c.gsub(/, \(:nil,/, ", line(:nil,")
49 | end
50 |
51 | lines = c.split("\n")
52 | lines = lines.map {|x| x.gsub(/^ ([a-z_:]+)/, ' rule( \\1')}
53 |
54 | print "# generated by './sql-bubble.rb #{bubble}'\n"
55 | print lines[0..-2].join("\n")
56 | print "\n"
57 |
58 |
--------------------------------------------------------------------------------
/sql-bubble.txt:
--------------------------------------------------------------------------------
1 | # This file contains the data used by the three syntax diagram rendering
2 | # programs:
3 | #
4 | # bubble-generator.tcl
5 | # bubble-generator-bnf.tcl
6 | # bubble-generator-bnf.tcl
7 | #
8 |
9 | # Graphs:
10 | #
11 | set all_graphs {
12 | sql-stmt-list {
13 | toploop {optx sql-stmt} ;
14 | }
15 | sql-stmt {
16 | line
17 | {opt EXPLAIN {opt QUERY PLAN}}
18 | {or
19 | alter-table-stmt
20 | analyze-stmt
21 | attach-stmt
22 | begin-stmt
23 | commit-stmt
24 | create-index-stmt
25 | create-table-stmt
26 | create-trigger-stmt
27 | create-view-stmt
28 | create-virtual-table-stmt
29 | delete-stmt
30 | delete-stmt-limited
31 | detach-stmt
32 | drop-index-stmt
33 | drop-table-stmt
34 | drop-trigger-stmt
35 | drop-view-stmt
36 | insert-stmt
37 | pragma-stmt
38 | reindex-stmt
39 | release-stmt
40 | rollback-stmt
41 | savepoint-stmt
42 | select-stmt
43 | update-stmt
44 | update-stmt-limited
45 | vacuum-stmt
46 | }
47 | }
48 | alter-table-stmt {
49 | stack
50 | {line ALTER TABLE {optx /database-name .} /table-name}
51 | {tailbranch
52 | {line RENAME TO /new-table-name}
53 | {line ADD {optx COLUMN} column-def}
54 | }
55 | }
56 | analyze-stmt {
57 | line ANALYZE {or nil /database-name /table-or-index-name
58 | {line /database-name . /table-or-index-name}}
59 | }
60 | attach-stmt {
61 | line ATTACH {or DATABASE nil} expr AS /database-name
62 | }
63 | begin-stmt {
64 | line BEGIN {or nil DEFERRED IMMEDIATE EXCLUSIVE}
65 | {optx TRANSACTION}
66 | }
67 | commit-stmt {
68 | line {or COMMIT END} {optx TRANSACTION}
69 | }
70 | rollback-stmt {
71 | line ROLLBACK {optx TRANSACTION}
72 | {optx TO {optx SAVEPOINT} /savepoint-name}
73 | }
74 | savepoint-stmt {
75 | line SAVEPOINT /savepoint-name
76 | }
77 | release-stmt {
78 | line RELEASE {optx SAVEPOINT} /savepoint-name
79 | }
80 | create-index-stmt {
81 | stack
82 | {line CREATE {opt UNIQUE} INDEX {opt IF NOT EXISTS}}
83 | {line {optx /database-name .} /index-name
84 | ON /table-name ( {loop indexed-column ,} )}
85 | }
86 | indexed-column {
87 | line /column-name {optx COLLATE /collation-name} {or ASC DESC nil}
88 | }
89 | create-table-stmt {
90 | stack
91 | {line CREATE {or {} TEMP TEMPORARY} TABLE {opt IF NOT EXISTS}}
92 | {line {optx /database-name .} /table-name
93 | {tailbranch
94 | {line ( {loop column-def ,} {loop {} {line , table-constraint}} )}
95 | {line AS select-stmt}
96 | }
97 | }
98 | }
99 | column-def {
100 | line /column-name {or type-name nil} {loop nil {nil column-constraint nil}}
101 | }
102 | type-name {
103 | line {loop /name {}} {or {}
104 | {line ( signed-number )}
105 | {line ( signed-number , signed-number )}
106 | }
107 | }
108 | column-constraint {
109 | stack
110 | {optx CONSTRAINT /name}
111 | {or
112 | {line PRIMARY KEY {or nil ASC DESC}
113 | conflict-clause {opt AUTOINCREMENT}
114 | }
115 | {line NOT NULL conflict-clause}
116 | {line UNIQUE conflict-clause}
117 | {line CHECK ( expr )}
118 | {line DEFAULT
119 | {or
120 | signed-number
121 | literal-value
122 | {line ( expr )}
123 | }
124 | }
125 | {line COLLATE /collation-name}
126 | {line foreign-key-clause}
127 | }
128 | }
129 | signed-number {
130 | line {or nil + -} /numeric-literal
131 | }
132 | table-constraint {
133 | stack
134 | {optx CONSTRAINT /name}
135 | {or
136 | {line {or {line PRIMARY KEY} UNIQUE}
137 | ( {loop indexed-column ,} ) conflict-clause}
138 | {line CHECK ( expr )}
139 | {line FOREIGN KEY ( {loop /column-name ,} ) foreign-key-clause }
140 | }
141 | }
142 | foreign-key-clause {
143 | stack
144 | {line REFERENCES /foreign-table {optx ( {loop /column-name ,} )}}
145 | {optx
146 | {loop
147 | {or
148 | {line ON {or DELETE UPDATE}
149 | {or {line SET NULL} {line SET DEFAULT}
150 | CASCADE RESTRICT {line NO ACTION}
151 | }
152 | }
153 | {line MATCH /name}
154 | }
155 | {}
156 | }
157 | }
158 | {optx
159 | {line {optx NOT} DEFERRABLE
160 | {or
161 | {line INITIALLY DEFERRED}
162 | {line INITIALLY IMMEDIATE}
163 | {}
164 | }
165 | }
166 | nil
167 | }
168 | }
169 | conflict-clause {
170 | opt {line ON CONFLICT {or ROLLBACK ABORT FAIL IGNORE REPLACE}}
171 | }
172 | create-trigger-stmt {
173 | stack
174 | {line CREATE {or {} TEMP TEMPORARY} TRIGGER {opt IF NOT EXISTS}}
175 | {line {optx /database-name .} /trigger-name
176 | {or BEFORE AFTER {line INSTEAD OF} nil}
177 | }
178 | {line
179 | {or DELETE INSERT
180 | {line UPDATE {opt OF {loop /column-name ,} }}
181 | }
182 | ON /table-name
183 | }
184 | {line {optx FOR EACH ROW}
185 | {optx WHEN expr}
186 | }
187 | {line BEGIN
188 | {loop
189 | {line {or update-stmt insert-stmt delete-stmt select-stmt} ;}
190 | nil
191 | }
192 | END
193 | }
194 | }
195 | create-view-stmt {
196 | stack
197 | {line CREATE {or {} TEMP TEMPORARY} VIEW {opt IF NOT EXISTS}}
198 | {line {optx /database-name .} /view-name AS select-stmt}
199 | }
200 | create-virtual-table-stmt {
201 | stack
202 | {line CREATE VIRTUAL TABLE {optx /database-name .} /table-name}
203 | {line USING /module-name {optx ( {loop /module-argument ,} )}}
204 | }
205 | delete-stmt {
206 | line DELETE FROM qualified-table-name {optx WHERE expr}
207 | }
208 | delete-stmt-limited {
209 | stack
210 | {line DELETE FROM qualified-table-name {optx WHERE expr}}
211 | {optx
212 | {stack
213 | {optx ORDER BY {loop ordering-term ,}}
214 | {line LIMIT /expr {optx {or OFFSET ,} /expr}}
215 | }
216 | }
217 | }
218 | detach-stmt {
219 | line DETACH {optx DATABASE} /database-name
220 | }
221 | drop-index-stmt {
222 | line DROP INDEX {optx IF EXISTS} {optx /database-name .} /index-name
223 | }
224 | drop-table-stmt {
225 | line DROP TABLE {optx IF EXISTS} {optx /database-name .} /table-name
226 | }
227 | drop-trigger-stmt {
228 | line DROP TRIGGER {optx IF EXISTS} {optx /database-name .} /trigger-name
229 | }
230 | drop-view-stmt {
231 | line DROP VIEW {optx IF EXISTS} {optx /database-name .} /view-name
232 | }
233 | expr {
234 | or
235 | {line literal-value}
236 | {line bind-parameter}
237 | {line {optx {optx /database-name .} /table-name .} /column-name}
238 | {line /unary-operator expr}
239 | {line expr /binary-operator expr}
240 | {line /function-name ( {or {line {optx DISTINCT} {toploop expr ,}} {} *} )}
241 | {line ( expr )}
242 | {line CAST ( expr AS type-name )}
243 | {line expr COLLATE /collation-name}
244 | {line expr {optx NOT} {or LIKE GLOB REGEXP MATCH} expr
245 | {optx ESCAPE expr}}
246 | {line expr {or ISNULL NOTNULL {line NOT NULL}}}
247 | {line expr IS {optx NOT} expr}
248 | {line expr {optx NOT} BETWEEN expr AND expr}
249 | {line expr {optx NOT} IN
250 | {or
251 | {line ( {or {} select-stmt {loop expr ,}} )}
252 | {line {optx /database-name .} /table-name}
253 | }
254 | }
255 | {line {optx {optx NOT} EXISTS} ( select-stmt )}
256 | {line CASE {optx expr} {loop {line WHEN expr THEN expr} {}}
257 | {optx ELSE expr} END}
258 | {line raise-function}
259 | }
260 | raise-function {
261 | line RAISE (
262 | {or IGNORE
263 | {line {or ROLLBACK ABORT FAIL} , /error-message }
264 | } )
265 | }
266 | literal-value {
267 | or
268 | {line /numeric-literal}
269 | {line /string-literal}
270 | {line /blob-literal}
271 | {line NULL}
272 | {line CURRENT_TIME}
273 | {line CURRENT_DATE}
274 | {line CURRENT_TIMESTAMP}
275 | }
276 | numeric-literal {
277 | line {or
278 | {line {loop /digit nil} {opt /decimal-point {loop nil /digit}}}
279 | {line /decimal-point {loop /digit nil}}
280 | }
281 | {opt E {or nil + -} {loop /digit nil}}
282 | }
283 | insert-stmt {
284 | stack
285 | {line
286 | {or
287 | {line INSERT {opt OR {or ROLLBACK ABORT REPLACE FAIL IGNORE}}}
288 | REPLACE
289 | }
290 | INTO {optx /database-name .} /table-name
291 | }
292 | {tailbranch
293 | {line
294 | {optx ( {loop /column-name ,} )}
295 | {tailbranch
296 | {line VALUES ( {loop expr ,} )}
297 | select-stmt
298 | }
299 | }
300 | {line DEFAULT VALUES}
301 | }
302 | }
303 | pragma-stmt {
304 | line PRAGMA {optx /database-name .} /pragma-name
305 | {or
306 | nil
307 | {line = pragma-value}
308 | {line ( pragma-value )}
309 | }
310 | }
311 | pragma-value {
312 | or
313 | signed-number
314 | /name
315 | /string-literal
316 | }
317 | reindex-stmt {
318 | line REINDEX
319 | {tailbranch nil
320 | /collation-name
321 | {line {optx /database-name .}
322 | {tailbranch /table-name /index-name}
323 | }
324 | }
325 | }
326 | select-stmt {
327 | stack
328 | {loop {line select-core nil} {nil compound-operator nil}}
329 | {optx ORDER BY {loop ordering-term ,}}
330 | {optx LIMIT /expr {optx {or OFFSET ,} /expr}}
331 | }
332 | select-core {
333 | stack
334 | {line SELECT {or nil DISTINCT ALL} {loop result-column ,}}
335 | {optx FROM join-source}
336 | {optx WHERE expr}
337 | {optx GROUP BY {loop ordering-term ,} {optx HAVING expr}}
338 |
339 | }
340 | result-column {
341 | or
342 | *
343 | {line /table-name . *}
344 | {line expr {optx {optx AS} /column-alias}}
345 | }
346 | join-source {
347 | line
348 | single-source
349 | {opt {loop {line nil join-op single-source join-constraint nil} {}}}
350 | }
351 | single-source {
352 | or
353 | {line
354 | {optx /database-name .} /table-name
355 | {optx {optx AS} /table-alias}
356 | {or nil {line INDEXED BY /index-name} {line NOT INDEXED}}
357 | }
358 | {line
359 | ( select-stmt ) {optx {optx AS} /table-alias}
360 | }
361 | {line ( join-source )}
362 | }
363 | join-op {
364 | or
365 | {line ,}
366 | {line
367 | {opt NATURAL}
368 | {or nil {line LEFT {or OUTER nil}} INNER CROSS}
369 | JOIN
370 | }
371 | }
372 | join-constraint {
373 | or
374 | {line ON expr}
375 | {line USING ( {loop /column-name ,} )}
376 | nil
377 | }
378 | ordering-term {
379 | line expr {opt COLLATE /collation-name} {or nil ASC DESC}
380 | }
381 | compound-operator {
382 | or {line UNION {optx ALL}} INTERSECT EXCEPT
383 | }
384 | update-stmt {
385 | stack
386 | {line UPDATE {opt OR {or ROLLBACK ABORT REPLACE FAIL IGNORE}}
387 | qualified-table-name}
388 | {line SET {loop {line /column-name = expr} ,} {optx WHERE expr}}
389 | }
390 | update-stmt-limited {
391 | stack
392 | {line UPDATE {opt OR {or ROLLBACK ABORT REPLACE FAIL IGNORE}}
393 | qualified-table-name}
394 | {line SET {loop {line /column-name = expr} ,} {optx WHERE expr}}
395 | {optx
396 | {stack
397 | {optx ORDER BY {loop ordering-term ,}}
398 | {line LIMIT /expr {optx {or OFFSET ,} /expr}}
399 | }
400 | }
401 | }
402 | qualified-table-name {
403 | line {optx /database-name .} /table-name
404 | {or nil {line INDEXED BY /index-name} {line NOT INDEXED}}
405 | }
406 | vacuum-stmt {
407 | line VACUUM
408 | }
409 | comment-syntax {
410 | or
411 | {line -- {loop nil /anything-except-newline}
412 | {or /newline /end-of-input}}
413 | {line /* {loop nil /anything-except-*/}
414 | {or */ /end-of-input}}
415 | }
416 | }
417 |
418 |
--------------------------------------------------------------------------------
/sql.pegjs:
--------------------------------------------------------------------------------
1 | // Originally generated from...
2 | // 1) sql-bubble.txt (from sqlite.org)
3 | // 2) ./sql-bubble.rb sql-bubble.txt > tmp/rules.rb
4 | // 3) ./bubble-to-pegjs.rb tmp/rules.rb bubble-to-pegjs_ex.rb > tmp/sql.pegjs
5 | //
6 | // Then, manually edited for pegjs suitability.
7 | //
8 | // Rules with indentation or with comments have manual edits.
9 | //
10 | start = sql_stmt_list
11 |
12 | sql_stmt_list =
13 | r: ( whitespace ( sql_stmt )? whitespace semicolon )+
14 | { return filter(flatten(r, true), ';') }
15 |
16 | sql_stmt =
17 | ( explain: ( EXPLAIN ( QUERY PLAN )? )?
18 | stmt: select_stmt )
19 | { return put_if_not_null(stmt, "explain", nonempty(flatstr(explain))) }
20 |
21 | // For now, just concentrate of SELECT statements only, although
22 | // we have all the machinery for all other statements, too.
23 | //
24 | // sql_stmt =
25 | // ( explain: ( EXPLAIN ( QUERY PLAN )? )?
26 | // stmt: (
27 | // alter_table_stmt
28 | // / analyze_stmt
29 | // / attach_stmt
30 | // / begin_stmt / commit_stmt
31 | // / create_index_stmt
32 | // / create_table_stmt
33 | // / create_trigger_stmt
34 | // / create_view_stmt
35 | // / create_virtual_table_stmt
36 | // / delete_stmt / delete_stmt_limited
37 | // / detach_stmt
38 | // / drop_index_stmt / drop_table_stmt / drop_trigger_stmt / drop_view_stmt
39 | // / insert_stmt
40 | // / pragma_stmt / reindex_stmt / release_stmt / rollback_stmt / savepoint_stmt
41 | // / select_stmt
42 | // / update_stmt / update_stmt_limited
43 | // / vacuum_stmt
44 | // ) )
45 | // { return { explain: flatstr(explain),
46 | // stmt: stmt } }
47 |
48 | alter_table_stmt =
49 | ( ( ALTER TABLE table_ref )
50 | ( RENAME TO new_table_name )
51 | ( ADD ( COLUMN )? column_def ) )
52 |
53 | analyze_stmt =
54 | ( ANALYZE ( database_name
55 | / table_or_index_name
56 | / ( database_name dot table_or_index_name ) )? )
57 |
58 | attach_stmt =
59 | ( ATTACH ( DATABASE )? expr AS database_name )
60 |
61 | begin_stmt =
62 | ( BEGIN ( DEFERRED / IMMEDIATE / EXCLUSIVE )? ( TRANSACTION )? )
63 |
64 | commit_stmt =
65 | ( ( COMMIT / END ) ( TRANSACTION )? )
66 |
67 | rollback_stmt =
68 | ( ROLLBACK ( TRANSACTION )? ( TO ( SAVEPOINT )? savepoint_name )? )
69 |
70 | savepoint_stmt =
71 | ( SAVEPOINT savepoint_name )
72 |
73 | release_stmt =
74 | ( RELEASE ( SAVEPOINT )? savepoint_name )
75 |
76 | create_index_stmt =
77 | ( ( CREATE ( UNIQUE )? INDEX ( IF NOT EXISTS )? ) ( ( database_name dot )? index_name ON table_name lparen ( indexed_column comma )+ rparen ) )
78 |
79 | indexed_column =
80 | ( column_name ( COLLATE collation_name )? ( ASC / DESC )? )
81 |
82 | create_table_stmt =
83 | ( CREATE ( TEMP / TEMPORARY )? TABLE ( IF NOT EXISTS )? )
84 | ( table_ref
85 | ( lparen ( column_def comma )+ ( comma table_constraint )+ rparen )
86 | ( AS select_stmt ) )
87 |
88 | column_def =
89 | ( column_name ( type_name )? ( column_constraint )+ )
90 |
91 | type_name =
92 | ( name )+
93 | ( ( lparen signed_number rparen )
94 | / ( lparen signed_number comma signed_number rparen ) )?
95 |
96 | column_constraint =
97 | ( ( CONSTRAINT name )?
98 | ( ( PRIMARY KEY ( ASC / DESC )? conflict_clause ( AUTOINCREMENT )? )
99 | / ( NOT NULL conflict_clause )
100 | / ( UNIQUE conflict_clause )
101 | / ( CHECK lparen expr rparen )
102 | / ( DEFAULT ( signed_number / literal_value / ( lparen expr rparen ) ) )
103 | / ( COLLATE collation_name )
104 | / foreign_key_clause ) )
105 |
106 | signed_number =
107 | ( ( plus / minus )? numeric_literal )
108 |
109 | table_constraint =
110 | ( ( CONSTRAINT name )? ( ( ( ( PRIMARY KEY ) / UNIQUE ) lparen ( indexed_column comma )+ rparen conflict_clause ) / ( CHECK lparen expr rparen ) / ( FOREIGN KEY lparen ( column_name comma )+ rparen foreign_key_clause ) ) )
111 |
112 | foreign_key_clause =
113 | ( ( REFERENCES foreign_table ( lparen ( column_name comma )+ rparen )? )
114 | ( ( ( ON ( DELETE / UPDATE )
115 | ( ( SET NULL )
116 | / ( SET DEFAULT )
117 | / CASCADE
118 | / RESTRICT
119 | / ( NO ACTION ) ) )
120 | / ( MATCH name ) )+ )?
121 | ( ( NOT )? DEFERRABLE ( ( INITIALLY DEFERRED ) / ( INITIALLY IMMEDIATE ) )? )? )
122 |
123 | conflict_clause =
124 | ( ( ON CONFLICT ( ROLLBACK / ABORT / FAIL / IGNORE / REPLACE ) ) )?
125 |
126 | create_trigger_stmt =
127 | ( ( CREATE ( TEMP / TEMPORARY )? TRIGGER ( IF NOT EXISTS )? )
128 | ( ( database_name dot )? trigger_name ( BEFORE / AFTER / ( INSTEAD OF ) )? )
129 | ( ( DELETE
130 | / INSERT
131 | / ( UPDATE ( OF ( column_name comma )+ )? ) ) ON table_name )
132 | ( ( FOR EACH ROW )? ( WHEN expr )? )
133 | ( BEGIN ( ( update_stmt
134 | / insert_stmt
135 | / delete_stmt
136 | / select_stmt ) semicolon )+ END ) )
137 |
138 | create_view_stmt =
139 | ( ( CREATE ( TEMP / TEMPORARY )? VIEW ( IF NOT EXISTS )? )
140 | ( ( database_name dot )? view_name AS select_stmt ) )
141 |
142 | create_virtual_table_stmt =
143 | ( ( CREATE VIRTUAL TABLE table_ref )
144 | ( USING module_name ( lparen ( module_argument comma )+ rparen )? ) )
145 |
146 | delete_stmt =
147 | ( DELETE FROM qualified_table_name ( WHERE expr )? )
148 |
149 | delete_stmt_limited =
150 | ( DELETE FROM qualified_table_name ( WHERE expr )?
151 | ( ( ( ORDER BY ( ordering_term comma )+ )?
152 | ( LIMIT expr ( ( OFFSET / comma ) expr )? ) ) )? )
153 |
154 | detach_stmt =
155 | ( DETACH ( DATABASE )? database_name )
156 |
157 | drop_index_stmt =
158 | ( DROP INDEX ( IF EXISTS )? ( database_name dot )? index_name )
159 |
160 | drop_table_stmt =
161 | ( DROP TABLE ( IF EXISTS )? table_ref )
162 |
163 | drop_trigger_stmt =
164 | ( DROP TRIGGER ( IF EXISTS )? ( database_name dot )? trigger_name )
165 |
166 | drop_view_stmt =
167 | ( DROP VIEW ( IF EXISTS )? ( database_name dot )? view_name )
168 |
169 | value =
170 | v: ( whitespace
171 | ( ( x: literal_value
172 | { return { literal: x } } )
173 | / ( b: bind_parameter
174 | { return { bind: b } } )
175 | / ( t: ( table_name dot column_name )
176 | { return { column: t[2], table: t[1] } } )
177 | / ( c: column_name
178 | { return { column: c } } )
179 | / ( unary_operator expr )
180 | / call_function
181 | / ( whitespace lparen expr whitespace rparen )
182 | / ( CAST lparen expr AS type_name rparen )
183 | / ( ( NOT ? EXISTS )? lparen select_stmt rparen )
184 | / ( CASE expr ? ( WHEN expr THEN expr )+ ( ELSE expr )? END )
185 | / raise_function ) )
186 | { return v[1] }
187 |
188 | expr =
189 | e: ( whitespace
190 | ( ( value binary_operator expr )
191 | / ( value COLLATE collation_name )
192 | / ( value NOT ? ( LIKE / GLOB / REGEXP / MATCH ) expr ( ESCAPE expr )? )
193 | / ( value ( ISNULL / NOTNULL / ( NOT NULL ) ) )
194 | / ( value IS NOT ? expr )
195 | / ( value NOT ? BETWEEN expr AND expr )
196 | / ( value NOT ? IN ( ( lparen ( select_stmt / ( expr comma )+ )? rparen )
197 | / table_ref ) )
198 | / value ) )
199 | { return e[1]; }
200 |
201 |
202 | call_function =
203 | ( function_name
204 | whitespace lparen
205 | ( ( DISTINCT ? ( expr (whitespace comma expr)* )+ )
206 | / whitespace star )?
207 | whitespace rparen )
208 |
209 | raise_function =
210 | ( RAISE lparen ( IGNORE / ( ( ROLLBACK / ABORT / FAIL ) comma error_message ) ) rparen )
211 |
212 | literal_value =
213 | ( numeric_literal / string_literal / blob_literal
214 | / NULL / CURRENT_TIME / CURRENT_DATE / CURRENT_TIMESTAMP )
215 |
216 | numeric_literal =
217 | digits:( ( ( ( digit )+ ( decimal_point ( digit )+ )? )
218 | / ( decimal_point ( digit )+ ) )
219 | ( E ( plus / minus )? ( digit )+ )? )
220 | { var x = flatstr(digits);
221 | if (x.indexOf('.') >= 0) {
222 | return parseFloat(x);
223 | }
224 | return parseInt(x);
225 | }
226 |
227 | insert_stmt =
228 | ( ( ( INSERT ( OR ( ROLLBACK / ABORT / REPLACE / FAIL / IGNORE ) )? )
229 | / REPLACE )
230 | INTO
231 | table_ref
232 | ( ( ( lparen ( column_name ( comma column_name )* ) rparen )?
233 | ( ( VALUES lparen ( expr comma )+ rparen )
234 | / select_stmt ) )
235 | / ( DEFAULT VALUES ) ) )
236 |
237 | pragma_stmt =
238 | ( PRAGMA ( database_name dot )? pragma_name
239 | ( ( equal pragma_value ) / ( lparen pragma_value rparen ) )? )
240 |
241 | pragma_value =
242 | ( signed_number / name / string_literal )
243 |
244 | reindex_stmt =
245 | ( REINDEX collation_name ( table_ref index_name ) )
246 |
247 | select_stmt =
248 | ( select_cores: ( select_core
249 | ( sc: ( compound_operator select_core )*
250 | { var acc = [];
251 | for (var i = 0; i < sc.length; i++) {
252 | acc[i] = merge(sc[i][0], sc[i][1]);
253 | }
254 | return acc;
255 | } ) )
256 | order_by: ( ( ORDER BY ordering_term ( whitespace comma ordering_term )* )? )
257 | limit: ( ( LIMIT expr ( ( OFFSET / comma ) expr )? )? ) )
258 | { var res = { stmt: "select",
259 | select_cores: flatten(select_cores, true) };
260 | res = put_if_not_null(res, "order_by", nonempty(order_by));
261 | res = put_if_not_null(res, "limit", nonempty(limit));
262 | return res;
263 | }
264 |
265 | select_core =
266 | ( SELECT d: ( ( DISTINCT / ALL )? )
267 | c: ( select_result
268 | ( cx: ( whitespace comma select_result )*
269 | { var acc = [];
270 | for (var i = 0; i < cx.length; i++) {
271 | acc[i] = cx[i][2];
272 | }
273 | return acc;
274 | } ) )
275 | f: ( j: ( ( FROM join_source )? )
276 | { return j ? j[1] : [] } )
277 | w: ( e: ( ( WHERE expr )? )
278 | { return e ? e[1] : [] } )
279 | g: ( GROUP BY ( ordering_term comma )+ ( HAVING expr )? )? )
280 | { c[1].unshift(c[0]);
281 | var res = { results: c[1] };
282 | res = put_if_not_null(res, "distinct", nonempty(flatstr(d)));
283 | res = put_if_not_null(res, "from", nonempty(f));
284 | res = put_if_not_null(res, "where", nonempty(w));
285 | res = put_if_not_null(res, "group_by", nonempty(g));
286 | return res;
287 | }
288 |
289 | select_result =
290 | r: ( whitespace
291 | ( ( c: ( column_ref ( a: ( AS whitespace column_alias )
292 | { return { alias: a[2] } } )? )
293 | { return merge(c[1], c[0]) } )
294 | / ( c: ( table_name dot star )
295 | { return { table: c[0],
296 | column: '*' } } )
297 | / ( star
298 | { return { column: '*' } } ) ) )
299 | { return r[1] }
300 |
301 | join_source =
302 | s: ( whitespace single_source
303 | ( join_op whitespace single_source join_constraint )* )
304 | { var acc = [s[1]];
305 | var rest = s[2];
306 | for (var i = 0; rest != null && i < rest.length; i++) {
307 | acc[acc.length] = merge(merge(rest[i][0], rest[i][2]), rest[i][3]);
308 | }
309 | return acc;
310 | }
311 |
312 | single_source =
313 | ( ( x: ( database_name dot table_name AS whitespace1 table_alias )
314 | { return { database: x[0], table: x[2], alias: x[5] } } )
315 | / ( x: ( database_name dot table_name )
316 | { return { database: x[0], table: x[2] } } )
317 | / ( x: ( table_name AS whitespace1 table_alias )
318 | { return { table: x[0], alias: x[3] } } )
319 | / ( x: table_name
320 | { return { table: x } } )
321 | / ( s: ( ( t: ( table_ref ( a: ( AS whitespace1 table_alias )
322 | { return { alias: a[2] } } )? )
323 | { return merge(t[1], t[0]) } )
324 | ( ( idx: ( INDEXED BY whitespace index_name )
325 | { return { indexed_by: idx[3] } } )
326 | / ( ( NOT INDEXED )
327 | { return { indexed_by: null } } ) )? )
328 | { return merge(s[1], s[0]) } )
329 | / ( p: ( lparen select_stmt rparen
330 | ( a: ( AS whitespace table_alias )
331 | { return { alias: a[2] } } )? )
332 | { return merge(p[3], p[1]) } )
333 | / ( j: ( lparen join_source rparen )
334 | { return j[1] } )
335 | )
336 |
337 | join_op =
338 | r: ( ( ( ( whitespace comma )
339 | { return "JOIN" } )
340 | / ( j: ( NATURAL ?
341 | ( ( LEFT ( OUTER )? )
342 | / INNER
343 | / CROSS )?
344 | JOIN )
345 | { return flatstr(j) } ) ) )
346 | { return { join_op: r } }
347 |
348 | join_constraint =
349 | r: ( ( ( ON expr )
350 | / ( USING
351 | whitespace lparen
352 | ( whitespace column_name ( whitespace comma whitespace column_name )* )
353 | whitespace rparen ) )? )
354 | { return { join_constraint: nonempty(r) } }
355 |
356 | ordering_term =
357 | ( whitespace
358 | ( expr ( COLLATE collation_name )? ( ASC / DESC )? ) )
359 |
360 | compound_operator =
361 | o: ( ( UNION ALL ? )
362 | / INTERSECT
363 | / EXCEPT )
364 | { return { compound_operator: flatstr(o) } }
365 |
366 | update_stmt =
367 | ( ( UPDATE ( OR ( ROLLBACK
368 | / ABORT
369 | / REPLACE
370 | / FAIL
371 | / IGNORE ) )? qualified_table_name )
372 | ( SET ( ( column_name equal expr ) comma )+ ( WHERE expr )? ) )
373 |
374 | update_stmt_limited =
375 | ( ( UPDATE ( OR ( ROLLBACK
376 | / ABORT
377 | / REPLACE
378 | / FAIL
379 | / IGNORE ) )? qualified_table_name )
380 | ( SET ( ( column_name equal expr ) comma )+ ( WHERE expr )? )
381 | ( ( ( ORDER BY ( ordering_term comma )+ )?
382 | ( LIMIT expr ( ( OFFSET / comma ) expr )? ) ) )? )
383 |
384 | qualified_table_name =
385 | ( table_ref ( ( INDEXED BY index_name ) / ( NOT INDEXED ) )? )
386 |
387 | table_ref =
388 | r: ( ( d: ( database_name dot )
389 | { return { database: d[0] } } )?
390 | ( x: table_name
391 | { return { table: x } } ) )
392 | { return merge(r[1], r[0]) }
393 |
394 | column_ref =
395 | r: ( ( t: ( table_name dot )
396 | { return { table: t[0] } } )?
397 | ( x: column_name
398 | { return { column: x } } ) )
399 | { return merge(r[1], r[0]) }
400 |
401 | vacuum_stmt =
402 | VACUUM
403 |
404 | comment_syntax =
405 | ( ( minusminus ( anything_except_newline )+ ( newline / end_of_input ) )
406 | / ( comment_beg ( anything_except_comment_end )+ ( comment_end / end_of_input ) ) )
407 |
408 | dot = '.'
409 | comma = ','
410 | semicolon = ';'
411 | minusminus = '--'
412 | minus = '-'
413 | plus = '+'
414 | lparen = '('
415 | rparen = ')'
416 | star = '*'
417 | newline = '\n'
418 | anything_except_newline = [^\n]*
419 | comment_beg = '/*'
420 | comment_end = '*/'
421 | anything_except_comment_end = .* & '*/'
422 | string_literal = '"' (escape_char / [^"])* '"'
423 | escape_char = '\\' .
424 | nil = ''
425 |
426 | whitespace =
427 | [ \t\n\r]*
428 | whitespace1 =
429 | [ \t\n\r]+
430 |
431 | unary_operator =
432 | x: ( whitespace
433 | ( '-' / '+' / '~' / 'NOT') )
434 | { return x[1] }
435 |
436 | binary_operator =
437 | x: ( whitespace
438 | ('||'
439 | / '*' / '/' / '%'
440 | / '+' / '-'
441 | / '<<' / '>>' / '&' / '|'
442 | / '<=' / '>='
443 | / '<' / '>'
444 | / '=' / '==' / '!=' / '<>'
445 | / 'IS' / 'IS NOT' / 'IN' / 'LIKE' / 'GLOB' / 'MATCH' / 'REGEXP'
446 | / 'AND'
447 | / 'OR') )
448 | { return x[1] }
449 |
450 | digit = [0-9]
451 | decimal_point = dot
452 | equal = '='
453 |
454 | name =
455 | str:[A-Za-z0-9_]+
456 | { return str.join('') }
457 |
458 | database_name = name
459 | table_name = name
460 | table_alias = name
461 | table_or_index_name = name
462 | new_table_name = name
463 | index_name = name
464 | column_name = name
465 | column_alias = name
466 | foreign_table = name
467 | savepoint_name = name
468 | collation_name = name
469 | trigger_name = name
470 | view_name = name
471 | module_name = name
472 | module_argument = name
473 | bind_parameter =
474 | '?' name
475 | function_name = name
476 | pragma_name = name
477 |
478 | error_message = string_literal
479 |
480 | CURRENT_TIME = 'now'
481 | CURRENT_DATE = 'now'
482 | CURRENT_TIMESTAMP = 'now'
483 |
484 | blob_literal = string_literal
485 |
486 | end_of_input = ''
487 |
488 | ABORT = whitespace1 "ABORT"
489 | ACTION = whitespace1 "ACTION"
490 | ADD = whitespace1 "ADD"
491 | AFTER = whitespace1 "AFTER"
492 | ALL = whitespace1 "ALL"
493 | ALTER = whitespace1 "ALTER"
494 | ANALYZE = whitespace1 "ANALYZE"
495 | AND = whitespace1 "AND"
496 | AS = whitespace1 "AS"
497 | ASC = whitespace1 "ASC"
498 | ATTACH = whitespace1 "ATTACH"
499 | AUTOINCREMENT = whitespace1 "AUTOINCREMENT"
500 | BEFORE = whitespace1 "BEFORE"
501 | BEGIN = whitespace1 "BEGIN"
502 | BETWEEN = whitespace1 "BETWEEN"
503 | BY = whitespace1 "BY"
504 | CASCADE = whitespace1 "CASCADE"
505 | CASE = whitespace1 "CASE"
506 | CAST = whitespace1 "CAST"
507 | CHECK = whitespace1 "CHECK"
508 | COLLATE = whitespace1 "COLLATE"
509 | COLUMN = whitespace1 "COLUMN"
510 | COMMIT = whitespace1 "COMMIT"
511 | CONFLICT = whitespace1 "CONFLICT"
512 | CONSTRAINT = whitespace1 "CONSTRAINT"
513 | CREATE =
514 | whitespace "CREATE"
515 | CROSS = whitespace1 "CROSS"
516 | DATABASE = whitespace1 "DATABASE"
517 | DEFAULT = whitespace1 "DEFAULT"
518 | DEFERRABLE = whitespace1 "DEFERRABLE"
519 | DEFERRED = whitespace1 "DEFERRED"
520 | DELETE =
521 | whitespace "DELETE"
522 | DESC = whitespace1 "DESC"
523 | DETACH = whitespace1 "DETACH"
524 | DISTINCT = whitespace1 "DISTINCT"
525 | DROP = whitespace1 "DROP"
526 | E =
527 | "E"
528 | EACH = whitespace1 "EACH"
529 | ELSE = whitespace1 "ELSE"
530 | END = whitespace1 "END"
531 | ESCAPE = whitespace1 "ESCAPE"
532 | EXCEPT = whitespace1 "EXCEPT"
533 | EXCLUSIVE = whitespace1 "EXCLUSIVE"
534 | EXISTS = whitespace1 "EXISTS"
535 | EXPLAIN =
536 | whitespace "EXPLAIN"
537 | FAIL = whitespace1 "FAIL"
538 | FOR = whitespace1 "FOR"
539 | FOREIGN = whitespace1 "FOREIGN"
540 | FROM = whitespace1 "FROM"
541 | GLOB = whitespace1 "GLOB"
542 | GROUP = whitespace1 "GROUP"
543 | HAVING = whitespace1 "HAVING"
544 | IF = whitespace1 "IF"
545 | IGNORE = whitespace1 "IGNORE"
546 | IMMEDIATE = whitespace1 "IMMEDIATE"
547 | IN = whitespace1 "IN"
548 | INDEX = whitespace1 "INDEX"
549 | INDEXED = whitespace1 "INDEXED"
550 | INITIALLY = whitespace1 "INITIALLY"
551 | INNER = whitespace1 "INNER"
552 | INSERT =
553 | whitespace "INSERT"
554 | INSTEAD = whitespace1 "INSTEAD"
555 | INTERSECT = whitespace1 "INTERSECT"
556 | INTO = whitespace1 "INTO"
557 | IS = whitespace1 "IS"
558 | ISNULL = whitespace1 "ISNULL"
559 | JOIN = whitespace1 "JOIN"
560 | KEY = whitespace1 "KEY"
561 | LEFT = whitespace1 "LEFT"
562 | LIKE = whitespace1 "LIKE"
563 | LIMIT = whitespace1 "LIMIT"
564 | MATCH = whitespace1 "MATCH"
565 | NATURAL = whitespace1 "NATURAL"
566 | NO = whitespace1 "NO"
567 | NOT = whitespace1 "NOT"
568 | NOTNULL = whitespace1 "NOTNULL"
569 | NULL = whitespace1 "NULL"
570 | OF = whitespace1 "OF"
571 | OFFSET = whitespace1 "OFFSET"
572 | ON = whitespace1 "ON"
573 | OR = whitespace1 "OR"
574 | ORDER = whitespace1 "ORDER"
575 | OUTER = whitespace1 "OUTER"
576 | PLAN = whitespace1 "PLAN"
577 | PRAGMA = whitespace1 "PRAGMA"
578 | PRIMARY = whitespace1 "PRIMARY"
579 | QUERY = whitespace1 "QUERY"
580 | RAISE = whitespace1 "RAISE"
581 | REFERENCES = whitespace1 "REFERENCES"
582 | REGEXP = whitespace1 "REGEXP"
583 | REINDEX = whitespace1 "REINDEX"
584 | RELEASE = whitespace1 "RELEASE"
585 | RENAME = whitespace1 "RENAME"
586 | REPLACE =
587 | whitespace "REPLACE"
588 | RESTRICT = whitespace1 "RESTRICT"
589 | ROLLBACK = whitespace1 "ROLLBACK"
590 | ROW = whitespace1 "ROW"
591 | SAVEPOINT = whitespace1 "SAVEPOINT"
592 | SELECT =
593 | whitespace "SELECT"
594 | SET = whitespace1 "SET"
595 | TABLE = whitespace1 "TABLE"
596 | TEMP = whitespace1 "TEMP"
597 | TEMPORARY = whitespace1 "TEMPORARY"
598 | THEN = whitespace1 "THEN"
599 | TO = whitespace1 "TO"
600 | TRANSACTION = whitespace1 "TRANSACTION"
601 | TRIGGER = whitespace1 "TRIGGER"
602 | UNION = whitespace1 "UNION"
603 | UNIQUE = whitespace1 "UNIQUE"
604 | UPDATE =
605 | whitespace "UPDATE"
606 | USING = whitespace1 "USING"
607 | VACUUM = whitespace1 "VACUUM"
608 | VALUES = whitespace1 "VALUES"
609 | VIEW = whitespace1 "VIEW"
610 | VIRTUAL = whitespace1 "VIRTUAL"
611 | WHEN = whitespace1 "WHEN"
612 | WHERE = whitespace1 "WHERE"
613 |
--------------------------------------------------------------------------------
/views/index.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
16 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
test markup
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------