org.infinispan.ppg.generator.package-info Maven / Gradle / Ivy
Show all versions of infinispan-protocol-parser-generator-maven-plugin
/**
* This package contains tooling for automatic generation of binary protocol parsers
* implementing Netty's ByteToMessageDecoder. These parsers are expected to operate
* without allocations (aside from allocations from the code specified in grammar).
*
* As the processing is quite general, ideally this code will end up in Netty project
* itself. To demo its capabilities it is now used for decoding the Hot Rod protocol
* and could be used for Memcached. Netty provides another means for HTTP parsing,
* therefore REST is not a target.
*
* The syntax of grammar is intentionally close to ANTLRv4 syntax despite it's neither
* a subset nor limited to that.
*
* Top-level statements
*
*
* namespace /identifier/;
* Defines an unique namespace for the elements.
* This may be useful when the grammar includes another grammar (e.g. older version of protocol).
*
* class /fully qualified class name/ [extends /base class name/];
* Defines the package and name of the decoder class, possibly with a base class.
* If the base class name is not set it defaults to {@link io.netty.handler.codec.ByteToMessageDecoder}.
*
* constants /class name/;
* Static final fields from this class can be referenced (without class name) in the grammar.
*
* intrinsics /class name/;
* This class is expected to contain static methods that will parse basic elements from the stream.
* Each such method must have {@link io.netty.buffer.ByteBuf} as the first parameter
* and any number (or none) of extra parameters.
* Underscores on the end of method name are ignored; this allows the intrinsics to clash
* with Java keywords, e.g. byte
.
* These intrinsics are referenced in the grammar using the method name (e.g. string
),
* if the method requires additional parameters these are passed in brackets (e.g. array[16]
).
* If the method advances buffer's {@link io.netty.buffer.ByteBuf#readerIndex() reader index} it's considered successful;
* when it does not advance the reader index the parsing stops and the method will be invoked again when
* more data arrives. Note that this limits non-reading intrinsics (e.g. arrays of length 0): these need
* to be handled directly in the grammar using sentinels (see below).
*
* Intrinsic methods should not have any side-effect.
*
* import /class name/;
* Adds this import to the generated parser.
*
* init { /custom code/ }
* This code will be added to the generated parser. This is the place to add constructor and extra fields
* and methods.
*
* exceptionally { /custom code /}
* This code will be placed into a method called when an exception occurs during decoding process.
* The exception is be provided as Throwable t
.
*
* deadend() { /custom code/ }
* This code will be invoked when the parsing cannot continue, e.g. because there is no matching branch.
*
* [root] /rule name/ [returns /type/] [switch /reference/]: /branch1/ [| /branch 2/ | ...| /branch N/];
* Rules are the core part of parsing. There must be exactly one rule in each file with
* the root
modifier; this is where the parsing starts.
*
* Each rule may return a type; in such case the parser contains a field that will hold the result.
* The type can be either stated explicitly using returns
or is inferred automatically.
* Automatic inference requires all of the branches to be of the same type. Type for branch can be inferred when:
*
there is only single reference to another rule in the branch
* the last statement in branch is an action (see below) that starts with new /type/
* the last statement in branch is an action that contains a number (possibly suffixed with L)
* the last statement in branch is an action that starts with throw
*
* All but the last branch must start with a sentinel predicate:
* myRule
* : { /predicate1/ }? /branch 1 elements/
* | { /predicate2/ }? /branch 2 elements/
* | /branch 3 elements/
* ;
*
* The first matching predicate triggers evaluation of the branch. Each predicate is a Java expression
* that returns boolean. The predicates may (and should) reference already matched rules.
*
* In order to generate a switch statement for a rule with many branches, the switch
modifier
* can be set on the rule. In that case the sentinels should not contain boolean predicates but values
* that can be used as the switch cases.
*
* The branch elements is a space-separated list of:
*
references to another rules
* byte values provided as decimal (e.g. 42
) or hexadecimal (e.g 0xFF
).
* constants with value 0..255
* actions: { /custom code/ }
* loops: #/reference/ ( /elements/ )
*
* Loops require some more explanation here: the reference after #
is not matched,
* it is expected to be already matched before. At this point its value is evaluated and if it's not equal to zero
* the referenced field is decremented and the following sequence of elements is matched. Here is an example:
*
* map returns Map<String, String>
* : numEntries { map = new HashMap<>(numEntries); } #numEntries ( key value { map.put(key, value); } )
* ;
*
* The loop can be used for unknown number of elements, too:
*
* list returns List<String>
* : { list = new ArrayList<>() } hasNext #hasNext ( value { list.add(value); } hasNext )
* ;
*
*
* The grammar must be non-recursive; rules can reference each other as long as this does not cause a cycle.
*
* Once the root rule is fully matched, all fields are zeroed and the parsing starts with root rule anew.
* The root rule is not expected to return anything the rightmost element in the parsing tree should be an action
* that will fire a callback (defined in base class or in the init
code).
*/
package org.infinispan.ppg.generator;