rockit 0.7.1

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 (57) hide show
  1. data/BUGS +13 -0
  2. data/LICENSE +280 -0
  3. data/README +172 -0
  4. data/TODO +53 -0
  5. data/VERSION +1 -0
  6. data/lib/packrat/grammar.rb +537 -0
  7. data/lib/rockit/prettyprint/box.rb +60 -0
  8. data/lib/rockit/prettyprint/renderer.rb +41 -0
  9. data/lib/rockit/prettyprint/text_renderer.rb +47 -0
  10. data/lib/rockit/tree/base.rb +223 -0
  11. data/lib/rockit/tree/enter_leave_visitor.rb +12 -0
  12. data/lib/rockit/tree/graphviz.rb +69 -0
  13. data/lib/rockit/tree/visitor.rb +12 -0
  14. data/lib/util/array_alternatives.rb +20 -0
  15. data/lib/util/enter_leave_visitor.rb +69 -0
  16. data/lib/util/graphviz_dot.rb +182 -0
  17. data/lib/util/string_location.rb +42 -0
  18. data/lib/util/visitor.rb +49 -0
  19. data/lib/util/visitor_combinators.rb +14 -0
  20. data/rakefile +200 -0
  21. data/tests/acceptance/packrat/minibasic/atest_minibasic.rb +45 -0
  22. data/tests/acceptance/packrat/minibasic/minibasic.rb +137 -0
  23. data/tests/acceptance/rockit/dparser/atest_any_operator.rb +33 -0
  24. data/tests/acceptance/rockit/dparser/atest_arithmetic_grammar.rb +30 -0
  25. data/tests/acceptance/rockit/dparser/atest_list_operator.rb +57 -0
  26. data/tests/acceptance/rockit/dparser/atest_mult_operator.rb +60 -0
  27. data/tests/acceptance/rockit/dparser/atest_operator_grammar.rb +61 -0
  28. data/tests/acceptance/rockit/dparser/atest_plus_operator.rb +55 -0
  29. data/tests/acceptance/rockit/dparser/atest_samples_calculator.rb +14 -0
  30. data/tests/acceptance/rockit/dparser/atest_samples_minibasic.rb +20 -0
  31. data/tests/acceptance/rockit/dparser/atest_samples_multifunccalculator.rb +36 -0
  32. data/tests/acceptance/rockit/dparser/atest_simple_grammar.rb +34 -0
  33. data/tests/acceptance/rockit/dparser/atest_speculative_code_action.rb +128 -0
  34. data/tests/acceptance/rockit/dparser/calc_tests_common.rb +103 -0
  35. data/tests/unit/packrat/test_interpreting_parser.rb +296 -0
  36. data/tests/unit/parse/utest_ebnf_grammar.rb +50 -0
  37. data/tests/unit/parse/utest_expand_grammar.rb +23 -0
  38. data/tests/unit/parse/utest_grammar.rb +160 -0
  39. data/tests/unit/rockit/assembler/llvm/utest_instructions.rb +41 -0
  40. data/tests/unit/rockit/assembler/llvm/utest_module.rb +19 -0
  41. data/tests/unit/rockit/prettyprint/utest_box.rb +44 -0
  42. data/tests/unit/rockit/tree/utest_tree_base.rb +301 -0
  43. data/tests/unit/rockit/tree/utest_tree_enter_leave_visitor.rb +69 -0
  44. data/tests/unit/rockit/tree/utest_tree_visitor.rb +63 -0
  45. data/tests/unit/rockit/utest_grammar.rb +145 -0
  46. data/tests/unit/rockit/utest_grammar_symbol.rb +11 -0
  47. data/tests/unit/rockit/utest_maybe_operator.rb +12 -0
  48. data/tests/unit/rockit/utest_regexp_terminal.rb +45 -0
  49. data/tests/unit/rockit/utest_repetition_operators.rb +35 -0
  50. data/tests/unit/rockit/utest_rule.rb +23 -0
  51. data/tests/unit/rockit/utest_string_terminal.rb +40 -0
  52. data/tests/unit/util/utest_array_alternatives.rb +23 -0
  53. data/tests/unit/util/utest_enter_leave_visitor.rb +89 -0
  54. data/tests/unit/util/utest_string_location.rb +42 -0
  55. data/tests/unit/util/utest_visitor.rb +92 -0
  56. data/tests/unit/util/utest_visitor_combinators.rb +64 -0
  57. metadata +112 -0
