temill 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []