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.
- 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
|
+
|