How to adopt "Python Style" indentation for Ruby

G

Giles Bowkett

Obviously based on the earlier thread. I think this is actually an
utterly terrible idea. Haven't you heard that song by Loverboy? "Pig
and elephant DNA just don't mix"? But I think the **implementation**
would be fascinating.

What would it take to write a Ruby pre-processor which parsed Ruby
written in a Pythonic style and turned it into actual valid Ruby? Or
even a Ruby library like Xavier's Perl example, which allows you to
code Ruby as if it were Python after just requiring the library? The
original post's main argument was that indentation is a stable,
consistent shorthand for Ruby's end statements. If all you're looking
to do is use tabs as syntactic sugar, then it should be pretty easy to
just write something which knows how to translate a sequence of tabs
in and tabs out into a series of end statements at the end.

Is this only doable in Perl, because Perl has source filtering? Would
you have to encase the Pythonic "Ruby" in a string and then run that
string through a processor after the fact, or could it "just work"
without any obvious prep on the user's part? If you did it as a
string, it should be as easy as counting tabs. But source filtering,
that sounds like the better way.

--
Giles Bowkett

I'm running a time management experiment: I'm only checking e-mail
twice per day, at 11am and 5pm. If you need to get in touch quicker
than that, call me on my cell.

Blog: http://gilesbowkett.blogspot.com
Portfolio: http://www.gilesgoatboy.org
 
B

Brian Candler

What would it take to write a Ruby pre-processor which parsed Ruby
written in a Pythonic style and turned it into actual valid Ruby? Or
even a Ruby library like Xavier's Perl example, which allows you to
code Ruby as if it were Python after just requiring the library? The
original post's main argument was that indentation is a stable,
consistent shorthand for Ruby's end statements. If all you're looking
to do is use tabs as syntactic sugar, then it should be pretty easy to
just write something which knows how to translate a sequence of tabs
in and tabs out into a series of end statements at the end.

Actually, I'd happily settle for something simpler: a pretty-printer which
just reads (normal) Ruby and re-outputs it with standard indentation.

This would immediately solve the problem of "where did I miss out an 'end'
statement?" - you'd just pipe it into this utility and inspect. In the past
I've had to resort to binary chops to solve this: copy source.rb to x.rb and
remove chunks of method definitions until I find the offending one. This is
painful.

As an alternative: the existing Ruby parser could be modified to spit out a
warning whenever it sees non-standard nesting, e.g. "end at line <x> doesn't
align with start of block/method at line <y>". It only need do this for an
'end' which is at the start of a line.

Regards,

Brian.
 
R

Rick DeNatale

Actually, I'd happily settle for something simpler: a pretty-printer which
just reads (normal) Ruby and re-outputs it with standard indentation.

This would immediately solve the problem of "where did I miss out an 'end'
statement?" -

In vim, I find that ggVG= does that job well enough.
 
B

bbiker

Actually, I'd happily settle for something simpler: a pretty-printer which
just reads (normal) Ruby and re-outputs it with standard indentation.

This would immediately solve the problem of "where did I miss out an 'end'
statement?" - you'd just pipe it into this utility and inspect. In the past
I've had to resort to binary chops to solve this: copy source.rb to x.rb and
remove chunks of method definitions until I find the offending one. This is
painful.

As an alternative: the existing Ruby parser could be modified to spit out a
warning whenever it sees non-standard nesting, e.g. "end at line <x> doesn't
align with start of block/method at line <y>". It only need do this for an
'end' which is at the start of a line.

Regards,

Brian.

I use Arachono Ruby IDE. Its editor has a feature whereby I can select
a number of lines to comment out or uncomment the lines. This makes
finding the missing end fairly easier.

I find python indentation much harder to read. For my use, I rather
stick with the "end"s.
 
B

Brad Phelan

bbiker said:
I use Arachono Ruby IDE. Its editor has a feature whereby I can select
a number of lines to comment out or uncomment the lines. This makes
finding the missing end fairly easier.

I find python indentation much harder to read. For my use, I rather
stick with the "end"s.

I've tried posting several times without success ... here again.

http://xtargets.com/snippets/posts/show/68

shows a partial solution to your challenge by overloading require. It is
a quick hack but shows you how to write your own pre-processor. The
new require checks to find files of type .pyrb and then coverts them
to .rb files before loading them.
 
