aniero-treehouse 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +43 -0
- data/README.txt +102 -0
- data/Rakefile +25 -0
- data/examples/simple_assignment.rb +54 -0
- data/lib/treehouse.rb +52 -0
- data/lib/treehouse/metaid.rb +16 -0
- data/lib/treehouse/node.rb +79 -0
- data/lib/treehouse/node_creator.rb +28 -0
- data/lib/treehouse/node_definition.rb +106 -0
- data/lib/treehouse/visitor.rb +49 -0
- data/lib/treehouse/visitor_definition.rb +49 -0
- data/spec/fixtures/assignments.txt +7 -0
- data/spec/fixtures/ey00-s00348.xen +14 -0
- data/spec/grammars/assignments.rb +38 -0
- data/spec/grammars/assignments.treetop +42 -0
- data/spec/grammars/dot_xen.rb +122 -0
- data/spec/grammars/dot_xen.treetop +100 -0
- data/spec/grammars/magic.rb +63 -0
- data/spec/integration/assignments_spec.rb +45 -0
- data/spec/integration/dot_xen_spec.rb +86 -0
- data/spec/integration/magic_spec.rb +40 -0
- data/spec/node_creator_spec.rb +47 -0
- data/spec/node_definition_spec.rb +60 -0
- data/spec/node_spec.rb +131 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/treehouse_spec.rb +17 -0
- data/spec/visitor_definition_spec.rb +44 -0
- data/spec/visitor_spec.rb +48 -0
- data/tasks/ann.rake +81 -0
- data/tasks/bones.rake +21 -0
- data/tasks/gem.rake +126 -0
- data/tasks/git.rake +41 -0
- data/tasks/manifest.rake +49 -0
- data/tasks/notes.rake +28 -0
- data/tasks/post_load.rake +39 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +57 -0
- data/tasks/setup.rb +268 -0
- data/tasks/spec.rake +55 -0
- data/tasks/svn.rake +48 -0
- data/tasks/test.rake +38 -0
- data/treehouse.gemspec +38 -0
- metadata +124 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -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
|
data/README.txt
ADDED
@@ -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.
|
data/Rakefile
ADDED
@@ -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)
|
data/lib/treehouse.rb
ADDED
@@ -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
|