aniero-treehouse 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +43 -0
  3. data/README.txt +102 -0
  4. data/Rakefile +25 -0
  5. data/examples/simple_assignment.rb +54 -0
  6. data/lib/treehouse.rb +52 -0
  7. data/lib/treehouse/metaid.rb +16 -0
  8. data/lib/treehouse/node.rb +79 -0
  9. data/lib/treehouse/node_creator.rb +28 -0
  10. data/lib/treehouse/node_definition.rb +106 -0
  11. data/lib/treehouse/visitor.rb +49 -0
  12. data/lib/treehouse/visitor_definition.rb +49 -0
  13. data/spec/fixtures/assignments.txt +7 -0
  14. data/spec/fixtures/ey00-s00348.xen +14 -0
  15. data/spec/grammars/assignments.rb +38 -0
  16. data/spec/grammars/assignments.treetop +42 -0
  17. data/spec/grammars/dot_xen.rb +122 -0
  18. data/spec/grammars/dot_xen.treetop +100 -0
  19. data/spec/grammars/magic.rb +63 -0
  20. data/spec/integration/assignments_spec.rb +45 -0
  21. data/spec/integration/dot_xen_spec.rb +86 -0
  22. data/spec/integration/magic_spec.rb +40 -0
  23. data/spec/node_creator_spec.rb +47 -0
  24. data/spec/node_definition_spec.rb +60 -0
  25. data/spec/node_spec.rb +131 -0
  26. data/spec/spec_helper.rb +34 -0
  27. data/spec/treehouse_spec.rb +17 -0
  28. data/spec/visitor_definition_spec.rb +44 -0
  29. data/spec/visitor_spec.rb +48 -0
  30. data/tasks/ann.rake +81 -0
  31. data/tasks/bones.rake +21 -0
  32. data/tasks/gem.rake +126 -0
  33. data/tasks/git.rake +41 -0
  34. data/tasks/manifest.rake +49 -0
  35. data/tasks/notes.rake +28 -0
  36. data/tasks/post_load.rake +39 -0
  37. data/tasks/rdoc.rake +51 -0
  38. data/tasks/rubyforge.rake +57 -0
  39. data/tasks/setup.rb +268 -0
  40. data/tasks/spec.rake +55 -0
  41. data/tasks/svn.rake +48 -0
  42. data/tasks/test.rake +38 -0
  43. data/treehouse.gemspec +38 -0
  44. metadata +124 -0
