summaryrefslogtreecommitdiffstats
path: root/lib/puppet/parser/grammar.ra
diff options
context:
space:
mode:
authorluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-10-04 18:24:24 +0000
committerluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-10-04 18:24:24 +0000
commit28cee40689440388994a4768bd301ae32c8ecc05 (patch)
treec865ab44f4c9247052cf83de16ffc8ebe8b15e54 /lib/puppet/parser/grammar.ra
parente0e291332bd4676962a28c7b220ae5c5e9651c0a (diff)
downloadpuppet-28cee40689440388994a4768bd301ae32c8ecc05.tar.gz
puppet-28cee40689440388994a4768bd301ae32c8ecc05.tar.xz
puppet-28cee40689440388994a4768bd301ae32c8ecc05.zip
Merging the changes from the override-refactor branch. This is a significant rewrite of the parser, but it has little affect on the rest of the code tree.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@1726 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib/puppet/parser/grammar.ra')
-rw-r--r--lib/puppet/parser/grammar.ra485
1 files changed, 265 insertions, 220 deletions
diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra
index 3a1a78e50..5c91aabdc 100644
--- a/lib/puppet/parser/grammar.ra
+++ b/lib/puppet/parser/grammar.ra
@@ -5,38 +5,52 @@
class Puppet::Parser::Parser
token LBRACK DQTEXT SQTEXT RBRACK LBRACE RBRACE SYMBOL FARROW COMMA TRUE
-token FALSE EQUALS LESSEQUAL NOTEQUAL DOT COLON TYPE
+token FALSE EQUALS LESSEQUAL NOTEQUAL DOT COLON TYPE LLCOLLECT RRCOLLECT
token QMARK LPAREN RPAREN ISEQUAL GREATEREQUAL GREATERTHAN LESSTHAN
token IF ELSE IMPORT DEFINE ELSIF VARIABLE CLASS INHERITS NODE BOOLEAN
-token NAME SEMIC CASE DEFAULT AT LCOLLECT RCOLLECT
+token NAME SEMIC CASE DEFAULT AT LCOLLECT RCOLLECT CLASSNAME CLASSREF
+token NOT OR AND
# We have 2 shift/reduce conflicts
#expect 2
rule
program: statements {
- # Make sure we always return an array.
- if val[0].is_a?(AST::ASTArray)
- result = val[0]
+ if val[0]
+ # Make sure we always return an array.
+ if val[0].is_a?(AST::ASTArray)
+ if val[0].children.empty?
+ result = nil
+ else
+ result = val[0]
+ end
+ else
+ result = aryfy(val[0])
+ end
else
- result = aryfy(val[0])
+ result = nil
end
}
- | nothing
+ | nil
-statements: statement
+statements: statement
| statements statement {
- if val[0].instance_of?(AST::ASTArray)
- val[0].push(val[1])
- result = val[0]
- else
- result = ast AST::ASTArray, :children => [val[0],val[1]]
+ if val[0] and val[1]
+ if val[0].instance_of?(AST::ASTArray)
+ val[0].push(val[1])
+ result = val[0]
+ else
+ result = ast AST::ASTArray, :children => [val[0],val[1]]
+ end
+ elsif obj = (val[0] || val[1])
+ result = obj
+ else result = nil
end
}
# The main list of valid statements
-statement: object
- | collectable
+statement: resource
+ | virtualresource
| collection
| assignment
| casestatement
@@ -46,15 +60,16 @@ statement: object
| definition
| hostclass
| nodedef
+ | resourceoverride
-fstatement: NAME LPAREN classnames RPAREN {
+fstatement: NAME LPAREN namestrings RPAREN {
args = aryfy(val[2])
result = ast AST::Function,
:name => val[0],
:arguments => args,
:ftype => :statement
}
- | NAME classnames {
+ | NAME namestrings {
args = aryfy(val[1])
result = ast AST::Function,
:name => val[0],
@@ -62,108 +77,163 @@ fstatement: NAME LPAREN classnames RPAREN {
:ftype => :statement
}
-# Includes are just syntactic sugar for classes with no names and
-# no arguments.
-#include: INCLUDE classnames {
-# result = function_include(val[1])
-#}
-
-# Define a new tag. Both of these functions should really be done generically,
-# but I'm not in a position to do that just yet. :/
-#tag: TAG classnames {
-# result = function_tag(val[1])
-#}
-
-classnames: classname
- | classnames COMMA classname {
+namestrings: namestring
+ | namestrings COMMA namestring {
result = aryfy(val[0], val[2])
result.line = @lexer.line
result.file = @lexer.file
}
-classname: name
+namestring: name
| variable
| quotedtext
-#object: name LBRACE objectname COLON params endcomma RBRACE {
-object: name LBRACE objectinstances endsemi RBRACE {
+resource: NAME LBRACE resourceinstances endsemi RBRACE {
if val[0].instance_of?(AST::ASTArray)
- raise Puppet::ParseError, "Invalid name"
+ error "Invalid name"
end
array = val[2]
- if array.instance_of?(AST::ObjectInst)
+ if array.instance_of?(AST::ResourceInst)
array = [array]
end
result = ast AST::ASTArray
- # this iterates across each specified objectinstance
+ # this iterates across each specified resourceinstance
array.each { |instance|
- unless instance.instance_of?(AST::ObjectInst)
+ unless instance.instance_of?(AST::ResourceInst)
raise Puppet::Dev, "Got something that isn't an instance"
end
# now, i need to somehow differentiate between those things with
# arrays in their names, and normal things
- result.push ast(AST::ObjectDef,
+ result.push ast(AST::ResourceDef,
:type => val[0],
- :name => instance[0],
+ :title => instance[0],
:params => instance[1])
}
-} | name LBRACE params endcomma RBRACE {
- if val[0].instance_of?(AST::ASTArray)
- Puppet.notice "invalid name"
- raise Puppet::ParseError, "Invalid name"
- end
- # an object but without a name
- # this cannot be an instance of a library type
- result = ast AST::ObjectDef, :type => val[0], :params => val[2]
-
-} | type LBRACE params endcomma RBRACE {
+} | NAME LBRACE params endcomma RBRACE {
+ # This is a deprecated syntax.
+ error "All resource specifications require names"
+} | TYPE LBRACE params endcomma RBRACE {
# a template setting for a type
if val[0].instance_of?(AST::ASTArray)
- raise Puppet::ParseError, "Invalid type"
+ error "Invalid type"
end
- result = ast(AST::TypeDefaults, :type => val[0], :params => val[2])
+ result = ast(AST::ResourceDefaults, :type => val[0], :params => val[2])
+}
+
+# Override a value set elsewhere in the configuration.
+resourceoverride: resourceref LBRACE params RBRACE {
+ result = ast AST::ResourceOverride, :object => val[0], :params => val[2]
}
-# Collectable objects; these get stored in the database, instead of
-# being passed to the client.
-collectable: AT object {
- unless Puppet[:storeconfigs]
- raise Puppet::ParseError, "You cannot collect without storeconfigs being set"
+# Exported and virtual resources; these don't get sent to the client
+# unless they get collected elsewhere in the db.
+virtualresource: at resource {
+ type = val[0]
+
+ if type == :exported and ! Puppet[:storeconfigs]
+ error "You cannot collect without storeconfigs being set"
end
- if val[1].is_a? AST::TypeDefaults
- raise Puppet::ParseError, "Defaults are not collectable"
+ if val[1].is_a? AST::ResourceDefaults
+ error "Defaults are not virtualizable"
end
- # Just mark our objects as collectable and pass them through.
+ method = type.to_s + "="
+
+ # Just mark our resources as exported and pass them through.
if val[1].instance_of?(AST::ASTArray)
val[1].each do |obj|
- obj.collectable = true
+ obj.send(method, true)
end
else
- val[1].collectable = true
+ val[1].send(method, true)
end
result = val[1]
}
+at: AT { result = :virtual }
+ | AT AT { result = :exported }
+
# A collection statement. Currently supports no arguments at all, but eventually
# will, I assume.
-collection: name LCOLLECT RCOLLECT {
- unless Puppet[:storeconfigs]
- raise Puppet::ParseError, "You cannot collect without storeconfigs being set"
+collection: collectname collectrhand {
+ if val[0] =~ /^[a-z]/
+ Puppet.warning addcontext("Collection names must now be capitalized")
+ end
+ type = val[0].downcase
+ args = {:type => type}
+
+ if val[1].is_a?(AST::CollExpr)
+ args[:query] = val[1]
+ args[:query].type = type
+ args[:form] = args[:query].form
+ else
+ args[:form] = val[1]
+ end
+ if args[:form] == :exported and ! Puppet[:storeconfigs]
+ error "You cannot collect exported resources without storeconfigs being set"
end
- result = ast AST::Collection, :type => val[0]
+ result = ast AST::Collection, args
}
-objectinst: objectname COLON params endcomma {
- result = ast AST::ObjectInst, :children => [val[0],val[2]]
+collectname: TYPE | NAME
+
+collectrhand: LCOLLECT collstatements RCOLLECT {
+ if val[1]
+ result = val[1]
+ result.form = :virtual
+ else
+ result = :virtual
+ end
}
+ | LLCOLLECT collstatements RRCOLLECT {
+ if val[1]
+ result = val[1]
+ result.form = :exported
+ else
+ result = :exported
+ end
+}
+
+# A mini-language for handling collection comparisons. This is organized
+# to avoid the need for precedence indications.
+collstatements: nil
+ | collstatement
+ | collstatements colljoin collstatement {
+ result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2]
+}
+
+collstatement: collexpr
+ | LPAREN collstatements RPAREN {
+ result = val[1]
+ result.parens = true
+}
+
+colljoin: AND | OR
-objectinstances: objectinst
- | objectinstances SEMIC objectinst {
- if val[0].instance_of?(AST::ObjectInst)
+collexpr: colllval ISEQUAL simplervalue {
+ result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2]
+ #result = ast AST::CollExpr
+ #result.push *val
+}
+ | colllval NOTEQUAL simplervalue {
+ result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2]
+ #result = ast AST::CollExpr
+ #result.push *val
+}
+
+colllval: variable
+ | name
+
+resourceinst: resourcename COLON params endcomma {
+ result = ast AST::ResourceInst, :children => [val[0],val[2]]
+}
+
+resourceinstances: resourceinst
+ | resourceinstances SEMIC resourceinst {
+ if val[0].instance_of?(AST::ResourceInst)
result = ast AST::ASTArray, :children => [val[0],val[2]]
else
val[0].push val[2]
@@ -182,7 +252,7 @@ type: TYPE {
result = ast AST::Type, :value => val[0]
}
-objectname: quotedtext
+resourcename: quotedtext
| name
| type
| selector
@@ -191,7 +261,7 @@ objectname: quotedtext
assignment: VARIABLE EQUALS rvalue {
# this is distinct from referencing a variable
- variable = ast AST::Name, :value => val[0].sub(/^\$/,'')
+ variable = ast AST::Name, :value => val[0]
result = ast AST::VarDef, :name => variable, :value => val[2]
}
@@ -210,8 +280,7 @@ params: # nothing
}
param: NAME FARROW rvalue {
- leaf = ast AST::String, :value => val[0]
- result = ast AST::ObjectParam, :param => leaf, :value => val[2]
+ result = ast AST::ResourceParam, :param => val[0], :value => val[2]
}
rvalues: rvalue
@@ -223,6 +292,13 @@ rvalues: rvalue
end
}
+simplervalue: quotedtext
+ | name
+ | type
+ | boolean
+ | selector
+ | variable
+
rvalue: quotedtext
| name
| type
@@ -230,11 +306,11 @@ rvalue: quotedtext
| selector
| variable
| array
- | objectref
+ | resourceref
| funcrvalue
# We currently require arguments in these functions.
-funcrvalue: NAME LPAREN classnames RPAREN {
+funcrvalue: NAME LPAREN namestrings RPAREN {
args = aryfy(val[2])
result = ast AST::Function,
:name => val[0],
@@ -252,8 +328,11 @@ boolean: BOOLEAN {
result = ast AST::Boolean, :value => val[0]
}
-objectref: name LBRACK rvalue RBRACK {
- result = ast AST::ObjectRef, :type => val[0], :name => val[2]
+resourceref: NAME LBRACK rvalue RBRACK {
+ Puppet.warning addcontext("Deprecation notice: Resource references should now be capitalized")
+ result = ast AST::ResourceRef, :type => val[0], :title => val[2]
+} | TYPE LBRACK rvalue RBRACK {
+ result = ast AST::ResourceRef, :type => val[0], :title => val[2]
}
ifstatement: IF iftest LBRACE statements RBRACE else {
@@ -333,7 +412,7 @@ sintvalues: selectval
}
selectval: selectlhand FARROW rvalue {
- result = ast AST::ObjectParam, :param => val[0], :value => val[2]
+ result = ast AST::ResourceParam, :param => val[0], :value => val[2]
}
selectlhand: name
@@ -378,7 +457,7 @@ import: IMPORT quotedtext {
end
files.each { |file|
- parser = Puppet::Parser::Parser.new()
+ parser = Puppet::Parser::Parser.new(interp)
parser.files = self.files
Puppet.debug("importing '%s'" % file)
@@ -396,8 +475,13 @@ import: IMPORT quotedtext {
end
# push the results into the main result array
# We always return an array when we parse.
- parser.parse.each do |child|
- result.push child
+ ast = parser.parse
+
+ # Things that just get added to the classtable or whatever return nil
+ if ast
+ ast.each do |child|
+ result.push child
+ end
end
}
}
@@ -405,165 +489,99 @@ import: IMPORT quotedtext {
# Disable definition inheritance for now. 8/27/06, luke
#definition: DEFINE NAME argumentlist parent LBRACE statements RBRACE {
-definition: DEFINE NAME argumentlist LBRACE statements RBRACE {
- args = {
- :type => ast(AST::Name, :value => val[1]),
- :args => val[2],
- :code => val[4] # Switch to 5 for parents
- }
+definition: DEFINE fqname argumentlist LBRACE statements RBRACE {
+ interp.newdefine fqname(val[1]), :arguments => val[2], :code => val[4]
+ @lexer.indefine = false
+ result = nil
- if val[3].instance_of?(AST::Name)
- args[:parentclass] = val[3]
- end
- result = ast AST::CompDef, args
#} | DEFINE NAME argumentlist parent LBRACE RBRACE {
-} | DEFINE NAME argumentlist LBRACE RBRACE {
- args = {
- :type => ast(AST::Name, :value => val[1]),
- :args => val[2],
- :code => ast(AST::ASTArray)
- }
-
- if val[3].instance_of?(AST::Name)
- args[:parentclass] = val[3]
- end
-
- result = ast AST::CompDef, args
+} | DEFINE fqname argumentlist LBRACE RBRACE {
+ interp.newdefine fqname(val[1]), :arguments => val[2]
+ @lexer.indefine = false
+ result = nil
}
#hostclass: CLASS NAME argumentlist parent LBRACE statements RBRACE {
-hostclass: CLASS NAME parent LBRACE statements RBRACE {
- #:args => val[2],
- args = {
- :type => ast(AST::Name, :value => val[1]),
- :code => val[4]
- }
- # It'll be an ASTArray if we didn't get a parent
- if val[2].instance_of?(AST::Name)
- args[:parentclass] = val[2]
- end
- result = ast AST::ClassDef, args
-} | CLASS NAME parent LBRACE RBRACE {
- args = {
- :type => ast(AST::Name, :value => val[1]),
- :code => ast(AST::ASTArray, :children => [])
- }
- # It'll be an ASTArray if we didn't get a parent
- if val[2].instance_of?(AST::Name)
- args[:parentclass] = val[2]
- end
- result = ast AST::ClassDef, args
+hostclass: CLASS fqname parent LBRACE statements RBRACE {
+ # Our class gets defined in the parent namespace, not our own.
+ @lexer.namepop
+ interp.newclass fqname(val[1]), :code => val[4], :parent => val[2]
+ result = nil
+} | CLASS fqname parent LBRACE RBRACE {
+ # Our class gets defined in the parent namespace, not our own.
+ @lexer.namepop
+ interp.newclass fqname(val[1]), :parent => val[2]
+ result = nil
}
nodedef: NODE hostnames parent LBRACE statements RBRACE {
- unless val[1].instance_of?(AST::ASTArray)
- val[1] = ast AST::ASTArray,
- :line => val[1].line,
- :file => val[1].file,
- :children => [val[1]]
- end
- args = {
- :names => val[1],
- :code => val[4]
- }
- if val[2].instance_of?(AST::Name)
- args[:parentclass] = val[2]
- end
- result = ast AST::NodeDef, args
+ interp.newnode val[1], :parent => val[2], :code => val[4]
+ result = nil
} | NODE hostnames parent LBRACE RBRACE {
- unless val[1].instance_of?(AST::ASTArray)
- val[1] = ast AST::ASTArray,
- :line => val[1].line,
- :file => val[1].file,
- :children => [val[1]]
- end
- args = {
- :names => val[1],
- :code => ast(AST::ASTArray, :children => [])
- }
- if val[2].instance_of?(AST::Name)
- args[:parentclass] = val[2]
- end
- result = ast AST::NodeDef,args
+ interp.newnode val[1], :parent => val[2]
+ result = nil
}
-# Multiple hostnames, as used for node names.
+fqname: NAME
+ | CLASSNAME
+
+# Multiple hostnames, as used for node names. These are all literal
+# strings, not AST objects.
hostnames: hostname
| hostnames COMMA hostname {
- if val[0].instance_of?(AST::ASTArray)
- result = val[0]
- result.push val[2]
- else
- result = ast AST::ASTArray, :children => [val[0], val[2]]
- end
+ result = val[0]
+ result = [result] unless result.is_a?(Array)
+ result << val[2]
}
-hostname: NAME {
- result = ast AST::HostName, :value => val[0]
-} | SQTEXT {
- result = ast AST::HostName, :value => val[0]
-} | DEFAULT {
- result = ast AST::Default, :value => val[0]
+hostname: NAME
+ | SQTEXT
+ | DEFAULT
+
+nil: {
+ result = nil
}
nothing: {
result = ast AST::ASTArray, :children => []
}
-argumentlist: nothing
+argumentlist: nil
| LPAREN nothing RPAREN {
- result = val[1]
+ result = nil
}
| LPAREN arguments RPAREN {
- if val[1].instance_of?(AST::ASTArray)
- result = val[1]
- else
- result = ast AST::ASTArray, :children => [val[1]]
- end
+ result = val[1]
+ result = [result] unless result[0].is_a?(Array)
}
arguments: argument
| arguments COMMA argument {
- if val[0].instance_of?(AST::ASTArray)
- val[0].push(val[2])
- result = val[0]
- else
- result = ast AST::ASTArray, :children => [val[0],val[2]]
- end
+ result = val[0]
+ result = [result] unless result[0].is_a?(Array)
+ result << val[2]
}
-argument: name EQUALS rvalue {
- msg = "Deprecation notice: #{val[0].value} must now include '$' in prototype"
- msg += " at line %s" % @lexer.line
- msg += " in file %s" % @lexer.file if @lexer.file
- Puppet.warning msg
- result = ast AST::CompArgument, :children => [val[0],val[2]]
+argument: NAME EQUALS rvalue {
+ Puppet.warning addcontext("Deprecation notice: #{val[0].value} must now include '$' in prototype")
+ result = [val[0], val[2]]
}
- | name {
- msg = "Deprecation notice: #{val[0].value} must now include '$' in prototype"
- msg += " at line %s" % @lexer.line
- msg += " in file %s" % @lexer.file if @lexer.file
- Puppet.warning msg
- result = ast AST::CompArgument, :children => [val[0]]
-} | lvariable EQUALS rvalue {
- result = ast AST::CompArgument, :children => [val[0],val[2]]
-} | lvariable {
- result = ast AST::CompArgument, :children => [val[0]]
+ | NAME {
+ Puppet.warning addcontext("Deprecation notice: #{val[0].value} must now include '$' in prototype")
+ result = [val[0]]
+} | VARIABLE EQUALS rvalue {
+ result = [val[0], val[2]]
+} | VARIABLE {
+ result = [val[0]]
}
-parent: nothing
+parent: nil
| INHERITS NAME {
- result = ast AST::Name, :value => val[1]
+ result = val[1]
}
variable: VARIABLE {
- name = val[0].sub(/^\$/,'')
- result = ast AST::Variable, :value => name
-}
-
-# This is variables as lvalues; we're assigning them, not deferencing them.
-lvariable: VARIABLE {
- result = ast AST::Name, :value => val[0].sub(/^\$/,'')
+ result = ast AST::Variable, :value => val[0]
}
array: LBRACK rvalues RBRACK {
@@ -602,9 +620,21 @@ Puppet[:paramcheck] = true
---- inner ----
require 'puppet/parser/functions'
-attr_reader :file
+attr_reader :file, :interp
attr_accessor :files
+# Add context to a message; useful for error messages and such.
+def addcontext(message, obj = nil)
+ obj ||= @lexer
+
+ message += " on line %s" % obj.line
+ if file = obj.file
+ message += " in file %s" % file
+ end
+
+ return message
+end
+
# Create an AST array out of all of the args
def aryfy(*args)
if args[0].instance_of?(AST::ASTArray)
@@ -636,6 +666,17 @@ def ast(klass, hash = nil)
return klass.new(hash)
end
+# Raise a Parse error.
+def error(message)
+ except = Puppet::ParseError.new(message)
+ except.line = @lexer.line
+ if @lexer.file
+ except.file = @lexer.file
+ end
+
+ raise except
+end
+
def file=(file)
unless FileTest.exists?(file)
unless file =~ /\.pp$/
@@ -653,23 +694,20 @@ def file=(file)
end
end
-def initialize
+def initialize(interpreter)
+ @interp = interpreter
+ initvars()
+end
+
+# Initialize or reset all of our variables.
+def initvars
@lexer = Puppet::Parser::Lexer.new()
@files = []
- #if Puppet[:debug]
- # @yydebug = true
- #end
end
-# Add a new file to be checked when we're checking to see if we should be
-# reparsed.
-def newfile(*files)
- files.each do |file|
- unless file.is_a? Puppet::LoadedFile
- file = Puppet::LoadedFile.new(file)
- end
- @files << file
- end
+# The fully qualifed name, with the full namespace.
+def fqname(name)
+ [@lexer.namespace, name].join("::").sub(/^::/, '')
end
def on_error(token,value,stack)
@@ -693,7 +731,7 @@ def parse(string = nil)
self.string = string
end
begin
- yyparse(@lexer,:scan)
+ main = yyparse(@lexer,:scan)
rescue Racc::ParseError => except
error = Puppet::ParseError.new(except)
error.line = @lexer.line
@@ -720,6 +758,13 @@ def parse(string = nil)
error.set_backtrace except.backtrace
raise error
end
+ if main
+ # Store the results as the top-level class.
+ interp.newclass("", :code => main)
+ return main
+ end
+ensure
+ @lexer.clear
end
# See if any of the files have changed.