piggly 1.2.0
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.
- data/README.markdown +84 -0
- data/Rakefile +19 -0
- data/bin/piggly +245 -0
- data/lib/piggly/compiler/cache.rb +151 -0
- data/lib/piggly/compiler/pretty.rb +67 -0
- data/lib/piggly/compiler/queue.rb +46 -0
- data/lib/piggly/compiler/tags.rb +244 -0
- data/lib/piggly/compiler/trace.rb +91 -0
- data/lib/piggly/compiler.rb +5 -0
- data/lib/piggly/config.rb +43 -0
- data/lib/piggly/filecache.rb +40 -0
- data/lib/piggly/installer.rb +95 -0
- data/lib/piggly/parser/grammar.tt +747 -0
- data/lib/piggly/parser/nodes.rb +319 -0
- data/lib/piggly/parser/parser.rb +11783 -0
- data/lib/piggly/parser/traversal.rb +48 -0
- data/lib/piggly/parser/treetop_ruby19_patch.rb +17 -0
- data/lib/piggly/parser.rb +67 -0
- data/lib/piggly/profile.rb +87 -0
- data/lib/piggly/reporter/html.rb +207 -0
- data/lib/piggly/reporter/piggly.css +187 -0
- data/lib/piggly/reporter/sortable.js +493 -0
- data/lib/piggly/reporter.rb +21 -0
- data/lib/piggly/task.rb +64 -0
- data/lib/piggly/util.rb +28 -0
- data/lib/piggly/version.rb +15 -0
- data/lib/piggly.rb +18 -0
- data/spec/compiler/cache_spec.rb +9 -0
- data/spec/compiler/pretty_spec.rb +9 -0
- data/spec/compiler/queue_spec.rb +3 -0
- data/spec/compiler/rewrite_spec.rb +3 -0
- data/spec/compiler/tags_spec.rb +285 -0
- data/spec/compiler/trace_spec.rb +173 -0
- data/spec/config_spec.rb +58 -0
- data/spec/filecache_spec.rb +70 -0
- data/spec/fixtures/snippets.sql +158 -0
- data/spec/grammar/expression_spec.rb +302 -0
- data/spec/grammar/statements/assignment_spec.rb +70 -0
- data/spec/grammar/statements/exception_spec.rb +52 -0
- data/spec/grammar/statements/if_spec.rb +178 -0
- data/spec/grammar/statements/loop_spec.rb +41 -0
- data/spec/grammar/statements/sql_spec.rb +71 -0
- data/spec/grammar/tokens/comment_spec.rb +58 -0
- data/spec/grammar/tokens/datatype_spec.rb +52 -0
- data/spec/grammar/tokens/identifier_spec.rb +58 -0
- data/spec/grammar/tokens/keyword_spec.rb +44 -0
- data/spec/grammar/tokens/label_spec.rb +40 -0
- data/spec/grammar/tokens/literal_spec.rb +30 -0
- data/spec/grammar/tokens/lval_spec.rb +50 -0
- data/spec/grammar/tokens/number_spec.rb +34 -0
- data/spec/grammar/tokens/sqlkeywords_spec.rb +45 -0
- data/spec/grammar/tokens/string_spec.rb +54 -0
- data/spec/grammar/tokens/whitespace_spec.rb +40 -0
- data/spec/parser_spec.rb +8 -0
- data/spec/profile_spec.rb +5 -0
- data/spec/reporter/html_spec.rb +0 -0
- data/spec/spec_helper.rb +61 -0
- data/spec/spec_suite.rb +5 -0
- metadata +121 -0
@@ -0,0 +1,319 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'traversal')
|
2
|
+
|
3
|
+
NodeClass = Treetop::Runtime::SyntaxNode
|
4
|
+
|
5
|
+
class NodeClass
|
6
|
+
include Piggly::NodeTraversal
|
7
|
+
|
8
|
+
# The 'text_value' method can be used to read the parse tree as Treetop
|
9
|
+
# originally read it. The 'source_text' method returns redefined value or falls
|
10
|
+
# back to original text_value if none was set.
|
11
|
+
|
12
|
+
attr_accessor :source_text
|
13
|
+
|
14
|
+
def value
|
15
|
+
puts "NodeClass#value is deprecated: #{caller.first}"
|
16
|
+
text_value
|
17
|
+
end
|
18
|
+
|
19
|
+
def source_text
|
20
|
+
@source_text || text_value
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Return a newly created Tag value, but only the tag.id is attached to the tree. The
|
25
|
+
# reason that is we maintain the Tags in a separate collection (to avoid a full traversal
|
26
|
+
# just to get the list of tags later), and we can retrieve the Tag associated with this
|
27
|
+
# node by its tag_id.
|
28
|
+
#
|
29
|
+
def tag(prefix = nil, id = nil)
|
30
|
+
unless defined? @tag_id
|
31
|
+
if named?(:body)
|
32
|
+
Piggly::BlockTag.new(prefix, id)
|
33
|
+
else
|
34
|
+
Piggly::EvaluationTag.new(prefix, id)
|
35
|
+
end.tap{|tag| @tag_id = tag.id }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def tag_id
|
40
|
+
@tag_id or raise RuntimeError, "Node is not tagged"
|
41
|
+
end
|
42
|
+
|
43
|
+
def tagged?
|
44
|
+
not @tag_id.nil?
|
45
|
+
end
|
46
|
+
|
47
|
+
# overridden in subclasses
|
48
|
+
def expression?; false end
|
49
|
+
def branch?; false end
|
50
|
+
def block?; false end
|
51
|
+
def stub?; false end
|
52
|
+
def loop?; false end
|
53
|
+
def for?; false end
|
54
|
+
def style; nil end
|
55
|
+
|
56
|
+
def indent(method = nil)
|
57
|
+
if method and respond_to?(method)
|
58
|
+
send(method).text_value[/\n[\t ]*\z/]
|
59
|
+
else
|
60
|
+
text_value[/\n[\t ]*\z/]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
alias o_inspect inspect
|
65
|
+
|
66
|
+
def inspect(indent = '')
|
67
|
+
if terminal?
|
68
|
+
em = extension_modules
|
69
|
+
interesting_methods = methods-[em.last ? em.last.methods : nil]-self.class.instance_methods
|
70
|
+
im = interesting_methods.size > 0 ? " (#{interesting_methods.join(",")})" : ""
|
71
|
+
tv = text_value
|
72
|
+
tv = "...#{tv[-20..-1]}" if tv.size > 20
|
73
|
+
|
74
|
+
indent +
|
75
|
+
self.class.to_s.sub(/.*:/,'') +
|
76
|
+
em.map{|m| "+"+m.to_s.sub(/.*:/,'')}*"" +
|
77
|
+
" offset=#{interval.first}" +
|
78
|
+
", #{tv.inspect}" +
|
79
|
+
im
|
80
|
+
else
|
81
|
+
o_inspect(indent)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# true if node is called 'label' in parent node
|
86
|
+
def named?(label)
|
87
|
+
if p = parent
|
88
|
+
p.respond_to?(label) and p.send(label).equal?(self)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
module Piggly
|
94
|
+
|
95
|
+
# CREATE OR REPLACE ...
|
96
|
+
class Procedure < NodeClass
|
97
|
+
end
|
98
|
+
|
99
|
+
# ...;
|
100
|
+
class Statement < NodeClass
|
101
|
+
end
|
102
|
+
|
103
|
+
class Expression < NodeClass
|
104
|
+
def expression?
|
105
|
+
true
|
106
|
+
end
|
107
|
+
|
108
|
+
def tag(prefix = nil, id = nil)
|
109
|
+
unless defined? @tag_id
|
110
|
+
if named?(:cond)
|
111
|
+
if parent.for?
|
112
|
+
# this object is the conditional statement in a FOR loop
|
113
|
+
Piggly::ForCollectionTag.new(prefix, id)
|
114
|
+
elsif parent.loop?
|
115
|
+
# this object is the conditional statement in a WHILE loop
|
116
|
+
Piggly::LoopConditionTag.new(prefix, id)
|
117
|
+
elsif parent.branch?
|
118
|
+
Piggly::BranchConditionTag.new(prefix, id)
|
119
|
+
end
|
120
|
+
else
|
121
|
+
Piggly::Evaluation.new(prefix, id)
|
122
|
+
end.tap{|tag| @tag_id = tag.id }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# DECLARE declaration BEGIN body END;
|
128
|
+
class Block < Statement
|
129
|
+
def block?
|
130
|
+
true
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Branches with child 'cond' (Expression) will get a BranchCondTag
|
135
|
+
class Branch < Statement
|
136
|
+
def branch?
|
137
|
+
true
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# IF boolean-cond THEN body
|
142
|
+
class If < Branch
|
143
|
+
end
|
144
|
+
|
145
|
+
# ELSE body END
|
146
|
+
class Else < NodeClass
|
147
|
+
end
|
148
|
+
|
149
|
+
# EXCEPTION WHEN boolean-cond THEN body
|
150
|
+
class Catch < Branch
|
151
|
+
end
|
152
|
+
|
153
|
+
# WHEN match-expr THEN body
|
154
|
+
class CaseWhen < Branch
|
155
|
+
end
|
156
|
+
|
157
|
+
# WHEN boolean-cond THEN body
|
158
|
+
class CondWhen < Branch
|
159
|
+
end
|
160
|
+
|
161
|
+
# CONTINUE label WHEN boolean-cond;
|
162
|
+
class ContinueWhen < Branch
|
163
|
+
end
|
164
|
+
|
165
|
+
# EXIT label WHEN boolean-cond;
|
166
|
+
class ExitWhen < Branch
|
167
|
+
end
|
168
|
+
|
169
|
+
class UnconditionalBranch < Statement
|
170
|
+
end
|
171
|
+
|
172
|
+
# RETURN expr
|
173
|
+
class Return < UnconditionalBranch
|
174
|
+
end
|
175
|
+
|
176
|
+
# EXIT label
|
177
|
+
class Exit < UnconditionalBranch
|
178
|
+
end
|
179
|
+
|
180
|
+
# CONTINUE label
|
181
|
+
class Continue < UnconditionalBranch
|
182
|
+
end
|
183
|
+
|
184
|
+
# RAISE EXCEPTION expr
|
185
|
+
class Throw < UnconditionalBranch
|
186
|
+
end
|
187
|
+
|
188
|
+
# Loops with child 'cond' (Expression/Sql) will get a LoopCondTag
|
189
|
+
class Loop < Statement
|
190
|
+
def loop?
|
191
|
+
true
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# FOR boolean-cond LOOP body END
|
196
|
+
class ForLoop < Loop
|
197
|
+
def for?; true end
|
198
|
+
end
|
199
|
+
|
200
|
+
# WHILE boolean-cond LOOP body END
|
201
|
+
class WhileLoop < Loop
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
# RAISE NOTICE expr
|
206
|
+
class Raise < Statement
|
207
|
+
end
|
208
|
+
|
209
|
+
# CASE search-expr WHEN ...
|
210
|
+
class Case < Statement
|
211
|
+
end
|
212
|
+
|
213
|
+
# CASE WHEN ...
|
214
|
+
class Cond < Statement
|
215
|
+
end
|
216
|
+
|
217
|
+
# lval := rval
|
218
|
+
class Assignment < Statement
|
219
|
+
end
|
220
|
+
|
221
|
+
# Lval of assignment (rval is an Expression)
|
222
|
+
class Assignable < NodeClass
|
223
|
+
end
|
224
|
+
|
225
|
+
class Sql < Statement
|
226
|
+
def style; 'tQ'; end
|
227
|
+
|
228
|
+
def tag(prefix = nil, id = nil)
|
229
|
+
unless defined? @tag_id
|
230
|
+
if named?(:cond) and parent.for?
|
231
|
+
# this object is the conditional statement in a FOR loop
|
232
|
+
Piggly::ForCollectionTag.new(prefix, id)
|
233
|
+
else
|
234
|
+
Piggly::EvaluationTag.new(prefix, id)
|
235
|
+
end.tap{|tag| @tag_id = tag.id }
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# Tokens have no children
|
241
|
+
class Token < NodeClass
|
242
|
+
def initialize(input, interval, elements = nil)
|
243
|
+
# prevent children from being assigned
|
244
|
+
super(input, interval, nil)
|
245
|
+
end
|
246
|
+
|
247
|
+
def terminal?
|
248
|
+
true
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
# This seems like it should be a Token, but it may contain TComment children
|
253
|
+
# that should be highlighted differently than the enclosing whitespace
|
254
|
+
class TWhitespace < NodeClass
|
255
|
+
end
|
256
|
+
|
257
|
+
class TKeyword < Token
|
258
|
+
def style; 'tK'; end
|
259
|
+
end
|
260
|
+
|
261
|
+
class TIdentifier < Token
|
262
|
+
def style; 'tI'; end
|
263
|
+
end
|
264
|
+
|
265
|
+
class TDatatype < Token
|
266
|
+
def style; 'tD'; end
|
267
|
+
end
|
268
|
+
|
269
|
+
class TString < Token
|
270
|
+
def style; 'tS'; end
|
271
|
+
end
|
272
|
+
|
273
|
+
class TDollarQuoteMarker < Token
|
274
|
+
def style; 'tM'; end
|
275
|
+
end
|
276
|
+
|
277
|
+
class TComment < Token
|
278
|
+
def style; 'tC'; end
|
279
|
+
end
|
280
|
+
|
281
|
+
class TLabel < Token
|
282
|
+
def style; 'tL'; end
|
283
|
+
end
|
284
|
+
|
285
|
+
# Text nodes have no children
|
286
|
+
class TextNode < NodeClass
|
287
|
+
def initialize(input, interval, elements = nil)
|
288
|
+
# prevent children from being assigned
|
289
|
+
super(input, interval, nil)
|
290
|
+
end
|
291
|
+
|
292
|
+
def terminal?
|
293
|
+
true
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
# Stub nodes have no children, or content
|
298
|
+
class StubNode < NodeClass
|
299
|
+
def initialize(input, interval, elements = nil)
|
300
|
+
# prevent children from being assigned
|
301
|
+
super(input, interval, nil)
|
302
|
+
end
|
303
|
+
|
304
|
+
def terminal?
|
305
|
+
true
|
306
|
+
end
|
307
|
+
|
308
|
+
def stub?
|
309
|
+
true
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
class NotImplemented < NodeClass
|
314
|
+
def parent=(object)
|
315
|
+
# this would go in the constructor, but parent is set from outside
|
316
|
+
raise Piggly::Parser::Failure, "Grammar does not implement #{object.source_text} at line #{input.line_of(object.interval.first)}"
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|