G

Giles Bowkett

Actually, I'd happily settle for something simpler: a pretty-printer which
just reads (normal) Ruby and re-outputs it with standard indentation.

This already exists, and was written by somebody on this list, but I'm
having a hard time googling it.

--
Giles Bowkett

I'm running a time management experiment: I'm only checking e-mail
twice per day, at 11am and 5pm. If you need to get in touch quicker
than that, call me on my cell.

Blog: http://gilesbowkett.blogspot.com
Portfolio: http://www.gilesgoatboy.org
 
G

Giles Bowkett

shows a partial solution to your challenge by overloading require. It is
a quick hack but shows you how to write your own pre-processor. The
new require checks to find files of type .pyrb and then coverts them
to .rb files before loading them.

Neat! So all you do is write .pyrb files and you're good to go. And it
works basically by putting the tabs on a stack and using that to drive
the number of ends.

--
Giles Bowkett

I'm running a time management experiment: I'm only checking e-mail
twice per day, at 11am and 5pm. If you need to get in touch quicker
than that, call me on my cell.

Blog: http://gilesbowkett.blogspot.com
Portfolio: http://www.gilesgoatboy.org
 
E

Eric Mahurin

I've tried posting several times without success ... here again.

http://xtargets.com/snippets/posts/show/68

shows a partial solution to your challenge by overloading require. It is
a quick hack but shows you how to write your own pre-processor. The
new require checks to find files of type .pyrb and then coverts them
to .rb files before loading them.

If you change this:

if l =~ /:\s*$/
stack.push indent
l.gsub! /:\s*$/,''
end

to this:

if l.sub!(/(\|[^\|]*\|)\s*:\s*$/,' do \1') or l.sub!(/:\s*$/,'')
stack.push indent
end

your example won't need the do's:

def foo:
[1,2,3,4].each |i|:
puts i
[1,2,3,4].each |i|:
puts i

