[#796] Re: value of assignment (Re: Order of the value of an expression changed? (PR#579)) — Sean Chittenden <sean@...>

> sean@chittenden.org wrote:

33 messages 2003/02/06
[#798] Re: value of assignment (Re: Order of the value of an expression changed? (PR#579)) — matz@... (Yukihiro Matsumoto) 2003/02/06

Hi,

[#826] Re: value of assignment (Re: Order of the value of an expression changed? (PR#579)) — Sean Chittenden <sean@...> 2003/02/10

> |I have read the thread and I think this is a pretty bad change. I

[#827] Re: value of assignment (Re: Order of the value of an expression changed? (PR#579)) — nobu.nokada@... 2003/02/10

Hi,

[#828] Re: value of assignment (Re: Order of the value of an expression changed? (PR#579)) — Sean Chittenden <sean@...> 2003/02/11

> > #BEGIN test.rb

[#829] Re: value of assignment (Re: Order of the value of an expression changed? (PR#579)) — matz@... (Yukihiro Matsumoto) 2003/02/11

Hi,

[#830] Re: value of assignment (Re: Order of the value of an expression changed? (PR#579)) — Sean Chittenden <sean@...> 2003/02/11

> |What was wrong with having the receiver set the return value though?

[#834] Re: value of assignment (Re: Order of the value of an expression changed? (PR#579)) — Matt Armstrong <matt@...> 2003/02/11

Sean Chittenden <sean@chittenden.org> writes:

[#835] Re: value of assignment (Re: Order of the value of an expression changed? (PR#579)) — Sean Chittenden <sean@...> 2003/02/11

> > f = Foo.new()

[#801] class of $1, $2 in 1.8.0 — dblack@...

Hi --

31 messages 2003/02/07
[#802] Re: class of $1, $2 in 1.8.0 — nobu.nokada@... 2003/02/07

Hi,

[#803] Re: class of $1, $2 in 1.8.0 — dblack@... 2003/02/07

Hi --

[#804] Re: class of $1, $2 in 1.8.0 — matz@... (Yukihiro Matsumoto) 2003/02/07

Hi,

[#805] Re: class of $1, $2 in 1.8.0 — dblack@... 2003/02/07

Hi --

[#806] Re: class of $1, $2 in 1.8.0 — "J.Herre" <jlst@...> 2003/02/07

[#807] Re: class of $1, $2 in 1.8.0 — Matt Armstrong <matt@...> 2003/02/07

J.Herre <jlst@gettysgroup.com> writes:

[#808] Re: class of $1, $2 in 1.8.0 — dblack@... 2003/02/07

Hi --

[#809] Re: class of $1, $2 in 1.8.0 — Ryan Pavlik <rpav@...> 2003/02/07

On Sat, 8 Feb 2003 06:52:17 +0900

[#810] Re: class of $1, $2 in 1.8.0 — dblack@... 2003/02/07

Hi --

[#889] Bob Jenkins' hashing implementation in Ruby — Mauricio Fern疣dez <batsman.geo@...>

16 messages 2003/02/28
[#892] Re: Bob Jenkins' hashing implementation in Ruby — ts <decoux@...> 2003/03/01

>>>>> "M" == Mauricio Fern疣dez <Mauricio> writes:

[#893] Re: Bob Jenkins' hashing implementation in Ruby — Mauricio Fern疣dez <batsman.geo@...> 2003/03/01

On Sat, Mar 01, 2003 at 08:42:40PM +0900, ts wrote:

RCR: Add __file__ and __line__ methods to Method and Proc classes

From: Brent Roman <brent@...>
Date: 2003-02-21 20:03:43 UTC
List: ruby-core #872
Please consider the attached patch to
the eval.c supplied with Ruby 1.6.8.

This patch adds instance methods __file__ and __line__ to the
built-in classes Method and Proc.  These methods are analogous
to the reserved words __FILE__ and __LINE__

The reserved words __FILE__ and __LINE__ cannot be used
to extract the source location of a previously parsed block of
ruby code, because the parser does a literal substitution of the String
or FixNum, respectively.  So, at least in Ruby 1.6.8, Matz's suggested
technique of evaluating __FILE__ and __LINE__ in the binding
of the block does not work.  Please see:

http://www.ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-talk/21555

--------
Aside:
I'd be very interested in hearing about alternative approaches to this
problem.  Has it already been addressed beta test versions of ruby?
Is there any reason why a similar approach shouldn't be taken for
later versions?
--------

On top of these methods for source reflection, I've written a simple
ruby class called SourceRef that manages "Source References".  I've
attached sourceref.rb just as an example of what's possible and hopefully
to motivate folks to accept the patch because it's just plain useful and
doesn't impact performance or memory footprint.

I've also modified the rbbr package to display the source to ruby methods
instead of a useless (no document) message.  And, I've added to rbbr
menu items to view, edit and reload the source file.
The combination of irb, rbbr, nedit (or some similar editor) and
my simple hacks actually become a lightweight, modular ruby IDE!

If this patch is approved -- or someone can show me how to write the
attached sourceref.rb without resorting to a patch to eval.c! -- I will
work with the owners of the irb and rbbr packages to incorporate
my (tiny) changes.

None of this is "rocket science", but I think it helps make Ruby
more accessible to a larger audience.

-- 
 Brent Roman                                   MBARI
 Software Engineer               Tel: (831) 775-1808
 7700 Sandholdt Road,         Moss Landing, CA 95039
 mailto:brent@mbari.org  http://www.mbari.org/~brent

Attachments (2)

eval-1.6.8.patch (2.27 KB, text/x-diff)
--- eval.c	2003/02/11 20:56:01	1.1
+++ eval.c	2003/02/20 08:21:42	1.3
@@ -3,7 +3,7 @@
   eval.c -
 
   $Author: brent $
-  $Date: 2003/02/11 20:56:01 $
+  $Date: 2003/02/20 08:21:42 $
   created at: Thu Jun 10 14:22:17 JST 1993
 
   Copyright (C) 1993-2001 Yukihiro Matsumoto
@@ -6971,6 +6971,54 @@
     return body;
 }
 
+/* NK: added */
+
+static VALUE
+method_source_file_name(VALUE method)
+{
+    struct METHOD *data;
+    const char *filename;
+
+    Data_Get_Struct(method, struct METHOD, data);
+    filename = data->body->nd_file;
+    return rb_str_new2( filename == NULL ? "" : filename );
+}
+
+static VALUE
+method_source_line(VALUE method)
+{
+    struct METHOD *data;
+
+    Data_Get_Struct(method, struct METHOD, data);
+    return INT2FIX( nd_line(data->body) );
+}
+
+/* NK: end */
+
+/* BAR: added */
+
+static VALUE
+proc_source_file_name(VALUE block)
+{
+    struct BLOCK *data;
+    const char *filename;
+
+    Data_Get_Struct(block, struct BLOCK, data);
+    filename = data->body->nd_file;
+    return rb_str_new2( filename == NULL ? "" : filename );
+}
+
+static VALUE
+proc_source_line(VALUE block)
+{
+    struct BLOCK *data;
+
+    Data_Get_Struct(block, struct BLOCK, data);
+    return INT2FIX( nd_line(data->body) );
+}
+
+/* BAR: end */
+
 void
 Init_Proc()
 {
@@ -6985,6 +7033,8 @@
     rb_define_method(rb_cProc, "[]", proc_call, -2);
     rb_define_method(rb_cProc, "==", proc_eq, 1);
     rb_define_method(rb_cProc, "to_s", proc_to_s, 0);
+    rb_define_method(rb_cProc, "__file__", proc_source_file_name, 0); /* BAR */
+    rb_define_method(rb_cProc, "__line__", proc_source_line, 0); /* BAR */    
     rb_define_global_function("proc", rb_f_lambda, 0);
     rb_define_global_function("lambda", rb_f_lambda, 0);
     rb_define_global_function("binding", rb_f_binding, 0);
@@ -7002,6 +7052,8 @@
     rb_define_method(rb_cMethod, "to_s", method_inspect, 0);
     rb_define_method(rb_cMethod, "to_proc", method_proc, 0);
     rb_define_method(rb_cMethod, "unbind", method_unbind, 0);
+    rb_define_method(rb_cMethod, "__file__", method_source_file_name, 0); /* NK */
+    rb_define_method(rb_cMethod, "__line__", method_source_line, 0); /* NK */    
     rb_define_method(rb_mKernel, "method", rb_obj_method, 1);
 
     rb_cUnboundMethod = rb_define_class("UnboundMethod", rb_cMethod);
sourceref.rb (9.37 KB, text/x-ruby)
##################  sourceref.rb -- brent@mbari.org  #####################
# $Source: /home/cvs/ESP/gen2/software/ruby/sourceref.rb,v $
# $Id: sourceref.rb,v 1.15 2003/02/21 03:38:34 brent Exp $
#
#  The SourceRef class relies on a patched version of ruby/eval.c
#  to provide access to the source file and line # where every Method
#  or Proc is defined.  SourceRef is a container for these and implements
#  methods to:
#
#    list source code to stdout,
#    view source code in an editor,
#    edit source code
#    reload source code
#
#  This file also adds methods to Object and Module to list, view, 
#  edit, and reload methods and modules as a convenience.
#
#  Examples:
#     SourceRef.instance_method (:list).source  ==> ./sourceref.rb:47
#     SourceRef.instance_method (:list).edit    ==>  opens an editor window
#     (SourceRef/:list).edit                    ==>  opens same window
#     SourceRef.source[:list].view              ==>  opens same window r/o
#     Date.edit    ==> edits all files that define methods in class Date
#     Date.source  ==> returns hash of Date methods to SourceRefs
#     puts Date.source.join  ==> display Data methods with SourceRefs
#     Date.sources ==> returns the list of files containing Date methods
#     Date.reload  ==> (re-)loads Date.sources
#
#   With a version of irb.rb modified to store the last backtrace,
#   the following features are available at the irb prompt:
#
#     list|edit|view|reload  Module|Method|Integer|Symbol|String
#
#   Module operates on the list of files that comprise the Module
#   Method operates on the text of the Method
#   Integer n operates on the most recent backtrace at level n
#   Symbol :sym searches backtrace for level corresponding to :sym
#   String fn:nnn operates on file "fn" at line nnn
#   no argument is equivalent to backtrace level 0
#
############################################################################

      
class SourceRef   #combines source file name and line number

  def initialize (file_name, line=0)
    @file = file_name
    @line = line
  end

  attr_reader :file, :line
  
  def to_s
    return file unless line > 0
    file+':'+line.to_s
  end
  alias_method :inspect, :to_s
  
  def to_srcRef
    self
  end
  
  def list (lineCount=16, lineOffset=0)
  # return the next lineCount lines of source text
  # or "" if no source is available
    text = ""; lineno=0
    begin
      File.open (file) {|f|
        2.upto(line+lineOffset) { f.readline; lineno+=1 }
        1.upto(lineCount) { text += f.readline; lineno+=1 }
      }
    rescue EOFError  # don't sweat EOF unless its before target line #
      if lineno < line
        raise $!,"--> Truncated ruby source file: #{self}"
      end
    rescue
      raise $!,"--> Missing ruby source: #{self}"
    end
    text
  end
  
  def edit (options=nil, readonly=false)
  # start an editor session on file at line
  # If X-windows display available, try nedit client, then nedit directly
    if ENV["DISPLAY"]
      neditArgs = "-lm Ruby #{options} '#{file}'"
      neditArgs = "-line #{line} " + neditArgs if line > 0
      neditArgs = "-read " + neditArgs if readonly
      # nclient will normally be a symlink to /usr/bin/X11/nc
      return self if 
        system ("nclient -noask -svrname ruby #{neditArgs} 2>/dev/null") || 
        system ("nedit #{neditArgs} &")
    end
  # if all else fails, fall back on the venerable 'vi'
    system ("vi #{"-R " if readonly} + #{file}")
    self
  end
  
  def view (options=nil)
  # start a read-only editor session on file at line
    edit (options, true)
  end
  
  def reload
  # load file referenced by receiver
    begin
      load file
    rescue LoadError 
      raise $!, "--> Missing ruby source fle: #{file}" 
    end
  end
  

  def SourceRef.find_in_back_trace (trace, symbol)
  # return first element in trace containing symbol
  # returns nil if no such stack level found
    for msg in trace
      if inPart = msg.split(':',4)[2]
        name = inPart.split('in\s*', 2)[1][1...-1]
        return msg if symbol == name.intern
      end
    end
    return nil
  end
   
  def SourceRef.from_back_trace (trace, level=0)
  # return sourceref at level in backtace
  #  or return level if no such level found
    traceLvl=level.kind_of?(Symbol) ?
                SourceRef.find_in_back_trace (trace, level) : trace[level]
    return level if traceLvl==nil
    possibleIRBprefix, traceLvl = traceLvl.split(' ', 3)
    traceLvl = possibleIRBprefix unless possibleIRBprefix == "from"
    traceLvl.split(':',3)[0..1].join(':').to_srcRef
  end


  module Code #for objects supporting __file__ & __line__

    def source
      SourceRef.new (__file__, __line__)
    end
    alias_method :to_srcRef, :source
    
    # can't use define_method because in ruby 1.6 self would be SourceRef::Code
    (OPS = [ :list, :edit, :view, :reload ]).each {|m|
      eval "def #{m}; source.#{m}; end"
    }
    
  end #module SourceRef::Code

  
  module CommandBundle   #add convenient commands for viewing source code
    private
    
    Code::OPS.each {|m|
      define_method (m) { |*args|
        src = args.length==0 ? IRB.conf[:exception] : args.shift
        #convert src to an appropriate SourceRef by whatever means possible
        #Modified irb.rb saves last back_trace & exception in IRB.conf
        if src.kind_of?(Module)
          src.sources.each {|srcRef| srcRef.method(m).call (*args)}
        else
          if src.kind_of?(Integer) || src.kind_of?(Symbol) 
            #assume parameter is a backtrace level or method name
            src = SourceRef.from_back_trace(IRB.conf[:exception].backtrace, src)
          end
          if src.respond_to? :to_srcRef
            src.to_srcRef.method(m).call (*args)
          else
            print "No source file corresponds to ",src.type,':',src.inspect,"\n"
          end
        end
      }
    }

    def startRBBR  #start another Ruby Class Browser
      require 'rbbr'
      (rbbrThread=Thread.new {RBBR.main}).priority=Thread.current.priority+1
      rbbrThread    
    end
    
  end  

end #class SourceRef


#mix source code manipulation utilites into the appropriate classes
class Proc; include SourceRef::Code; end
class Method; include SourceRef::Code; end
class Object; include SourceRef::CommandBundle; end


class Symbol
  def intern  #just for mathematical completeness!
    self
  end
end

class Hash
  def join (sep = " => ")
  # most useful for displaying hashes with puts hsh.join
    strAry = []
    each {|key,value| strAry << key.inspect+sep+value.to_s}
    strAry
  end
end

class String
  def to_srcRef
  # parse a source reference from string of form fn:line#
    strip!
    a = split(':')
    return SourceRef.new (self, 0) if a.length < 2
    SourceRef.new (a[0..-2].join(':'), a[-1].to_i)
  end
end

class Exception
  def to_srcRef (level=0)
  # default given any exception the best guess as to its location
    SourceRef.from_back_trace(backtrace, level)
  end
end

class SyntaxError < ScriptError
  # Decode the Source Ref from the compiler error message
  def to_srcRef (level=nil)
    return super.to_srcRef level if level
    to_s.split("\s",2)[0].to_srcRef
  end
end

  
class Module

  def sourceHash (methodType, methodNameArray)
  # private method to build a hash of methodNames to sourceRefs
  # exclude methods for which no ruby source is available
    h = {};
    methodGetter = method(methodType)
    methodNameArray.each {|m|
      m = m.intern
      src = methodGetter[m].source
      h[m] = src if src.line > 0 && src.file != "(eval)"
    }
    h
  end
  private :sourceHash    

  def singleton_source
  # return hash on receiver's singleton methods to corresponding SourceRefs
    sourceHash (:method, singleton_methods)
  end
  
  def instance_source (includeAncestors=false)
  # return hash on receiver's instance methods to corresponding SourceRefs
  #        optionally include accessible methods in ancestor classes
    sourceHash (:instance_method,
                private_instance_methods+
                protected_instance_methods(includeAncestors)+
                public_instance_methods(includeAncestors))
  end
  
  def source (*args)
  # return hash on receiver's methods to corresponding SourceRefs
  # note that instance_methods will overwrite singletons of the same name
    singleton_source.update(instance_source(*args))
  end
  
  def % (method_name)
  # return singleton method named method_name in module
  # Use / below unless method_name is also an instance method
    method method_name
  end
    
  def / (method_name)
  # return method named method_name in module
    begin
      return instance_method method_name
    rescue NameError
    end
    method method_name
  end
    
  def sources (*args)
  # return array of unique source file names for all receiver's methods
    (singleton_source.values+instance_source(*args).values).collect{|s|
       s.file
    }.uniq.collect{|fn| SourceRef.new(fn)}    
  end
    
  def reload (*args)
  # load all source files that define receiver's methods
    sources(*args).each {|s| s.reload}
  end
  
  def edit (*args)
  # start editor sessions on all files that define receiver's methods
    sources(*args).each{|srcFile| srcFile.edit (*args)}
  end

  def view (*args)
  # start read-only editor sessions on files containing receiver's methods
    sources(*args).each{|srcFile| srcFile.view (*args)}
  end
  
  def list (*args)
  # return first few lines of all files containing self.methods
    result=""
    sources.each{|srcFile| result+=srcFile.list (*args)}
    result
  end

end

In This Thread

Prev Next