So it’s been a while since I’ve made a post on RubyShell. I’m still working on it, so no worries, I’ve just got a little less time than usual. Anyways, an update on some things I’m working on right now; namely, a better validator and some improved path handling.

Yay SexpProcessor!

So ParseTree comes with a useful little class called SexpProcessor that happily provides a way to visit nodes in the parse tree. Until now, I was using some nastiness in dealing with the sexps directly to figure out when I had calls that were invalid so I could mark them as invalid Ruby. Now, however, we do something different…

Thanks to SexpProcessor, I’ve replaced this less-than-spectacular methodology with a simple SexpProcessor subclass that looks for the first call (fcall, vcall, or call) and tracks what it was so it can be checked later. Currently, checks for overwriting assignments (assignments to variables that will shadow existing methods) are still done manually, through what is essentially a manual SexpProcessor. I’ll be rolling that into the ValidationProcessor (as the new class is called) sometime today. There’s some more promising stuff that can come out of this, as it’s a cleaner solution than what we had before. Fixing corner cases should hopefully be a little easier now.

For example, invalid initial method calls are currently always marked as commands (i.e., as invalid Ruby). This is bad, as sometimes there are invalid method calls that are still clearly Ruby but are passed on to the shell as a command anyway. Here’s one:


RubyShell> math.magic { |test| power }
Error: Command `math.magic' not found.

That should still be processed as Ruby, and instead give a Ruby error. The processor will let us do this more easily, as we can look for, say, an :iter node (for a block) and decide that that will always be valid Ruby, even if it errors out when it runs.

Paths as First-Class Citizens

Daniel Draper mentioned in a comment on the 0.5 release announcement post an interesting possibility:

I’d love to do this:

Have a dir called foo/bar/ that has say 100 directories in it.

Simply do

ls foo/bar.first to see whats in the first one of those

or maybe

ls foo/bar.children.first

Then if you wanted to move all dirs up a level you could do

foo/bar.children.each_with_parent { |c,parent| mv c, parent.parent }

and so on.

My brain pondered this quietly for a few days, and now I’ve taken quite a liking to the idea. I’ve already opened a feature request on Rubyforge to keep track of my progress on this. Basically, i want to see if I can make paths first-class citizens.

Path notation, it bears mentioning, is fairly closely-related to Ruby’s regular expression syntax and/or its division syntax. This introduces some interesting complications if we try and support `directory literals’, so to speak, within Ruby. So instead, I decided to opt for a middle way. Within the context of commands (i.e., code that has been marked as invalid Ruby), I intend on doing some additional parsing and creating actual path objects that can be operated upon like objects. For this purpose, I’ve created a new RubyShell::Parsing module to contain the Parser and CommandParser that will do that extra handling. Alias handling will also be moved into there.

Now, in the context of Ruby code, you’ll still be able to access the same functionality, but you’ll need to do it via a method. Right now, I’m thinking it’ll probably be named path. So you’d do something like path('foo/bar').children.each_with_parent .... It might be shortened to something else, like d (e.g., d('foo/bar').children...). Finally, I’m thinking of whether I’ll allow this kind of manipulation without that method invocation if it’s the first thing on the line. So, for example:


RubyShell> foo/bar.children.each_with_parent { |c,parent| mv c, parent.parent }
RubyShell>  foo/bar.children.each_with_parent { |c,parent| mv c, parent.parent }

The former would run as if it had been a call to path('foo/bar'). The latter wouldn’t, because it isn’t literally the first thing on the line (there were some issues with the leading whitespace being stripped before it was processed earlier for the alias command (which does the same thing—alias turns into alias_cmd if it’s the first thing on the line, for shortening purposes), but I think I’ve knocked those out). We’ll see whether I’ll implement this latter one or not, as I don’t want to make too many exceptions like that.

Anyway, that’s an update of what’s in the works! Output redirection is also in the back of my mind, so no worries there. I just need to sit down and map out how the implementation is going to work.

Sorry, comments are closed for this article.