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.
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,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