temill 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1155d75f0ef504687a7d27f1e32e5f78fba0bfec
4
+ data.tar.gz: 27230f848bea78c6b73c77a9447c4b423e84efd8
5
+ SHA512:
6
+ metadata.gz: 6c1b8272fab37933ca4dca81c021076b54f14eef52964627f3ce75a588288e755fba9791a4ed97a8cb8d42c5966c85c3c931c28b088e5553abaca7a5623066fc
7
+ data.tar.gz: 60e8050055d35c03a5e43c9ae75b4a72cabc27d3cac1fda9668a3af24def3eb9d699f1652afae4fff8d0c71e26b93395964f4bc1e34b8d610e7e5bab0dfa1525
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.13.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in temill.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Yusuke Takeuchi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # Temill
2
+
3
+ Temill shows objects to embedded comments in source code.
4
+
5
+ ## Sample
6
+ ```ruby
7
+ require 'temill'
8
+
9
+ Temill.show('Hello!, World!')
10
+
11
+ Temill.show <<-EOT
12
+ You can use
13
+ heredoc
14
+ EOT
15
+
16
+ 5.times{| i |
17
+ Temill.show(i ** 5)
18
+ }
19
+
20
+ Temill.show(
21
+ %w(More complex arguments).map{| s |
22
+ [s, s.upcase, s.downcase]
23
+ }
24
+ )
25
+
26
+ Temill.emit
27
+ ```
28
+ will output
29
+ ```ruby
30
+ #--------------------------------
31
+ #/path/to/source.rb
32
+ #--------------------------------
33
+ require 'temill'
34
+
35
+ Temill.show('Hello!, World!')
36
+ # temill showing 1 results for line 3 (line 3 in this output)
37
+ # "Hello!, World!"
38
+
39
+ Temill.show <<-EOT
40
+ You can use
41
+ heredoc
42
+ EOT
43
+ # temill showing 1 results for line 5 (line 7 in this output)
44
+ # " You can use\n heredoc\n"
45
+
46
+ 5.times{| i |
47
+ Temill.show(i ** 5)
48
+ # temill showing 5 results for line 11 (line 15 in this output)
49
+ # 0
50
+ # 1
51
+ # 32
52
+ # 243
53
+ # 1024
54
+ }
55
+
56
+ Temill.show(
57
+ %w(More complex arguments).map{| s |
58
+ [s, s.upcase, s.downcase]
59
+ }
60
+ )
61
+ # temill showing 1 results for line 14 (line 24 in this output)
62
+ # [["More", "MORE", "more"],
63
+ # ["complex", "COMPLEX", "complex"],
64
+ # ["arguments", "ARGUMENTS", "arguments"]]
65
+
66
+ Temill.emit
67
+ ```
68
+
69
+
70
+ ## Installation
71
+
72
+ Add this line to your application's Gemfile:
73
+
74
+ ```ruby
75
+ gem 'temill'
76
+ ```
77
+
78
+ And then execute:
79
+
80
+ $ bundle
81
+
82
+ Or install it yourself as:
83
+
84
+ $ gem install temill
85
+
86
+
87
+ ## Development
88
+
89
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
90
+
91
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
92
+
93
+ ## Contributing
94
+
95
+ Bug reports and pull requests are welcome on GitHub at https://github.com/YusukeTakeuchi/temill.
96
+
97
+
98
+ ## License
99
+
100
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
101
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "temill"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,305 @@
1
+ require 'pp'
2
+ require 'digest/sha2'
3
+ require 'awesome_print'
4
+
5
+ # most features are implemented in TemillImpl
6
+ # @see Temill::TemillImpl
7
+ class Temill
8
+
9
+ # @param [Hash] opts
10
+ # @option opts [String] :default_indent indent string used when indent guess fails
11
+ # (defaults to ' ')
12
+ # @option opts [Integer] :tabstop the number of spaces a tab counts for.
13
+ # make sense only if both spaces and tabs are used in a line
14
+ # (defaults to 4)
15
+ # @option opts [bool] :compact if true, show only lines that #show methods called are involved
16
+ # (defaults to false)
17
+ # @option opts [Symbol,#call] :inspect method to convert obj into String
18
+ # (defaults to :pretty_inspect)
19
+ def initialize(**opts)
20
+ _initialize(**opts)
21
+ end
22
+
23
+ EVAL_PATH_PREFIX = '(temill eval)/'
24
+
25
+ DefaultOptions = {
26
+ tabstop: 4,
27
+ default_indent: ' ',
28
+ compact: false,
29
+ inspect: :pretty_inspect
30
+ }
31
+
32
+ # Implementation of Temill:
33
+ # this module will be both included and extended to Temill
34
+ module TemillImpl
35
+ def _initialize(**options)
36
+ @options = DefaultOptions.merge(options)
37
+ reset
38
+ end
39
+
40
+ # clear all results added by #show
41
+ def reset
42
+ # absolute_path => SourceFile
43
+ @source_files = {}
44
+ self
45
+ end
46
+
47
+ # set options
48
+ # @see Temil#initialize
49
+ def set_options(**options)
50
+ @options.update(options)
51
+ self
52
+ end
53
+
54
+ # store values to be shown. You have to use emit to actually output the results.
55
+ # @overload show(val)
56
+ # @param [Object] val value to show
57
+ # @overload show(*vals)
58
+ # @param [Array<Object>] vals values to show
59
+ # @overload show(&block)
60
+ # show the result of block
61
+ # @return [Object] the object shown
62
+ def show(*vals, &block)
63
+ if not vals.empty? and block
64
+ raise ArgumentError, 'either value or block can be specified'
65
+ end
66
+
67
+ loc = caller_locations.first
68
+ path = loc.absolute_path
69
+
70
+ sf = (@source_files[path] ||= SourceFile.from_path(path, @options))
71
+
72
+ if block
73
+ obj = block.call
74
+ else
75
+ case vals.size
76
+ when 0
77
+ obj = nil
78
+ when 1
79
+ obj = vals.first
80
+ else
81
+ obj = vals
82
+ end
83
+ end
84
+ sf.add_result(loc, obj, block_given?)
85
+
86
+ obj
87
+ end
88
+
89
+ # same as Kernel.eval, but the evaluated code is handled as if
90
+ # it was an independent file.
91
+ # @param [String] src
92
+ # @param [Binding] bind
93
+ # @param [String] fname
94
+ def eval(src, bind=TOPLEVEL_BINDING, fname=default_eval_fname(caller_locations.first))
95
+ path = EVAL_PATH_PREFIX + Digest::SHA256.hexdigest(src) + '/' + fname
96
+ @source_files[path] ||= SourceFile.from_inline_source(src, path, @options)
97
+ Kernel.eval(src, bind, path)
98
+ end
99
+
100
+ private def default_eval_fname(loc)
101
+ "#{File.basename(loc.path)}:#{loc.lineno}:#{loc.base_label}"
102
+ end
103
+
104
+ # output results to stdout or any IO
105
+ # @param [IO] f IO to output to
106
+ def emit(f=$stdout)
107
+ execute_emitter(Emitters::StdoutEmitter.new(f, @options))
108
+ end
109
+
110
+ # output results to files in a directory.
111
+ # a file of original source code corresponds to a output file.
112
+ # @param [String] dir
113
+ def emit_to_directory(dir)
114
+ execute_emitter(Emitters::DirectoryEmitter.new(dir, @options))
115
+ end
116
+
117
+ # output results to strings.
118
+ # @return [Hash<String,String>] the keys are filenames and the values are the result for the file
119
+ def emit_to_string
120
+ execute_emitter(Emitters::StringEmitter.new(@options))
121
+ end
122
+
123
+ # @param [#call] emitter
124
+ def execute_emitter(emitter=nil, &block)
125
+ if emitter
126
+ emitter.call(@source_files.values, &block)
127
+ elsif block
128
+ block.call(@source_files.values)
129
+ else
130
+ raise ArgumentError, 'no emitter specified'
131
+ end
132
+ end
133
+
134
+ end
135
+
136
+ class SourceFile
137
+ attr_reader :path, :lines, :sexp
138
+ attr_reader :insertion_points
139
+
140
+ def initialize(src, path, options)
141
+ @path = path
142
+ @lines = [nil] + src.lines
143
+ @sexp = Ruby23Parser.new.parse(src, path).value
144
+ @options = options
145
+ @insertion_points = InsertionPointSet.new
146
+ end
147
+
148
+ def self.from_inline_source(src, virtual_path, options)
149
+ new(src, virtual_path, options)
150
+ end
151
+
152
+ def self.from_path(path, options)
153
+ new(File.read(path), path, options)
154
+ end
155
+
156
+ # @param [Thread::Backtrace::Location] caller_loc
157
+ # @param [Object] v value to show
158
+ # @param [bool] with_block whether Temill#show is called with block
159
+ def add_result(caller_loc, v, with_block)
160
+ caller_lineno = caller_loc.lineno
161
+ unless @insertion_points.at_caller_lineno(caller_lineno)
162
+ @insertion_points.add(make_insertion_point(caller_loc, with_block))
163
+ end
164
+ @insertion_points.at_caller_lineno(caller_lineno) << v
165
+ self
166
+ end
167
+
168
+ def each_source_line(&block)
169
+ return enum_for(__method__) unless block
170
+ 1.upto(@lines.size-1){| i |
171
+ block.call(@lines[i], i)
172
+ }
173
+ end
174
+
175
+ private
176
+
177
+ # @param [Thread::Backtrace::Location] caller_loc
178
+ def make_insertion_point(caller_loc, with_block=false)
179
+ caller_lineno = caller_loc.lineno
180
+ last_line = caller_lineno
181
+ inner_block_range = nil
182
+ #ParserUtils.add_line_ranges_to_sexp(@sexp) # DEBUG
183
+ @sexp.deep_each_with_self{| v |
184
+ if v.kind_of?(Sexp) and
185
+ [:iter, :call].include?(v.first) and
186
+ v.line_range.min == caller_lineno
187
+ last_line = [last_line, v.line_range.max].max
188
+ if with_block and v.first == :iter and block_sexp = v[3]
189
+ inner_block_range ||= block_sexp.line_range
190
+ if inner_block_range.max < block_sexp.line_range.max
191
+ inner_block_range = v[3].line_range
192
+ end
193
+ end
194
+ end
195
+ }
196
+
197
+ # We don't want to use the caller line to guess the indent level
198
+ # of the inner body of the block.
199
+ if inner_block_range
200
+ if inner_block_range.min == caller_lineno
201
+ if inner_block_range.max == caller_lineno
202
+ inner_block_range = nil
203
+ else
204
+ inner_block_range = (caller_lineno+1)..(inner_block_range.max)
205
+ end
206
+ end
207
+ end
208
+
209
+ if inner_block_range
210
+ emitter_lineno = inner_block_range.max
211
+ indent = guess_indent_in_block(inner_block_range)
212
+ elsif with_block and
213
+ end_line = @lines[last_line] and
214
+ /\A\s*(?:end|})\s*$/ =~ end_line
215
+ emitter_lineno = last_line - 1
216
+ indent = indent_at(last_line) + @options[:default_indent]
217
+ else
218
+ emitter_lineno = last_line
219
+ indent = indent_at(caller_lineno)
220
+ end
221
+
222
+ InsertionPoint.new(caller_loc, emitter_lineno, with_block, indent)
223
+ end
224
+
225
+ def indent_at(lineno)
226
+ @lines[lineno][/\A[ \t]*/]
227
+ end
228
+
229
+ def guess_indent_in_block(range)
230
+ @lines[range].map{| line |
231
+ line[/\A([ \t]*)\S/] && $1 # remove empty lines
232
+ }.compact.uniq.sort_by{| indent |
233
+ indent.gsub(/\t/, ' ' * @options[:tabstop]).size
234
+ }.first
235
+ end
236
+
237
+
238
+ # represent a set of insertion points in a single source file
239
+ class InsertionPointSet
240
+ include Enumerable
241
+
242
+ def initialize
243
+ @caller_lineno_to_ip = {}
244
+ @emitter_lineno_to_ips = {}
245
+ end
246
+
247
+ def add(ip)
248
+ if @caller_lineno_to_ip[ip.caller_lineno]
249
+ false
250
+ else
251
+ @caller_lineno_to_ip[ip.caller_lineno] = ip
252
+ ((@emitter_lineno_to_ips[ip.emitter_lineno] ||= []) << ip).sort_by!{| ips |
253
+ ips.caller_lineno
254
+ }
255
+ true
256
+ end
257
+ end
258
+
259
+ # @return [InsertionPoint]
260
+ # @return [nil]
261
+ def at_caller_lineno(lineno)
262
+ @caller_lineno_to_ip[lineno]
263
+ end
264
+
265
+ # @return [Array<InsertionPoint>]
266
+ # @return [nil]
267
+ def at_emitter_lineno(lineno)
268
+ @emitter_lineno_to_ips[lineno]
269
+ end
270
+
271
+ def each(&block)
272
+ @caller_lineno_to_ip.values.each(&block)
273
+ end
274
+ end
275
+
276
+ class InsertionPoint
277
+ attr_reader :caller_loc
278
+ attr_reader :caller_lineno, :emitter_lineno
279
+ attr_reader :indent
280
+ attr_reader :results
281
+
282
+ # @param [Thread::Backtrace::Location] caller_loc
283
+ # @param [Integer] emitter_lineno line number after where to emit results
284
+ # @param [bool] with_block whether called with block
285
+ # @param [String] indent
286
+ def initialize(caller_loc, emitter_lineno, with_block, indent)
287
+ @caller_location = caller_loc
288
+ @caller_lineno = caller_loc.lineno
289
+ @emitter_lineno = emitter_lineno
290
+ @with_block = with_block
291
+ @indent = indent
292
+ @results = []
293
+ end
294
+
295
+ def <<(result)
296
+ @results << result
297
+ end
298
+ end
299
+ end
300
+
301
+ extend TemillImpl
302
+ include TemillImpl
303
+
304
+ _initialize
305
+ end
@@ -0,0 +1,166 @@
1
+ require 'pathname'
2
+ require 'fileutils'
3
+
4
+ class Temill
5
+ module Emitters
6
+ class Base
7
+ def initialize(options)
8
+ @options = options
9
+ end
10
+
11
+ # @param [Hash<String, SourceFile>] source_files
12
+ def execute(source_files)
13
+ end
14
+
15
+ # @param [Array<SourceFile>] source_files
16
+ def call(source_files)
17
+ execute(source_files)
18
+ end
19
+
20
+ def emit_for_source_file(sf, out_f)
21
+ lines_map = [] # map source lineno to output lineno
22
+ compact_ranges = sf.insertion_points.map{| ip | ip.caller_lineno .. ip.emitter_lineno }
23
+ printer = Printer.new(out_f, **@options)
24
+ sf.each_source_line{| line,i |
25
+ lines_map[i] = printer.lineno
26
+ printer.print_raw(line) if not @options[:compact] or compact_ranges.any?{| r | r.cover?(i) }
27
+ if ips = sf.insertion_points.at_emitter_lineno(i)
28
+ ips.each{| ip |
29
+ printer.indent = ip.indent
30
+ printer.print_str(annotation(ip, lines_map[ip.caller_lineno]))
31
+ ip.results.each{| obj |
32
+ printer.print(obj)
33
+ }
34
+ }
35
+ end
36
+ }
37
+ end
38
+
39
+ def annotation(ip, output_line_for_caller)
40
+ "temill showing #{ip.results.size} results" +
41
+ " for line #{ip.caller_lineno}" +
42
+ " (line #{output_line_for_caller} in this output)"
43
+ end
44
+ end
45
+
46
+ class StdoutEmitter < Base
47
+ def initialize(io, options)
48
+ @io = io
49
+ super(options)
50
+ end
51
+
52
+ def execute(source_files)
53
+ source_files.each{| sf |
54
+ puts '#--------------------------------'
55
+ puts "\##{sf.path}"
56
+ puts '#--------------------------------'
57
+ emit_for_source_file(sf, @io)
58
+ }
59
+ nil
60
+ end
61
+ end
62
+
63
+ class StringEmitter < Base
64
+ def execute(source_files)
65
+ source_files.map{| sf |
66
+ sio = StringIO.new('', 'w')
67
+ emit_for_source_file(sf, sio)
68
+ [sf.path, sio.string]
69
+ }.to_h
70
+ end
71
+ end
72
+
73
+ class DirectoryEmitter < Base
74
+ def initialize(dir_path, options)
75
+ @dir_path = Pathname.new(dir_path)
76
+ super(options)
77
+ end
78
+
79
+ def execute(source_files)
80
+ FileUtils.makedirs(@dir_path)
81
+ written = []
82
+ source_files.each{| sf |
83
+ fname = output_fname(sf.path, written)
84
+ File.open(fname, 'w'){| f |
85
+ emit_for_source_file(sf, f)
86
+ written << fname
87
+ }
88
+ }
89
+ nil
90
+ end
91
+
92
+ def output_fname(base_fname, written)
93
+ fname_base = (@dir_path + File.basename(base_fname)).to_s
94
+ current_fname = fname_base
95
+ suffix_n = 1
96
+ while written.include?(current_fname)
97
+ current_fname = fname_base + ".#{suffix_n}"
98
+ suffix_n += 1
99
+ end
100
+ current_fname
101
+ end
102
+ end
103
+
104
+ class Printer
105
+ attr_reader :output_lines
106
+ attr_accessor :indent
107
+
108
+ def initialize(io = $stdout, **options)
109
+ @options = options
110
+ @output_lines = 0
111
+ @indent = ''
112
+ @io = io
113
+ end
114
+
115
+ def lineno
116
+ @output_lines + 1
117
+ end
118
+
119
+ def obj_to_s(obj)
120
+ case f = @options[:inspect]
121
+ when Symbol
122
+ obj.__send__(f)
123
+ when nil
124
+ obj.pretty_inspect
125
+ else
126
+ f.call(obj)
127
+ end
128
+ end
129
+
130
+ def print(obj)
131
+ print_str(obj_to_s(obj))
132
+ end
133
+
134
+ def print_str(str)
135
+ str.each_line{| line |
136
+ out @indent
137
+ out '# '
138
+ out_nl line
139
+ }
140
+ end
141
+
142
+ def print_empty_line
143
+ out @indent
144
+ out_nl '#'
145
+ end
146
+
147
+ def print_raw(str)
148
+ str.each_line{| line |
149
+ out_nl line
150
+ }
151
+ end
152
+
153
+ def out(str)
154
+ @io.print(str)
155
+ self
156
+ end
157
+
158
+ def out_nl(str=nil)
159
+ @io.puts(str)
160
+ @output_lines += 1
161
+ self
162
+ end
163
+ end
164
+
165
+ end
166
+ end
@@ -0,0 +1,140 @@
1
+ require 'ruby_parser'
2
+
3
+ class Temill
4
+ ParserResult = Struct.new(:value, :line_range, :sym)
5
+
6
+ # Override parser and scanner actions since we need the range of
7
+ # each expr in Ruby programs
8
+ module ParserHack
9
+ def before_reduce(val, *rest)
10
+ #ap val
11
+ val.map{| v |
12
+ if v.kind_of?(ParserResult)
13
+ v.value
14
+ else
15
+ v
16
+ end
17
+ }
18
+ end
19
+
20
+ def after_reduce(val, _values, result)
21
+ min = max = nil
22
+ #debug_print_state_stack
23
+ val.each{| v |
24
+ if v.kind_of?(ParserResult) and v.sym != :tNL
25
+ min = [min, v.line_range.min].compact.min
26
+ max = [max, v.line_range.max].compact.max
27
+ end
28
+ }
29
+
30
+ if result.kind_of?(Sexp)
31
+ result.line_range = min..max
32
+ end
33
+ # sym is not specified, but OK as long as it is used to check whether
34
+ # the symbol is newline or not.
35
+ ParserResult.new(result, min..max, nil)
36
+ end
37
+
38
+ # wrap the value of each token to ParserResult
39
+ def next_token
40
+ sym,val = super
41
+ lineno = lexer.lineno
42
+ [sym, ParserResult.new(val, lineno..lineno, sym)]
43
+ end
44
+
45
+ def debug_print_state_stack
46
+ if @racc_tstack
47
+ pp @racc_tstack.zip(@racc_vstack).map{| tok,v |
48
+ [token_to_str(tok), v]
49
+ }
50
+ end
51
+ end
52
+ end
53
+
54
+ module RaccHack
55
+
56
+ # override all actions and make them call before_reduce/after_reduce
57
+ # before/after each action is executed
58
+ def self.included(klass)
59
+ #racc_action_table,
60
+ #racc_action_check,
61
+ #racc_action_default,
62
+ #racc_action_pointer,
63
+ #racc_goto_table,
64
+ #racc_goto_check,
65
+ #racc_goto_default,
66
+ #racc_goto_pointer,
67
+ #racc_nt_base,
68
+ #racc_reduce_table,
69
+ #racc_token_table,
70
+ #racc_shift_n,
71
+ #racc_reduce_n,
72
+ #racc_use_result_var = klass::Racc_arg
73
+
74
+ racc_reduce_table = klass::Racc_arg[9]
75
+
76
+ racc_reduce_table.each_slice(3).map{| _,_,method_name |
77
+ method_name
78
+ }.uniq.each{| method_name |
79
+ begin
80
+ method = klass.instance_method(method_name)
81
+ rescue NameError
82
+ next
83
+ end
84
+ method.tap{| original_umethod |
85
+ klass.__send__(:define_method, method_name){| val,_values,result |
86
+ new_val = before_reduce(val, _values, result)
87
+ new_result = original_umethod.bind(self).call(new_val, _values, result)
88
+ after_reduce(val, _values, new_result)
89
+ }
90
+ }
91
+ }
92
+ end
93
+
94
+ # should return new val
95
+ def before_reduce(val, _values, result)
96
+ val
97
+ end
98
+
99
+ # should return new result
100
+ #
101
+ # Note that val is equal to the object passed to before_reduce,
102
+ # not new val returned by before_reduce
103
+ def after_reduce(val, _values, result)
104
+ result
105
+ end
106
+ end
107
+
108
+ module ParserUtils
109
+ module_function
110
+
111
+ def add_line_ranges_to_sexp(sexp)
112
+ if sexp.kind_of?(Sexp)
113
+ sexp.each{| elt |
114
+ add_line_ranges_to_sexp(elt)
115
+ }
116
+ sexp << sexp.line_range
117
+ end
118
+ sexp
119
+ end
120
+ end
121
+
122
+ # XXX: only Ruby23Parser is supported
123
+ class Ruby23Parser < ::Ruby23Parser
124
+ include RaccHack
125
+ prepend ParserHack
126
+ end
127
+ end
128
+
129
+ class Sexp
130
+ attr_accessor :line_range
131
+
132
+ # same as deep_each, but pass self first
133
+ def deep_each_with_self(&block)
134
+ return enum_for(__method__) unless block
135
+
136
+ block.call(self)
137
+ deep_each(&block)
138
+ end
139
+ end
140
+
@@ -0,0 +1,3 @@
1
+ class Temill
2
+ VERSION = "0.1.0"
3
+ end
data/lib/temill.rb ADDED
@@ -0,0 +1,18 @@
1
+ # FIXME:
2
+ # with cparser,
3
+ # Temill.show(
4
+ # 55
5
+ # )
6
+ # results in
7
+ # Temill.show(
8
+ # 55
9
+ # # temill showing 1 results for line 1 (line 1 in this output)
10
+ # # 55
11
+ # )
12
+ # ,which is not what we want.
13
+ Racc_No_Extensions = true
14
+
15
+ require "temill/version"
16
+ require 'temill/core'
17
+ require 'temill/parser'
18
+ require 'temill/emitter'
data/temill.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'temill/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "temill"
8
+ spec.version = Temill::VERSION
9
+ spec.authors = ["Yusuke Takeuchi"]
10
+ spec.email = ["v.takeuchi+gh@gmail.com"]
11
+
12
+ spec.summary = %q{Temill shows objects in embedded comments in source files.}
13
+ spec.description = spec.summary
14
+ spec.homepage = "https://github.com/YusukeTakeuchi/temill"
15
+ spec.license = "MIT"
16
+
17
+
18
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
+ f.match(%r{^(test|spec|features)/})
20
+ end
21
+ spec.bindir = "exe"
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ["lib"]
24
+
25
+ spec.required_ruby_version = '>= 2.3'
26
+
27
+ spec.add_dependency 'ruby_parser', '~> 3.8'
28
+
29
+ spec.add_development_dependency "bundler", "~> 1.13"
30
+ spec.add_development_dependency "rake", "~> 10.0"
31
+ spec.add_development_dependency "minitest", "~> 5.0"
32
+ spec.add_development_dependency 'awesome_print', '~> 1.7'
33
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: temill
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Yusuke Takeuchi
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-11-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ruby_parser
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.8'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.13'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.13'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: awesome_print
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.7'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.7'
83
+ description: Temill shows objects in embedded comments in source files.
84
+ email:
85
+ - v.takeuchi+gh@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".travis.yml"
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - bin/console
97
+ - bin/setup
98
+ - lib/temill.rb
99
+ - lib/temill/core.rb
100
+ - lib/temill/emitter.rb
101
+ - lib/temill/parser.rb
102
+ - lib/temill/version.rb
103
+ - temill.gemspec
104
+ homepage: https://github.com/YusukeTakeuchi/temill
105
+ licenses:
106
+ - MIT
107
+ metadata: {}
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '2.3'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.5.1
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: Temill shows objects in embedded comments in source files.
128
+ test_files: []