Feb 27
Ruby Shell as Domain Specific Language
My favorite example of a DSL is the latest jmock API just because you really have to fight the language. Fortunately Ruby makes things easier. I’ve been thinking a shell in ruby might be a nice change of pace so this is a small example of how to go about implementing one.
The Language
So basically I’d like to be able to do something like the following at a shell-like prompt:
dir1 = dir("sub1") dir2 = dir("sub2") dir1.list("*"){|x| cp(x, dir2)}
Eval and Binding
So the way to do this in Ruby is to use the Kernel::eval method and Ruby’s binding feature. Kernel::eval simply interprets Ruby test presented as a string. Binding allows you to execute that string within a particular namespace binding from the code. For instance:
class MyClass def initialize() @name = "bob" end def internal_binding binding //returns the name bindings in this context end end m = MyClass.new() eval('@name', m) //evals to bob! eval('@number=52', m) //sets m.@number = 52
Implementing the Language
So to create my language all I really need to do is this
class Location def initialize(path) @full_path = File.expand_path(path) end def dir(path) Location.new(File.expand_path(path, @full_path)) end def chdir(path) @full_path = File.expand_path(path, @full_path) end def list(pattern) Dir.glob(@full_path + "//" + pattern).collect!{ |x| Location.new(x) } end def get_binding() binding end def full_name @full_path end def cp(source, dest) //...etc end end default = Location.new(".") b = default.get_binding STDOUT.write(">> ") while( (line = STDIN.gets()) != "") do begin eval(line, b) rescue SystemExit raise rescue Exception => ex puts ex.message end STDOUT.write(">> ") end
Michael Smit is a software engineer in Maryland who currently develops software for spacecraft, spacecraft simulators, and spacecraft control systems.The Author
No Comments
Leave a comment