A CommonLisp CLOS-like parse tree library for Ruby... read (and write) it and weep.
Ruby
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
lib
spec
.gitignore
MIT-LICENSE
README.rdoc
Rakefile
cry.gemspec
cry.rb

README.rdoc

Cry

A CommonLisp CLOS-like parse tree library for Ruby… read (and write) it and weep.

For example, you could create a parse tree like so:

parse_tree = Cry::ParseTree.new(:+, 1, 3)

… then evaluate it:

parse_tree.evaluate # => 4

… You can also create and evaluate more complex parse trees:

Cry::ParseTree.new( :+, Cry::ParseTree.new( :+, 1, Cry::ParseTree.new(:*, 2, 2) ), Cry::ParseTree.new(:+, 1, 3) ).evaluate # => 9

Lineage

Cry::ParseTree just inherits from Array, so you can use all of Array's convenient methods for manipulating your ParseTrees too.

Parameter order

The first parameter is the method as a String or Symbol, this never changes. The second parameter is the object that the method will be called on. This can either be a normal object, or an unevaluated Cry::ParseTree for delayed recursive evaluation. Any subsequent parameters can either be normal objects, or Cry::ParseTrees, and will be passed as arguments to the method call.

Usage

require 'cry'

Cry::ParseTree.new(:*, Cry::ParseTree.new(:+, 1, 3), 23).evaluate # => 92

This is similar to the following Lisp:

(* (+ 1 3) 23) # => 92 Take note that ParseTree's second parameter is always your object (be it a class or not).

The method specified will be called on the object, similar to CLOS. To distinguish Cry::ParseTree objects from Arrays when using inspect, they are denoted by parentheses:

Cry::ParseTree.new(:<<, [1], 2).inspect # => "(:<<, [1], 2)"

Realize that you have access to all of Ruby, so you can do crazy things like this:

Cry::ParseTree.new(:instance_variable_set, self, '@parse_tree', 'Cry::ParseTree.new(:*, Cry::ParseTree.new(:+, 1, 3), 23).evaluate').evaluate Cry::ParseTree.new(:eval, Kernel, @parse_tree).evaluate # => 92

Manipulation

The nice thing is that the parse trees are lazily evaluated. This means you can change them around before evaluations.

parse_tree = Cry::ParseTree.new(:+, 1, 3)

parse_tree.node_method # => :+
parse_tree.node_object # => 1
parse_tree.node_arguments # => [3]

parse_tree.node_method = :*
parse_tree.node_object = 2
parse_tree.node_arguments = [4]

parse_tree.evaluate # => 8

Also note that evaluate is not a destructive method.

parse_tree.evaluate # => 8
parse_tree.node_object = 3
parse_tree.evaluate # => 12

Blocks

To use methods that accept a block, just use a Proc for your last argument:

Cry::ParseTree.new(:inject, [1, 2, 3], 0, lambda{|sum, i| sum + i }).evaluate # => 6

Author

Author

Larry Diehl (larrytheliquid)

Website

larrytheliquid.com/2008/06/05/learn-to-cry-by-writing-parse-trees-in-ruby