Grammar
NAME
Config::BINDish::Grammar
- the central class of everything in Config::BINDish
DESCRIPTION
This class is responsible for the actual parsing of the configuration. It is not recommended for direct use.
Config::BINDish
read
method must be used instead.
The class inherits from the standard Grammar class.
ATTRIBUTES
Bool:D $.flat
If set this attribute expects blocks to be flattened down. I.e. whenever a block has a duplicate declaration in the
config the later declaration must be applied on top of the first one. This doesn't change grammar's behavior but rather
serves as a note for the actions class to take care of the situation. One way or another, if this attribute is True
then the user expects a single block foo "bar"
representation to exists after the following sample is parsed:
foo "bar" { fubar 1; }
baz { }
foo "bar" { fubar 2; fubaz 3.14; }
How the options are dealt with is the sole prerogative of the actions implementation.
Config::BINDish::Actions
re-delegates handling of flattening to the underlying
Config::BINDish::AST class.
It, in turn, will overwrite earlier option declarations with latter ones. So, when one queries for fubar
the value
returned will be 2.
See #$*CFG-FLAT-BLOCKS.
Config::BINDish::Grammar::Strictness $.strict = False
Defines what strictness modes are activated. See Config::BINDish::Grammar::Strictness.
%.blocks
User-defined blocks in hash form. Passed to declare-blocks
method at object construction time.
See Config::BINDish::Grammar::BlockProps and #Pre-declaration section for more information.
%.options
User-defined options in hash form. Passed to declare-options
method at object construction time.
See Config::BINDish::Grammar::OptionProps and #Pre-declaration section for more information.
DYNAMIC VARIABLES
The grammar declares and uses a set of dynamic variables to pass certain information between its rules, tokens, methods, and the actions object.
$*CFG-GRAMMAR
The primary grammar object. Due to the way the Grammar is implemented self
does not always point to the same
object created by the initial parse
method. These instances are not full clones of the original grammar and do not
inherit all attribute values from it. For this reason when access to the user-set attributes is needed the original
grammar is better be easily available.
$*CFG-FLAT-BLOCKS
Set to $.flat
attribute value.
$*CFG-INNER-PARENT
Provided for actions convenience. Intended to hold an instance of Config::BINDish::AST::Parent.
$*CFG-TOP
Provided for actions convenience. Intended to hold an instance of Config::BINDish::AST::TOP.
Config::BINDish::Grammar::Value $*CFG-VALUE
Must be provided directly or indirectly by any rule or token invoking the <value>
token. This variable will be set to a
Config::BINDish::Grammar::Value
instance created by set-value
method.
Config::BINDish::Grammar::Value $*CFG-KEYWORD
Set by <keyword>
token. The value stored will be of type Str and have type name
"keyword".
$*CFG-BLOCK-TYPE
, $*CFG-BLOCK-NAME
, $*CFG-BLOCK-CLASS
All three are of Config::BINDish::Grammar::Value type. When the following example is parsed:
foo "bar" baz { }
The variables will be set to:
type: "foo" of Str, type-name "keyword"
name: "bar" of Str, type name "dq-string"
class: "baz" of Str, type name "keyword"
Note that the name could be of any value, supported by the grammar. If an extension adds a new value type then the type can also be used as a type name. Say, with Config::BINDish::INET loaded one can have the following valid declaration:
IP 192.168.1.42 { }
For which the name will be set to type IP::Addr
, type-name "IPv4".
$*CFG-BLOCK-ERR-POS
This variable is set to a Match object pointing at the location where current block declaration starts. Can be used for error reporting.
METHODS
set-value(Mu \type, *%value)
This methods creates a new
Config::BINDish::Grammar::Value
object and assigns it to $*CFG-VALUE
. Takes value's type object as its single positional parameter. Type name and
payload are passed as the only named argument of the method call. Here is how a single-quoted string is handled by
the grammar:
token sq-string {
\' ~ \' $<string>=<.qstring("'")>
{
self.set-value: Str, :sq-string($<string>)
}
}
In this example the new
Config::BINDish::Grammar::Value
object will be created with payload set from $<string>
, type name set to "sq-string", and type set to
Str.
submethod setup-BINDish
When the grammar is instantiated it iterates over all of its parents and roles and invokes each unique setup-BINDish
submethod it finds. While it may seem to look as a duplicate of TWEAK
, the purpose of using this approach is to
eliminate the differences in handling of submethods by Raku versions 6.c/d and 6.e.
See also #Pre-declaration section in #EXTENSIONS.
multi declare-blocks(%blocks)
multi declare-blocks(*%blocks)
Pre-declare blocks properties. See #Pre-declaration section below for details.
multi declare-options(%options)
multi declare-options(*%options)
Pre-declare options properties. See #Pre-declaration section below for details.
cfg-ctx()
Returns current active context object. See Config::BINDish::Grammar::Context.
The method will automatically fall back to $*CFG-GRAMMAR
if the instance it is invoked upon is not the primary
grammar object.
push-ctx(*%ctx-profile)
Creates and pushes to context stack a new Config::BINDish::Grammar::Context object. Returns the new context.
Falls back to $*CFG-GRAMMAR
if necessary.
pop-ctx()
Pops and returns the current context object from the context stack. Will die if attempt to pop the "TOP" context is made.
block-ok(Str:D $block-type --
Bool)>
Returns True if block $block-type
can be used in the current context. For example, if a block type name-servers
is pre-declared to be only allowed within a network block then block-ok
will return False
if name-servers is used in a host block or at the configuration top-level.
option-ok(Str:D $keyword --
Bool)>
Similarly to block-ok
method, returns True only if option $keyword
is allowed in the current block context.
enter-option()
Set the current context as "OPTION". Uses $*CFG-KEYWORD to determine option name and fetch its pre-declarion properties if there are any.
Knowing if current context is option or not is rather useful to find out of a syntax element should have special treatment. For example, if a keyword is seen in option context it is treated as a value, not as a boolean option. For example:
access-allowed private-acl;
Here private-acl
is parsed as a string 'private-acl' with type name set to keyword
. Our code could then
consult with the type name to find out the value is by chance a reference to a block or another option.
leave-option()
Must be invoked when leaving option context.
leave-block()
Must be invoked when leaving block context.
validate-block()
Does complex validation of a block based on its type, name, and, possibly, class. Uses pre-set variables $*CFG-BLOCK-TYPE, $*CFG-BLOCK-NAME, $*CFG-BLOCK-CLASS. If block passes the validation then a new block context is pushed onto the context stack.
Can throw one of Config::BINDish::X::Parse::Context, Config::BINDish::X::Parse::ExtraPart, Config::BINDish::X::Parse::MissingPart, or Config::BINDish::X::Parse::Unknown.
validate-option()
Does complex validation of an option based on its name. Uses pre-set variables $*CFG-KEYWORD and $*CFG-VALUE.
Contrary to validate-block
method results in leaving option context.
Can throw one of Config::BINDish::X::Parse::Context, Config::BINDish::X::Parse::ValueType, or Config::BINDish::X::Parse::Unknown.
validate-value()
Makes sure a value can be used in the current context. Uses variable $*CFG-VALUE.
Can throw Config::BINDish::X::Parse::ValueType.
multi method panic(Str:D $msg)
Throws
Config::BINDish::X::Parse::General
with message $msg
.
multi method panic(Config::BINDish::X::Parse:U \exception, Str $msg?, *%profile)
Creates an object of type exception
using %profile
as named arguments for constructor and throws it. If $msg
is specified then it is added to the profile as named argument :$msg
.
Both panic
methods also pass the grammar object they're invoked upon as cursor
named argument to provide
the exception instance with error location and other useful information.
GRAMMAR ELEMENTS
Rules and tokens listed in this section are the ones considered public API of this module. Those not listed here but implemented by the grammar are considered implementation detail and can be changed or removed any time without prior notice.
Description provided here would mostly be rather succinct. Checking with the grammar source is the most correct way of understanding it.
Rules And Tokens
rule TOP
See Grammar.
Pushes "TOP" context onto the stack and invokes statement-list
.
token enter-TOP
Does nothing, used as anchor for Config::BINDish::Actions.
rule statement-list
Used in the global and block contexts.
proto statement
Currently defined statements are (as named within :sym<...>
postfix):
comment
value
option
block
empty
token statement-terminator
Matches a statement terminator. Doesn't take the current strictness mode into account and tries matching as if non-strict syntax mode is used.
token statement-terminate
A wrapper around statement-terminator
which validates it against the current syntax strictness mode.
token block-head
Parses the block type keyword. Sets $*CFG-BLOCK-TYPE.
token block-name
Parses the block name. Sets $*CFG-BLOCK-NAME.
token block-class
Parses the block name. Sets $*CFG-BLOCK-CLASS.
token block-body
Validates the block and sets the current block context.
C-comment
, CPP-comment
, UNIX-comment
Three types of comments
keyword
Parses a keyword which is defined as an alpha-numeric identifier starting and ending with a word boundary. Sets $*CFG-KEYWORD.
token dq-string
, token sq-string
Double- and single-quoted strings. The standard grammar and actions do not care about the exact nature of a string. But extensions could adjust their behavior based on the string type. For example, Config::BINDish::Expandable implements macro expansion for double-quoted strings only.
proto token value
Umbrella-rule for all value types. If you plan an extension for a new value type then this is where you plug it into.
The following value types are currently implemented, listed as named within :sym<...>
postfix:
string
keyword
num
rat
int
bool
Named captures
Some named captures are also considered public API.
$<err-pos>
Records the location to which call of method panic
should be bound. In other words, if the location within a rule
body where we make the decision about syntax validity is different from the location where the user would understand it
best then panic
should be invoked as:
$<err-pos>.panic: X::Parse::AError, ...;
For example, we parse an option like:
pi 3.1415926;
When all the information we need to validate it is collected our grammar points at the position right after the
terminating semi-colon. If we do self.panic(...)
then the error message look kind of the following:
Option pi cannot be used in block...
at line N
pi 3.1415926;⏏
But with $<err-pos>.panic(...)
it would rather be:
Option pi cannot be used in block...
at line N
⏏pi 3.1415926;
Which certainly makes more sense to the user.
$<option-name>
, $<option-value>
Point to parsed option name and value Matches.
$<block-type>
Points to parsed block type Match object.
$<string>
Points to actual string body of a stringy value. I.e. for what "The answer is 42" this capture will be a Match object pointing at The answer is 42 part of the source, quotes excluded.
EXTENSIONS
This section provides tips for writing own grammar extensions. See Config::BINDish::Actions to find read about action extensions.
Pre-declaration
Note: This section also applies to attributes %.blocks and %.options mentioned above.
An extension can pre-declare own blocks and options. It must do so by declaring a submethod named setup-BINDish
and calling methods declare-blocks
and declare-options
. Both methods take Hash objects or named arguments.
In other words, the following two calls are identical:
self.declare-options: opt1 => {:top-only}, opt2 => %(:in<foo>, :type<Num>);
self.declare-options: %( opt1 => {:top-only}, opt2 => %(:in<foo>, :type<Num>) );
Each hash or named argument key define the keyword of corresponding construct. Apparently, in the above example
options opt1
and opt2
are declared.
Keys of each option or block declaration define corresponding properties of each entitity. They're also named after attributes of the following typeobjects:
Config::BINDish::Grammar::StatementProps, Config::BINDish::Grammar::ContainerProps - common to both options and blocks
Config::BINDish::Grammar::OptionProps - specific to options
Config::BINDish::Grammar::BlockProps - specific to blocks
Keys common to both options and blocks
Statement level keys:
top-only
- a statement can not be part of any block declarationin
- set of blocks inside which a statement is allowed
Container level keys:
type-name
- allowed type namestype
– allowed types
The above two keys are used as RHS for smartmatching. It allows explicit declarations of the following kind:
my $cfg = Config::BINDish.new:
:strict{:options},
options => %(
multi-type => { :in<general>, type => Int | Rat },
stringy => { :in<general>, type-name => /\- string$/ },
);
With this we can have options like:
multi-type 2M;
multi-type 1.5G;
stringy "double quoted"; # Because of type name 'dq-string'
stringy 'single quoted"; # ... and 'sq-string'
But not:
multi-type 1.5e2; # The value is Num
stringy bareword; # The value is a string, but its type name is 'keyword'
Option-only keys
Options currently do not have any declaration keys unique to them.
Block-only keys
named
- specifies if block must have a name. If omitted then the name part of block declaration is optional. If True, name is required; if False then block can't have a name.classified
- similar to thenamed
above, but controls block class. Ignored altogether ifnamed
is False.value-only
- if True then the block can only have a list of values in its body. Set to False by default. Only value-only blocks can contain keywords as values; all other blocks treat keywords as boolean options.
Context
Grammar object has a way of handling configuration file context by maintaining own context stack of
Config::BINDish::Grammar::Context
objects. See methods push-ctx
, pop-ctx
, and cur-ctx
.
The only property of context which is guaranteed to be defined is its type. It's a string with context name. Currently only three of them can be seen: "TOP", "BLOCK", and "OPTION".
Extending The Grammar
Config::BINDish contains general information about writing extensions. Here we provide only grammar-specific details.
Only the rules and tokens documented in corresponding section above are guaranteed to be supported and not be changed or removed without deprecation.
The most common way to extend the grammar would be to add a new kind of statement or value type. This must be as easy as adding new rules or tokens akin to the following example:
role MyExtension is BINDish-grammar {
...
multi rule statement:sym<your-statement> {
# Your statement syntax rules
}
multi token value:sym<your-type> {
# Your value syntax rules
}
...
}
SEE ALSO
Config::BINDish, Config::BINDish::Grammar::Strictness, Config::BINDish::Grammar::StatementProps, Config::BINDish::Grammar::ContainerProps, Config::BINDish::Grammar::OptionProps, Config::BINDish::Grammar::BlockProps
AUTHOR
Vadim Belman <[email protected]>