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