puppet 0.23.1 → 0.23.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puppet might be problematic. Click here for more details.
- data/CHANGELOG +31 -0
- data/bin/puppetd +2 -1
- data/conf/redhat/puppet.spec +9 -6
- data/conf/redhat/server.init +4 -5
- data/examples/code/mac_dscl.pp +28 -0
- data/examples/code/mac_dscl_revert.pp +26 -0
- data/examples/code/mac_netinfo.pp +5 -0
- data/examples/code/mac_pkgdmg.pp +7 -0
- data/ext/puppet-test +69 -2
- data/lib/puppet.rb +2 -2
- data/lib/puppet/configuration.rb +5 -1
- data/lib/puppet/network/server/mongrel.rb +3 -3
- data/lib/puppet/parser/ast.rb +2 -2
- data/lib/puppet/parser/ast/component.rb +3 -3
- data/lib/puppet/parser/ast/node.rb +2 -2
- data/lib/puppet/parser/collector.rb +2 -2
- data/lib/puppet/parser/interpreter.rb +38 -215
- data/lib/puppet/parser/parser.rb +11 -228
- data/lib/puppet/parser/parser_support.rb +447 -0
- data/lib/puppet/parser/resource/param.rb +2 -2
- data/lib/puppet/provider.rb +5 -3
- data/lib/puppet/provider/cron/crontab.rb +22 -9
- data/lib/puppet/provider/group/directoryservice.rb +23 -0
- data/lib/puppet/provider/interface/redhat.rb +251 -0
- data/lib/puppet/provider/interface/sunos.rb +116 -0
- data/lib/puppet/provider/mount.rb +4 -1
- data/lib/puppet/provider/nameservice/directoryservice.rb +341 -0
- data/lib/puppet/provider/package/dpkg.rb +2 -2
- data/lib/puppet/provider/package/openbsd.rb +2 -2
- data/lib/puppet/provider/package/rpm.rb +2 -4
- data/lib/puppet/provider/package/sun.rb +2 -2
- data/lib/puppet/provider/parsedfile.rb +32 -29
- data/lib/puppet/provider/user/directoryservice.rb +116 -0
- data/lib/puppet/rails/host.rb +1 -1
- data/lib/puppet/reference/configuration.rb +7 -4
- data/lib/puppet/type/interface.rb +57 -0
- data/lib/puppet/type/pfile/group.rb +2 -2
- data/lib/puppet/util/config.rb +10 -3
- data/lib/puppet/util/fileparsing.rb +2 -2
- data/test/language/ast/hostclass.rb +1 -17
- data/test/language/interpreter.rb +18 -388
- data/test/language/node.rb +8 -8
- data/test/language/parser.rb +444 -45
- data/test/lib/puppettest/parsertesting.rb +2 -2
- data/test/lib/puppettest/support/collection.rb +2 -2
- data/test/network/server/mongrel_test.rb +24 -3
- data/test/rails/collection.rb +34 -1
- data/test/ral/providers/cron/crontab.rb +198 -40
- data/test/ral/providers/mount/parsed.rb +69 -46
- data/test/ral/providers/parsedfile.rb +20 -28
- data/test/ral/types/cron.rb +20 -24
- data/test/ral/types/interface.rb +40 -0
- data/test/ral/types/package.rb +6 -2
- data/test/util/config.rb +106 -30
- metadata +14 -2
data/lib/puppet/parser/parser.rb
CHANGED
@@ -29,236 +29,19 @@ module Puppet
|
|
29
29
|
|
30
30
|
class Parser < Racc::Parser
|
31
31
|
|
32
|
-
module_eval <<'..end grammar.ra modeval..
|
33
|
-
require 'puppet/parser/functions'
|
32
|
+
module_eval <<'..end grammar.ra modeval..idc5e5087e93', 'grammar.ra', 640
|
34
33
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
# Add context to a message; useful for error messages and such.
|
39
|
-
def addcontext(message, obj = nil)
|
40
|
-
obj ||= @lexer
|
41
|
-
|
42
|
-
message += " on line %s" % obj.line
|
43
|
-
if file = obj.file
|
44
|
-
message += " in file %s" % file
|
45
|
-
end
|
46
|
-
|
47
|
-
return message
|
48
|
-
end
|
49
|
-
|
50
|
-
# Create an AST array out of all of the args
|
51
|
-
def aryfy(*args)
|
52
|
-
if args[0].instance_of?(AST::ASTArray)
|
53
|
-
result = args.shift
|
54
|
-
args.each { |arg|
|
55
|
-
result.push arg
|
56
|
-
}
|
57
|
-
else
|
58
|
-
result = ast AST::ASTArray, :children => args
|
59
|
-
end
|
60
|
-
|
61
|
-
return result
|
62
|
-
end
|
63
|
-
|
64
|
-
# Create an AST object, and automatically add the file and line information if
|
65
|
-
# available.
|
66
|
-
def ast(klass, hash = nil)
|
67
|
-
hash ||= {}
|
68
|
-
unless hash.include?(:line)
|
69
|
-
hash[:line] = @lexer.line
|
70
|
-
end
|
71
|
-
|
72
|
-
unless hash.include?(:file)
|
73
|
-
if file = @lexer.file
|
74
|
-
hash[:file] = file
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
return klass.new(hash)
|
79
|
-
end
|
80
|
-
|
81
|
-
# Raise a Parse error.
|
82
|
-
def error(message)
|
83
|
-
if brace = @lexer.expected
|
84
|
-
message += "; expected '%s'"
|
85
|
-
end
|
86
|
-
except = Puppet::ParseError.new(message)
|
87
|
-
except.line = @lexer.line
|
88
|
-
if @lexer.file
|
89
|
-
except.file = @lexer.file
|
90
|
-
end
|
91
|
-
|
92
|
-
raise except
|
93
|
-
end
|
94
|
-
|
95
|
-
def file=(file)
|
96
|
-
unless FileTest.exists?(file)
|
97
|
-
unless file =~ /\.pp$/
|
98
|
-
file = file + ".pp"
|
99
|
-
end
|
100
|
-
unless FileTest.exists?(file)
|
101
|
-
raise Puppet::Error, "Could not find file %s" % file
|
102
|
-
end
|
103
|
-
end
|
104
|
-
if @files.detect { |f| f.file == file }
|
105
|
-
raise Puppet::AlreadyImportedError.new("Import loop detected")
|
106
|
-
else
|
107
|
-
@files << Puppet::Util::LoadedFile.new(file)
|
108
|
-
@lexer.file = file
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
# Import our files.
|
113
|
-
def import(file)
|
114
|
-
if Puppet[:ignoreimport]
|
115
|
-
return AST::ASTArray.new(:children => [])
|
116
|
-
end
|
117
|
-
# use a path relative to the file doing the importing
|
118
|
-
if @lexer.file
|
119
|
-
dir = @lexer.file.sub(%r{[^/]+$},'').sub(/\/$/, '')
|
120
|
-
else
|
121
|
-
dir = "."
|
122
|
-
end
|
123
|
-
if dir == ""
|
124
|
-
dir = "."
|
125
|
-
end
|
126
|
-
result = ast AST::ASTArray
|
127
|
-
|
128
|
-
# We can't interpolate at this point since we don't have any
|
129
|
-
# scopes set up. Warn the user if they use a variable reference
|
130
|
-
pat = file
|
131
|
-
if pat.index("$")
|
132
|
-
Puppet.warning(
|
133
|
-
"The import of #{pat} contains a variable reference;" +
|
134
|
-
" variables are not interpolated for imports " +
|
135
|
-
"in file #{@lexer.file} at line #{@lexer.line}"
|
136
|
-
)
|
137
|
-
end
|
138
|
-
files = Puppet::Module::find_manifests(pat, dir)
|
139
|
-
if files.size == 0
|
140
|
-
raise Puppet::ImportError.new("No file(s) found for import " +
|
141
|
-
"of '#{pat}'")
|
142
|
-
end
|
143
|
-
|
144
|
-
files.collect { |file|
|
145
|
-
parser = Puppet::Parser::Parser.new(interp)
|
146
|
-
parser.files = self.files
|
147
|
-
Puppet.debug("importing '%s'" % file)
|
148
|
-
|
149
|
-
unless file =~ /^#{File::SEPARATOR}/
|
150
|
-
file = File.join(dir, file)
|
151
|
-
end
|
152
|
-
begin
|
153
|
-
parser.file = file
|
154
|
-
rescue Puppet::AlreadyImportedError
|
155
|
-
# This file has already been imported to just move on
|
156
|
-
next
|
157
|
-
end
|
158
|
-
|
159
|
-
# This will normally add code to the 'main' class.
|
160
|
-
parser.parse
|
161
|
-
}
|
162
|
-
end
|
163
|
-
|
164
|
-
def initialize(interpreter)
|
165
|
-
@interp = interpreter
|
166
|
-
initvars()
|
167
|
-
end
|
168
|
-
|
169
|
-
# Initialize or reset all of our variables.
|
170
|
-
def initvars
|
171
|
-
@lexer = Puppet::Parser::Lexer.new()
|
172
|
-
@files = []
|
173
|
-
end
|
174
|
-
|
175
|
-
# The fully qualifed name, with the full namespace.
|
176
|
-
def classname(name)
|
177
|
-
[@lexer.namespace, name].join("::").sub(/^::/, '')
|
178
|
-
end
|
179
|
-
|
180
|
-
def on_error(token,value,stack)
|
181
|
-
#on '%s' at '%s' in\n'%s'" % [token,value,stack]
|
182
|
-
#error = "line %s: parse error after '%s'" %
|
183
|
-
# [@lexer.line,@lexer.last]
|
184
|
-
error = "Syntax error at '%s'" % [value]
|
185
|
-
|
186
|
-
if brace = @lexer.expected
|
187
|
-
error += "; expected '%s'" % brace
|
188
|
-
end
|
189
|
-
|
190
|
-
except = Puppet::ParseError.new(error)
|
191
|
-
except.line = @lexer.line
|
192
|
-
if @lexer.file
|
193
|
-
except.file = @lexer.file
|
194
|
-
end
|
195
|
-
|
196
|
-
raise except
|
197
|
-
end
|
198
|
-
|
199
|
-
# how should I do error handling here?
|
200
|
-
def parse(string = nil)
|
201
|
-
if string
|
202
|
-
self.string = string
|
203
|
-
end
|
204
|
-
begin
|
205
|
-
main = yyparse(@lexer,:scan)
|
206
|
-
rescue Racc::ParseError => except
|
207
|
-
error = Puppet::ParseError.new(except)
|
208
|
-
error.line = @lexer.line
|
209
|
-
error.file = @lexer.file
|
210
|
-
error.set_backtrace except.backtrace
|
211
|
-
raise error
|
212
|
-
rescue Puppet::ParseError => except
|
213
|
-
except.line ||= @lexer.line
|
214
|
-
except.file ||= @lexer.file
|
215
|
-
raise except
|
216
|
-
rescue Puppet::Error => except
|
217
|
-
# and this is a framework error
|
218
|
-
except.line ||= @lexer.line
|
219
|
-
except.file ||= @lexer.file
|
220
|
-
raise except
|
221
|
-
rescue Puppet::DevError => except
|
222
|
-
except.line ||= @lexer.line
|
223
|
-
except.file ||= @lexer.file
|
224
|
-
raise except
|
225
|
-
rescue => except
|
226
|
-
error = Puppet::DevError.new(except.message)
|
227
|
-
error.line = @lexer.line
|
228
|
-
error.file = @lexer.file
|
229
|
-
error.set_backtrace except.backtrace
|
230
|
-
raise error
|
231
|
-
end
|
232
|
-
if main
|
233
|
-
# Store the results as the top-level class.
|
234
|
-
interp.newclass("", :code => main)
|
235
|
-
return main
|
236
|
-
end
|
237
|
-
ensure
|
238
|
-
@lexer.clear
|
239
|
-
end
|
240
|
-
|
241
|
-
# See if any of the files have changed.
|
242
|
-
def reparse?
|
243
|
-
if file = @files.detect { |file| file.changed? }
|
244
|
-
return file.stamp
|
245
|
-
else
|
246
|
-
return false
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
def string=(string)
|
251
|
-
@lexer.string = string
|
252
|
-
end
|
34
|
+
# It got too annoying having code in a file that needs to be compiled.
|
35
|
+
require 'puppet/parser/parser_support'
|
253
36
|
|
254
37
|
# Make emacs happy
|
255
38
|
# Local Variables:
|
256
39
|
# mode: ruby
|
257
40
|
# End:
|
258
41
|
|
259
|
-
# $Id: parser.rb
|
42
|
+
# $Id: parser.rb 2742 2007-08-03 23:49:53Z luke $
|
260
43
|
|
261
|
-
..end grammar.ra modeval..
|
44
|
+
..end grammar.ra modeval..idc5e5087e93
|
262
45
|
|
263
46
|
##### racc 1.4.5 generates ###
|
264
47
|
|
@@ -1741,7 +1524,7 @@ module_eval <<'.,.,', 'grammar.ra', 496
|
|
1741
1524
|
|
1742
1525
|
module_eval <<'.,.,', 'grammar.ra', 506
|
1743
1526
|
def _reduce_135( val, _values, result )
|
1744
|
-
|
1527
|
+
newdefine classname(val[1]), :arguments => val[2], :code => val[4]
|
1745
1528
|
@lexer.indefine = false
|
1746
1529
|
result = nil
|
1747
1530
|
|
@@ -1752,7 +1535,7 @@ module_eval <<'.,.,', 'grammar.ra', 506
|
|
1752
1535
|
|
1753
1536
|
module_eval <<'.,.,', 'grammar.ra', 510
|
1754
1537
|
def _reduce_136( val, _values, result )
|
1755
|
-
|
1538
|
+
newdefine classname(val[1]), :arguments => val[2]
|
1756
1539
|
@lexer.indefine = false
|
1757
1540
|
result = nil
|
1758
1541
|
result
|
@@ -1763,7 +1546,7 @@ module_eval <<'.,.,', 'grammar.ra', 518
|
|
1763
1546
|
def _reduce_137( val, _values, result )
|
1764
1547
|
# Our class gets defined in the parent namespace, not our own.
|
1765
1548
|
@lexer.namepop
|
1766
|
-
|
1549
|
+
newclass classname(val[1]), :code => val[4], :parent => val[2]
|
1767
1550
|
result = nil
|
1768
1551
|
result
|
1769
1552
|
end
|
@@ -1773,7 +1556,7 @@ module_eval <<'.,.,', 'grammar.ra', 523
|
|
1773
1556
|
def _reduce_138( val, _values, result )
|
1774
1557
|
# Our class gets defined in the parent namespace, not our own.
|
1775
1558
|
@lexer.namepop
|
1776
|
-
|
1559
|
+
newclass classname(val[1]), :parent => val[2]
|
1777
1560
|
result = nil
|
1778
1561
|
result
|
1779
1562
|
end
|
@@ -1781,7 +1564,7 @@ module_eval <<'.,.,', 'grammar.ra', 523
|
|
1781
1564
|
|
1782
1565
|
module_eval <<'.,.,', 'grammar.ra', 528
|
1783
1566
|
def _reduce_139( val, _values, result )
|
1784
|
-
|
1567
|
+
newnode val[1], :parent => val[2], :code => val[4]
|
1785
1568
|
result = nil
|
1786
1569
|
result
|
1787
1570
|
end
|
@@ -1789,7 +1572,7 @@ module_eval <<'.,.,', 'grammar.ra', 528
|
|
1789
1572
|
|
1790
1573
|
module_eval <<'.,.,', 'grammar.ra', 531
|
1791
1574
|
def _reduce_140( val, _values, result )
|
1792
|
-
|
1575
|
+
newnode val[1], :parent => val[2]
|
1793
1576
|
result = nil
|
1794
1577
|
result
|
1795
1578
|
end
|
@@ -0,0 +1,447 @@
|
|
1
|
+
class Puppet::Parser::Parser
|
2
|
+
require 'puppet/parser/functions'
|
3
|
+
|
4
|
+
ASTSet = Struct.new(:classes, :definitions, :nodes)
|
5
|
+
|
6
|
+
# Define an accessor method for each table. We hide the existence of
|
7
|
+
# the struct.
|
8
|
+
[:classes, :definitions, :nodes].each do |name|
|
9
|
+
define_method(name) do
|
10
|
+
@astset.send(name)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
AST = Puppet::Parser::AST
|
15
|
+
|
16
|
+
attr_reader :file, :interp
|
17
|
+
attr_accessor :files
|
18
|
+
|
19
|
+
|
20
|
+
# Add context to a message; useful for error messages and such.
|
21
|
+
def addcontext(message, obj = nil)
|
22
|
+
obj ||= @lexer
|
23
|
+
|
24
|
+
message += " on line %s" % obj.line
|
25
|
+
if file = obj.file
|
26
|
+
message += " in file %s" % file
|
27
|
+
end
|
28
|
+
|
29
|
+
return message
|
30
|
+
end
|
31
|
+
|
32
|
+
# Create an AST array out of all of the args
|
33
|
+
def aryfy(*args)
|
34
|
+
if args[0].instance_of?(AST::ASTArray)
|
35
|
+
result = args.shift
|
36
|
+
args.each { |arg|
|
37
|
+
result.push arg
|
38
|
+
}
|
39
|
+
else
|
40
|
+
result = ast AST::ASTArray, :children => args
|
41
|
+
end
|
42
|
+
|
43
|
+
return result
|
44
|
+
end
|
45
|
+
|
46
|
+
# Create an AST object, and automatically add the file and line information if
|
47
|
+
# available.
|
48
|
+
def ast(klass, hash = nil)
|
49
|
+
hash ||= {}
|
50
|
+
unless hash.include?(:line)
|
51
|
+
hash[:line] = @lexer.line
|
52
|
+
end
|
53
|
+
|
54
|
+
unless hash.include?(:file)
|
55
|
+
if file = @lexer.file
|
56
|
+
hash[:file] = file
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
return klass.new(hash)
|
61
|
+
end
|
62
|
+
|
63
|
+
# The fully qualifed name, with the full namespace.
|
64
|
+
def classname(name)
|
65
|
+
[@lexer.namespace, name].join("::").sub(/^::/, '')
|
66
|
+
end
|
67
|
+
|
68
|
+
def clear
|
69
|
+
initvars
|
70
|
+
end
|
71
|
+
|
72
|
+
# Raise a Parse error.
|
73
|
+
def error(message)
|
74
|
+
if brace = @lexer.expected
|
75
|
+
message += "; expected '%s'"
|
76
|
+
end
|
77
|
+
except = Puppet::ParseError.new(message)
|
78
|
+
except.line = @lexer.line
|
79
|
+
if @lexer.file
|
80
|
+
except.file = @lexer.file
|
81
|
+
end
|
82
|
+
|
83
|
+
raise except
|
84
|
+
end
|
85
|
+
|
86
|
+
def file=(file)
|
87
|
+
unless FileTest.exists?(file)
|
88
|
+
unless file =~ /\.pp$/
|
89
|
+
file = file + ".pp"
|
90
|
+
end
|
91
|
+
unless FileTest.exists?(file)
|
92
|
+
raise Puppet::Error, "Could not find file %s" % file
|
93
|
+
end
|
94
|
+
end
|
95
|
+
if @files.detect { |f| f.file == file }
|
96
|
+
raise Puppet::AlreadyImportedError.new("Import loop detected")
|
97
|
+
else
|
98
|
+
@files << Puppet::Util::LoadedFile.new(file)
|
99
|
+
@lexer.file = file
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Find a class definition, relative to the current namespace.
|
104
|
+
def findclass(namespace, name)
|
105
|
+
fqfind namespace, name, classes
|
106
|
+
end
|
107
|
+
|
108
|
+
# Find a component definition, relative to the current namespace.
|
109
|
+
def finddefine(namespace, name)
|
110
|
+
fqfind namespace, name, definitions
|
111
|
+
end
|
112
|
+
|
113
|
+
# This is only used when nodes are looking up the code for their
|
114
|
+
# parent nodes.
|
115
|
+
def findnode(name)
|
116
|
+
fqfind "", name, nodes
|
117
|
+
end
|
118
|
+
|
119
|
+
# The recursive method used to actually look these objects up.
|
120
|
+
def fqfind(namespace, name, table)
|
121
|
+
namespace = namespace.downcase
|
122
|
+
name = name.downcase
|
123
|
+
if name =~ /^::/ or namespace == ""
|
124
|
+
classname = name.sub(/^::/, '')
|
125
|
+
unless table[classname]
|
126
|
+
self.load(classname)
|
127
|
+
end
|
128
|
+
return table[classname]
|
129
|
+
end
|
130
|
+
ary = namespace.split("::")
|
131
|
+
|
132
|
+
while ary.length > 0
|
133
|
+
newname = (ary + [name]).join("::").sub(/^::/, '')
|
134
|
+
if obj = table[newname] or (self.load(newname) and obj = table[newname])
|
135
|
+
return obj
|
136
|
+
end
|
137
|
+
|
138
|
+
# Delete the second to last object, which reduces our namespace by one.
|
139
|
+
ary.pop
|
140
|
+
end
|
141
|
+
|
142
|
+
# If we've gotten to this point without finding it, see if the name
|
143
|
+
# exists at the top namespace
|
144
|
+
if obj = table[name] or (self.load(name) and obj = table[name])
|
145
|
+
return obj
|
146
|
+
end
|
147
|
+
|
148
|
+
return nil
|
149
|
+
end
|
150
|
+
|
151
|
+
# Import our files.
|
152
|
+
def import(file)
|
153
|
+
if Puppet[:ignoreimport]
|
154
|
+
return AST::ASTArray.new(:children => [])
|
155
|
+
end
|
156
|
+
# use a path relative to the file doing the importing
|
157
|
+
if @lexer.file
|
158
|
+
dir = @lexer.file.sub(%r{[^/]+$},'').sub(/\/$/, '')
|
159
|
+
else
|
160
|
+
dir = "."
|
161
|
+
end
|
162
|
+
if dir == ""
|
163
|
+
dir = "."
|
164
|
+
end
|
165
|
+
result = ast AST::ASTArray
|
166
|
+
|
167
|
+
# We can't interpolate at this point since we don't have any
|
168
|
+
# scopes set up. Warn the user if they use a variable reference
|
169
|
+
pat = file
|
170
|
+
if pat.index("$")
|
171
|
+
Puppet.warning(
|
172
|
+
"The import of #{pat} contains a variable reference;" +
|
173
|
+
" variables are not interpolated for imports " +
|
174
|
+
"in file #{@lexer.file} at line #{@lexer.line}"
|
175
|
+
)
|
176
|
+
end
|
177
|
+
files = Puppet::Module::find_manifests(pat, dir)
|
178
|
+
if files.size == 0
|
179
|
+
raise Puppet::ImportError.new("No file(s) found for import " +
|
180
|
+
"of '#{pat}'")
|
181
|
+
end
|
182
|
+
|
183
|
+
files.collect { |file|
|
184
|
+
parser = Puppet::Parser::Parser.new(@astset)
|
185
|
+
parser.files = self.files
|
186
|
+
Puppet.debug("importing '%s'" % file)
|
187
|
+
|
188
|
+
unless file =~ /^#{File::SEPARATOR}/
|
189
|
+
file = File.join(dir, file)
|
190
|
+
end
|
191
|
+
begin
|
192
|
+
parser.file = file
|
193
|
+
rescue Puppet::AlreadyImportedError
|
194
|
+
# This file has already been imported to just move on
|
195
|
+
next
|
196
|
+
end
|
197
|
+
|
198
|
+
# This will normally add code to the 'main' class.
|
199
|
+
parser.parse
|
200
|
+
}
|
201
|
+
end
|
202
|
+
|
203
|
+
def initialize(astset = nil)
|
204
|
+
initvars()
|
205
|
+
if astset
|
206
|
+
@astset = astset
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Initialize or reset all of our variables.
|
211
|
+
def initvars
|
212
|
+
@lexer = Puppet::Parser::Lexer.new()
|
213
|
+
@files = []
|
214
|
+
@loaded = []
|
215
|
+
|
216
|
+
# This is where we store our classes and definitions and nodes.
|
217
|
+
# Clear each hash, just to help the GC a bit.
|
218
|
+
if defined?(@astset)
|
219
|
+
[:classes, :definitions, :nodes].each do |name|
|
220
|
+
@astset.send(name).clear
|
221
|
+
end
|
222
|
+
end
|
223
|
+
@astset = ASTSet.new({}, {}, {})
|
224
|
+
end
|
225
|
+
|
226
|
+
# Try to load a class, since we could not find it.
|
227
|
+
def load(classname)
|
228
|
+
return false if classname == ""
|
229
|
+
filename = classname.gsub("::", File::SEPARATOR)
|
230
|
+
|
231
|
+
loaded = false
|
232
|
+
# First try to load the top-level module
|
233
|
+
mod = filename.scan(/^[\w-]+/).shift
|
234
|
+
unless @loaded.include?(mod)
|
235
|
+
@loaded << mod
|
236
|
+
begin
|
237
|
+
import(mod)
|
238
|
+
Puppet.info "Autoloaded module %s" % mod
|
239
|
+
loaded = true
|
240
|
+
rescue Puppet::ImportError => detail
|
241
|
+
# We couldn't load the module
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
unless filename == mod and ! @loaded.include?(mod)
|
246
|
+
@loaded << mod
|
247
|
+
# Then the individual file
|
248
|
+
begin
|
249
|
+
import(filename)
|
250
|
+
Puppet.info "Autoloaded file %s from module %s" % [filename, mod]
|
251
|
+
loaded = true
|
252
|
+
rescue Puppet::ImportError => detail
|
253
|
+
# We couldn't load the file
|
254
|
+
end
|
255
|
+
end
|
256
|
+
return loaded
|
257
|
+
end
|
258
|
+
|
259
|
+
# Split an fq name into a namespace and name
|
260
|
+
def namesplit(fullname)
|
261
|
+
ary = fullname.split("::")
|
262
|
+
n = ary.pop || ""
|
263
|
+
ns = ary.join("::")
|
264
|
+
return ns, n
|
265
|
+
end
|
266
|
+
|
267
|
+
# Create a new class, or merge with an existing class.
|
268
|
+
def newclass(name, options = {})
|
269
|
+
name = name.downcase
|
270
|
+
|
271
|
+
if definitions.include?(name)
|
272
|
+
raise Puppet::ParseError, "Cannot redefine class %s as a definition" % name
|
273
|
+
end
|
274
|
+
code = options[:code]
|
275
|
+
parent = options[:parent]
|
276
|
+
|
277
|
+
# If the class is already defined, then add code to it.
|
278
|
+
if other = @astset.classes[name]
|
279
|
+
# Make sure the parents match
|
280
|
+
if parent and other.parentclass and (parent != other.parentclass)
|
281
|
+
error("Class %s is already defined at %s:%s; cannot redefine" % [name, other.file, other.line])
|
282
|
+
end
|
283
|
+
|
284
|
+
# This might be dangerous...
|
285
|
+
if parent and ! other.parentclass
|
286
|
+
other.parentclass = parent
|
287
|
+
end
|
288
|
+
|
289
|
+
# This might just be an empty, stub class.
|
290
|
+
if code
|
291
|
+
tmp = name
|
292
|
+
if tmp == ""
|
293
|
+
tmp = "main"
|
294
|
+
end
|
295
|
+
|
296
|
+
Puppet.debug addcontext("Adding code to %s" % tmp)
|
297
|
+
# Else, add our code to it.
|
298
|
+
if other.code and code
|
299
|
+
other.code.children += code.children
|
300
|
+
else
|
301
|
+
other.code ||= code
|
302
|
+
end
|
303
|
+
end
|
304
|
+
else
|
305
|
+
# Define it anew.
|
306
|
+
# Note we're doing something somewhat weird here -- we're setting
|
307
|
+
# the class's namespace to its fully qualified name. This means
|
308
|
+
# anything inside that class starts looking in that namespace first.
|
309
|
+
args = {:namespace => name, :classname => name, :parser => self}
|
310
|
+
args[:code] = code if code
|
311
|
+
args[:parentclass] = parent if parent
|
312
|
+
@astset.classes[name] = ast AST::HostClass, args
|
313
|
+
end
|
314
|
+
|
315
|
+
return @astset.classes[name]
|
316
|
+
end
|
317
|
+
|
318
|
+
# Create a new definition.
|
319
|
+
def newdefine(name, options = {})
|
320
|
+
name = name.downcase
|
321
|
+
if @astset.classes.include?(name)
|
322
|
+
raise Puppet::ParseError, "Cannot redefine class %s as a definition" %
|
323
|
+
name
|
324
|
+
end
|
325
|
+
# Make sure our definition doesn't already exist
|
326
|
+
if other = @astset.definitions[name]
|
327
|
+
error("%s is already defined at %s:%s; cannot redefine" % [name, other.file, other.line])
|
328
|
+
end
|
329
|
+
|
330
|
+
ns, whatever = namesplit(name)
|
331
|
+
args = {
|
332
|
+
:namespace => ns,
|
333
|
+
:arguments => options[:arguments],
|
334
|
+
:code => options[:code],
|
335
|
+
:parser => self,
|
336
|
+
:classname => name
|
337
|
+
}
|
338
|
+
|
339
|
+
[:code, :arguments].each do |param|
|
340
|
+
args[param] = options[param] if options[param]
|
341
|
+
end
|
342
|
+
|
343
|
+
@astset.definitions[name] = ast AST::Component, args
|
344
|
+
end
|
345
|
+
|
346
|
+
# Create a new node. Nodes are special, because they're stored in a global
|
347
|
+
# table, not according to namespaces.
|
348
|
+
def newnode(names, options = {})
|
349
|
+
names = [names] unless names.instance_of?(Array)
|
350
|
+
names.collect do |name|
|
351
|
+
name = name.to_s.downcase
|
352
|
+
if other = @astset.nodes[name]
|
353
|
+
error("Node %s is already defined at %s:%s; cannot redefine" % [other.name, other.file, other.line])
|
354
|
+
end
|
355
|
+
name = name.to_s if name.is_a?(Symbol)
|
356
|
+
args = {
|
357
|
+
:name => name,
|
358
|
+
:parser => self
|
359
|
+
}
|
360
|
+
if options[:code]
|
361
|
+
args[:code] = options[:code]
|
362
|
+
end
|
363
|
+
if options[:parent]
|
364
|
+
args[:parentclass] = options[:parent]
|
365
|
+
end
|
366
|
+
@astset.nodes[name] = ast(AST::Node, args)
|
367
|
+
@astset.nodes[name].classname = name
|
368
|
+
@astset.nodes[name]
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
def on_error(token,value,stack)
|
373
|
+
#on '%s' at '%s' in\n'%s'" % [token,value,stack]
|
374
|
+
#error = "line %s: parse error after '%s'" %
|
375
|
+
# [@lexer.line,@lexer.last]
|
376
|
+
error = "Syntax error at '%s'" % [value]
|
377
|
+
|
378
|
+
if brace = @lexer.expected
|
379
|
+
error += "; expected '%s'" % brace
|
380
|
+
end
|
381
|
+
|
382
|
+
except = Puppet::ParseError.new(error)
|
383
|
+
except.line = @lexer.line
|
384
|
+
if @lexer.file
|
385
|
+
except.file = @lexer.file
|
386
|
+
end
|
387
|
+
|
388
|
+
raise except
|
389
|
+
end
|
390
|
+
|
391
|
+
# how should I do error handling here?
|
392
|
+
def parse(string = nil)
|
393
|
+
if string
|
394
|
+
self.string = string
|
395
|
+
end
|
396
|
+
begin
|
397
|
+
main = yyparse(@lexer,:scan)
|
398
|
+
rescue Racc::ParseError => except
|
399
|
+
error = Puppet::ParseError.new(except)
|
400
|
+
error.line = @lexer.line
|
401
|
+
error.file = @lexer.file
|
402
|
+
error.set_backtrace except.backtrace
|
403
|
+
raise error
|
404
|
+
rescue Puppet::ParseError => except
|
405
|
+
except.line ||= @lexer.line
|
406
|
+
except.file ||= @lexer.file
|
407
|
+
raise except
|
408
|
+
rescue Puppet::Error => except
|
409
|
+
# and this is a framework error
|
410
|
+
except.line ||= @lexer.line
|
411
|
+
except.file ||= @lexer.file
|
412
|
+
raise except
|
413
|
+
rescue Puppet::DevError => except
|
414
|
+
except.line ||= @lexer.line
|
415
|
+
except.file ||= @lexer.file
|
416
|
+
raise except
|
417
|
+
rescue => except
|
418
|
+
error = Puppet::DevError.new(except.message)
|
419
|
+
error.line = @lexer.line
|
420
|
+
error.file = @lexer.file
|
421
|
+
error.set_backtrace except.backtrace
|
422
|
+
raise error
|
423
|
+
end
|
424
|
+
if main
|
425
|
+
# Store the results as the top-level class.
|
426
|
+
newclass("", :code => main)
|
427
|
+
end
|
428
|
+
return @astset
|
429
|
+
ensure
|
430
|
+
@lexer.clear
|
431
|
+
end
|
432
|
+
|
433
|
+
# See if any of the files have changed.
|
434
|
+
def reparse?
|
435
|
+
if file = @files.detect { |file| file.changed? }
|
436
|
+
return file.stamp
|
437
|
+
else
|
438
|
+
return false
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
def string=(string)
|
443
|
+
@lexer.string = string
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
# $Id: parser_support.rb 2744 2007-08-04 20:00:19Z luke $
|