@@ -0,0 +1,4 @@
1
+ == 1.0.0 / 2008-06-29
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
@@ -0,0 +1,43 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ examples/simple_assignment.rb
6
+ lib/treehouse.rb
7
+ lib/treehouse/metaid.rb
8
+ lib/treehouse/node.rb
9
+ lib/treehouse/node_creator.rb
10
+ lib/treehouse/node_definition.rb
11
+ lib/treehouse/visitor.rb
12
+ lib/treehouse/visitor_definition.rb
13
+ spec/fixtures/assignments.txt
14
+ spec/fixtures/ey00-s00348.xen
15
+ spec/grammars/assignments.rb
16
+ spec/grammars/assignments.treetop
17
+ spec/grammars/dot_xen.rb
18
+ spec/grammars/dot_xen.treetop
19
+ spec/grammars/magic.rb
20
+ spec/integration/assignments_spec.rb
21
+ spec/integration/dot_xen_spec.rb
22
+ spec/integration/magic_spec.rb
23
+ spec/node_creator_spec.rb
24
+ spec/node_definition_spec.rb
25
+ spec/node_spec.rb
26
+ spec/spec_helper.rb
27
+ spec/treehouse_spec.rb
28
+ spec/visitor_definition_spec.rb
29
+ spec/visitor_spec.rb
30
+ tasks/ann.rake
31
+ tasks/bones.rake
32
+ tasks/gem.rake
33
+ tasks/git.rake
34
+ tasks/manifest.rake
35
+ tasks/notes.rake
36
+ tasks/post_load.rake
37
+ tasks/rdoc.rake
38
+ tasks/rubyforge.rake
39
+ tasks/setup.rb
40
+ tasks/spec.rake
41
+ tasks/svn.rake
42
+ tasks/test.rake
43
+ treehouse.gemspec
@@ -0,0 +1,102 @@
1
+ Treehouse
2
+ by Nathan Witmer
3
+ http://github.com/aniero/treehouse
4
+
5
+ == DESCRIPTION:
6
+
7
+ Simple node and visitor definitions for Treetop grammars.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * Simple node definition syntax for defining an AST
12
+ * Simple visitor definition to walk an AST
13
+
14
+ == SYNOPSIS:
15
+
16
+ Given a treetop grammar:
17
+
18
+ grammar SimpleAssignment
19
+ rule assignment
20
+ lhs:variable space* "=" space* rhs:variable <create_node(:assignment)>
21
+ end
22
+ rule variable
23
+ [a-z]+ <create_node(:variable)>
24
+ end
25
+ rule space
26
+ [ ]+
27
+ end
28
+ end
29
+
30
+ You can use Treehouse to define nodes for the grammar:
31
+
32
+ module SimpleAssignment
33
+ include Treehouse::NodeDefinition
34
+
35
+ node :assignment, :lhs, :rhs
36
+ node :variable, :value => :text_value
37
+ end
38
+
39
+ When you parse the grammar and call .build, it will return an AST using the nodes you defined, auto-building everything
40
+ for you:
41
+
42
+ ast = Parser.parse("foo = bar").build
43
+
44
+ ast.class #=> SimpleAssignment::Assignment
45
+ ast.lhs.class #=> SimpleAssignment::Variable
46
+ ast.lhs.value #=> "foo"
47
+
48
+ You can also define visitors for an AST:
49
+
50
+ module SimpleAssignment
51
+ include Treehouse::VisitorDefinition
52
+
53
+ visitor :hash_visitor do
54
+ visits Assignment do |a|
55
+ hash = {}
56
+ hash[ visit(lhs) ] = visit(rhs)
57
+ hash
58
+ end
59
+ visits Variable do |v|
60
+ v.value
61
+ end
62
+ end
63
+ end
64
+
65
+ SimpleAssignment::HashVisitor.visit(ast) #=> {"foo" => "bar"}
66
+
67
+ See examples/simple_assignment.rb for the full code.
68
+
69
+ == REQUIREMENTS:
70
+
71
+ * treetop
72
+ * attributes
73
+ * active_support
74
+
75
+ == INSTALL:
76
+
77
+ * sudo gem install aniero-treehouse -s http://gems.github.com
78
+
79
+ == LICENSE:
80
+
81
+ (The MIT License)
82
+
83
+ Copyright (c) 2008 Nathan Witmer
84
+
85
+ Permission is hereby granted, free of charge, to any person obtaining
86
+ a copy of this software and associated documentation files (the
87
+ 'Software'), to deal in the Software without restriction, including
88
+ without limitation the rights to use, copy, modify, merge, publish,
89
+ distribute, sublicense, and/or sell copies of the Software, and to
90
+ permit persons to whom the Software is furnished to do so, subject to
91
+ the following conditions:
92
+
93
+ The above copyright notice and this permission notice shall be
94
+ included in all copies or substantial portions of the Software.
95
+
96
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
97
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
98
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
99
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
100
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
101
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
102
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,25 @@
1
+ # Look in the tasks/setup.rb file for the various options that can be
2
+ # configured in this Rakefile. The .rake files in the tasks directory
3
+ # are where the options are used.
4
+
5
+ load 'tasks/setup.rb'
6
+
7
+ ensure_in_path 'lib'
8
+ require 'treehouse'
9
+
10
+ task :default => 'spec:run'
11
+
12
+ PROJ.name = 'treehouse'
13
+ PROJ.authors = 'Nathan Witmer'
14
+ PROJ.email = 'nwitmer at gmail dot com'
15
+ PROJ.url = 'http://github.com/aniero/treehouse'
16
+ PROJ.rubyforge.name = ''
17
+ PROJ.version = Treehouse.version
18
+
19
+ PROJ.spec.opts << '--color --format specdoc'
20
+
21
+ # EOF
22
+
23
+ depend_on "treetop"
24
+ depend_on "attributes"
25
+ depend_on "activesupport", ">= 2.0.2"
@@ -0,0 +1,54 @@
1
+ require File.join(File.dirname(__FILE__), "..", "lib", "treehouse")
2
+
3
+ Treetop.load_from_string <<-GRAMMAR
4
+ module SimpleAssignment
5
+ grammar Grammar
6
+ rule assignment
7
+ lhs:variable space* "=" space* rhs:variable <AST.create_node(:assignment)>
8
+ end
9
+ rule variable
10
+ [a-z]+ <AST.create_node(:variable)>
11
+ end
12
+ rule space
13
+ [ ]+
14
+ end
15
+ end
16
+ end
17
+ GRAMMAR
18
+
19
+ module SimpleAssignment
20
+
21
+ # Define nodes for the AST
22
+ module AST
23
+ include Treehouse::NodeDefinition
24
+ node :assignment, :lhs, :rhs
25
+ node :variable, :value => :text_value
26
+ end
27
+
28
+ include Treehouse::VisitorDefinition
29
+
30
+ # Define a simple visitor to walk the AST and build a hash
31
+ visitor :hash_visitor do
32
+ visits AST::Assignment do |assignment|
33
+ { visit(assignment.lhs) => visit(assignment.rhs) }
34
+ end
35
+ visits AST::Variable do |var|
36
+ var.value
37
+ end
38
+ end
39
+
40
+ class Parser < ::Treetop::Runtime::CompiledParser
41
+ include Grammar
42
+ def self.parse(io)
43
+ new.parse(io).build
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ require "pp"
50
+ ast = SimpleAssignment::Parser.parse("foo=bar")
51
+ puts "----- AST -----"
52
+ pp ast
53
+ puts "\n----- visitor output -----"
54
+ pp SimpleAssignment::HashVisitor.visit(ast)
@@ -0,0 +1,52 @@
1
+ module Treehouse
2
+
3
+ # :stopdoc:
4
+ VERSION = '0.0.2'
5
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
6
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
7
+ # :startdoc:
8
+
9
+ # Returns the version string for the library.
10
+ #
11
+ def self.version
12
+ VERSION
13
+ end
14
+
15
+ # Returns the library path for the module. If any arguments are given,
16
+ # they will be joined to the end of the libray path using
17
+ # <tt>File.join</tt>.
18
+ #
19
+ def self.libpath( *args )
20
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, *args)
21
+ end
22
+
23
+ # Returns the lpath for the module. If any arguments are given,
24
+ # they will be joined to the end of the path using
25
+ # <tt>File.join</tt>.
26
+ #
27
+ def self.path( *args )
28
+ args.empty? ? PATH : ::File.join(PATH, *args)
29
+ end
30
+
31
+ # Utility method used to rquire all files ending in .rb that lie in the
32
+ # directory below this file that has the same name as the filename passed
33
+ # in. Optionally, a specific _directory_ name can be passed in such that
34
+ # the _filename_ does not have to be equivalent to the directory.
35
+ #
36
+ def self.require_all_libs_relative_to( fname, dir = nil )
37
+ dir ||= ::File.basename(fname, '.*')
38
+ search_me = ::File.expand_path(
39
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
40
+
41
+ Dir.glob(search_me).sort.each {|rb| require rb}
42
+ end
43
+
44
+ end # module Treehouse
45
+
46
+ require "rubygems"
47
+ gem "activesupport"
48
+ gem "attributes"
49
+
50
+ %w(active_support/core_ext/string attributes treetop).each { |lib| require lib }
51
+
52
+ Treehouse.require_all_libs_relative_to __FILE__
@@ -0,0 +1,16 @@
1
+ # thanks, _why!
2
+ class Object
3
+ # The hidden singleton lurks behind everyone
4
+ def metaclass; class << self; self; end; end
5
+ def meta_eval &blk; metaclass.instance_eval &blk; end
6
+
7
+ # Adds methods to a metaclass
8
+ def meta_def name, &blk
9
+ meta_eval { define_method name, &blk }
10
+ end
11
+
12
+ # Defines an instance method within a class
13
+ # def class_def name, &blk
14
+ # class_eval { define_method name, &blk }
15
+ # end
16
+ end
@@ -0,0 +1,79 @@
1
+ module Treehouse
2
+
3
+ # Treehouse nodes are auto-generated by node definitions (see Treehouse::NodeDefinition).
4
+ # A node is meant to be a part of an AST external to the AST that Treetop generates at parse-time
5
+ # with Treetop::Runtime::SyntaxNodes.
6
+ #
7
+ class Node
8
+
9
+ # Create a subclass of Node with the given attributes, both simple and mapped.
10
+ #
11
+ # (see NodeDefinitions#node for more information)
12
+ #
13
+ def self.create(*attribs)
14
+ Class.new(self) do
15
+ attribs.each do |attrib|
16
+ case attrib
17
+ when Symbol, String
18
+ attribute(attrib.to_s) { raise "no value given for #{attrib}" }
19
+ when Hash
20
+ attrib.each do |name, symbol|
21
+ attribute(name.to_s) { raise "no value given for #{name}" }
22
+ attribute_mapping[name.to_s] = symbol
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ # The mapping of attribute names to the auto-build values/methods/lambdas used by create_node
30
+ def self.attribute_mapping
31
+ @attribute_mapping ||= {}
32
+ end
33
+
34
+ # Instantiate a node.
35
+ #
36
+ # Values can either be a hash of values to set the values of this node's attributes, or a Treetop syntax node
37
+ # which is used to automatically build this node.
38
+ #
39
+ def initialize(values={})
40
+ if values.kind_of?(Treetop::Runtime::SyntaxNode)
41
+ build_from_parsed_node(values)
42
+ else
43
+ values.each do |name, value|
44
+ send("#{name}=", value)
45
+ end
46
+ end
47
+ end
48
+
49
+ protected
50
+
51
+ # Auto-builds this node using the provided parsed node and the defined attributes and mapped attributes.
52
+ def build_from_parsed_node(parsed_node)
53
+ attributes.each do |attrib|
54
+ if value = mapping(attrib)
55
+ if value.kind_of?(Proc)
56
+ value = value.call(parsed_node)
57
+ else
58
+ value = parsed_node.send(mapping(attrib))
59
+ end
60
+ else
61
+ value = parsed_node.send(attrib)
62
+ end
63
+ value = value.map { |val| val.respond_to?(:build) ? val.build : val } if value.kind_of?(Array)
64
+ value = value.build if value.respond_to?(:build)
65
+ send("#{attrib}=", value)
66
+ end
67
+ end
68
+
69
+ def attributes
70
+ self.class.attributes
71
+ end
72
+
73
+ def mapping(name)
74
+ self.class.attribute_mapping[name]
75
+ end
76
+
77
+ end
78
+
79
+ end
@@ -0,0 +1,28 @@
1
+ module Treehouse
2
+ class NodeCreator
3
+
4
+ # Creates a node creator for the given node class. This is meant to act as a stand-in for the treetop
5
+ # syntax node class.
6
+ #
7
+ def initialize(node_class)
8
+ @node_class = node_class
9
+ end
10
+
11
+ # Returns a new treetop syntax node to act as a standin for a normal syntax node. Before returning the node,
12
+ # this defines a build method on the syntax node that will instantiate the node class using the auto-build
13
+ # functionality. If this build method isn't enough, you'll have to define one yourself inline in the treetop
14
+ # gramamr.
15
+ #
16
+ def new(*args)
17
+ parsed_node = Treetop::Runtime::SyntaxNode.new(*args)
18
+
19
+ node_class = @node_class # local scope for the block below
20
+ parsed_node.meta_def :build do
21
+ node_class.new(self)
22
+ end
23
+
24
+ parsed_node
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,106 @@
1
+ module Treehouse::NodeDefinition
2
+
3
+ module ModuleMethods
4
+
5
+ # Returns a NodeCreator to act as a stand-in for a normal node class definition.
6
+ # According to the treetop metagrammar, node class definitions can have more than just a class in them.
7
+ # For example:
8
+ #
9
+ # rule variable
10
+ # [a-z]+ <Variable>
11
+ # end
12
+ #
13
+ # Instead, use this method to use AST node auto-build functionality. Given
14
+ #
15
+ # node :variable, :value => :text_value
16
+ #
17
+ # You can define the grammar as
18
+ #
19
+ # rule variable
20
+ # [a-z]+ <create_node(:variable)>
21
+ # end
22
+ #
23
+ def create_node(name)
24
+ Treehouse::NodeCreator.new(const_get(name.to_s.camelize))
25
+ end
26
+
27
+ # Define a node.
28
+ #
29
+ # This creates a new class (a subclass of Treehouse::Node) with the given attribute names.
30
+ #
31
+ # Attribute names can be lists of symbols (simple attributes) and/or a hash of name-value pairs (mapped attributes).
32
+ # The new class will have attributes matching the names and hash keys given. More on the hash values in a minute.
33
+ #
34
+ # node :foo
35
+ #
36
+ # Creates a Foo class in the local scope.
37
+ #
38
+ # node :calculation, :left, :right, :operator => lambda { |node| node.elements[1] }
39
+ #
40
+ # Creates a Calculation class with left, right, and operator attributes.
41
+ #
42
+ # There are two ways to instantiate a class generated by this method.
43
+ #
44
+ # The first method is to provide a hash with name/value pairs for the attributes:
45
+ #
46
+ # c = Calculation.new(:left => "lhs", :right => "rhs", :operator => "=")
47
+ # c.left # => "lhs"
48
+ #
49
+ # If an attribute isn't initialized in this way, you will get an exception if you try to access it.
50
+ #
51
+ # This simple way of instantiating a node can be used in a grammar to build an AST with these nodes manually.
52
+ #
53
+ # The second method takes a treetop syntax node and auto-builds the node using the syntax node as a basis.
54
+ # The auto-build functionality uses the attribute names and mapped attributes in the following way:
55
+ #
56
+ # * Simple attributes: calls the method by that name on the syntax node
57
+ # * Mapped attributes: if the attribute value is a symbol or a string, it calls that method on the syntax node.
58
+ # If the attribute value is a lambda, it yields the syntax node to the lambda.
59
+ #
60
+ # If the value (or array of values) returned by one of these methods is another syntax node, the auto-build code
61
+ # will call the build method on each syntax node or array item (assuming each syntax node has a build method,
62
+ # usually auto-defined using create_node). Any value not responding to the build method is left alone.
63
+ #
64
+ # Simple example:
65
+ #
66
+ # rule assignment
67
+ # variable:lhs "=" variable:rhs <create_node(:assignment)>
68
+ # end
69
+ #
70
+ # node :assignment, :lhs, :rhs
71
+ #
72
+ # Assignment.new(syntax_node) will return an instance with an lhs and rhs set from that node.
73
+ #
74
+ # If a block is given, the block is evaluated in the context of the new class (for method definitions, etc.) e.g.
75
+ #
76
+ # node :foo do
77
+ # def name; "hello!"; end
78
+ # end
79
+ # Foo.new.name #=> "hello!"
80
+ #
81
+ def node(name, *attribute_names, &blk)
82
+ klass = Treehouse::Node.create *attribute_names
83
+ const_set name.to_s.camelize, klass
84
+ klass.class_eval &blk if block_given?
85
+ end
86
+
87
+ end
88
+
89
+ # Include this module to get access to the node and create_node methods.
90
+ # A good place to include this is in a separate AST module, e.g.
91
+ #
92
+ # module AST
93
+ # include NodeDefinition
94
+ # node :foo
95
+ # end
96
+ #
97
+ # And then in your grammar:
98
+ #
99
+ # rule foo
100
+ # "foo" <AST.create_node(:foo)>
101
+ # end
102
+ #
103
+ def self.included(base)
104
+ base.extend ModuleMethods
105
+ end
106
+ end