Unfortunately, this isn't too easy to implement in a formal parser
though, since the "|" after each initially looks ambiguous with the
"|" operator. The ":" to start the indentation style block should
probaly be where the "do" or "{" used to. You could even get rid of
the || by using ":" to start the args and "\n" to end the args (you'd
need an exlicit * to mean don't care about the args):

def foo :
[1,2,3,4].each : i
puts i
[1,2,3,4].each : i
puts i

In this example, you get rid of 3 lines and 9 symbols/identifiers (and
add the 3 ":"s) compared to the original ruby.

All of these ":" solutions would have potential incompatibilities with
the ? : operator and :symbol.
 
B

bradphelan

Neat! So all you do is write .pyrb files and you're good to go. And it
works basically by putting the tabs on a stack and using that to drive
the number of ends.

--
Giles Bowkett

I'm running a time management experiment: I'm only checking e-mail
twice per day, at 11am and 5pm. If you need to get in touch quicker
than that, call me on my cell.

Blog:http://gilesbowkett.blogspot.com
Portfolio:http://www.gilesgoatboy.org

Google groups seems to be broken. My posts do not appear. Just seeing
if this gets throught. My other post was via a different news agent.
 
B

Brad Phelan

Eric said:
I've tried posting several times without success ... here again.

http://xtargets.com/snippets/posts/show/68

shows a partial solution to your challenge by overloading require. It is
a quick hack but shows you how to write your own pre-processor. The
new require checks to find files of type .pyrb and then coverts them
to .rb files before loading them.

If you change this:

if l =~ /:\s*$/
stack.push indent
l.gsub! /:\s*$/,''
end

to this:

if l.sub!(/(\|[^\|]*\|)\s*:\s*$/,' do \1') or
l.sub!(/:\s*$/,'')
stack.push indent
end

your example won't need the do's:

def foo:
[1,2,3,4].each |i|:
puts i
[1,2,3,4].each |i|:
puts i

Unfortunately, this isn't too easy to implement in a formal parser
though, since the "|" after each initially looks ambiguous with the
"|" operator. The ":" to start the indentation style block should
probaly be where the "do" or "{" used to. You could even get rid of
the || by using ":" to start the args and "\n" to end the args (you'd
need an exlicit * to mean don't care about the args):

def foo :
[1,2,3,4].each : i
puts i
[1,2,3,4].each : i
puts i

In this example, you get rid of 3 lines and 9 symbols/identifiers (and
add the 3 ":"s) compared to the original ruby.

All of these ":" solutions would have potential incompatibilities with
the ? : operator and :symbol.

I think I would prefer the leaving the 'do' in and keeping the rubish
syntax. It will avoid the potential for the conflicts that you describe.

though I do like the economy of
def foo :
[1,2,3,4].each : i
puts i
[1,2,3,4].each : i
puts i

Perhapps another symbol other than :
 
E

Eric Mahurin

though I do like the economy of
def foo :
[1,2,3,4].each : i
puts i
[1,2,3,4].each : i
puts i

Perhaps another symbol other than :

:: could work. As long as it is not followed by an identifier
character it shouldn't collide. You could use this code for starting
the block:

if l.sub!(/::\s*$/,'') or l.sub!(/::\s+(.*)$/,' do |\1|')
stack.push indent
end

and then this should work:

def foo ::
[1,2,3,4].each :: i
puts i
[1,2,3,4].each :: i
puts i

With this code, to do the equivalent of do...end w/o args you'd need
an explicit do before the :: . This could be fixed in a real parser
since it would know whether the block is for a builtin
def/while/if/class statement versus a standard lambda block. In the
lambda block context maybe you'd want "::\n" to mean "do ||" and use
":: *\n" to mean "do |*|" (which is equivalent to a block without
args). Otherwise you couldn't get the equivalent of "do ||".
 
R

Rick DeNatale

Why do I get the impression that this thread is trying to develop a
new language called FrankenRuPy? <G>
 
B

Brad Phelan

Rick said:
Why do I get the impression that this thread is trying to develop a
new language called FrankenRuPy? <G>

Now, now! It's just a sweet little creation. It couldn't hurt anyone
could it ?

B
 
C

Chad Perrin

Now, now! It's just a sweet little creation. It couldn't hurt anyone
could it ?

What if it does? It might accidentally cause the death of the mad
scientist that created it, and attract the attention of the townsfolk
who will come with torches and pitchforks to kill the beast! Oh noes!

Oh, wait, that's the plot of an old book. Never mind.
 
B

Brad Phelan

Chad said:
What if it does? It might accidentally cause the death of the mad
scientist that created it, and attract the attention of the townsfolk
who will come with torches and pitchforks to kill the beast! Oh noes!

Oh, wait, that's the plot of an old book. Never mind.

I upgraded the little library so that it has better error detection
and can handle else, end and rescue dedents even if the colon : eol
operator has been used.


http://xtargets.com/snippets/posts/show/68

def foo:
[1,2,3,4].each do |i|:
puts i
[1,2,3,4].each do |j|:
puts i
if i == 2 :
puts "foo"
else:
puts "bar"


is now possible. BTW if this triggers a rubyforge project I do like
the name "Frankenrupy", Rick!
 
B

Brad Phelan

Brad said:
Chad said:
What if it does? It might accidentally cause the death of the mad
scientist that created it, and attract the attention of the townsfolk
who will come with torches and pitchforks to kill the beast! Oh noes!

Oh, wait, that's the plot of an old book. Never mind.

I upgraded the little library so that it has better error detection
and can handle else, end and rescue dedents even if the colon : eol
operator has been used.


http://xtargets.com/snippets/posts/show/68

def foo:
[1,2,3,4].each do |i|:
puts i
[1,2,3,4].each do |j|:
puts i
if i == 2 :
puts "foo"
else:
puts "bar"


is now possible. BTW if this triggers a rubyforge project I do like
the name "Frankenrupy", Rick!

In general I think that above is not a good idea for Ruby. However it
does look good when dealing with libraries like Markaby where the number
of closing ends starts to look very scary From some of my own templates


table do
@components.each do |row|
tr do
row.each do |col|
td do
pre do
text col
end
end
end
end
end
end
end

whereas it could look like


table do :
@components.each do |row| :
tr do
row.each do |col| :
td do :
pre do :
text col



which gives it a feel like a YAML file.
 
D

dblack

Hi --

In general I think that above is not a good idea for Ruby. However it does
look good when dealing with libraries like Markaby where the number of
closing ends starts to look very scary From some of my own templates


table do
@components.each do |row|
tr do
row.each do |col|
td do
pre do
text col
end
end
end
end
end
end
end

whereas it could look like


table do :
@components.each do |row| :
tr do
row.each do |col| :
td do :
pre do :
text col



which gives it a feel like a YAML file.

I prefer Ruby files that feel like Ruby files (why is everyone so
concerned with trying to figure what, other than Ruby, Ruby should
look like?), but meanwhile if you use standard indentation (and get
rid of the extraneous end in your example :) it looks a lot nicer to
start with:

table do
@components.each do |row|
tr do
row.each do |col|
td do
pre do
text col
end
end
end
end
end
end


David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
M

M. Edward (Ed) Borasky

I prefer Ruby files that feel like Ruby files (why is everyone so
concerned with trying to figure what, other than Ruby, Ruby should
look like?), but meanwhile if you use standard indentation (and get
rid of the extraneous end in your example :) it looks a lot nicer to
start with:

table do
@components.each do |row|
tr do
row.each do |col|
td do
pre do
text col
end
end
end
end
end
end


David
But I want a *blue* bike shed next to *my* nuclear power plant. <ducking>

Anyhow, here's a couple of examples of things pretty much "standard
Ruby" that I still find confusing coming from other descendants of Algol
60 syntax:

1. attribute_accessor :person

self.person = "Ed"

I really want it to be "attribute_accessor person" -- the constant
mixing of the same name with and without a preceding colon in Ruby is as
confusing to me as Perl's "$hash{'key'}" referring to an entry in
"%hash" vs. another variable entirely called "$hash".

2. Both curly braces and begin / end pairs to define scope, with one
variant having a higher binding priority than the other. The fact that I
don't even remember which one it is that has the higher binding priority
is an added factor in my dislike.

There are some others, but those are the two biggies. I'm slowly getting
used to semicolons as separators and open syntactic forms forcing a
continuation, but even those irritated me at first when I started
learning R after spending a number of years with Perl.
 
R

Rick DeNatale

Hi --



I prefer Ruby files that feel like Ruby files (why is everyone so
concerned with trying to figure what, other than Ruby, Ruby should
look like?), but meanwhile if you use standard indentation (and get
rid of the extraneous end in your example :) it looks a lot nicer to
start with:

table do
@components.each do |row|
tr do
row.each do |col|
td do
pre do
text col
end
end
end
end
end
end

And with the judicious use of {} vs. do end there are other
alternatives which may suit individual tastes depending on how tightly
you like to see the code:

table do
@components.each do |row|
tr do
row.each do |col|
td {pre {text col}
end
end
end
end

or the ultra tight:

table {@components.each {|row| tr {row.each {|col| td {pre {text col}}}}}

or if you aren't religious about reserving brackets for one-line
blocks (I'm not):

table {
@components.each { |row|
tr {
row.each { |col|
td {pre {text col}
}
}
}
}

Or anywhere in-between.
 
J

Joel VanderWerf

M. Edward (Ed) Borasky wrote:
...
But I want a *blue* bike shed next to *my* nuclear power plant. <ducking>

Coming soon from the Pragmatic Programmers: "What Color is Your Bike
Shed?" (Chapter 1: Why Ruby Syntax Sucks; previews are available on
ruby-talk.)
Anyhow, here's a couple of examples of things pretty much "standard
Ruby" that I still find confusing coming from other descendants of Algol
60 syntax:

1. attribute_accessor :person

self.person = "Ed"

I really want it to be "attribute_accessor person" -- the constant
mixing of the same name with and without a preceding colon in Ruby is as
confusing to me as Perl's "$hash{'key'}" referring to an entry in
"%hash" vs. another variable entirely called "$hash".

I can understand that being confusing at first, if you've never used an
expression-based language, but once you have seen code in which the
argument is evaluated from some nontrivial expression (or called from
some other class method), you should find it not at all strange, but
rather like an old friend.

attr_accessor(*File.read("attr_list").split)

It starts getting confusing again when you have to remember that the
alias keyword _doesn't_ work this way (but Module#alias_method does).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,900
Latest member
Nell636132

Latest Threads

Top