3
# Parser using the Pops model
4
# This grammar is a half step between the current 3.1. grammar and egrammar.
5
# FIXME! Keep as reference until egrammar is proven to work.
7
class Puppet::Pops::Impl::Parser::Parser
9
token STRING DQPRE DQMID DQPOST
10
token LBRACK RBRACK LBRACE RBRACE SYMBOL FARROW COMMA TRUE
11
token FALSE EQUALS APPENDS LESSEQUAL NOTEQUAL DOT COLON LLCOLLECT RRCOLLECT
12
token QMARK LPAREN RPAREN ISEQUAL GREATEREQUAL GREATERTHAN LESSTHAN
13
token IF ELSE IMPORT DEFINE ELSIF VARIABLE CLASS INHERITS NODE BOOLEAN
14
token NAME SEMIC CASE DEFAULT AT LCOLLECT RCOLLECT CLASSREF
15
token NOT OR AND UNDEF PARROW PLUS MINUS TIMES DIV LSHIFT RSHIFT UMINUS
16
token MATCH NOMATCH REGEX IN_EDGE OUT_EDGE IN_EDGE_SUB OUT_EDGE_SUB
23
# left LCOLLECT LLCOLLECT
31
left GREATEREQUAL GREATERTHAN LESSTHAN LESSEQUAL
34
# left IN_EDGE OUT_EDGE IN_EDGE_SUB OUT_EDGE_SUB
38
# Produces [Model::BlockExpression, Model::Expression, nil] depending on multiple statements, single statement or empty
40
: statements { result = Factory.block_or_expression(*val[0]) }
43
# Change may have issues with nil; i.e. program is a sequence of nils/nops
44
# Simplified from original which had validation for top level constructs - see statement rule
45
# Produces Array<Model::Expression>
47
: statement { result = [val[0]]}
48
| statements statement { result = val[0].push val[1] }
50
# Removed validation construct regarding "top level statements" as it did not seem to catch all problems
51
# and relied on a "top-level-ness" encoded in the abstract syntax tree objects
53
# The main list of valid statements
54
# Produces Model::Expression
72
| call_method_with_lambda
91
# Produces Model::RelationshipExpression
93
: relationship_side edge relationship_side { result = val[0].relop(val[1][:value], val[2]); loc result, val[1] }
94
| relationship edge relationship_side { result = val[0].relop(val[1][:value], val[2]); loc result, val[1] }
96
# Produces Model::Expression
114
# Produces Model::CallNamedFunctionExpression
116
: NAME LPAREN expressions RPAREN { result = Factory.CALL_NAMED(val[0][:value], false, val[2]) ; loc result, val[0], val[3] }
117
| NAME LPAREN expressions COMMA RPAREN { result = Factory.CALL_NAMED(val[0][:value], false, val[2]) ; loc result, val[0], val[4] }
118
| NAME LPAREN RPAREN { result = Factory.CALL_NAMED(val[0][:value], false, []) ; loc result, val[0], val[2] }
119
| NAME func_call_args { result = Factory.CALL_NAMED(val[0][:value], false, val[1]) ; loc result, val[0] }
121
call_method_with_lambda
122
: call_method { result = val[0] }
123
| call_method lambda { result = val[0]; val[0].lambda = val[1] }
126
: named_access LPAREN expressions RPAREN { result = Factory.CALL_METHOD(val[0], val[2]); loc result, val[1], val[3] }
127
| named_access LPAREN RPAREN { result = Factory.CALL_METHOD(val[0], []); loc result, val[1], val[3] }
128
| named_access { result = Factory.CALL_METHOD(val[0], []); loc result, val[0] }
131
: named_access_lval DOT NAME {
132
result = val[0].dot(Factory.fqn(val[2][:value]))
133
loc result, val[1], val[2]
136
# Obviously not ideal, it is not possible to use literal array or hash as lhs
137
# These must be assigned to a variable - this is also an issue in other places
144
| call_named_rval_function
147
: LAMBDA lambda_parameter_list statements RBRACE {
148
result = Factory.LAMBDA(val[1], val[2])
149
loc result, val[0], val[3]
151
| LAMBDA lambda_parameter_list RBRACE {
152
result = Factory.LAMBDA(val[1], nil)
153
loc result, val[0], val[2]
155
# Produces Array<Model::Parameter>
156
lambda_parameter_list
157
: PIPE PIPE { result = [] }
158
| PIPE parameters endcomma PIPE { result = val[1] }
160
# Produces Array<Model::Expression>
162
: rvalue { result = [val[0]] }
163
| func_call_args COMMA rvalue { result = val[0].push(val[2]) }
165
# Produces Array<Model::Expression>
167
: expression { result = [val[0]] }
168
| expressions comma expression { result = val[0].push(val[2]) }
171
# Produces [Model::ResourceExpression, Model::ResourceDefaultsExpression]
173
: classname LBRACE resourceinstances endsemi RBRACE {
174
result = Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2])
175
loc result, val[0], val[4]
177
| classname LBRACE attribute_operations endcomma RBRACE {
178
# This is a deprecated syntax.
179
# It also fails hard - TODO: create model and validate this case
180
error "All resource specifications require names"
182
| type LBRACE attribute_operations endcomma RBRACE {
183
# a defaults setting for a type
184
result = Factory.RESOURCE_DEFAULTS(val[0], val[2])
185
loc result, val[0], val[4]
188
# Override a value set elsewhere in the configuration.
189
# Produces Model::ResourceOverrideExpression
191
: resourceref LBRACE attribute_operations endcomma RBRACE {
193
result = Factory.RESOURCE_OVERRIDE(val[0], val[2])
194
loc result, val[0], val[4]
197
# Exported and virtual resources; these don't get sent to the client
198
# unless they get collected elsewhere in the db.
199
# The original had validation here; checking if storeconfigs is on; this is moved to a validation step
200
# Also, validation was performed if an attempt was made to virtualize or export a resource defaults
201
# this is also now deferred to validation
202
# Produces [Model::ResourceExpression, Model::ResourceDefaultsExpression]
205
val[1].form = val[0] # :virtual, :exported, (or :regular)
209
# Produces Symbol corresponding to resource form
211
: AT { result = :virtual }
212
| AT AT { result = :exported }
214
# A collection statement. Currently supports no arguments at all, but eventually
217
# Produces Model::CollectExpression
220
: type collect_query LBRACE attribute_operations endcomma RBRACE {
222
result = Factory.COLLECT(val[0].value.downcase, val[1], val[3])
223
loc result, val[0], val[5]
225
| type collect_query {
226
result = Factory.COLLECT(val[0].value.downcase, val[1], [])
227
loc result, val[0], val[1]
231
: LCOLLECT optional_query RCOLLECT { result = Factory.VIRTUAL_QUERY(val[1]) ; loc result, val[0], val[2] }
232
| LLCOLLECT optional_query RRCOLLECT { result = Factory.EXPORTED_QUERY(val[1]) ; loc result, val[0], val[2] }
234
# ORIGINAL COMMENT: A mini-language for handling collection comparisons. This is organized
235
# to avoid the need for precedence indications.
236
# (New implementation is slightly different; and when finished, it may be possible to streamline the
237
# grammar - the difference is mostly in evaluation, not in grammar)
243
# ORIGINAL: Had a weird list structure where AND and OR where at the same level, and hence, there was the
244
# need to keep track of where parenthesis were (to get order correct).
246
# This is now not needed as AND has higher precedence than OR, and parenthesis are low in precedence
249
: predicate_lval ISEQUAL expression { result = (val[0] == val[2]) ; loc result, val[1] }
250
| predicate_lval NOTEQUAL expression { result = (val[0].ne(val[2])) ; loc result, val[1] }
251
| LPAREN query RPAREN { result = val[1] }
252
| query AND query { result = val[0].and(val[2]) ; loc result, val[1] }
253
| query OR query { result = val[0].or(val[2]) ; loc result, val[1] }
256
# Produces Model::VariableExpression, or Model::QualifiedName
262
: resourcename COLON attribute_operations endcomma { result = Factory.RESOURCE_BODY(val[0], val[2]) }
265
: resourceinst { result = [val[0]] }
266
| resourceinstances SEMIC resourceinst { result = val[0].push val[2] }
278
# Assignment, only assignment to variable is legal, but parser builds expression for [] = anyway to
279
# enable a better error message
281
: VARIABLE EQUALS expression { result = Factory.var(Factory.fqn(val[0][:value])).set(val[2]) ; loc result, val[1] }
282
| hasharrayaccess EQUALS expression { result val[0].set(val[2]); loc result, val[1] }
285
: VARIABLE APPENDS expression { result = Factory.var(val[0][:value]).plus_set(val[1]) ; loc result, val[1] }
287
# Produces Array<Model::AttributeOperation>
290
| attribute_operation { result = [val[0]] }
291
| attribute_operations COMMA attribute_operation { result = val[0].push(val[2]) }
299
# Several grammar issues here: the addparam did not allow keyword and booleans as names.
300
# In this version, the wrong combinations are validated instead of producing syntax errors
301
# (Can give nicer error message +> is not applicable to...)
302
# WAT - Boolean as attribute name?
303
# Produces Model::AttributeOperation
306
: attribute_name FARROW expression {
307
result = Factory.ATTRIBUTE_OP(val[0][:value], :'=>', val[2])
308
loc result, val[0], val[2]
310
| attribute_name PARROW expression {
311
result = Factory.ATTRIBUTE_OP(val[0][:value], :'+>', val[2])
312
loc result, val[0], val[2]
315
# Produces Model::CallNamedFunction
316
call_named_rval_function
317
: NAME LPAREN expressions RPAREN { result = Factory.CALL_NAMED(val[0][:value], true, val[2]) ; loc result, val[0], val[3] }
318
| NAME LPAREN RPAREN { result = Factory.CALL_NAMED(val[0][:value], true, []) ; loc result, val[0], val[2] }
321
: STRING { result = Factory.literal(val[0][:value]) ; loc result, val[0] }
322
| dqpre dqrval { result = Factory.string(val[0], *val[1]) ; loc result, val[0], val[1][-1] }
324
dqpre : DQPRE { result = Factory.literal(val[0][:value]); loc result, val[0] }
325
dqpost : DQPOST { result = Factory.literal(val[0][:value]); loc result, val[0] }
326
dqmid : DQMID { result = Factory.literal(val[0][:value]); loc result, val[0] }
327
text_expression : expression { result = Factory.TEXT(val[0]) }
330
: text_expression dqtail { result = [val[0]] + val[1] }
333
: dqpost { result = [val[0]] }
334
| dqmid dqrval { result = [val[0]] + val[1] }
337
# Reference to Resource (future also reference to other instances of other types than Resources).
338
# First form (lower case name) is deprecated (deprecation message handled in validation). Note that
339
# this requires use of token NAME since a rule call to name causes shift reduce conflict with
340
# a function call NAME NAME (calling function with NAME as argument e.g. foo bar).
342
# Produces InstanceReference
344
: NAME LBRACK expressions RBRACK {
345
# Would want to use rule name here, but can't (need a NAME with higher precedence), so must
346
# create a QualifiedName instance here for NAME
347
result = Factory.INSTANCE(Factory.QNAME_OR_NUMBER(val[0][:value]), val[2]);
348
loc result, val[0], val[2][-1]
350
| type LBRACK expressions RBRACK {
351
result = Factory.INSTANCE(val[0], val[2]);
352
loc result, val[0], val[2][-1]
355
# Changed from Puppet 3x where there is no else part on unless
358
: UNLESS expression LBRACE statements RBRACE unless_else {
360
result = Factory.UNLESS(val[1], Factory.block_or_expression(*val[3]), val[5])
361
loc result, val[0], val[4]
363
| UNLESS expression LBRACE RBRACE unless_else {
365
result = Factory.UNLESS(val[1], nil, nil)
366
loc result, val[0], val[4]
369
# Different from else part of if, since "elsif" is not supported, but else is
371
# Produces [Model::Expression, nil] - nil if there is no else or elsif part
374
| ELSE LBRACE statements RBRACE {
376
result = Factory.block_or_expression(*val[2])
377
loc result, val[0], val[3]
379
| ELSE LBRACE RBRACE {
381
result = nil # don't think a nop is needed here either
384
# Produces Model::IfExpression
386
: IF if_expression_part {
390
# Produces Model::IfExpression
392
: expression LBRACE statements RBRACE else {
394
result = Factory.IF(val[0], Factory.block_or_expression(*val[2]), val[4])
395
loc(result, val[0], (val[4] ? val[4] : val[3]))
397
| expression LBRACE RBRACE else {
398
result = Factory.IF(val[0], nil, val[3])
399
loc(result, val[0], (val[3] ? val[3] : val[2]))
402
# Produces [Model::Expression, nil] - nil if there is no else or elsif part
405
| ELSIF if_expression_part { result = val[1] }
406
| ELSE LBRACE statements RBRACE {
408
result = Factory.block_or_expression(*val[2])
409
loc result, val[0], val[3]
411
| ELSE LBRACE RBRACE {
413
result = nil # don't think a nop is needed here either
416
# Produces Model::Expression
420
| expression IN expression { result = val[0].in val[2] ; loc result, val[1] }
421
| expression MATCH match_rvalue { result = val[0] =~ val[2] ; loc result, val[1] }
422
| expression NOMATCH match_rvalue { result = val[0].mne val[2] ; loc result, val[1] }
423
| expression PLUS expression { result = val[0] + val[2] ; loc result, val[1] }
424
| expression MINUS expression { result = val[0] - val[2] ; loc result, val[1] }
425
| expression DIV expression { result = val[0] / val[2] ; loc result, val[1] }
426
| expression TIMES expression { result = val[0] * val[2] ; loc result, val[1] }
427
| expression LSHIFT expression { result = val[0] << val[2] ; loc result, val[1] }
428
| expression RSHIFT expression { result = val[0] >> val[2] ; loc result, val[1] }
429
| MINUS expression =UMINUS { result = val[1].minus() ; loc result, val[0] }
430
| expression NOTEQUAL expression { result = val[0].ne val[2] ; loc result, val[1] }
431
| expression ISEQUAL expression { result = val[0] == val[2] ; loc result, val[1] }
432
| expression GREATERTHAN expression { result = val[0] > val[2] ; loc result, val[1] }
433
| expression GREATEREQUAL expression { result = val[0] >= val[2] ; loc result, val[1] }
434
| expression LESSTHAN expression { result = val[0] < val[2] ; loc result, val[1] }
435
| expression LESSEQUAL expression { result = val[0] <= val[2] ; loc result, val[1] }
436
| NOT expression { result = val[1].not ; loc result, val[0] }
437
| expression AND expression { result = val[0].and val[2] ; loc result, val[1] }
438
| expression OR expression { result = val[0].or val[2] ; loc result, val[1] }
439
| LPAREN expression RPAREN { result = val[1] ; }
440
| call_method_with_lambda
446
# Produces Model::CaseExpression
448
: CASE expression LBRACE case_options RBRACE {
450
result = Factory.CASE(val[1], *val[3])
451
loc result, val[0], val[4]
454
# Produces Array<Model::CaseOption>
456
: case_option { result = [val[0]] }
457
| case_options case_option { result = val[0].push val[1] }
459
# Produced Model::CaseOption (aka When)
461
: case_values COLON LBRACE statements RBRACE {
463
result = Factory.WHEN(val[0], val[3])
464
loc result, val[1], val[4]
466
| case_values COLON LBRACE RBRACE {
468
result = Factory.WHEN(val[0], nil)
469
loc result, val[1], val[3]
472
# Produces Array<Expression> mostly literals
474
: selectable { result = [val[0]] }
475
| case_values COMMA selectable { result = val[0].push val[2] }
477
# Produces Model::SelectorExpression
479
: selectable QMARK selector_entries { result = val[0].select(*val[2]) ; loc result, val[1] }
481
# Produces Array<Model::SelectorEntry>
483
: selector_entry { result = [val[0]] }
484
| LBRACE selector_entry_list endcomma RBRACE {
489
# Produces Array<Model::SelectorEntry>
491
: selector_entry { result = [val[0]] }
492
| selector_entry_list COMMA selector_entry { result = val[0].push val[2] }
494
# Produces a Model::SelectorEntry
496
: selectable FARROW rvalue { result = Factory.MAP(val[0], val[2]) ; loc result, val[1] }
498
# Produces Model::Expression (most of the literals)
504
| call_named_rval_function
513
# Produces nil (noop)
516
error "Import not supported in this version of the parser", \
517
:line => stmt.context[:line], :file => stmt.context[:file]
521
# IMPORT (T.B DEPRECATED IN PUPPET WHEN IT HAS BEEN FIGURED OUT HOW TO SUPPORT
522
# THE THINGS IMPORTS ARE USED FOR.
523
# BOLDLY DECIDED TO SKIP THIS COMPLETELY IN THIS IMPLEMENTATION - will trigger an error
525
# These are only used for importing, no interpolation
527
: STRING { result = [val[0][:value]] }
531
| strings COMMA string { result = val[0].push val[2] }
533
# Produces Model::Definition
535
: DEFINE classname parameter_list LBRACE statements RBRACE {
537
result = Factory.DEFINITION(classname(val[1][:value]), val[2], val[4])
538
loc result, val[0], val[5]
539
@lexer.indefine = false
541
| DEFINE classname parameter_list LBRACE RBRACE {
543
result = Factory.DEFINITION(classname(val[1][:value]), val[2], nil)
544
loc result, val[0], val[4]
545
@lexer.indefine = false
548
# ORIGINAL COMMENT: Our class gets defined in the parent namespace, not our own.
549
# WAT ??! This is way odd; should get its complete name, classnames do not nest
550
# Seems like the call to classname makes use of the name scope
551
# (This is uneccesary, since the parent name is known when evaluating)
553
# Produces Model::HostClassDefinition
556
: CLASS classname parameter_list classparent LBRACE statements RBRACE {
559
result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5])
560
loc result, val[0], val[6]
562
| CLASS classname parameter_list classparent LBRACE RBRACE {
565
result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), nil)
566
loc result, val[0], val[5]
569
# Produces Model::NodeDefinition
571
: NODE hostnames nodeparent LBRACE statements RBRACE {
573
result = Factory.NODE(val[1], val[2], val[4])
574
loc result, val[0], val[5]
576
| NODE hostnames nodeparent LBRACE RBRACE {
578
result = Factory.NODE(val[1], val[2], nil)
579
loc result, val[0], val[4]
584
: NAME { result = val[0] }
585
| CLASS { result = val[0] }
587
# Hostnames is not a list of names, it is a list of name matchers (including a Regexp).
588
# (The old implementation had a special "Hostname" object with some minimal validation)
590
# Produces Array<Model::LiteralExpression>
593
: nodename { result = [result] }
594
| hostnames COMMA nodename { result = val[0].push(val[2]) }
596
# Produces Model::LiteralExpression
601
# Produces a LiteralExpression (string, :default, or regexp)
603
: NAME { result = Factory.fqn(val[0][:value]); loc result, val[0] }
604
| STRING { result = Factory.literal(val[0][:value]); loc result, val[0] }
605
| DEFAULT { result = Factory.literal(:default); loc result, val[0] }
609
# Produces Array<Model::Parameter>
611
: nil { result = [] }
612
| LPAREN RPAREN { result = [] }
613
| LPAREN parameters endcomma RPAREN { result = val[1] }
615
# Produces Array<Model::Parameter>
617
: parameter { result = [val[0]] }
618
| parameters COMMA parameter { result = val[0].push(val[2]) }
620
# Produces Model::Parameter
622
: VARIABLE EQUALS expression { result = Factory.PARAM(val[0][:value], val[2]) ; loc result, val[0] }
623
| VARIABLE { result = Factory.PARAM(val[0][:value]); loc result, val[0] }
625
# Produces Expression, since hostname is an Expression
628
| INHERITS hostname { result = val[1] }
630
# Produces String, name or nil result
633
| INHERITS classnameordefault { result = val[1] }
635
# Produces String (this construct allows a class to be named "default" and to be referenced as
637
# TODO: Investigate the validity
638
# Produces a String (classname), or a token (DEFAULT).
654
| call_named_rval_function
658
: LBRACK expressions RBRACK { result = Factory.LIST(val[1]); loc result, val[0], val[2] }
659
| LBRACK expressions COMMA RBRACK { result = Factory.LIST(val[1]); loc result, val[0], val[3] }
660
| LBRACK RBRACK { result = Factory.literal([]) ; loc result, val[0] }
664
: LBRACE hashpairs RBRACE { result = Factory.HASH(val[1]); loc result, val[0], val[2] }
665
| LBRACE hashpairs COMMA RBRACE { result = Factory.HASH(val[1]); loc result, val[0], val[3] }
666
| LBRACE RBRACE { result = Factory.literal({}) ; loc result, val[0], val[3] }
669
: hashpair { result = [val[0]] }
670
| hashpairs COMMA hashpair { result = val[0].push val[2] }
673
: key FARROW expression { result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1] }
676
: NAME { result = Factory.literal(val[0][:value]) ; loc result, val[0] }
677
| quotedtext { result = val[0] }
679
# NOTE: Limitation that LHS is a variable, means that it is not possible to do foo(10)[2] without
680
# using an intermediate variable
683
: variable LBRACK expression RBRACK { result = val[0][val[2]]; loc result, val[0], val[3] }
687
| hasharrayaccesses LBRACK expression RBRACK { result = val[0][val[2]] ; loc result, val[1], val[3] }
689
# Produces Model::VariableExpression
690
variable : VARIABLE { result = Factory.fqn(val[0][:value]).var ; loc result, val[0] }
691
undef : UNDEF { result = Factory.literal(:undef); loc result, val[0] }
692
name : NAME { result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] }
693
type : CLASSREF { result = Factory.QREF(val[0][:value]) ; loc result, val[0] }
696
: DEFAULT { result = Factory.literal(:default); loc result, val[0] }
699
# Assumes lexer produces a Boolean value for booleans, or this will go wrong (e.g. produce. LiteralString)
700
: BOOLEAN { result = Factory.literal(val[0][:value]) ; loc result, val[0] }
703
: REGEX { result = Factory.literal(val[0][:value]); loc result, val[0] }
705
# ---Special markers & syntactic sugar
707
# WAT !!!! this means array can be [1=>2=>3], func (1=>2=>3), and other retarded constructs
708
# TODO: Remove the FARROW (investigate if there is any validity)
715
| COMMA { result = nil }
724
## Empty list - not really needed? TODO: Check if this can be removed
732
require 'puppet/util/loadedfile'
733
require 'puppet/pops'
736
class ParseError < Puppet::Error; end
737
class ImportError < Racc::ParseError; end
738
class AlreadyImportedError < ImportError; end