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,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,7 @@
1
+ foo=bar
2
+
3
+ baz = blech
4
+
5
+
6
+ no = way
7
+
@@ -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
+