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
@@ -0,0 +1,49 @@
|
|
1
|
+
module Treehouse
|
2
|
+
|
3
|
+
# Represents a visitor for an AST. See Treehouse::VisitorDefinition more more information.
|
4
|
+
#
|
5
|
+
class Visitor
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
# Describe a visitor block for a given node type. If no block is given, an empty lambda is used.
|
10
|
+
# The block always takes at least one argument, an instance of the node you're visiting, as well
|
11
|
+
# as any additional arguments if desired.
|
12
|
+
#
|
13
|
+
def visits(*constants, &blk)
|
14
|
+
constants.each do |constant|
|
15
|
+
nodes[constant] = (blk || lambda {})
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Visit the given node using the visitor mapping defined with .visits.
|
20
|
+
# This finds the block to call for the given node and calls it with the node and additional arguments, if any.
|
21
|
+
#
|
22
|
+
# Raises an exception if no visitor is found for the given node.
|
23
|
+
#
|
24
|
+
def visit(node, *args)
|
25
|
+
block = visitor_for(node)
|
26
|
+
if args.empty?
|
27
|
+
block.call(node)
|
28
|
+
else
|
29
|
+
block.call(node, *args)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
# Node class / lambda mapping
|
36
|
+
def nodes
|
37
|
+
@nodes ||= {}
|
38
|
+
end
|
39
|
+
|
40
|
+
# Look up a visitor block for this node
|
41
|
+
def visitor_for(node)
|
42
|
+
nodes[node.class] or raise "could not find visitor definition for #{node.class}: #{node.inspect}"
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require Treehouse.libpath(%w(treehouse visitor))
|
2
|
+
|
3
|
+
module Treehouse::VisitorDefinition
|
4
|
+
|
5
|
+
module ModuleMethods
|
6
|
+
|
7
|
+
# Define a visitor class with the given name.
|
8
|
+
#
|
9
|
+
# visitor :string_visitor
|
10
|
+
#
|
11
|
+
# Creates StringVisitor in the local scope.
|
12
|
+
#
|
13
|
+
# This method takes a block, which is evaluated in the context of the new visitor class. Since the visitor
|
14
|
+
# class is a subclass of Treehouse::Visitor, you have node visitor definition methods available.
|
15
|
+
# (See Treehouse::Visitor)
|
16
|
+
#
|
17
|
+
# Given an AST built with
|
18
|
+
#
|
19
|
+
# node :assignment, :lhs, :rhs
|
20
|
+
# node :variable, :value
|
21
|
+
#
|
22
|
+
# Define a visitor:
|
23
|
+
#
|
24
|
+
# visitor :hash_visitor do
|
25
|
+
# visits Assignment do |assignment|
|
26
|
+
# hash = {}
|
27
|
+
# hash[ visit(assignment.lhs) ] = visit(assignment.rhs) # visitors are entirely external to the AST
|
28
|
+
# end
|
29
|
+
# visits Variable do |var|
|
30
|
+
# var.value
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# And call it:
|
35
|
+
#
|
36
|
+
# HashVisitor.visit( assignment_node )
|
37
|
+
#
|
38
|
+
def visitor(name, &blk)
|
39
|
+
klass = Class.new(Treehouse::Visitor)
|
40
|
+
const_set name.to_s.camelize, klass
|
41
|
+
klass.class_eval &blk if block_given?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.included(base)
|
46
|
+
base.extend ModuleMethods
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# -*- mode: python; -*-
|
2
|
+
kernel = '/boot/vmlinuz-2.6.18-xenU'
|
3
|
+
memory = 712
|
4
|
+
maxmem = 4096
|
5
|
+
name = 'ey00-s00348'
|
6
|
+
vif = [ 'bridge=xenbr0' ]
|
7
|
+
disk = [
|
8
|
+
'phy:/dev/ey00-data4/root-s00348,sda1,w',
|
9
|
+
'phy:/dev/ey00-data4/swap-s00348,sda2,w',
|
10
|
+
'phy:/dev/ey00-data4/gfs-00218,sdb1,w!',
|
11
|
+
]
|
12
|
+
root = "/dev/sda1 ro"
|
13
|
+
vcpus = 1
|
14
|
+
cpu_cap = 100
|
@@ -0,0 +1,38 @@
|
|
1
|
+
Treetop.load(File.join(File.dirname(__FILE__), "assignments.treetop"))
|
2
|
+
|
3
|
+
module AssignmentsLanguage
|
4
|
+
module Grammar
|
5
|
+
include Treehouse::NodeDefinition
|
6
|
+
|
7
|
+
node :assignments, :assignments
|
8
|
+
node :assignment, :lhs, :rhs
|
9
|
+
node :blank_line
|
10
|
+
node :variable, :value
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
include Treehouse::VisitorDefinition
|
15
|
+
|
16
|
+
visitor :string_visitor do
|
17
|
+
visits Grammar::Assignments do |assignments|
|
18
|
+
assignments.assignments.map { |child| visit(child) }.compact.join("")
|
19
|
+
end
|
20
|
+
visits Grammar::Assignment do |assignment|
|
21
|
+
visit(assignment.lhs) + " = " + visit(assignment.rhs) + "\n"
|
22
|
+
end
|
23
|
+
visits Grammar::BlankLine do
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
visits Grammar::Variable do |variable|
|
27
|
+
variable.value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Parser < ::Treetop::Runtime::CompiledParser
|
32
|
+
include Grammar
|
33
|
+
def self.parse(io)
|
34
|
+
new.parse(io).eval
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# This demonstrates a grammar using inline evals to use Treetop to build an external Treehouse-based AST.
|
2
|
+
module AssignmentsLanguage
|
3
|
+
grammar Grammar
|
4
|
+
|
5
|
+
rule assignments
|
6
|
+
( blank_line / assignment )* {
|
7
|
+
def eval
|
8
|
+
Assignments.new :assignments => elements.map {|child| child.eval}
|
9
|
+
end
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
rule assignment
|
14
|
+
lhs:variable whitespace* "=" whitespace* rhs:variable {
|
15
|
+
def eval
|
16
|
+
Assignment.new(:lhs => lhs.eval, :rhs => rhs.eval)
|
17
|
+
end
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
rule variable
|
22
|
+
[a-z]+ {
|
23
|
+
def eval
|
24
|
+
Variable.new( :value => text_value)
|
25
|
+
end
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
rule whitespace
|
30
|
+
[ \t]
|
31
|
+
end
|
32
|
+
|
33
|
+
rule blank_line
|
34
|
+
whitespace* [\n] {
|
35
|
+
def eval
|
36
|
+
BlankLine.new
|
37
|
+
end
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
Treetop.load(File.join(File.dirname(__FILE__), "dot_xen.treetop"))
|
2
|
+
|
3
|
+
module DotXen
|
4
|
+
module AST
|
5
|
+
include Treehouse::NodeDefinition
|
6
|
+
|
7
|
+
node :config_file, :disks, :vars, :comments
|
8
|
+
node :assignment, :lhs, :rhs
|
9
|
+
node :array_assignment, :lhs, :rhs
|
10
|
+
|
11
|
+
node :array_list, :values
|
12
|
+
|
13
|
+
node :literal_number, :value
|
14
|
+
node :literal_string, :value
|
15
|
+
node :single_quoted_string, :value
|
16
|
+
node :double_quoted_string, :value
|
17
|
+
|
18
|
+
node :comment, :text
|
19
|
+
|
20
|
+
node :disk, :volume, :device, :mode do
|
21
|
+
|
22
|
+
# scan the variable list for disk definitions
|
23
|
+
def self.build(variables)
|
24
|
+
disks = variables.detect { |var| var.lhs.value == "disk" }
|
25
|
+
unless disks.nil?
|
26
|
+
variables.delete(disks)
|
27
|
+
disks.rhs.values.map do |disk|
|
28
|
+
volume, device, mode = disk.value.split(/,/)
|
29
|
+
new(:volume => volume, :device => device, :mode => mode)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
include Treehouse::VisitorDefinition
|
38
|
+
|
39
|
+
visitor :hash_visitor do
|
40
|
+
visits(AST::ConfigFile) do |config_file|
|
41
|
+
hash = {:comments => [], :vars => {}, :disks => []}
|
42
|
+
config_file.comments.each { |comment| visit(comment, hash) }
|
43
|
+
config_file.vars.each { |var| visit(var, hash) }
|
44
|
+
config_file.disks.each { |disk| visit(disk, hash) }
|
45
|
+
hash
|
46
|
+
end
|
47
|
+
|
48
|
+
visits AST::Assignment do |assignment, hash|
|
49
|
+
hash[:vars][visit(assignment.lhs)] = visit(assignment.rhs)
|
50
|
+
end
|
51
|
+
|
52
|
+
visits AST::ArrayAssignment do |array, hash|
|
53
|
+
hash[:vars][visit(array.lhs)] = visit(array.rhs)
|
54
|
+
end
|
55
|
+
|
56
|
+
visits AST::ArrayList do |list|
|
57
|
+
list.values.map { |value| visit(value) }
|
58
|
+
end
|
59
|
+
|
60
|
+
visits AST::Comment do |comment, hash|
|
61
|
+
hash[:comments] << comment.text
|
62
|
+
end
|
63
|
+
|
64
|
+
visits AST::Disk do |disk, hash|
|
65
|
+
hash[:disks] << [disk.volume, disk.device, disk.mode].join(",")
|
66
|
+
end
|
67
|
+
|
68
|
+
visits AST::LiteralString, AST::LiteralNumber, AST::SingleQuotedString, AST::DoubleQuotedString do |literal|
|
69
|
+
literal.value
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
visitor :string_visitor do
|
74
|
+
visits(AST::ConfigFile) do |config_file|
|
75
|
+
strings = []
|
76
|
+
config_file.comments.each { |comment| visit(comment, strings) }
|
77
|
+
config_file.vars.each { |var| visit(var, strings) }
|
78
|
+
if config_file.disks
|
79
|
+
strings << "disk = [\n"
|
80
|
+
strings << config_file.disks.map { |disk| " " + visit(disk) }.join(",\n")
|
81
|
+
strings << "\n]\n"
|
82
|
+
end
|
83
|
+
strings.flatten.join("")
|
84
|
+
end
|
85
|
+
|
86
|
+
visits AST::Assignment, AST::ArrayAssignment do |assignment, strings|
|
87
|
+
strings << [visit(assignment.lhs), " = ", visit(assignment.rhs), "\n"]
|
88
|
+
end
|
89
|
+
|
90
|
+
visits AST::ArrayList do |list|
|
91
|
+
["[ ", list.values.map { |value| visit(value) }.join(",\n"), " ]"]
|
92
|
+
end
|
93
|
+
|
94
|
+
visits AST::Comment do |comment, strings|
|
95
|
+
strings << "# #{comment.text}\n"
|
96
|
+
end
|
97
|
+
|
98
|
+
visits AST::Disk do |disk|
|
99
|
+
"\"" + [disk.volume, disk.device, disk.mode].join(",") + "\""
|
100
|
+
end
|
101
|
+
|
102
|
+
visits AST::LiteralString, AST::LiteralNumber do |literal|
|
103
|
+
literal.value
|
104
|
+
end
|
105
|
+
|
106
|
+
visits AST::SingleQuotedString do |literal|
|
107
|
+
"'" + literal.value + "'"
|
108
|
+
end
|
109
|
+
|
110
|
+
visits AST::DoubleQuotedString do |literal|
|
111
|
+
'"' + literal.value + '"'
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class Parser < ::Treetop::Runtime::CompiledParser
|
116
|
+
include Grammar
|
117
|
+
def self.parse(io)
|
118
|
+
new.parse(io).eval
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module DotXen
|
2
|
+
grammar Grammar
|
3
|
+
rule config_file
|
4
|
+
(comment / blank_line / assignment)* {
|
5
|
+
def eval
|
6
|
+
env = {:vars => [], :comments => []}
|
7
|
+
elements.each { |e| e.eval(env) }
|
8
|
+
env[:disks] = AST::Disk.build(env[:vars])
|
9
|
+
AST::ConfigFile.new(env)
|
10
|
+
end
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
rule assignment
|
15
|
+
space_no_newline* lhs:variable space_no_newline* '=' space_no_newline* rhs:variable space* comment* {
|
16
|
+
def eval(env={})
|
17
|
+
env[:vars] << AST::Assignment.new(:lhs => lhs.eval, :rhs => rhs.eval)
|
18
|
+
end
|
19
|
+
}
|
20
|
+
/
|
21
|
+
space_no_newline* lhs:variable space_no_newline* '=' space_no_newline* '[' space* rhs:array_list space* ']' space* comment* {
|
22
|
+
def eval(env={})
|
23
|
+
env[:vars] << AST::ArrayAssignment.new(:lhs => lhs.eval, :rhs => rhs.eval)
|
24
|
+
end
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
rule array_list
|
29
|
+
space* value:(variable/comment) ','? space* remains:(space* var:array_list space* ','?)* {
|
30
|
+
def eval(env={})
|
31
|
+
AST::ArrayList.new :values => [[value.eval] + remains.elements.map { |vars| vars.var.eval.values }].flatten
|
32
|
+
end
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
rule variable
|
37
|
+
space_no_newline* value:number space* comment* {
|
38
|
+
def eval(env={})
|
39
|
+
value.eval
|
40
|
+
end
|
41
|
+
}
|
42
|
+
/
|
43
|
+
space_no_newline* value:string space* comment* {
|
44
|
+
def eval(env={})
|
45
|
+
value.eval
|
46
|
+
end
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
rule number
|
51
|
+
[0-9]+ {
|
52
|
+
def eval(env={})
|
53
|
+
AST::LiteralNumber.new(:value => text_value.to_i)
|
54
|
+
end
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
rule string
|
59
|
+
([A-Za-z_-])+ {
|
60
|
+
def eval(env={})
|
61
|
+
AST::LiteralString.new(:value => text_value)
|
62
|
+
end
|
63
|
+
} /
|
64
|
+
'"' (!'"' . / '\"')* '"' {
|
65
|
+
def eval(env={})
|
66
|
+
AST::DoubleQuotedString.new(:value => elements[1].text_value)
|
67
|
+
end
|
68
|
+
} /
|
69
|
+
"'" (!"'" .)* "'" {
|
70
|
+
def eval(env={})
|
71
|
+
AST::SingleQuotedString.new(:value => elements[1].text_value)
|
72
|
+
end
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
rule comment
|
77
|
+
space_no_newline* [!\#] value:([^\n])* "\n" space* {
|
78
|
+
def eval(env={})
|
79
|
+
env[:comments] << AST::Comment.new(:text => value.text_value)
|
80
|
+
end
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
rule blank_line
|
85
|
+
"\n"
|
86
|
+
end
|
87
|
+
|
88
|
+
rule non_space_char
|
89
|
+
![ \n] .
|
90
|
+
end
|
91
|
+
|
92
|
+
rule space_no_newline
|
93
|
+
[ \t]
|
94
|
+
end
|
95
|
+
|
96
|
+
rule space
|
97
|
+
space_no_newline / "\n"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# This demonstrates a grammar making full use of Treehouse's magic nodes to build an AST.
|
2
|
+
|
3
|
+
Treetop.load_from_string <<-GRAMMAR
|
4
|
+
|
5
|
+
module MagicAssignments
|
6
|
+
grammar Grammar
|
7
|
+
rule assignments
|
8
|
+
( blank_line / assignment )* <AST.create_node(:assignments)>
|
9
|
+
end
|
10
|
+
rule assignment
|
11
|
+
lhs:variable whitespace* "=" whitespace* rhs:variable [\\n] <AST.create_node(:assignment)>
|
12
|
+
end
|
13
|
+
rule variable
|
14
|
+
[a-z]+ <AST.create_node(:variable)>
|
15
|
+
end
|
16
|
+
rule whitespace
|
17
|
+
[ ]
|
18
|
+
end
|
19
|
+
rule blank_line
|
20
|
+
whitespace* [\\n] <AST.create_node(:blank_line)>
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
GRAMMAR
|
26
|
+
|
27
|
+
module MagicAssignments
|
28
|
+
module AST
|
29
|
+
include Treehouse::NodeDefinition
|
30
|
+
|
31
|
+
node :assignments, :assignments => :elements
|
32
|
+
node :assignment, :lhs, :rhs
|
33
|
+
node :blank_line
|
34
|
+
node :variable, :value => :text_value
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
include Treehouse::VisitorDefinition
|
39
|
+
|
40
|
+
visitor :hash_visitor do
|
41
|
+
visits AST::Assignments do |assignments|
|
42
|
+
hash = {}
|
43
|
+
assignments.assignments.each { |child| visit(child, hash) }
|
44
|
+
hash
|
45
|
+
end
|
46
|
+
visits AST::Assignment do |assignment, hash|
|
47
|
+
hash[visit(assignment.lhs)] = visit(assignment.rhs)
|
48
|
+
end
|
49
|
+
visits AST::BlankLine
|
50
|
+
visits AST::Variable do |variable|
|
51
|
+
variable.value
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Parser < ::Treetop::Runtime::CompiledParser
|
56
|
+
include Grammar
|
57
|
+
def self.parse(io)
|
58
|
+
new.parse(io).build
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|