File: //proc/self/root/usr/lib/ruby/site_ruby/1.8/puppet/external/nagios/grammar.ry
# vim: syntax=ruby
class Nagios::Parser
token DEFINE NAME PARAM LCURLY RCURLY VALUE RETURN
rule
decls: decl { return val[0] if val[0] }
| decls decl {
if val[1].nil?
result = val[0]
else
if val[0].nil?
result = val[1]
else
result = [ val[0], val[1] ].flatten
end
end
}
;
decl: object { result = [val[0]] }
| RETURN { result = nil }
;
object: DEFINE NAME LCURLY returns vars RCURLY {
result = Nagios::Base.create(val[1],val[4])
}
;
vars: var
| vars var {
val[1].each {|p,v|
val[0][p] = v
}
result = val[0]
}
;
var: PARAM VALUE returns { result = {val[0] => val[1]} }
| PARAM returns { result = {val[0] => "" } }
;
returns: RETURN
| RETURN returns
;
end
----inner
require 'strscan'
class ::Nagios::Parser::SyntaxError < RuntimeError; end
def parse(src)
if src.respond_to?("force_encoding") then
src.force_encoding("ASCII-8BIT")
end
@ss = StringScanner.new(src)
# state variables
@in_parameter_value = false
@in_object_definition = false
@done = false
@line = 1
@yydebug = true
do_parse
end
# This tokenizes the outside of object definitions,
# and detects when we start defining an object.
# We ignore whitespaces, comments and inline comments.
# We yield when finding newlines, the "define" keyword,
# the object name and the opening curly bracket.
def tokenize_outside_definitions
case
when (chars = @ss.skip(/[ \t]+/)) # ignore whitespace /\s+/
;
when (text = @ss.scan(/\#.*$/)) # ignore comments
;
when (text = @ss.scan(/;.*$/)) # ignore inline comments
;
when (text = @ss.scan(/\n/)) # newline
[:RETURN, text]
when (text = @ss.scan(/\b(define)\b/)) # the "define" keyword
[:DEFINE, text]
when (text = @ss.scan(/[^{ \t\n]+/)) # the name of the object being defined (everything not an opening curly bracket or a separator)
[:NAME, text]
when (text = @ss.scan(/\{/)) # the opening curly bracket - we enter object definition
@in_object_definition = true
[:LCURLY, text]
else
text = @ss.string[@ss.pos .. -1]
raise ScanError, "can not match: '#{text}'"
end # case
end
# This tokenizes until we find the parameter name.
def tokenize_parameter_name
case
when (chars = @ss.skip(/[ \t]+/)) # ignore whitespace /\s+/
;
when (text = @ss.scan(/\#.*$/)) # ignore comments
;
when (text = @ss.scan(/;.*$/)) # ignore inline comments
;
when (text = @ss.scan(/\n/)) # newline
[:RETURN, text]
when (text = @ss.scan(/\}/)) # closing curly bracket : end of definition
@in_object_definition = false
[:RCURLY, text]
when (not @in_parameter_value and (text = @ss.scan(/\S+/))) # This is the name of the parameter
@in_parameter_value = true
[:PARAM, text]
else
text = @ss.string[@ss.pos .. -1]
raise ScanError, "can not match: '#{text}'"
end # case
end
# This tokenizes the parameter value.
# There is a special handling for lines containing semicolons :
# - unescaped semicolons are line comments (and should stop parsing of the line)
# - escaped (with backslash \) semicolons should be kept in the parameter value (without the backslash)
def tokenize_parameter_value
case
when (chars = @ss.skip(/[ \t]+/)) # ignore whitespace /\s+/
;
when (text = @ss.scan(/\#.*$/)) # ignore comments
;
when (text = @ss.scan(/\n/)) # newline
@in_parameter_value = false
[:RETURN, text]
when (text = @ss.scan(/.+$/)) # Value of parameter
@in_parameter_value = false
# Special handling of inline comments (;) and escaped semicolons (\;)
# We split the string on escaped semicolons (\;),
# Then we rebuild it as long as there are no inline comments (;)
# We join the rebuilt string with unescaped semicolons (on purpose)
array = text.split('\;', 0)
text = ""
array.each do |elt|
# Now we split at inline comments. If we have more than 1 element in the array
# it means we have an inline comment, so we are able to stop parsing
# However we still want to reconstruct the string with its first part (before the comment)
linearray = elt.split(';', 0)
# Let's reconstruct the string with a (unescaped) semicolon
if text != "" then
text += ';'
end
text += linearray[0]
# Now we can stop
if linearray.length > 1 then
break
end
end
# We strip the text to remove spaces between end of string and beginning of inline comment
[:VALUE, text.strip]
else
text = @ss.string[@ss.pos .. -1]
raise ScanError, "can not match: '#{text}'"
end # case
end
# This tokenizes inside an object definition.
# Two cases : parameter name and parameter value
def tokenize_inside_definitions
if @in_parameter_value
tokenize_parameter_value
else
tokenize_parameter_name
end
end
# The lexer. Very simple.
def token
text = @ss.peek(1)
@line += 1 if text == "\n"
token = if @in_object_definition
tokenize_inside_definitions
else
tokenize_outside_definitions
end
token
end
def next_token
return if @ss.eos?
# skips empty actions
until _next_token = token or @ss.eos?; end
_next_token
end
def yydebug
1
end
def yywrap
0
end
def on_error(token, value, vstack )
# text = @ss.string[@ss.pos .. -1]
text = @ss.peek(20)
msg = ""
unless value.nil?
msg = "line #{@line}: syntax error at value '#{value}' : #{text}"
else
msg = "line #{@line}: syntax error at token '#{token}' : #{text}"
end
if @ss.eos?
msg = "line #{@line}: Unexpected end of file"
end
if token == '$end'.intern
puts "okay, this is silly"
else
raise ::Nagios::Parser::SyntaxError, msg
end
end