aniero-treehouse 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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