piggly 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/README.markdown +84 -0
  2. data/Rakefile +19 -0
  3. data/bin/piggly +245 -0
  4. data/lib/piggly/compiler/cache.rb +151 -0
  5. data/lib/piggly/compiler/pretty.rb +67 -0
  6. data/lib/piggly/compiler/queue.rb +46 -0
  7. data/lib/piggly/compiler/tags.rb +244 -0
  8. data/lib/piggly/compiler/trace.rb +91 -0
  9. data/lib/piggly/compiler.rb +5 -0
  10. data/lib/piggly/config.rb +43 -0
  11. data/lib/piggly/filecache.rb +40 -0
  12. data/lib/piggly/installer.rb +95 -0
  13. data/lib/piggly/parser/grammar.tt +747 -0
  14. data/lib/piggly/parser/nodes.rb +319 -0
  15. data/lib/piggly/parser/parser.rb +11783 -0
  16. data/lib/piggly/parser/traversal.rb +48 -0
  17. data/lib/piggly/parser/treetop_ruby19_patch.rb +17 -0
  18. data/lib/piggly/parser.rb +67 -0
  19. data/lib/piggly/profile.rb +87 -0
  20. data/lib/piggly/reporter/html.rb +207 -0
  21. data/lib/piggly/reporter/piggly.css +187 -0
  22. data/lib/piggly/reporter/sortable.js +493 -0
  23. data/lib/piggly/reporter.rb +21 -0
  24. data/lib/piggly/task.rb +64 -0
  25. data/lib/piggly/util.rb +28 -0
  26. data/lib/piggly/version.rb +15 -0
  27. data/lib/piggly.rb +18 -0
  28. data/spec/compiler/cache_spec.rb +9 -0
  29. data/spec/compiler/pretty_spec.rb +9 -0
  30. data/spec/compiler/queue_spec.rb +3 -0
  31. data/spec/compiler/rewrite_spec.rb +3 -0
  32. data/spec/compiler/tags_spec.rb +285 -0
  33. data/spec/compiler/trace_spec.rb +173 -0
  34. data/spec/config_spec.rb +58 -0
  35. data/spec/filecache_spec.rb +70 -0
  36. data/spec/fixtures/snippets.sql +158 -0
  37. data/spec/grammar/expression_spec.rb +302 -0
  38. data/spec/grammar/statements/assignment_spec.rb +70 -0
  39. data/spec/grammar/statements/exception_spec.rb +52 -0
  40. data/spec/grammar/statements/if_spec.rb +178 -0
  41. data/spec/grammar/statements/loop_spec.rb +41 -0
  42. data/spec/grammar/statements/sql_spec.rb +71 -0
  43. data/spec/grammar/tokens/comment_spec.rb +58 -0
  44. data/spec/grammar/tokens/datatype_spec.rb +52 -0
  45. data/spec/grammar/tokens/identifier_spec.rb +58 -0
  46. data/spec/grammar/tokens/keyword_spec.rb +44 -0
  47. data/spec/grammar/tokens/label_spec.rb +40 -0
  48. data/spec/grammar/tokens/literal_spec.rb +30 -0
  49. data/spec/grammar/tokens/lval_spec.rb +50 -0
  50. data/spec/grammar/tokens/number_spec.rb +34 -0
  51. data/spec/grammar/tokens/sqlkeywords_spec.rb +45 -0
  52. data/spec/grammar/tokens/string_spec.rb +54 -0
  53. data/spec/grammar/tokens/whitespace_spec.rb +40 -0
  54. data/spec/parser_spec.rb +8 -0
  55. data/spec/profile_spec.rb +5 -0
  56. data/spec/reporter/html_spec.rb +0 -0
  57. data/spec/spec_helper.rb +61 -0
  58. data/spec/spec_suite.rb +5 -0
  59. metadata +121 -0