@@ -0,0 +1,537 @@
1
+ require 'strscan'
2
+
3
+ module Packrat; end
4
+
5
+ # A version of puts that limits output to 80 columns width
6
+ def lputs(str)
7
+ tabs = 0
8
+ len = (0...(str.length)).inject(0) do |l,i|
9
+ if str[i,1] == "\t"
10
+ tabs += 1
11
+ l + 8
12
+ else
13
+ l + 1
14
+ end
15
+ end
16
+ if len > 80
17
+ s = str[0,80-3-(tabs*8)] + "..."
18
+ else
19
+ s = str
20
+ end
21
+ puts s
22
+ end
23
+
24
+ class Regexp
25
+ def to_packrat_grammar_element
26
+ Packrat::RegexpLiteral.new(self)
27
+ end
28
+ end
29
+
30
+ class Symbol
31
+ def to_packrat_grammar_element
32
+ Packrat::RuleRef.new(self)
33
+ end
34
+ end
35
+
36
+ class String
37
+ def to_packrat_grammar_element
38
+ Packrat::StringLiteral.new(self)
39
+ end
40
+ end
41
+
42
+ class Packrat::GrammarElement
43
+ def to_packrat_grammar_element; self; end
44
+ # A GrammarElement is hidden if it does not produce a result that should
45
+ # be used in any way. This is mostly used for whitespace.
46
+ attr_accessor :hidden
47
+ end
48
+
49
+ class Packrat::RegexpLiteral < Packrat::GrammarElement
50
+ def initialize(re)
51
+ @re = re
52
+ end
53
+ def inspect; @re.inspect; end
54
+ end
55
+
56
+ # A StringLiteral works like a RegexpLiteral. The only reason we use
57
+ # a special class for it is so that we can inspect it in a more natural way
58
+ # (as a string instead of a Regexp).
59
+ class Packrat::StringLiteral < Packrat::RegexpLiteral
60
+ def initialize(str)
61
+ super(Regexp.new(Regexp.escape(str)))
62
+ @str = str
63
+ end
64
+ def inspect; @str.inspect; end
65
+ end
66
+
67
+ class Packrat::RuleRef < Packrat::GrammarElement
68
+ attr_reader :rule_name
69
+ def initialize(ruleName)
70
+ @rule_name = ruleName
71
+ end
72
+ def inspect; @rule_name.inspect; end
73
+ end
74
+
75
+ # A grammar Rule is a set of one or more Productions for the same
76
+ # (lhs) nonterminal. It makes an ordered choice between its productions
77
+ # by trying to parse with them in order.
78
+ class Packrat::Rule
79
+ attr_reader :name, :prods, :grammar
80
+ def initialize(name, prods = [])
81
+ @name, @prods = name, prods
82
+ end
83
+ def grammar=(grammar)
84
+ @grammar = grammar
85
+ @prods.each {|p| p.grammar = grammar}
86
+ end
87
+ def <<(prod)
88
+ @prods << prod
89
+ end
90
+ def inspect
91
+ s = "#{name.to_s} ->"
92
+ "\n" + s + " " +
93
+ @prods.map {|p| p.inspect(false)}.join("\n" +
94
+ " " * (s.length - 1) + "| ")
95
+ end
96
+ end
97
+
98
+ # A grammar Production is sequence of rhs elements describing how the lhs
99
+ # symbol should be parsed.
100
+ class Packrat::Production
101
+ attr_accessor :grammar
102
+ attr_reader :name, :rhs, :result_modifier
103
+ def initialize(name, rhs)
104
+ @name, @rhs = name, rhs
105
+ if Packrat::ResultModifier === rhs.last
106
+ @result_modifier = @rhs.pop
107
+ else
108
+ # Default modifier is to create a Sexpr with the production name
109
+ # as the head of the returned array.
110
+ @result_modifier = Packrat::SexprModifier.new(@name)
111
+ end
112
+ @rhs.map! {|e| e.to_packrat_grammar_element}
113
+ end
114
+ def inspect(withLhs = true)
115
+ rhs = @rhs.map {|e| e.inspect}.join(' ')
116
+ withLhs ? "#{name.to_s} -> " + rhs : rhs
117
+ end
118
+ end
119
+
120
+ # Report results of parsing a prod or grammar element
121
+ class Packrat::ErrorReporter < Packrat::GrammarElement
122
+ def initialize(sub)
123
+ @sub = sub
124
+ end
125
+ def parse(parser)
126
+ res = @sub.parse(parser)
127
+ if res == false
128
+ lputs "\t\t\t FAIL #{@sub.inspect}"
129
+ puts ""
130
+ else
131
+ lputs " Match #{@sub.inspect}"
132
+ puts ""
133
+ end
134
+ res
135
+ end
136
+ def method_missing(method, *args)
137
+ @sub.send(method, *args)
138
+ end
139
+ end
140
+
141
+ module Packrat::GrammarBuild
142
+ attr_reader :start
143
+ def start_symbol(name); @start = name; end
144
+ def rules; @rules ||= (Hash.new {|h,k| h[k] = Packrat::Rule.new(k)}); end
145
+ def rule(name, rhss)
146
+ rhss.each {|rhs| prod(name, rhs)}
147
+ end
148
+ def prod(name, rhs)
149
+ pr = Packrat::Production.new(name, rhs)
150
+ pr = Packrat::ErrorReporter.new(pr) if $DEBUG
151
+ rules[name] << pr
152
+ end
153
+ def [](name); @rules[name]; end
154
+ def start_rule; self[self.start]; end
155
+ def hidden(elem)
156
+ e = elem.to_packrat_grammar_element
157
+ e.hidden = true
158
+ e
159
+ end
160
+ # Finalize the building of the grammar by conducting postprocessing.
161
+ def finalize!
162
+ postprocess_set_grammar_on_rules
163
+ postprocess_create_ast_classes
164
+ end
165
+ def postprocess_set_grammar_on_rules
166
+ rules.values.each {|r| r.grammar = self}
167
+ end
168
+ def postprocess_create_ast_classes
169
+ each_prod do |p|
170
+ if Packrat::ASTBuilder === p.result_modifier
171
+ ast_class(p.result_modifier.name, p)
172
+ end
173
+ end
174
+ end
175
+ def each_prod
176
+ rules.values.each {|r| r.prods.each {|p| yield(p)}}
177
+ end
178
+ end
179
+
180
+ class Packrat::Grammar
181
+ extend Packrat::GrammarBuild
182
+
183
+ class <<self
184
+ def new(&grammarBuilder)
185
+ # We must name all the sub-classes so that its AST tree classes
186
+ # are named.
187
+ @num_grammars ||= 0
188
+ const_set("Grammar" + (@num_grammars += 1).to_s,
189
+ klass = Class.new(self))
190
+ # Add a module to hold the AST classes
191
+ klass.const_set("ASTs", Module.new)
192
+ klass.module_eval(&grammarBuilder)
193
+ klass
194
+ end
195
+
196
+ def interpreting_parser(klass = Packrat::InterpretingParser)
197
+ self.finalize!
198
+ klass.new_subclass(self)
199
+ end
200
+ end
201
+ end
202
+
203
+ class Packrat::Repeat < Packrat::GrammarElement
204
+ def initialize(subElement, minimumReps = 0, maximumReps = false)
205
+ @min, @max = minimumReps, maximumReps
206
+ @sub = subElement.to_packrat_grammar_element
207
+ end
208
+ def inspect
209
+ subi = @sub.inspect
210
+ return "mult(#{subi})" if @min == 0 && @max == false
211
+ return "plus(#{subi})" if @min == 1 && @max == false
212
+ return "rep(@min, @max, #{subi})"
213
+ end
214
+ end
215
+
216
+ module Packrat::GrammarBuild
217
+ def plus(element); Packrat::Repeat.new(element, 1, false); end
218
+ def mult(element); Packrat::Repeat.new(element, 0, false); end
219
+ def rep(min, max, element); Packrat::Repeat.new(element, min, max); end
220
+ end
221
+
222
+ class Packrat::Maybe < Packrat::GrammarElement
223
+ def initialize(sub)
224
+ @sub = sub.to_packrat_grammar_element
225
+ end
226
+ def parse(parser)
227
+ res = @sub.parse(parser)
228
+ return res if res
229
+ return nil
230
+ end
231
+ def inspect
232
+ "(#{@sub.inspect})?"
233
+ end
234
+ end
235
+
236
+ module Packrat::GrammarBuild
237
+ def maybe(element); Packrat::Maybe.new(element); end
238
+ end
239
+
240
+ # The last element of a prod can be a result modifier that modifies
241
+ # the result to be returned by the prod when parsing.
242
+ class Packrat::ResultModifier
243
+ end
244
+
245
+ # Create a Sexpr based on the name of the matched production and the
246
+ # result-array.
247
+ class Packrat::SexprModifier < Packrat::ResultModifier
248
+ def initialize(name)
249
+ @name = name
250
+ end
251
+ def modify_result(prod, result)
252
+ # Add the production name in the front
253
+ result.unshift @name
254
+ result
255
+ end
256
+ end
257
+
258
+ # Lift one of the sub-results as the result from parsing a production.
259
+ class Packrat::LiftModifier < Packrat::ResultModifier
260
+ def initialize(valueIndex, &block)
261
+ @value_index = valueIndex
262
+ @block = block
263
+ end
264
+ def modify_result(prod, result)
265
+ extracted_result = result[@value_index]
266
+ @block? @block.call(extracted_result) : extracted_result
267
+ end
268
+ end
269
+
270
+ module Packrat::GrammarBuild
271
+ def sexpr(name); Packrat::SexprModifier.new(name); end
272
+ def lift(index, &b); Packrat::LiftModifier.new(index, &b); end
273
+ end
274
+
275
+ module Packrat::GrammarBuild
276
+ # any() can be implemented in many ways but if all the sub-elements are
277
+ # strings we simply create a regexp matching any of them. If they are not
278
+ # all strings we add an internal rule with the alternatives as productions.
279
+ def any(*subs)
280
+ if subs.all? {|e| String === e}
281
+ re_string = subs.map {|s| "(" + Regexp.escape(s) + ")"}.join("|")
282
+ Packrat::RegexpLiteral.new(Regexp.new(re_string))
283
+ else
284
+ name = internal_rule_name()
285
+ rule(name, subs.map {|s| [s, lift(0)]})
286
+ Packrat::RuleRef.new(name)
287
+ end
288
+ end
289
+
290
+ def next_internal_rule_num
291
+ @internal_rule_counter ||= 0
292
+ @internal_rule_counter += 1
293
+ end
294
+
295
+ def internal_rule_name()
296
+ ("_r_" + next_internal_rule_num.to_s).intern
297
+ end
298
+ end
299
+
300
+ class Packrat::EOS < Packrat::GrammarElement
301
+ def parse(parser)
302
+ parser.eos? ? 0 : false
303
+ end
304
+ def inspect; "EOS"; end
305
+ end
306
+
307
+ module Packrat::GrammarBuild
308
+ def eos(); hidden(Packrat::EOS.new); end
309
+ end
310
+
311
+ class Packrat::ASTBuilder < Packrat::ResultModifier
312
+ attr_reader :name
313
+ def initialize(nodeName)
314
+ @name = nodeName
315
+ end
316
+ def modify_result(prod, result)
317
+ astklass = prod.grammar.ast_class(@name, prod)
318
+ astklass.new(*result)
319
+ end
320
+ end
321
+
322
+ module Packrat::GrammarBuild
323
+ def ast(name)
324
+ Packrat::ASTBuilder.new(name)
325
+ end
326
+
327
+ # Return the ast class with the given <nodeName> for the given <production>.
328
+ # If not previously created we create it and add it to the Tree module.
329
+ def ast_class(name, prod)
330
+ acn = ast_class_name(name)
331
+ begin
332
+ const_get("ASTs").const_get(acn)
333
+ rescue
334
+ const_get("ASTs").const_set(acn, make_ast_class(acn, prod))
335
+ end
336
+ end
337
+
338
+ def ast_class_name(name)
339
+ s = name.to_s
340
+ s[0,1].upcase + s[1..-1]
341
+ end
342
+
343
+ def make_ast_class(klassName, production)
344
+ Packrat::AST.new_subclass(klassName, production)
345
+ end
346
+ end
347
+
348
+ # Node in AST trees.
349
+ class Packrat::AST
350
+ class <<self
351
+ attr_accessor :sig
352
+ def new_subclass(name, production)
353
+ klass = Class.new(self)
354
+ klass.sig = extract_sig(production)
355
+ # Add accessor methods for all symbols in the sig
356
+ num_strings = 0
357
+ klass.sig.each_with_index do |sn, i|
358
+ if Symbol === sn
359
+ # We should subtract the num_strings in the index below
360
+ # if we optimize this so that non-named children are never
361
+ # added to the result array!
362
+ klass.module_eval %{
363
+ def #{sn.to_s}
364
+ @children[#{i}]
365
+ end
366
+ }
367
+ elsif String === sn
368
+ num_strings += 1
369
+ end
370
+ end
371
+ klass
372
+ end
373
+
374
+ # Return a sig for the given <production>. The sig has strings in the
375
+ # positions where the production rhs has a String or StringLiteral,
376
+ # has symbols in the positions where a rhs element refer to another
377
+ # production, and has nil in other positions.
378
+ def extract_sig(production)
379
+ production.rhs.map do |e|
380
+ case e
381
+ when String
382
+ e
383
+ when Packrat::StringLiteral
384
+ e.inspect # gives us the string itself
385
+ when Packrat::RuleRef
386
+ sub_element_name(e.rule_name)
387
+ else
388
+ nil # Expand this so that names are lifted out of Maybe, and "s" is added when plus and mult etc
389
+ end
390
+ end
391
+ end
392
+
393
+ def sub_element_name(name)
394
+ parts = name.to_s.split(/([A-Z][a-z0-9]*)/).select {|e| e.length > 0}
395
+ parts.map {|p| p.downcase}.join("_").intern
396
+ end
397
+
398
+ def [](*args); new(*args); end
399
+ end
400
+
401
+ def initialize(*children)
402
+ @children = children
403
+ end
404
+ attr_reader :children
405
+
406
+ def [](index); @children[index]; end
407
+
408
+ def ==(other)
409
+ self.class == other.class && @children == other.children
410
+ end
411
+
412
+ def inspect
413
+ self.class.inspect.split("::").last + "[" +
414
+ @children.map {|c| c.inspect}.join(", ") + "]"
415
+ end
416
+ end
417
+
418
+ class Packrat::InterpretingParser
419
+ class <<self
420
+ attr_accessor :grammar
421
+ def new_subclass(grammar)
422
+ klass = Class.new(self)
423
+ klass.grammar = grammar
424
+ klass
425
+ end
426
+ def parse_string(str)
427
+ # We always add a whitespace since StringScanner cannot match /\s*/
428
+ # (typically used as whitespace) at EOS
429
+ new(str + " ").parse_string
430
+ end
431
+ end
432
+
433
+ attr_reader :results, :grammar
434
+
435
+ def initialize(string)
436
+ @str = string
437
+ @s = StringScanner.new(string)
438
+ @grammar = self.class.grammar
439
+ end
440
+
441
+ def parse_string
442
+ @grammar.start_rule.parse(self)
443
+ end
444
+
445
+ # Get and Set current position in string.
446
+ def pos; @s.pos; end
447
+ def pos=(p); @s.pos = p; end
448
+
449
+ def eos?; @s.eos?; end
450
+
451
+ # Extract a lexeme of length <len> from the given <pos>.
452
+ def lexeme(pos, len)
453
+ @str[pos, len]
454
+ end
455
+
456
+ # Skip using <re> at the current position in the string. Returns nil
457
+ # if the re did not match or the length of the match if it matched.
458
+ def skip(re)
459
+ @s.skip(re)
460
+ end
461
+ end
462
+
463
+ class Packrat::ErrorLoggingInterpretingParser < Packrat::InterpretingParser
464
+ def skip(re)
465
+ oldpos = pos
466
+ r = super
467
+ if r
468
+ endp = pos - ((r > 0) ? 1 : 0)
469
+ puts "#{oldpos.to_s.rjust(3)} - #{endp.to_s.ljust(3)} #{lexeme(oldpos,r).inspect} #{re.inspect}"
470
+ else
471
+ puts "\t\t\tNOT #{re.inspect}"
472
+ end
473
+ r
474
+ end
475
+ end
476
+
477
+ class Packrat::RegexpLiteral
478
+ def parse(parser)
479
+ oldpos = parser.pos
480
+ len = parser.skip(@re)
481
+ len ? parser.lexeme(oldpos, len) : false
482
+ end
483
+ end
484
+
485
+ class Packrat::Production
486
+ def parse(parser)
487
+ result = []
488
+ @rhs.each do |e|
489
+ res = e.parse(parser)
490
+ if res == false
491
+ return false
492
+ else
493
+ result << res unless e.hidden
494
+ end
495
+ end
496
+ return @result_modifier.modify_result(self, result)
497
+ end
498
+ end
499
+
500
+ class Packrat::Rule
501
+ def parse(parser)
502
+ prods.each do |prod|
503
+ oldpos = parser.pos
504
+ res = prod.parse(parser)
505
+ return res if res
506
+ parser.pos = oldpos
507
+ end
508
+ return false
509
+ end
510
+ end
511
+
512
+ class Packrat::RuleRef
513
+ def parse(parser)
514
+ parser.grammar[@rule_name].parse(parser)
515
+ end
516
+ end
517
+
518
+ class Packrat::Repeat
519
+ def parse(parser)
520
+ result_list = []
521
+ oldpos = parser.pos
522
+ while (res = @sub.parse(parser))
523
+ result_list << res
524
+ end
525
+ if valid_result?(result_list)
526
+ return result_list
527
+ else
528
+ parser.pos = oldpos
529
+ return false
530
+ end
531
+ end
532
+ def valid_result?(list)
533
+ return false if @min && list.length < @min
534
+ return false if @max && list.length > @max
535
+ true
536
+ end
537
+ end
@@ -0,0 +1,60 @@
1
+ # A Formatting Language similar to BOX described in
2
+ # [vandenbrand1996a] and [dejonge2003a]
3
+ #
4
+ module Rockit
5
+ module PrettyPrint
6
+ module Box
7
+ # An element of the Box language
8
+ class Term
9
+ end
10
+
11
+ # H formats its elements horizontally with a given number of spaces
12
+ # in between.
13
+ class H < Term
14
+ attr_reader :num_hdelims, :elements
15
+
16
+ def initialize(numHorizontalDelims = 1, *elements)
17
+ @num_hdelims = numHdelims
18
+ @elements = elements
19
+ end
20
+ end
21
+
22
+ # H formats its elements horizontally with a given number of spaces
23
+ # in between.
24
+ class V < Term
25
+ attr_reader :elements
26
+
27
+ def initialize(*elements)
28
+ @elements = elements
29
+ end
30
+ end
31
+
32
+ # Epsilon is a H operator with no spaces in between
33
+ class Epsilon < H
34
+ def initialize(*elements)
35
+ super(0, *elements)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ require 'rockit/prettyprint/text_renderer'
43
+
44
+ # We must do the def's below after requiring the text_renderer
45
+ # since there is a circular dependency between the definitions.
46
+ module Rockit
47
+ module PrettyPrint
48
+ module Box
49
+ class Term
50
+ def render
51
+ render_on(Box::TextRenderer.new)
52
+ end
53
+
54
+ def render_on(renderer)
55
+ renderer.render(self)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,41 @@
1
+ # Renderers for Box
2
+ #
3
+ module Rockit
4
+ module PrettyPrint
5
+ module Box
6
+ class Renderer
7
+ def reset_buffer
8
+ @buffer = ""
9
+ end
10
+
11
+ def string_buffer
12
+ @buffer
13
+ end
14
+
15
+ def render(boxTree)
16
+ reset_buffer
17
+ render_term(boxTree)
18
+ string_buffer
19
+ end
20
+
21
+ def <<(string)
22
+ @buffer << string
23
+ end
24
+
25
+ def render_term(boxTree)
26
+ raise NotImplementedError
27
+ end
28
+
29
+ def render_hdelim(num)
30
+ render_term(horizontal_delim * num)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ class String
38
+ def render_as_text(renderer)
39
+ renderer << self
40
+ end
41
+ end
@@ -0,0 +1,47 @@
1
+ # Basic Text Renderer for Box
2
+ #
3
+ require 'rockit/prettyprint/renderer'
4
+
5
+ module Rockit
6
+ module PrettyPrint
7
+ module Box
8
+ class TextRenderer < Renderer
9
+ attr_reader :max_horizontal
10
+ attr_reader :horizontal_delim, :vertical_delim
11
+
12
+ def initialize(maxHorizontal = 80, horizontalDelim = " ",
13
+ verticalDelim = "\n")
14
+ @max_horizontal = maxHorizontal
15
+ @horizontal_delim = horizontalDelim
16
+ @vertical_delim = verticalDelim
17
+ end
18
+
19
+ def render_term(term)
20
+ # Instead of fiddling with visitors we simply add a uniquely named
21
+ # method render_as_text to each Box term we support.
22
+ term.render_as_text(self)
23
+ end
24
+ end
25
+
26
+ class H < Term
27
+ def render_as_text(renderer)
28
+ elements[0..-2].each do |e|
29
+ renderer.render_term(e)
30
+ renderer.render_hdelim(num_spaces)
31
+ end
32
+ renderer.render_term(elements[-1])
33
+ end
34
+ end
35
+
36
+ class V < Term
37
+ def render_as_text(renderer)
38
+ elements[0..-2].each do |e|
39
+ renderer.render_term(e)
40
+ renderer << "\n"
41
+ end
42
+ renderer.render_term(elements[-1])
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end