--------------010506000505070401000702 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Michael Neumann wrote: > Would be really great if that works. > > trace { x + y } # x+y > assert { x + y 0 } # assertion 'x+y ' failed... It is possible with proc_source, but that's quite ugly. --------------010506000505070401000702 Content-Type: text/plain; name roc_source.rb" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename roc_source.rb" require 'stringio' require 'irb/ruby-lex' # Tell the ruby interpreter to load code lines of required files # into this filename -> lines Hash. This behaviour seems to be # very undocumented and therefore shouldn't really be relied on. SCRIPT_LINES__ } unless defined? SCRIPT_LINES__ module ProcSource def get_lines(filename, start_line ) case filename # special "(irb)" descriptor? when "(irb)" IRB.conf[:MAIN_CONTEXT].io.line(start_line .. -1) # special "(eval...)" descriptor? when /^\(eval.+\)$/ EVAL_LINES__[filename][start_line .. -1] # regular file else # Ruby already parsed this file? (see disclaimer above) if lines CRIPT_LINES__[filename] lines[(start_line - 1) .. -1] # If the file exists we're going to try reading it in elsif File.exist?(filename) begin File.readlines(filename)[(start_line - 1) .. -1] rescue nil end end end end def handle(proc) filename, line roc.source_descriptor lines et_lines(filename, line) || [] lexer ubyLex.new lexer.set_input(StringIO.new(lines.join)) state before_constructor nesting_level start_token, end_token il, nil found alse while token exer.token puts "token: #{token.inspect}", "state: #{state.inspect}" if $DEBUG # we've not yet found any proc-constructor -- we'll try to find one. if [:before_constructor, :check_more].include?(state) # checking more and newline? -> done if token.is_a?(RubyToken::TkNL) and state :check_more state done break end # token is Proc? if token.is_a?(RubyToken::TkCONSTANT) and token.instance_variable_get(:@name) "Proc" # method call? if lexer.token.is_a?(RubyToken::TkDOT) method exer.token # constructor? if method.is_a?(RubyToken::TkIDENTIFIER) and method.instance_variable_get(:@name) "new" unless state :check_more # okay, code will follow soon. state before_code else # multiple procs on one line return end end end # token is lambda or proc call? elsif token.is_a?(RubyToken::TkIDENTIFIER) and %w{proc lambda}.include?(token.instance_variable_get(:@name)) unless state :check_more # okay, code will follow soon. state before_code else # multiple procs on one line return end end # we're waiting for the code start to appear. elsif state :before_code if token.is_a?(RubyToken::TkfLBRACE) or token.is_a?(RubyToken::TkDO) # found the code start, update state and remember current token state in_code start_token oken end # okay, we're inside code elsif state :in_code if token.is_a?(RubyToken::TkRBRACE) or token.is_a?(RubyToken::TkEND) nesting_level - if nesting_level 0 # we're done! end_token oken # parse another time to check if there are multiple procs on one line # we can't handle that case correctly so we return no source code at all state check_more end elsif token.is_a?(RubyToken::TkfLBRACE) or token.is_a?(RubyToken::TkDO) or token.is_a?(RubyToken::TkBEGIN) or token.is_a?(RubyToken::TkCASE) or token.is_a?(RubyToken::TkCLASS) or token.is_a?(RubyToken::TkDEF) or token.is_a?(RubyToken::TkFOR) or token.is_a?(RubyToken::TkIF) or token.is_a?(RubyToken::TkMODULE) or token.is_a?(RubyToken::TkUNLESS) or token.is_a?(RubyToken::TkUNTIL) or token.is_a?(RubyToken::TkWHILE) or token.is_a?(RubyToken::TklBEGIN) nesting_level + end end end if start_token and end_token start_line, end_line tart_token.line_no - 1, end_token.line_no - 1 source ines[start_line .. end_line] start_offset tart_token.char_no start_offset + if start_token.is_a?(RubyToken::TkDO) end_offset (source.last.length - end_token.char_no) source.first.slice!(0 .. start_offset) source.last.slice!(end_offset .. -1) p [start_token, end_token, source] if $DEBUG # Can't use .strip because newline at end of code might be important # (Stuff would break when somebody does proc { ... #foo\n}) proc.source ource.join.gsub(/^ | $/, "") end end module_function :handle, :get_lines end class Proc def source_descriptor if md ^#<Proc:0x[0-9A-Fa-f]+@(.+):(\d+)>$/.match(old_inspect) filename, line d.captures return filename, line.to_i end end attr_accessor :source def source ProcSource.handle(self) unless @source @source end alias :old_inspect :inspect def inspect if source "proc {#{source}}" else old_inspect end end def other) if self.source and other.source self.source other.source else self.id other.id end end def _dump(depth ) if source source else raise(TypeError, "Can't serialize Proc with unknown source code.") end end def to_yaml(*args) self.source # force @source to be set super.sub("object:Proc", "proc") end def self.allocate; from_string ""; end def self.from_string(string) result val("proc {#{string}}") result.source tring return result end def self._load(code) self.from_string(code) end def self.marshal_load; end def marshal_load; end end if defined?(YAML) YAML.add_ruby_type(/^proc/) { |type, val| Proc.from_string(val["source"]) } end EVAL_LINES__ ash.new alias :old_eval :eval def eval(code, *args) context, descriptor, start_line, *more args descriptor || (eval#{code.hash})" start_line || lines || ode.grep(/.*/) EVAL_LINES__[descriptor] || rray.new EVAL_LINES__[descriptor][start_line, lines.length] ines old_eval(code, context, descriptor, start_line, *more) end # This uses .inspect instead of the original Hash dumping method. # #class Hash # def _dump(depth) # Marshal.dump [self.inspect, default_proc] # end # # def self._load(data) # hash, default_proc arshal.load(data) # hash val(hash) # Hash.new(&default_proc).merge(hash) # end #end --------------010506000505070401000702--