2
# The Puppet Pops Metamodel
4
# This module contains a formal description of the Puppet Pops (*P*uppet *OP*eration instruction*S*).
5
# It describes a Metamodel containing DSL instructions, a description of PuppetType and related
6
# classes needed to evaluate puppet logic.
7
# The metamodel resembles the existing AST model, but it is a semantic model of instructions and
8
# the types that they operate on rather than an Abstract Syntax Tree, although closely related.
10
# The metamodel is anemic (has no behavior) except basic datatype and type
11
# assertions and reference/containment assertions.
12
# The metamodel is also a generalized description of the Puppet DSL to enable the
13
# same metamodel to be used to express Puppet DSL models (instances) with different semantics as
14
# the language evolves.
16
# The metamodel is concretized by a validator for a particular version of
17
# the Puppet DSL language.
19
# This metamodel is expressed using RGen.
22
require 'rgen/metamodel_builder'
24
module Puppet::Pops::Model
25
extend RGen::MetamodelBuilder::ModuleExtension
27
# A base class for modeled objects that makes them Visitable, and Adaptable.
29
class PopsObject < RGen::MetamodelBuilder::MMBase
33
# A Positioned object has an offset measured in an opaque unit (representing characters) from the start
34
# of a source text (starting
35
# from 0), and a length measured in the same opaque unit. The resolution of the opaque unit requires the
36
# aid of a Locator instance that knows about the measure. This information is stored in the model's
37
# root node - a Program.
39
# The offset and length are optional if the source of the model is not from parsed text.
41
class Positioned < PopsObject
43
has_attr 'offset', Integer
44
has_attr 'length', Integer
47
# @abstract base class for expressions
48
class Expression < Positioned
52
# A Nop - the "no op" expression.
53
# @note not really needed since the evaluator can evaluate nil with the meaning of NoOp
54
# @todo deprecate? May be useful if there is the need to differentiate between nil and Nop when transforming model.
56
class Nop < Expression
59
# A binary expression is abstract and has a left and a right expression. The order of evaluation
60
# and semantics are determined by the concrete subclass.
62
class BinaryExpression < Expression
65
# @!attribute [rw] left_expr
66
# @return [Expression]
67
contains_one_uni 'left_expr', Expression, :lowerBound => 1
68
contains_one_uni 'right_expr', Expression, :lowerBound => 1
71
# An unary expression is abstract and contains one expression. The semantics are determined by
72
# a concrete subclass.
74
class UnaryExpression < Expression
76
contains_one_uni 'expr', Expression, :lowerBound => 1
79
# A class that simply evaluates to the contained expression.
80
# It is of value in order to preserve user entered parentheses in transformations, and
81
# transformations from model to source.
83
class ParenthesizedExpression < UnaryExpression; end
85
# A boolean not expression, reversing the truth of the unary expr.
87
class NotExpression < UnaryExpression; end
89
# An arithmetic expression reversing the polarity of the numeric unary expr.
91
class UnaryMinusExpression < UnaryExpression; end
93
# Unfolds an array (a.k.a 'splat')
94
class UnfoldExpression < UnaryExpression; end
96
OpAssignment = RGen::MetamodelBuilder::DataTypes::Enum.new(
97
:literals => [:'=', :'+=', :'-='],
98
:name => 'OpAssignment')
100
# An assignment expression assigns a value to the lval() of the left_expr.
102
class AssignmentExpression < BinaryExpression
103
has_attr 'operator', OpAssignment, :lowerBound => 1
106
OpArithmetic = RGen::MetamodelBuilder::DataTypes::Enum.new(
107
:literals => [:'+', :'-', :'*', :'%', :'/', :'<<', :'>>' ],
108
:name => 'OpArithmetic')
110
# An arithmetic expression applies an arithmetic operator on left and right expressions.
112
class ArithmeticExpression < BinaryExpression
113
has_attr 'operator', OpArithmetic, :lowerBound => 1
116
OpRelationship = RGen::MetamodelBuilder::DataTypes::Enum.new(
117
:literals => [:'->', :'<-', :'~>', :'<~'],
118
:name => 'OpRelationship')
120
# A relationship expression associates the left and right expressions
122
class RelationshipExpression < BinaryExpression
123
has_attr 'operator', OpRelationship, :lowerBound => 1
126
# A binary expression, that accesses the value denoted by right in left. i.e. typically
127
# expressed concretely in a language as left[right].
129
class AccessExpression < Expression
130
contains_one_uni 'left_expr', Expression, :lowerBound => 1
131
contains_many_uni 'keys', Expression, :lowerBound => 1
134
OpComparison = RGen::MetamodelBuilder::DataTypes::Enum.new(
135
:literals => [:'==', :'!=', :'<', :'>', :'<=', :'>=' ],
136
:name => 'OpComparison')
138
# A comparison expression compares left and right using a comparison operator.
140
class ComparisonExpression < BinaryExpression
141
has_attr 'operator', OpComparison, :lowerBound => 1
144
OpMatch = RGen::MetamodelBuilder::DataTypes::Enum.new(
145
:literals => [:'!~', :'=~'],
148
# A match expression matches left and right using a matching operator.
150
class MatchExpression < BinaryExpression
151
has_attr 'operator', OpMatch, :lowerBound => 1
154
# An 'in' expression checks if left is 'in' right
156
class InExpression < BinaryExpression; end
158
# A boolean expression applies a logical connective operator (and, or) to left and right expressions.
160
class BooleanExpression < BinaryExpression
164
# An and expression applies the logical connective operator and to left and right expression
165
# and does not evaluate the right expression if the left expression is false.
167
class AndExpression < BooleanExpression; end
169
# An or expression applies the logical connective operator or to the left and right expression
170
# and does not evaluate the right expression if the left expression is true
172
class OrExpression < BooleanExpression; end
174
# A literal list / array containing 0:M expressions.
176
class LiteralList < Expression
177
contains_many_uni 'values', Expression
180
# A Keyed entry has a key and a value expression. It is typically used as an entry in a Hash.
182
class KeyedEntry < Positioned
183
contains_one_uni 'key', Expression, :lowerBound => 1
184
contains_one_uni 'value', Expression, :lowerBound => 1
187
# A literal hash is a collection of KeyedEntry objects
189
class LiteralHash < Expression
190
contains_many_uni 'entries', KeyedEntry
193
# A block contains a list of expressions
195
class BlockExpression < Expression
196
contains_many_uni 'statements', Expression
199
# A case option entry in a CaseStatement
201
class CaseOption < Expression
202
contains_many_uni 'values', Expression, :lowerBound => 1
203
contains_one_uni 'then_expr', Expression, :lowerBound => 1
206
# A case expression has a test, a list of options (multi values => block map).
207
# One CaseOption may contain a LiteralDefault as value. This option will be picked if nothing
210
class CaseExpression < Expression
211
contains_one_uni 'test', Expression, :lowerBound => 1
212
contains_many_uni 'options', CaseOption
215
# A query expression is an expression that is applied to some collection.
216
# The contained optional expression may contain different types of relational expressions depending
217
# on what the query is applied to.
219
class QueryExpression < Expression
221
contains_one_uni 'expr', Expression, :lowerBound => 0
224
# An exported query is a special form of query that searches for exported objects.
226
class ExportedQuery < QueryExpression
229
# A virtual query is a special form of query that searches for virtual objects.
231
class VirtualQuery < QueryExpression
234
OpAttribute = RGen::MetamodelBuilder::DataTypes::Enum.new(
235
:literals => [:'=>', :'+>', ],
236
:name => 'OpAttribute')
238
class AbstractAttributeOperation < Positioned
241
# An attribute operation sets or appends a value to a named attribute.
243
class AttributeOperation < AbstractAttributeOperation
244
has_attr 'attribute_name', String, :lowerBound => 1
245
has_attr 'operator', OpAttribute, :lowerBound => 1
246
contains_one_uni 'value_expr', Expression, :lowerBound => 1
249
# An attribute operation containing an expression that must evaluate to a Hash
251
class AttributesOperation < AbstractAttributeOperation
252
contains_one_uni 'expr', Expression, :lowerBound => 1
255
# An object that collects stored objects from the central cache and returns
256
# them to the current host. Operations may optionally be applied.
258
class CollectExpression < Expression
259
contains_one_uni 'type_expr', Expression, :lowerBound => 1
260
contains_one_uni 'query', QueryExpression, :lowerBound => 1
261
contains_many_uni 'operations', AttributeOperation
264
class Parameter < Positioned
265
has_attr 'name', String, :lowerBound => 1
266
contains_one_uni 'value', Expression
267
contains_one_uni 'type_expr', Expression, :lowerBound => 0
268
has_attr 'captures_rest', Boolean
271
# Abstract base class for definitions.
273
class Definition < Expression
277
# Abstract base class for named and parameterized definitions.
278
class NamedDefinition < Definition
280
has_attr 'name', String, :lowerBound => 1
281
contains_many_uni 'parameters', Parameter
282
contains_one_uni 'body', Expression
285
# A resource type definition (a 'define' in the DSL).
287
class ResourceTypeDefinition < NamedDefinition
290
# A node definition matches hosts using Strings, or Regular expressions. It may inherit from
291
# a parent node (also using a String or Regular expression).
293
class NodeDefinition < Definition
294
contains_one_uni 'parent', Expression
295
contains_many_uni 'host_matches', Expression, :lowerBound => 1
296
contains_one_uni 'body', Expression
299
class LocatableExpression < Expression
300
has_many_attr 'line_offsets', Integer
301
has_attr 'locator', Object, :lowerBound => 1, :transient => true
304
# Contains one expression which has offsets reported virtually (offset against the Program's
307
class SubLocatedExpression < Expression
308
contains_one_uni 'expr', Expression, :lowerBound => 1
310
# line offset index for contained expressions
311
has_many_attr 'line_offsets', Integer
313
# Number of preceding lines (before the line_offsets)
314
has_attr 'leading_line_count', Integer
316
# The offset of the leading source line (i.e. size of "left margin").
317
has_attr 'leading_line_offset', Integer
319
# The locator for the sub-locatable's children (not for the sublocator itself)
320
# The locator is not serialized and is recreated on demand from the indexing information
323
has_attr 'locator', Object, :lowerBound => 1, :transient => true
326
# A heredoc is a wrapper around a LiteralString or a ConcatenatedStringExpression with a specification
327
# of syntax. The expectation is that "syntax" has meaning to a validator. A syntax of nil or '' means
328
# "unspecified syntax".
330
class HeredocExpression < Expression
331
has_attr 'syntax', String
332
contains_one_uni 'text_expr', Expression, :lowerBound => 1
337
class HostClassDefinition < NamedDefinition
338
has_attr 'parent_class', String
341
# i.e {|parameters| body }
342
class LambdaExpression < Expression
343
contains_many_uni 'parameters', Parameter
344
contains_one_uni 'body', Expression
347
# If expression. If test is true, the then_expr part should be evaluated, else the (optional)
348
# else_expr. An 'elsif' is simply an else_expr = IfExpression, and 'else' is simply else == Block.
349
# a 'then' is typically a Block.
351
class IfExpression < Expression
352
contains_one_uni 'test', Expression, :lowerBound => 1
353
contains_one_uni 'then_expr', Expression, :lowerBound => 1
354
contains_one_uni 'else_expr', Expression
357
# An if expression with boolean reversed test.
359
class UnlessExpression < IfExpression
364
class CallExpression < Expression
366
# A bit of a crutch; functions are either procedures (void return) or has an rvalue
367
# this flag tells the evaluator that it is a failure to call a function that is void/procedure
368
# where a value is expected.
370
has_attr 'rval_required', Boolean, :defaultValueLiteral => "false"
371
contains_one_uni 'functor_expr', Expression, :lowerBound => 1
372
contains_many_uni 'arguments', Expression
373
contains_one_uni 'lambda', Expression
376
# A function call where the functor_expr should evaluate to something callable.
378
class CallFunctionExpression < CallExpression; end
380
# A function call where the given functor_expr should evaluate to the name
383
class CallNamedFunctionExpression < CallExpression; end
385
# A method/function call where the function expr is a NamedAccess and with support for
386
# an optional lambda block
388
class CallMethodExpression < CallExpression
391
# Abstract base class for literals.
393
class Literal < Expression
397
# A literal value is an abstract value holder. The type of the contained value is
398
# determined by the concrete subclass.
400
class LiteralValue < Literal
404
# A Regular Expression Literal.
406
class LiteralRegularExpression < LiteralValue
407
has_attr 'value', Object, :lowerBound => 1, :transient => true
408
has_attr 'pattern', String, :lowerBound => 1
413
class LiteralString < LiteralValue
414
has_attr 'value', String, :lowerBound => 1
417
class LiteralNumber < LiteralValue
421
# A literal number has a radix of decimal (10), octal (8), or hex (16) to enable string conversion with the input radix.
422
# By default, a radix of 10 is used.
424
class LiteralInteger < LiteralNumber
425
has_attr 'radix', Integer, :lowerBound => 1, :defaultValueLiteral => "10"
426
has_attr 'value', Integer, :lowerBound => 1
429
class LiteralFloat < LiteralNumber
430
has_attr 'value', Float, :lowerBound => 1
435
class LiteralUndef < Literal; end
438
class LiteralDefault < Literal; end
440
# DSL `true` or `false`
441
class LiteralBoolean < LiteralValue
442
has_attr 'value', Boolean, :lowerBound => 1
445
# A text expression is an interpolation of an expression. If the embedded expression is
446
# a QualifiedName, it is taken as a variable name and resolved. All other expressions are evaluated.
447
# The result is transformed to a string.
449
class TextExpression < UnaryExpression; end
451
# An interpolated/concatenated string. The contained segments are expressions. Verbatim sections
452
# should be LiteralString instances, and interpolated expressions should either be
453
# TextExpression instances (if QualifiedNames should be turned into variables), or any other expression
454
# if such treatment is not needed.
456
class ConcatenatedString < Expression
457
contains_many_uni 'segments', Expression
460
# A DSL NAME (one or multiple parts separated by '::').
462
class QualifiedName < LiteralValue
463
has_attr 'value', String, :lowerBound => 1
466
# Represents a parsed reserved word
467
class ReservedWord < LiteralValue
468
has_attr 'word', String, :lowerBound => 1
471
# A DSL CLASSREF (one or multiple parts separated by '::' where (at least) the first part starts with an upper case letter).
473
class QualifiedReference < LiteralValue
474
has_attr 'value', String, :lowerBound => 1
477
# A Variable expression looks up value of expr (some kind of name) in scope.
478
# The expression is typically a QualifiedName, or QualifiedReference.
480
class VariableExpression < UnaryExpression; end
483
class EppExpression < Expression
484
# EPP can be specified without giving any parameter specification.
485
# However, the parameters of the lambda in that case are the empty
486
# array, which is the same as when the parameters are explicity
487
# specified as empty. This attribute tracks that difference.
488
has_attr 'parameters_specified', Boolean
489
contains_one_uni 'body', Expression
493
class RenderStringExpression < LiteralString
496
# An expression to evluate and render
497
class RenderExpression < UnaryExpression
500
# A resource body describes one resource instance
502
class ResourceBody < Positioned
503
contains_one_uni 'title', Expression
504
contains_many_uni 'operations', AbstractAttributeOperation
507
ResourceFormEnum = RGen::MetamodelBuilder::DataTypes::Enum.new(
508
:literals => [:regular, :virtual, :exported ],
509
:name => 'ResourceFormEnum')
511
# An abstract resource describes the form of the resource (regular, virtual or exported)
512
# and adds convenience methods to ask if it is virtual or exported.
513
# All derived classes may not support all forms, and these needs to be validated
515
class AbstractResource < Expression
517
has_attr 'form', ResourceFormEnum, :lowerBound => 1, :defaultValueLiteral => "regular"
518
has_attr 'virtual', Boolean, :derived => true
519
has_attr 'exported', Boolean, :derived => true
522
# A resource expression is used to instantiate one or many resource. Resources may optionally
523
# be virtual or exported, an exported resource is always virtual.
525
class ResourceExpression < AbstractResource
526
contains_one_uni 'type_name', Expression, :lowerBound => 1
527
contains_many_uni 'bodies', ResourceBody
530
# A resource defaults sets defaults for a resource type. This class inherits from AbstractResource
531
# but does only support the :regular form (this is intentional to be able to produce better error messages
532
# when illegal forms are applied to a model.
534
class ResourceDefaultsExpression < AbstractResource
535
contains_one_uni 'type_ref', Expression
536
contains_many_uni 'operations', AbstractAttributeOperation
539
# A resource override overrides already set values.
541
class ResourceOverrideExpression < AbstractResource
542
contains_one_uni 'resources', Expression, :lowerBound => 1
543
contains_many_uni 'operations', AbstractAttributeOperation
546
# A selector entry describes a map from matching_expr to value_expr.
548
class SelectorEntry < Positioned
549
contains_one_uni 'matching_expr', Expression, :lowerBound => 1
550
contains_one_uni 'value_expr', Expression, :lowerBound => 1
553
# A selector expression represents a mapping from a left_expr to a matching SelectorEntry.
555
class SelectorExpression < Expression
556
contains_one_uni 'left_expr', Expression, :lowerBound => 1
557
contains_many_uni 'selectors', SelectorEntry
560
# A named access expression looks up a named part. (e.g. $a.b)
562
class NamedAccessExpression < BinaryExpression; end
564
# A Program is the top level construct returned by the parser
565
# it contains the parsed result in the body, and has a reference to the full source text,
566
# and its origin. The line_offset's is an array with the start offset of each line.
568
class Program < PopsObject
569
contains_one_uni 'body', Expression
570
has_many 'definitions', Definition
571
has_attr 'source_text', String
572
has_attr 'source_ref', String
573
has_many_attr 'line_offsets', Integer
574
has_attr 'locator', Object, :lowerBound => 1, :transient => true