piggly-nsd 2.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +170 -0
- data/Rakefile +33 -0
- data/bin/piggly +8 -0
- data/lib/piggly/command/base.rb +148 -0
- data/lib/piggly/command/report.rb +162 -0
- data/lib/piggly/command/trace.rb +90 -0
- data/lib/piggly/command/untrace.rb +78 -0
- data/lib/piggly/command.rb +8 -0
- data/lib/piggly/compiler/cache_dir.rb +119 -0
- data/lib/piggly/compiler/coverage_report.rb +63 -0
- data/lib/piggly/compiler/trace_compiler.rb +117 -0
- data/lib/piggly/compiler.rb +7 -0
- data/lib/piggly/config.rb +80 -0
- data/lib/piggly/dumper/index.rb +121 -0
- data/lib/piggly/dumper/qualified_name.rb +36 -0
- data/lib/piggly/dumper/qualified_type.rb +141 -0
- data/lib/piggly/dumper/reified_procedure.rb +172 -0
- data/lib/piggly/dumper/skeleton_procedure.rb +112 -0
- data/lib/piggly/dumper.rb +9 -0
- data/lib/piggly/installer.rb +137 -0
- data/lib/piggly/parser/grammar.tt +748 -0
- data/lib/piggly/parser/nodes.rb +378 -0
- data/lib/piggly/parser/traversal.rb +50 -0
- data/lib/piggly/parser/treetop_ruby19_patch.rb +21 -0
- data/lib/piggly/parser.rb +69 -0
- data/lib/piggly/profile.rb +108 -0
- data/lib/piggly/reporter/base.rb +106 -0
- data/lib/piggly/reporter/html_dsl.rb +63 -0
- data/lib/piggly/reporter/index.rb +114 -0
- data/lib/piggly/reporter/procedure.rb +129 -0
- data/lib/piggly/reporter/resources/highlight.js +38 -0
- data/lib/piggly/reporter/resources/piggly.css +515 -0
- data/lib/piggly/reporter/resources/sortable.js +493 -0
- data/lib/piggly/reporter.rb +8 -0
- data/lib/piggly/tags.rb +280 -0
- data/lib/piggly/task.rb +215 -0
- data/lib/piggly/util/blankslate.rb +114 -0
- data/lib/piggly/util/cacheable.rb +19 -0
- data/lib/piggly/util/enumerable.rb +44 -0
- data/lib/piggly/util/file.rb +17 -0
- data/lib/piggly/util/process_queue.rb +96 -0
- data/lib/piggly/util/thunk.rb +39 -0
- data/lib/piggly/util.rb +9 -0
- data/lib/piggly/version.rb +15 -0
- data/lib/piggly.rb +20 -0
- data/spec/examples/compiler/cacheable_spec.rb +190 -0
- data/spec/examples/compiler/report_spec.rb +25 -0
- data/spec/examples/compiler/trace_spec.rb +123 -0
- data/spec/examples/config_spec.rb +63 -0
- data/spec/examples/dumper/index_spec.rb +199 -0
- data/spec/examples/dumper/procedure_spec.rb +116 -0
- data/spec/examples/grammar/expression_spec.rb +302 -0
- data/spec/examples/grammar/statements/assignment_spec.rb +70 -0
- data/spec/examples/grammar/statements/declaration_spec.rb +21 -0
- data/spec/examples/grammar/statements/exception_spec.rb +78 -0
- data/spec/examples/grammar/statements/if_spec.rb +191 -0
- data/spec/examples/grammar/statements/loop_spec.rb +41 -0
- data/spec/examples/grammar/statements/sql_spec.rb +71 -0
- data/spec/examples/grammar/tokens/comment_spec.rb +58 -0
- data/spec/examples/grammar/tokens/datatype_spec.rb +58 -0
- data/spec/examples/grammar/tokens/identifier_spec.rb +74 -0
- data/spec/examples/grammar/tokens/keyword_spec.rb +44 -0
- data/spec/examples/grammar/tokens/label_spec.rb +40 -0
- data/spec/examples/grammar/tokens/literal_spec.rb +30 -0
- data/spec/examples/grammar/tokens/lval_spec.rb +50 -0
- data/spec/examples/grammar/tokens/number_spec.rb +34 -0
- data/spec/examples/grammar/tokens/sqlkeywords_spec.rb +45 -0
- data/spec/examples/grammar/tokens/string_spec.rb +54 -0
- data/spec/examples/grammar/tokens/whitespace_spec.rb +40 -0
- data/spec/examples/installer_spec.rb +59 -0
- data/spec/examples/parser/nodes_spec.rb +73 -0
- data/spec/examples/parser/traversal_spec.rb +14 -0
- data/spec/examples/parser_spec.rb +118 -0
- data/spec/examples/profile_spec.rb +153 -0
- data/spec/examples/reporter/html/dsl_spec.rb +0 -0
- data/spec/examples/reporter/html/index_spec.rb +0 -0
- data/spec/examples/reporter/html_spec.rb +1 -0
- data/spec/examples/reporter_spec.rb +0 -0
- data/spec/examples/tags_spec.rb +285 -0
- data/spec/examples/task_spec.rb +0 -0
- data/spec/examples/util/cacheable_spec.rb +41 -0
- data/spec/examples/util/enumerable_spec.rb +64 -0
- data/spec/examples/util/file_spec.rb +40 -0
- data/spec/examples/util/process_queue_spec.rb +16 -0
- data/spec/examples/util/thunk_spec.rb +59 -0
- data/spec/examples/version_spec.rb +0 -0
- data/spec/issues/007_spec.rb +25 -0
- data/spec/issues/008_spec.rb +73 -0
- data/spec/issues/018_spec.rb +25 -0
- data/spec/issues/028_spec.rb +48 -0
- data/spec/issues/032_spec.rb +98 -0
- data/spec/issues/036_spec.rb +41 -0
- data/spec/spec_helper.rb +312 -0
- data/spec/spec_suite.rb +5 -0
- metadata +162 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Piggly
|
|
4
|
+
|
|
5
|
+
describe Parser, "statements" do
|
|
6
|
+
include GrammarHelper
|
|
7
|
+
|
|
8
|
+
describe "single variable declarations" do
|
|
9
|
+
it "parse successfully" do
|
|
10
|
+
node = parse(:stmtDeclare, "declare t text;")
|
|
11
|
+
node.count{|e| e.identifier? }.should == 1
|
|
12
|
+
node.count{|e| e.datatype? }.should == 1
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "allows an initial assignment" do
|
|
16
|
+
node = parse(:stmtDeclare, "declare a text := 10;")
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Piggly
|
|
4
|
+
describe Parser, "control structures" do
|
|
5
|
+
include GrammarHelper
|
|
6
|
+
|
|
7
|
+
describe "exceptions" do
|
|
8
|
+
describe "raise" do
|
|
9
|
+
it "parses successfully" do
|
|
10
|
+
node, rest = parse_some(:statement, "RAISE EXCEPTION 'message';")
|
|
11
|
+
node.should be_statement
|
|
12
|
+
rest.should == ''
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "handles exception" do
|
|
16
|
+
node = parse(:statement, "RAISE EXCEPTION 'message';")
|
|
17
|
+
node.count{|e| e.is_a?(Parser::Nodes::Throw) }.should == 1
|
|
18
|
+
node.count{|e| e.is_a?(Parser::Nodes::Raise) }.should == 0
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "handles events" do
|
|
22
|
+
%w(WARNING LOG INFO NOTICE DEBUG).each do |event|
|
|
23
|
+
node = parse(:statement, "RAISE #{event} 'message';")
|
|
24
|
+
node.count{|e| e.is_a?(Parser::Nodes::Throw) }.should == 0
|
|
25
|
+
node.count{|e| e.is_a?(Parser::Nodes::Raise) }.should == 1
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "doesn't require a message" do
|
|
30
|
+
node = parse(:statement, "RAISE EXCEPTION;")
|
|
31
|
+
node.count{|e| e.is_a?(Parser::Nodes::Throw) }.should == 1
|
|
32
|
+
node.count{|e| e.is_a?(Parser::Nodes::Raise) }.should == 0
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "doesn't require a message" do
|
|
36
|
+
%w(WARNING LOG INFO NOTICE DEBUG).each do |event|
|
|
37
|
+
node = parse(:statement, "RAISE #{event};")
|
|
38
|
+
node.count{|e| e.is_a?(Parser::Nodes::Throw) }.should == 0
|
|
39
|
+
node.count{|e| e.is_a?(Parser::Nodes::Raise) }.should == 1
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "has default level of EXCEPTION" do
|
|
44
|
+
node = parse(:statement, "RAISE 'message';")
|
|
45
|
+
node.count{|e| e.is_a?(Parser::Nodes::Throw) }.should == 1
|
|
46
|
+
node.count{|e| e.is_a?(Parser::Nodes::Raise) }.should == 0
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "doesn't require a level or message" do
|
|
50
|
+
node = parse(:statement, "RAISE;")
|
|
51
|
+
node.count{|e| e.is_a?(Parser::Nodes::Throw) }.should == 1
|
|
52
|
+
node.count{|e| e.is_a?(Parser::Nodes::Raise) }.should == 0
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe "catch" do
|
|
57
|
+
before do
|
|
58
|
+
@text = 'BEGIN a := 10; EXCEPTION WHEN cond THEN b := 10; WHEN cond THEN b := 20; END;'
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "parses successfully" do
|
|
62
|
+
node, rest = parse_some(:statement, @text)
|
|
63
|
+
node.should be_statement
|
|
64
|
+
rest.should == ''
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "has Catch node" do
|
|
68
|
+
node = parse(:statement, @text)
|
|
69
|
+
catches = node.select{|e| e.is_a?(Parser::Nodes::Catch) }
|
|
70
|
+
catches.size.should == 2
|
|
71
|
+
|
|
72
|
+
catches[0].count{|e| e.named?(:cond) and e.expression? }.should == 1
|
|
73
|
+
catches[1].count{|e| e.named?(:cond) and e.expression? }.should == 1
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
require '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
|
+
expect{ parse(:statement, 'IF cond THEN a := 10; END IF') }.to raise_error(Piggly::Parser::Failure)
|
|
12
|
+
expect{ parse_some(:stmtIf, 'IF cond THEN a := 10; END IF') }.to raise_error(Piggly::Parser::Failure)
|
|
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_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.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_expression
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "can have missing body" do
|
|
34
|
+
node = parse(:statement, 'IF cond THEN END IF;')
|
|
35
|
+
node.should be_statement
|
|
36
|
+
node.count{|e| e.if? }.should == 1
|
|
37
|
+
node.count{|e| e.named?(:cond) }.should == 1
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "can have comment body" do
|
|
41
|
+
node = parse(:statement, 'IF cond THEN /* removed */ END IF;')
|
|
42
|
+
node.should be_statement
|
|
43
|
+
node.count{|e| e.if? }.should == 1
|
|
44
|
+
node.count{|e| e.comment? }.should == 1
|
|
45
|
+
node.find{|e| e.comment? }.source_text.should == '/* removed */'
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "can have single statement body" do
|
|
49
|
+
node = parse(:statement, 'IF cond THEN a := 10; END IF;')
|
|
50
|
+
node.count{|e| e.if? }.should == 1
|
|
51
|
+
node.count{|e| e.assignment? }.should == 1
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "can have multiple statement body" do
|
|
55
|
+
node = parse(:statement, 'IF cond THEN a := 10; b := 10; END IF;')
|
|
56
|
+
node.count{|e| e.if? }.should == 1
|
|
57
|
+
node.count{|e| e.assignment? }.should == 2
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "can contain comments" do
|
|
61
|
+
node = parse(:statement, "IF cond /* comment */ THEN -- foo\n NULL; /* foo */ END IF;")
|
|
62
|
+
node.should be_statement
|
|
63
|
+
node.count{|e| e.comment? }.should == 3
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe "if .. then .. else .. end if" do
|
|
68
|
+
it "parses successfully" do
|
|
69
|
+
node, rest = parse_some(:statement, 'IF cond THEN a := 10; ELSE a := 20; END IF;')
|
|
70
|
+
node.should be_statement
|
|
71
|
+
rest.should == ''
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "has an Else node named 'else'" do
|
|
75
|
+
node = parse(:statement, 'IF cond THEN a := 10; ELSE a := 20; END IF;')
|
|
76
|
+
node.count{|e| e.named?(:else) and e.else? }.should == 1
|
|
77
|
+
node.find{|e| e.named?(:else) }.source_text.should == 'ELSE a := 20; '
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "can have missing else body" do
|
|
81
|
+
node = parse(:statement, 'IF cond THEN a := 10; ELSE END IF;')
|
|
82
|
+
node.count{|e| e.if? }.should == 1
|
|
83
|
+
node.count{|e| e.assignment? }.should == 1
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "can have comment body" do
|
|
87
|
+
node = parse(:statement, 'IF cond THEN a := 10; ELSE /* removed */ END IF;')
|
|
88
|
+
node.count{|e| e.if? }.should == 1
|
|
89
|
+
node.count{|e| e.comment? }.should == 1
|
|
90
|
+
node.count{|e| e.assignment? }.should == 1
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "can have single statement body" do
|
|
94
|
+
node = parse(:statement, 'IF cond THEN a := 10; ELSE a := 20; END IF;')
|
|
95
|
+
node.count{|e| e.if? }.should == 1
|
|
96
|
+
node.count{|e| e.assignment? }.should == 2
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "can have multiple statement body" do
|
|
100
|
+
node = parse(:statement, 'IF cond THEN a := 10; ELSE a := 20; b := 30; END IF;')
|
|
101
|
+
node.count{|e| e.if? }.should == 1
|
|
102
|
+
node.count{|e| e.assignment? }.should == 3
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
describe "if .. then .. elsif .. then .. end if" do
|
|
107
|
+
it "parses successfully" do
|
|
108
|
+
node, rest = parse_some(:statement, 'IF cond THEN a := 10; ELSIF cond THEN a := 20; END IF;')
|
|
109
|
+
node.should be_statement
|
|
110
|
+
rest.should == ''
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "can have comment body" do
|
|
114
|
+
node = parse(:statement, 'IF cond THEN a := 10; ELSIF cond THEN /* removed */ END IF;')
|
|
115
|
+
node.count{|e| e.if? }.should == 2
|
|
116
|
+
node.count{|e| e.comment? }.should == 1
|
|
117
|
+
node.count{|e| e.assignment? }.should == 1
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "can having missing body" do
|
|
121
|
+
node = parse(:statement, 'IF cond THEN a := 10; ELSIF cond THEN END IF;')
|
|
122
|
+
node.count{|e| e.if? }.should == 2
|
|
123
|
+
node.count{|e| e.assignment? }.should == 1
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it "can have single statement body" do
|
|
127
|
+
node = parse(:statement, 'IF cond THEN a := 10; ELSIF cond THEN a := 20; END IF;')
|
|
128
|
+
node.count{|e| e.if? }.should == 2
|
|
129
|
+
node.count{|e| e.assignment? }.should == 2
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it "can have multiple statement body" do
|
|
133
|
+
node = parse(:statement, 'IF cond THEN a := 10; ELSIF cond THEN a := 20; b := 30; END IF;')
|
|
134
|
+
node.count{|e| e.if? }.should == 2
|
|
135
|
+
node.count{|e| e.assignment? }.should == 3
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it "can have many elsif branches" do
|
|
139
|
+
node = parse(:statement, <<-SQL.strip)
|
|
140
|
+
IF cond THEN a := 10;
|
|
141
|
+
ELSIF cond THEN a := 20;
|
|
142
|
+
ELSIF cond THEN a := 30;
|
|
143
|
+
ELSIF cond THEN a := 40;
|
|
144
|
+
ELSIF cond THEN a := 50;
|
|
145
|
+
ELSIF cond THEN a := 60;
|
|
146
|
+
END IF;
|
|
147
|
+
SQL
|
|
148
|
+
|
|
149
|
+
node.count{|e| e.named?(:cond) }.should == 6
|
|
150
|
+
node.count{|e| e.if? }.should == 6
|
|
151
|
+
node.count{|e| e.if? and e.named?(:else) }.should == 5
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it "has no Else nodes" do
|
|
155
|
+
node = parse(:statement, 'IF cond THEN a := 10; ELSIF cond THEN a := 20; END IF;')
|
|
156
|
+
node.count{|e| e.else? }.should == 0
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
describe "if .. then .. elsif .. then .. else .. endif" do
|
|
161
|
+
before do
|
|
162
|
+
@text = 'IF cond THEN a := 10; ELSIF cond THEN a := 20; ELSE a := 30; END IF;'
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it "parses successfully" do
|
|
166
|
+
node, rest = parse_some(:statement, @text)
|
|
167
|
+
node.should be_statement
|
|
168
|
+
rest.should == ''
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it "has an If node named 'else'" do
|
|
172
|
+
node = parse(:statement, @text)
|
|
173
|
+
node.count{|e| e.named?(:else) and e.if? }.should == 1
|
|
174
|
+
node.find{|e| e.named?(:else) and e.if? }.source_text.should == 'ELSIF cond THEN a := 20; ELSE a := 30; '
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "has an Else node named 'else'" do
|
|
178
|
+
node = parse(:statement, @text)
|
|
179
|
+
node.count{|e| e.named?(:else) and e.else? }.should == 1
|
|
180
|
+
node.find{|e| e.named?(:else) and e.else? }.source_text.should == 'ELSE a := 30; '
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
it "has two If nodes" do
|
|
184
|
+
node = parse(:statement, @text)
|
|
185
|
+
node.count{|e| e.if? }.should == 2
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
end
|
|
191
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require '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_statement
|
|
12
|
+
|
|
13
|
+
cond = node.find{|e| e.named?(:cond) }
|
|
14
|
+
cond.source_text.should == '0 .. 100 '
|
|
15
|
+
cond.should be_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_statement
|
|
21
|
+
|
|
22
|
+
cond = node.find{|e| e.named?(:cond) }
|
|
23
|
+
cond.source_text.should == 'SELECT * FROM table '
|
|
24
|
+
cond.should be_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 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Piggly
|
|
4
|
+
describe Parser, "statements" do
|
|
5
|
+
include GrammarHelper
|
|
6
|
+
|
|
7
|
+
describe "SQL statements" do
|
|
8
|
+
it "parse successfully" do
|
|
9
|
+
node, rest = parse_some(:statement, 'SELECT id FROM users;')
|
|
10
|
+
node.should be_statement
|
|
11
|
+
node.count{|e| e.sql? }.should == 1
|
|
12
|
+
node.find{|e| e.sql? }.source_text.should == 'SELECT id FROM users;'
|
|
13
|
+
rest.should == ''
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "must end with a semicolon" do
|
|
17
|
+
expect{ parse(:statement, 'SELECT id FROM users') }.to raise_error(Piggly::Parser::Failure)
|
|
18
|
+
expect{ parse_some(:statement, 'SELECT id FROM users') }.to raise_error(Piggly::Parser::Failure)
|
|
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.sql? }
|
|
30
|
+
sql.count{|e| e.comment? }.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.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.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.sql? }
|
|
55
|
+
sql.count{|e| e.string? }.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 '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_comment
|
|
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_comment
|
|
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_comment
|
|
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_comment
|
|
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_comment
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "terminates 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_comment
|
|
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_comment
|
|
49
|
+
rest.should == " comments */"
|
|
50
|
+
|
|
51
|
+
node, rest = parse_some(:tComment, "-- nested -- line comments")
|
|
52
|
+
node.count{|e| e.comment? }.should == 1
|
|
53
|
+
rest.should == ''
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require '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_datatype
|
|
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_datatype
|
|
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_datatype
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "can have namespace notation" do
|
|
27
|
+
%w[public.users namespace.relation%rowtype].test_each do |s|
|
|
28
|
+
parse(:tType, s).should be_datatype
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "can consist of several words" do
|
|
33
|
+
["timestamp with time zone", "character varying"].test_each do |s|
|
|
34
|
+
parse(:tType, s).should be_datatype
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "can specify arrays" do
|
|
39
|
+
["integer[]", "varchar(10)[]", "numeric(10,2)[]", "timestamp without time zone[]"].test_each do |s|
|
|
40
|
+
parse(:tType, s).should be_datatype
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "can specify multi-dimensional" do
|
|
45
|
+
["integer[][]", "char(1)[][]", "character varying[][]"].test_each do |s|
|
|
46
|
+
parse(:tType, s).should be_datatype
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "are terminated by symbol outside of parentheses" do
|
|
51
|
+
node, rest = parse_some(:tType, "character varying, ")
|
|
52
|
+
node.should be_datatype
|
|
53
|
+
rest.should == ', '
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require '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
|
+
expect{ parse(:tIdentifier, s); puts s }.to raise_error(Piggly::Parser::Failure)
|
|
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_identifier
|
|
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_identifier
|
|
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_identifier
|
|
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_identifier
|
|
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_identifier
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "is terminated by an operator" do
|
|
41
|
+
GrammarHelper::KEYWORDS.select{|s| s !~ /^[a-z]/i }.test_each do |op|
|
|
42
|
+
node, rest = parse_some(:tIdentifier, "xyv#{op}")
|
|
43
|
+
node.should be_identifier
|
|
44
|
+
rest.should == op
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "is terminated by an operator" do
|
|
49
|
+
GrammarHelper::KEYWORDS.select{|s| s !~ /^[a-z]/i }.test_each do |op|
|
|
50
|
+
node, rest = parse_some(:tIdentifier, "xyv_#{op}")
|
|
51
|
+
node.should be_identifier
|
|
52
|
+
rest.should == op
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "can be one single character" do
|
|
57
|
+
%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|
|
|
58
|
+
parse(:tIdentifier, s).should be_identifier
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "can contain underscores" do
|
|
63
|
+
%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|
|
|
64
|
+
parse(:tIdentifier, s).should be_identifier
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "can contain numbers" do
|
|
69
|
+
parse(:tIdentifier, 'foo9000').should be_identifier
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require '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_keyword
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "cannot have trailing characters" do
|
|
15
|
+
GrammarHelper::KEYWORDS.each do |k|
|
|
16
|
+
expect{ parse(:keyword, "#{k}abc") }.to raise_error(Piggly::Parser::Failure)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "cannot have preceeding characters" do
|
|
21
|
+
GrammarHelper::KEYWORDS.each do |k|
|
|
22
|
+
expect{ parse(:keyword, "#{k}abc") }.to raise_error(Piggly::Parser::Failure)
|
|
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_keyword
|
|
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_keyword
|
|
38
|
+
rest.should == ' '
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require '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
|
+
expect{ parse(:tLabelDefinition, s) }.to raise_error(Piggly::Parser::Failure)
|
|
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(:tLabelDefinition, s).should be_label
|
|
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(:tLabelDefinition, s).should be_label
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "cannot be multiple unquoted words" do
|
|
27
|
+
["<< a b >>", "<< ab cd >>"].test_each do |s|
|
|
28
|
+
expect{ parse(:tLabelDefinition, s) }.to raise_error(Piggly::Parser::Failure)
|
|
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(:tLabelDefinition, s).should be_label
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
end
|