@@ -0,0 +1,178 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ module Piggly
4
+
5
+ describe Parser, "control structures" do
6
+ include GrammarHelper
7
+
8
+ describe "if statements" do
9
+ describe "if .. then .. end if" do
10
+ it "must end with a semicolon" do
11
+ lambda{ parse(:statement, 'IF cond THEN a := 10; END IF') }.should raise_error
12
+ lambda{ parse_some(:stmtIf, 'IF cond THEN a := 10; END IF') }.should raise_error
13
+ end
14
+
15
+ it "parses successfully" do
16
+ node, rest = parse_some(:statement, 'IF cond THEN a := 10; END IF;')
17
+ node.should be_a(Statement)
18
+ rest.should == ''
19
+ end
20
+
21
+ it "does not have an Else node" do
22
+ node = parse(:statement, 'IF cond THEN a := 10; END IF;')
23
+ node.count{|e| e.is_a?(Else) }.should == 0
24
+ node.count{|e| e.named?(:else) and not e.empty? }.should == 0
25
+ end
26
+
27
+ it "has a 'cond' Expression" do
28
+ node = parse(:statement, 'IF cond THEN a := 10; END IF;')
29
+ node.count{|e| e.named?(:cond) }.should == 1
30
+ node.find{|e| e.named?(:cond) }.should be_a(Expression)
31
+ end
32
+
33
+ it "can have missing body" do
34
+ node = parse(:statement, 'IF cond THEN END IF;')
35
+ node.count{|e| e.instance_of?(Statement) }.should == 1
36
+ end
37
+
38
+ it "can have comment body" do
39
+ node = parse(:statement, 'IF cond THEN /* removed */ END IF;')
40
+ node.count{|e| e.instance_of?(Statement) }.should == 1
41
+ node.count{|e| e.instance_of?(TComment) }.should == 1
42
+ node.find{|e| e.instance_of?(TComment) }.source_text.should == '/* removed */'
43
+ end
44
+
45
+ it "can have single statement body" do
46
+ node = parse(:statement, 'IF cond THEN a := 10; END IF;')
47
+ node.count{|e| e.instance_of?(Statement) }.should == 2
48
+ end
49
+
50
+ it "can have multiple statement body" do
51
+ node = parse(:statement, 'IF cond THEN a := 10; b := 10; END IF;')
52
+ node.count{|e| e.instance_of?(Statement) }.should == 3
53
+ end
54
+
55
+ it "can contain comments" do
56
+ node = parse(:statement, "IF cond /* comment */ THEN -- foo\n NULL; /* foo */ END IF;")
57
+ node.should be_a(Statement)
58
+ node.count{|e| e.is_a?(TComment) }.should == 3
59
+ end
60
+ end
61
+
62
+ describe "if .. then .. else .. end if" do
63
+ it "parses successfully" do
64
+ node, rest = parse_some(:statement, 'IF cond THEN a := 10; ELSE a := 20; END IF;')
65
+ node.should be_a(Statement)
66
+ rest.should == ''
67
+ end
68
+
69
+ it "has an Else node named 'else'" do
70
+ node = parse(:statement, 'IF cond THEN a := 10; ELSE a := 20; END IF;')
71
+ node.count{|e| e.named?(:else) and e.is_a?(Else) }.should == 1
72
+ node.find{|e| e.named?(:else) }.source_text.should == 'ELSE a := 20; '
73
+ end
74
+
75
+ it "can have missing else body" do
76
+ node = parse(:statement, 'IF cond THEN a := 10; ELSE END IF;')
77
+ node.count{|e| e.instance_of?(Statement) }.should == 2
78
+ end
79
+
80
+ it "can have comment body" do
81
+ node = parse(:statement, 'IF cond THEN a := 10; ELSE /* removed */ END IF;')
82
+ node.count{|e| e.instance_of?(Statement) }.should == 2
83
+ node.count{|e| e.is_a?(TComment) }.should == 1
84
+ end
85
+
86
+ it "can have single statement body" do
87
+ node = parse(:statement, 'IF cond THEN a := 10; ELSE a := 20; END IF;')
88
+ node.count{|e| e.instance_of?(Statement) }.should == 3
89
+ end
90
+
91
+ it "can have multiple statement body" do
92
+ node = parse(:statement, 'IF cond THEN a := 10; ELSE a := 20; b := 30; END IF;')
93
+ node.count{|e| e.instance_of?(Statement) }.should == 4
94
+ end
95
+ end
96
+
97
+ describe "if .. then .. elsif .. then .. end if" do
98
+ it "parses successfully" do
99
+ node, rest = parse_some(:statement, 'IF cond THEN a := 10; ELSIF cond THEN a := 20; END IF;')
100
+ node.should be_a(Statement)
101
+ rest.should == ''
102
+ end
103
+
104
+ it "can have comment body" do
105
+ node = parse(:statement, 'IF cond THEN a := 10; ELSIF cond THEN /* removed */ END IF;')
106
+ node.count{|e| e.is_a?(TComment) }.should == 1
107
+ node.count{|e| e.instance_of?(Statement) }.should == 2
108
+ end
109
+
110
+ it "can having missing body" do
111
+ node = parse(:statement, 'IF cond THEN a := 10; ELSIF cond THEN END IF;')
112
+ node.count{|e| e.instance_of?(Statement) }.should == 2
113
+ end
114
+
115
+ it "can have single statement body" do
116
+ node = parse(:statement, 'IF cond THEN a := 10; ELSIF cond THEN a := 20; END IF;')
117
+ node.count{|e| e.instance_of?(Statement) }.should == 3
118
+ end
119
+
120
+ it "can have multiple statement body" do
121
+ node = parse(:statement, 'IF cond THEN a := 10; ELSIF cond THEN a := 20; b := 30; END IF;')
122
+ node.count{|e| e.instance_of?(Statement) }.should == 4
123
+ end
124
+
125
+ it "can have many elsif branches" do
126
+ node = parse(:statement, <<-SQL.strip)
127
+ IF cond THEN a := 10;
128
+ ELSIF cond THEN a := 20;
129
+ ELSIF cond THEN a := 30;
130
+ ELSIF cond THEN a := 40;
131
+ ELSIF cond THEN a := 50;
132
+ ELSIF cond THEN a := 60;
133
+ END IF;
134
+ SQL
135
+
136
+ node.count{|e| e.named?(:cond) }.should == 6
137
+ node.count{|e| e.is_a?(If) }.should == 6
138
+ node.count{|e| e.is_a?(If) and e.named?(:else) }.should == 5
139
+ end
140
+
141
+ it "has no Else nodes" do
142
+ node = parse(:statement, 'IF cond THEN a := 10; ELSIF cond THEN a := 20; END IF;')
143
+ node.count{|e| e.is_a?(Else) }.should == 0
144
+ end
145
+ end
146
+
147
+ describe "if .. then .. elsif .. then .. else .. endif" do
148
+ before do
149
+ @text = 'IF cond THEN a := 10; ELSIF cond THEN a := 20; ELSE a := 30; END IF;'
150
+ end
151
+
152
+ it "parses successfully" do
153
+ node, rest = parse_some(:statement, @text)
154
+ node.should be_a(Statement)
155
+ rest.should == ''
156
+ end
157
+
158
+ it "has an If node named 'else'" do
159
+ node = parse(:statement, @text)
160
+ node.count{|e| e.named?(:else) and e.is_a?(If) }.should == 1
161
+ node.find{|e| e.named?(:else) and e.is_a?(If) }.source_text.should == 'ELSIF cond THEN a := 20; ELSE a := 30; '
162
+ end
163
+
164
+ it "has an Else node named 'else'" do
165
+ node = parse(:statement, @text)
166
+ node.count{|e| e.named?(:else) and e.is_a?(Else) }.should == 1
167
+ node.find{|e| e.named?(:else) and e.is_a?(Else) }.source_text.should == 'ELSE a := 30; '
168
+ end
169
+
170
+ it "has two If nodes" do
171
+ node = parse(:statement, @text)
172
+ node.count{|e| e.is_a?(If) }.should == 2
173
+ end
174
+ end
175
+ end
176
+
177
+ end
178
+ end
@@ -0,0 +1,41 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ module Piggly
4
+ describe Parser, "control structures" do
5
+ include GrammarHelper
6
+
7
+ describe "loops" do
8
+ describe "for loops" do
9
+ it "can loop over integers" do
10
+ node = parse(:stmtForLoop, 'FOR x IN 0 .. 100 LOOP a := x; END LOOP;')
11
+ node.should be_a(Statement)
12
+
13
+ cond = node.find{|e| e.named?(:cond) }
14
+ cond.source_text.should == '0 .. 100 '
15
+ cond.should be_a(Expression)
16
+ end
17
+
18
+ it "can loop over query results" do
19
+ node = parse(:stmtForLoop, 'FOR x IN SELECT * FROM table LOOP a := x; END LOOP;')
20
+ node.should be_a(Statement)
21
+
22
+ cond = node.find{|e| e.named?(:cond) }
23
+ cond.source_text.should == 'SELECT * FROM table '
24
+ cond.should be_a(Sql)
25
+ end
26
+ end
27
+
28
+ describe "while loops" do
29
+ end
30
+
31
+ describe "unconditional loops" do
32
+ end
33
+
34
+ describe "continue" do
35
+ end
36
+
37
+ describe "break" do
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,71 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ module Piggly
4
+ describe Parser, "statements" do
5
+ include GrammarHelper
6
+
7
+ describe "SQL statements" do
8
+ it "parses successfully" do
9
+ node, rest = parse_some(:statement, 'SELECT id FROM users;')
10
+ node.should be_a(Statement)
11
+ node.count{|e| e.is_a?(Sql) }.should == 1
12
+ node.find{|e| e.is_a?(Sql) }.source_text.should == 'SELECT id FROM users;'
13
+ rest.should == ''
14
+ end
15
+
16
+ it "must end with a semicolon" do
17
+ lambda{ parse(:statement, 'SELECT id FROM users') }.should raise_error
18
+ lambda{ parse_some(:statement, 'SELECT id FROM users') }.should raise_error
19
+ end
20
+
21
+ it "can contain comments" do
22
+ node = parse(:statement, <<-SQL.strip)
23
+ SELECT INTO user u.id, /* u.name */, p.fist_name, p.last_name
24
+ FROM users u
25
+ INNER JOIN people p ON p.id = u.person_id
26
+ WHERE u.disabled -- can't login
27
+ AND u.id = 100;
28
+ SQL
29
+ sql = node.find{|e| e.is_a?(Sql) }
30
+ sql.count{|e| e.is_a?(TComment) }.should == 2
31
+ end
32
+
33
+ it "can be followed by comments" do
34
+ node, rest = parse_some(:statement, 'SELECT id FROM users; -- comment')
35
+ node.find{|e| e.is_a?(Sql) }.source_text == 'SELECT id FROM users;'
36
+ node.tail.source_text.should == ' -- comment'
37
+ rest.should == ''
38
+ end
39
+
40
+ it "can be followed by whitespace" do
41
+ node, rest = parse_some(:statement, "SELECT id FROM users; \n")
42
+ node.find{|e| e.is_a?(Sql) }.source_text == 'SELECT id FROM users;'
43
+ node.tail.source_text.should == " \n"
44
+ rest.should == ''
45
+ end
46
+
47
+ it "can contain strings" do
48
+ node, rest = parse_some(:statement, <<-SQL.strip)
49
+ SELECT INTO user u.id, u.first_name, u.last_name
50
+ FROM users u
51
+ WHERE first_name ILIKE '%a%'
52
+ OR last_name ILIKE '%b%';
53
+ SQL
54
+ sql = node.find{|e| e.is_a?(Sql) }
55
+ sql.count{|e| e.is_a?(TString) }.should == 2
56
+ end
57
+
58
+ it "can contain strings and comments" do
59
+ node = parse(:statement, <<-SQL.strip)
60
+ a := (SELECT fk, count(*), 'not a statement;'
61
+ FROM dataset
62
+ WHERE id > 100 /* filter out 'reserved' range */
63
+ AND id < 900 -- shouldn't be a string
64
+ OR source_text <> 'alpha /* no comment */;'
65
+ GROUP BY /* pk ; 'abc' */ fk);
66
+ SQL
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,58 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ module Piggly
4
+ describe Parser, "tokens" do
5
+ include GrammarHelper
6
+
7
+ describe "comments" do
8
+ it "can begin with -- and terminate at EOF" do
9
+ GrammarHelper::COMMENTS.map{|s| "-- #{s}" }.test_each do |s|
10
+ parse(:tComment, s).should be_a(TComment)
11
+ end
12
+ end
13
+
14
+ it "can begin with -- and terminate at line ending" do
15
+ GrammarHelper::COMMENTS.map{|s| "-- #{s}\n" }.test_each do |s|
16
+ parse(:tComment, s).should be_a(TComment)
17
+ end
18
+
19
+ GrammarHelper::COMMENTS.map{|s| "-- #{s}\n\n" }.test_each do |s|
20
+ node, rest = parse_some(:tComment, s)
21
+ node.should be_a(TComment)
22
+ rest.should == "\n"
23
+ end
24
+
25
+ GrammarHelper::COMMENTS.map{|s| "-- #{s}\nremaining cruft\n" }.test_each do |s|
26
+ node, rest = parse_some(:tComment, s)
27
+ node.should be_a(TComment)
28
+ rest.should == "remaining cruft\n"
29
+ end
30
+ end
31
+
32
+ it "can be /* c-style */" do
33
+ GrammarHelper::COMMENTS.map{|s| "/* #{s} */" }.test_each do |s|
34
+ parse(:tComment, s).should be_a(TComment)
35
+ end
36
+ end
37
+
38
+ it "terminate after */ marker" do
39
+ GrammarHelper::COMMENTS.map{|s| "/* #{s} */remaining cruft\n" }.test_each do |s|
40
+ node, rest = parse_some(:tComment, s)
41
+ node.should be_a(TComment)
42
+ rest.should == "remaining cruft\n"
43
+ end
44
+ end
45
+
46
+ it "cannot be nested" do
47
+ node, rest = parse_some(:tComment, "/* nested /*INLINE*/ comments */")
48
+ node.should be_a(TComment)
49
+ rest.should == " comments */"
50
+
51
+ node, rest = parse_some(:tComment, "-- nested -- line comments")
52
+ node.count{|e| e.is_a?(TComment) }.should == 1
53
+ rest.should == ''
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,52 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ module Piggly
4
+ describe Parser, "tokens" do
5
+ include GrammarHelper
6
+
7
+ describe "data types" do
8
+ it "can consist of a single word" do
9
+ %w[int boolean char varchar text date timestamp record].test_each do |s|
10
+ parse(:tType, s).should be_a(TDatatype)
11
+ end
12
+ end
13
+
14
+ it "can have parameterized types" do
15
+ ["numeric(10,2)", "decimal(12,4)", "char(1)", "varchar(100)"].test_each do |s|
16
+ parse(:tType, s).should be_a(TDatatype)
17
+ end
18
+ end
19
+
20
+ it "can end in %ROWTYPE" do
21
+ %w[users%rowtype].test_each do |s|
22
+ parse(:tType, s).should be_a(TDatatype)
23
+ end
24
+ end
25
+
26
+ it "can consist of several words" do
27
+ ["timestamp with time zone", "character varying"].test_each do |s|
28
+ parse(:tType, s).should be_a(TDatatype)
29
+ end
30
+ end
31
+
32
+ it "can specify arrays" do
33
+ ["integer[]", "varchar(10)[]", "numeric(10,2)[]", "timestamp without time zone[]"].test_each do |s|
34
+ parse(:tType, s).should be_a(TDatatype)
35
+ end
36
+ end
37
+
38
+ it "can specify multi-dimensional" do
39
+ ["integer[][]", "char(1)[][]", "character varying[][]"].test_each do |s|
40
+ parse(:tType, s).should be_a(TDatatype)
41
+ end
42
+ end
43
+
44
+ it "are terminated by symbol outside of parentheses" do
45
+ node, rest = parse_some(:tType, "character varying, ")
46
+ node.should be_a(TDatatype)
47
+ rest.should == ', '
48
+ end
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,58 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ module Piggly
4
+ describe Parser, "tokens" do
5
+ include GrammarHelper
6
+
7
+ describe "identifiers" do
8
+ it "cannot be a keyword" do
9
+ GrammarHelper::KEYWORDS.test_each do |s|
10
+ lambda{ parse(:tIdentifier, s) }.should raise_error
11
+ end
12
+ end
13
+
14
+ it "can be quoted keyword" do
15
+ GrammarHelper::KEYWORDS.map{|s| '"' + s + '"' }.test_each do |s|
16
+ parse(:tIdentifier, s).should be_a(TIdentifier)
17
+ end
18
+ end
19
+
20
+ it "can begin with a keyword" do
21
+ GrammarHelper::KEYWORDS.select{|s| s =~ /^[a-z]/i }.map{|s| "#{s}xyz" }.test_each do |s|
22
+ parse(:tIdentifier, s).should be_a(TIdentifier)
23
+ end
24
+
25
+ GrammarHelper::KEYWORDS.select{|s| s =~ /^[a-z]/i }.map{|s| "#{s}_xyz" }.test_each do |s|
26
+ parse(:tIdentifier, s).should be_a(TIdentifier)
27
+ end
28
+ end
29
+
30
+ it "can end with a keyword" do
31
+ GrammarHelper::KEYWORDS.select{|s| s =~ /^[a-z]/i }.map{|s| "xyz#{s}" }.test_each do |s|
32
+ parse(:tIdentifier, s).should be_a(TIdentifier)
33
+ end
34
+
35
+ GrammarHelper::KEYWORDS.select{|s| s =~ /^[a-z]/i }.map{|s| "xyz_#{s}" }.test_each do |s|
36
+ parse(:tIdentifier, s).should be_a(TIdentifier)
37
+ end
38
+ end
39
+
40
+ it "can be one single character" do
41
+ %w[_ a b c d e f g h i j k l m n o p q r s t u v w x y z].test_each do |s|
42
+ parse(:tIdentifier, s).should be_a(TIdentifier)
43
+ end
44
+ end
45
+
46
+ it "can contain underscores" do
47
+ %w[_abc abc_ ab_c a_bc ab_cd ab_c_d a_bc_d ab_c_d a_b_c_d a__b__c__d].test_each do |s|
48
+ parse(:tIdentifier, s).should be_a(TIdentifier)
49
+ end
50
+ end
51
+
52
+ it "can contain numbers" do
53
+ parse(:tIdentifier, 'foo9000').should be_a(TIdentifier)
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,44 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ module Piggly
4
+ describe Parser, "tokens" do
5
+ include GrammarHelper
6
+
7
+ describe "keywords" do
8
+ it "parse successfully" do
9
+ GrammarHelper::KEYWORDS.test_each do |k|
10
+ parse(:keyword, k).should be_a(TKeyword)
11
+ end
12
+ end
13
+
14
+ it "cannot have trailing characters" do
15
+ GrammarHelper::KEYWORDS.each do |k|
16
+ lambda{ parse(:keyword, "#{k}abc") }.should raise_error
17
+ end
18
+ end
19
+
20
+ it "cannot have preceeding characters" do
21
+ GrammarHelper::KEYWORDS.each do |k|
22
+ lambda{ parse(:keyword, "abc#{k}") }.should raise_error
23
+ end
24
+ end
25
+
26
+ it "are terminated by symbols" do
27
+ GrammarHelper::KEYWORDS.test_each do |k|
28
+ node, rest = parse_some(:keyword, "#{k}+")
29
+ node.should be_a(TKeyword)
30
+ rest.should == '+'
31
+ end
32
+ end
33
+
34
+ it "are terminated by spaces" do
35
+ GrammarHelper::KEYWORDS.test_each do |k|
36
+ node, rest = parse_some(:keyword, "#{k} ")
37
+ node.should be_a(TKeyword)
38
+ rest.should == ' '
39
+ end
40
+ end
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,40 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ module Piggly
4
+ describe Parser, "tokens" do
5
+ include GrammarHelper
6
+
7
+ describe "labels" do
8
+ it "must be enclosed in << and >>" do
9
+ ['', 'a', 'abc', '<< a', 'a >>'].test_each do |s|
10
+ lambda{ parse(:tLabel, s) }.should raise_error
11
+ end
12
+ end
13
+
14
+ it "can have space padding" do
15
+ %w[a abc _].map{|s| "<< #{s} >>" }.test_each do |s|
16
+ parse(:tLabel, s).should be_a(TLabel)
17
+ end
18
+ end
19
+
20
+ it "can have no space padding" do
21
+ %w[a abc _].map{|s| "<<#{s}>>" }.test_each do |s|
22
+ parse(:tLabel, s).should be_a(TLabel)
23
+ end
24
+ end
25
+
26
+ it "cannot be multiple unquoted words" do
27
+ ["<< a b >>", "<< ab cd >>"].test_each do |s|
28
+ lambda{ parse(:tLabel, s) }.should raise_error
29
+ end
30
+ end
31
+
32
+ it "can be enclosed in double quotes" do
33
+ ['<< "a" >>', '<< "a b" >>', '<< "ab cd" >>'].test_each do |s|
34
+ parse(:tLabel, s).should be_a(TLabel)
35
+ end
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,30 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ module Piggly
4
+ describe Parser, "tokens" do
5
+ include GrammarHelper
6
+
7
+ describe "literals" do
8
+ it "can be a cast call on a string" do
9
+ node = parse(:tLiteral, "cast('100.00' as numeric(10, 2))")
10
+ node = parse(:tLiteral, "cast( '100.00' as numeric (10, 2) )")
11
+ end
12
+
13
+ it "can be a cast call on a number" do
14
+ node = parse(:tLiteral, "cast(100.00 as character varying(8))")
15
+ node = parse(:tLiteral, "cast( 100.00 as character varying (8) )")
16
+ end
17
+
18
+ it "can be a ::cast on a string" do
19
+ node = parse(:tLiteral, "'100'::int")
20
+ node = parse(:tLiteral, "'100' :: int")
21
+ end
22
+
23
+ it "can be a ::cast on a number" do
24
+ node = parse(:tLiteral, '100 :: varchar')
25
+ node = parse(:tLiteral, '100::varchar')
26
+ end
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,50 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ module Piggly
4
+ describe Parser, "tokens" do
5
+ include GrammarHelper
6
+
7
+ describe "l-values" do
8
+ it "can be a simple identifier" do
9
+ parse(:lValue, 'id').should be_a(Assignable)
10
+ end
11
+
12
+ it "can be an attribute accessor" do
13
+ parse(:lValue, 'record.id').should be_a(Assignable)
14
+ parse(:lValue, 'public.dataset.id').should be_a(Assignable)
15
+ end
16
+
17
+ it "can use quoted attributes" do
18
+ parse(:lValue, 'record."ID"').should be_a(Assignable)
19
+ parse(:lValue, '"schema name"."table name"."column name"').should be_a(Assignable)
20
+ end
21
+
22
+ it "can be an array accessor" do
23
+ parse(:lValue, 'names[0]').should be_a(Assignable)
24
+ parse(:lValue, 'names[1000]').should be_a(Assignable)
25
+ end
26
+
27
+ it "can contain comments in array accessors" do
28
+ node = parse(:lValue, 'names[3 /* comment */]')
29
+ node.should be_a(Assignable)
30
+ node.count{|e| e.is_a?(TComment) }
31
+
32
+ node = parse(:lValue, "names[9 -- comment \n]")
33
+ node.should be_a(Assignable)
34
+ node.count{|e| e.is_a?(TComment) }
35
+ end
36
+
37
+ it "can be an array accessed by another l-value" do
38
+ parse(:lValue, 'names[face.id]').should be_a(Assignable)
39
+ end
40
+
41
+ it "can be a nested array access"
42
+ # names[faces[0].id].id doesn't work because it requires context-sensitivity [faces[0]
43
+
44
+ it "can be a multi-dimensional array access" do
45
+ parse(:lValue, 'data[10][2][0]').should be_a(Assignable)
46
+ end
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,34 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ module Piggly
4
+ describe Parser, "tokens" do
5
+ include GrammarHelper
6
+
7
+ describe "numbers" do
8
+ it "can be an integer in binary notation" do
9
+ node = parse(:tNumber, "B'0'")
10
+ end
11
+
12
+ it "can be an integer in hexadecimal notation" do
13
+ node = parse(:tNumber, "X'F'")
14
+ end
15
+
16
+ it "can be an integer in decimal notation" do
17
+ node = parse(:tNumber, '11223344556677889900')
18
+ end
19
+
20
+ it "can be a real number in decimial notation" do
21
+ node = parse(:tNumber, '3.2267')
22
+ end
23
+
24
+ it "can be a real number in scientific notation" do
25
+ node = parse(:tNumber, '1.3e3')
26
+ end
27
+
28
+ it "can be an integer in scientific notation" do
29
+ node = parse(:tNumber, '5e4')
30
+ end
31
+ end
32
+
33
+ end
34
+ end