parse.c

Parser for DDL (Device Description Language)

For both devices and controllers, the DDL description is used to generate structures which are used by DMP.

Summary
parse.cParser for DDL (Device Description Language)
DevicesFor building a simple device, the property map is a static part of the code of the device itself, and the device has no need for DDL (except to serve it up as opaque files as required by EPI-11.
controllersControllers must parse DDL dynamically on encountering new device types.
Structures generated during parseThe main entry point parseroot returns a <struct rootdev_s> which in turn contains three principle structures:
Macros
address map memory blocks
TokenisingTokens are statically declared and have an enumeration and a string value.
Functions
tokmatchtok
tokmatchofs
flagnamesUtility function that prints the names of those flags which are set in a word into a string.
resolveuuidResolve a UUID alias to a full UUID using currently scoped UUIDname definitions.
findbvFind the struct bv_s corresponding to a behavior in a set.
init_behaviorsAdd our defined behaviorsets to the kbehaviors structure where we can find them quickly.
setlang
findlabel
propnameGenerate a property name derived from its xml ID if it has one, otherwise from its property number which is its position in document order.
add_devtaskAdd a task (during parsing) for execution once the entire tree has been constructed but before returning it to the caller.
ddlrefTrace a DDL property cross reference using xml IDs and the syntax specified in the DDL spec.
bset_startProcess a behaviorset.
bset_startProcess a behaviorset.
lset_startProcess a languageset.
lset_endWe’ve accumulated the full language array in dcxp.
pprop_startStart tag for property or pseudo property (includedev, propertypointer)
prop_wrapupCall after initial elements within property (label, behavior, value, protocol) but before children or included properties.
findaddrsearch the map for our insertion point
mappropAdd a dmp property to the property map.
propref_start
queue_moduleAdd a DDL module to the queue for future processing.
parserootParse the root device of a component.
freerootdevFree all the resources used by a rootdev_s.

Devices

For building a simple device, the property map is a static part of the code of the device itself, and the device has no need for DDL (except to serve it up as opaque files as required by EPI-11.

When building a device DDL description forms part of the build (see mapgen.c), then if extra functions or properties need to be added, this is done by modifying the DDL and so ensures that the description should match the actual properties.

The property map in a device relates incoming addresses to a handler routine (provided by the application code) which is called by DMP when the corresponding property is addressed.

DDL extension

To allow the application layer handler function to be specified within the DDL itself, an expension element has been defined.  This is the element <ea:propext name=”” value=””/> which is in the namespace: “http://www.engarts.com/namespace/2011/ddlx” This can be added within any <protocol> element and allows a generic specification of a field name and value for incorporation into the property map.  The use of a namespace allows this extension element to be added to descriptions within the rules of DDLv1.1 and compliant parsers should ignore it (unless they are programmed to use it).

controllers

Controllers must parse DDL dynamically on encountering new device types.  Once parsed, the resulting structures may be stored and tracked by DCID using the generic UUID code which is also used for tracking components by CID.

Structures generated during parse

The main entry point parseroot returns a <struct rootdev_s> which in turn contains three principle structures:

  • a tree structure consisting of linked <struct ddlprop_s>s representing the entire device including all its subdevices and containing all declared properties including immediate, implied and NULL value ones and propertypointers.  Once parsing is complete this tree is no longer used by any Acacian code but it may be useful to the application.  In particular it can be iterated through property by property or interrogated recursively to examine the device structure, immediate values, labels etc.
  • a <struct addrmap_s> which is used extensively by DMP code to connect the DMP adresses in incoming messages to the corresponding property structures, or for looking up property sizes and attributes when constructing outgoing messages.  This is an operation which must be fast and efficient.
  • a singly linked list of <struct dmpprop_s>s with one for each declared DMP accessible property.  The list format is merely a convenient for, to record these structures in. they are cross referenced both from the ddlprop_s tree and the addrmap_s so should not be freed until after those structures.  The rootdev_s also contains a string pool and hash table for fast lookup of ID references.

Macros

address map memory blocks

AFMAPINCincrement to grow map when required
AFMAPISIZEinitial size of address map
AFTESTBLKINCincrement used to grow addrfind.p when ntests > 1

Tokenising

Tokens are statically declared and have an enumeration and a string value.  All tokens are stored within a single token array alltoks[] indexed by enumeration value.

Two routines are provided to test whether a name matches a context dependent set of allowable tokens.  These are virtually identical except that tokmatchtok() returns the token itself whilst tokmatchofs() returns the offset of the name in the allowed array.  So if the name is in the allowed array then tokmatchtok(name, allowed) == allowed->toks[tokmatchofs(name, allowed)].

sorting

The token search routines use a binary search which means the keys must be sorted.  In this case that means that the tokens in an allowtok_s MUST be in ascending lexical order of the strings they represent.  To facilitate sorting, most token names in C are simply the token string with TK_ prepended.  For token strings which are very long or contains characters which do not make valif C names, the name is derived from the string so that it sorts in the correct order.

If the C names of tokens sort in the same order as the strings they represent, then when defining allowtok_s structures it is simple to sort the elements correctly using:

LC_ALL=C sort

Summary
Functions
tokmatchtok
tokmatchofs
flagnamesUtility function that prints the names of those flags which are set in a word into a string.
resolveuuidResolve a UUID alias to a full UUID using currently scoped UUIDname definitions.
findbvFind the struct bv_s corresponding to a behavior in a set.
init_behaviorsAdd our defined behaviorsets to the kbehaviors structure where we can find them quickly.
setlang
findlabel
propnameGenerate a property name derived from its xml ID if it has one, otherwise from its property number which is its position in document order.
add_devtaskAdd a task (during parsing) for execution once the entire tree has been constructed but before returning it to the caller.
ddlrefTrace a DDL property cross reference using xml IDs and the syntax specified in the DDL spec.
bset_startProcess a behaviorset.
bset_startProcess a behaviorset.
lset_startProcess a languageset.
lset_endWe’ve accumulated the full language array in dcxp.
pprop_startStart tag for property or pseudo property (includedev, propertypointer)
prop_wrapupCall after initial elements within property (label, behavior, value, protocol) but before children or included properties.
findaddrsearch the map for our insertion point
mappropAdd a dmp property to the property map.
propref_start
queue_moduleAdd a DDL module to the queue for future processing.
parserootParse the root device of a component.
freerootdevFree all the resources used by a rootdev_s.

Functions

tokmatchtok

static tok_t tokmatchtok(const ddlchar_t *str,
const struct allowtok_s *allowed)

tokmatchofs

static int tokmatchofs(const ddlchar_t *str,
const struct allowtok_s *allowed)

flagnames

char * flagnames(uint32_t flags,
const char **names,
char *buf,
const char *format)

Utility function that prints the names of those flags which are set in a word into a string.

resolveuuid

const ddlchar_t * resolveuuid(struct dcxt_s *dcxp,
const ddlchar_t *name,
uint8_t *dcid)

Resolve a UUID alias to a full UUID using currently scoped UUIDname definitions.

findbv

const struct bv_s * findbv(const uint8_t *uuid,
const ddlchar_t *name,
struct bvset_s **bvset)

Find the struct bv_s corresponding to a behavior in a set.

We use standard acn UUID search to find the set structure, then search its hash table for the name.

init_behaviors

static void init_behaviors(struct dcxt_s *dcxp)

Add our defined behaviorsets to the kbehaviors structure where we can find them quickly.

setlang

void setlang(const ddlchar_t **ltags)

findlabel

propname

const char * propname(struct ddlprop_s *pp,
enum pname_flags_e flags)

Generate a property name derived from its xml ID if it has one, otherwise from its property number which is its position in document order.

The flags argument can include.

pn_translatetranslate characters so resulting name is a valid C identifier (this is used by <mapgen>).
pn_pathinclude the names of ancestor devices in the name which becomes the path to the property from the root device and guarantees it will be unique within the whole device.

returns

A pointer to the property name which is in a statically allocated buffer and will be overwritten in the next call.

add_devtask

void * add_devtask(struct dcxt_s *dcxp,
devtask_fn *task,
size_t size)

Add a task (during parsing) for execution once the entire tree has been constructed but before returning it to the caller.

This is useful for behavior actions which need to reference child properties (which have notyet been parsed at the time the initial bahevior action is executed) or other parts of the tree.  It is also used to implement propertypointers which need the entire tree to navigate arbitrary reference paths.

arguments

dcxpthe current parse context, includes the information needed to queue the task.
taska pointer to the task function to be called back when the tree is complete.
sizeadd_devtask() creates a task specific area of this size which may be used to store any necessary data needed to execute the task.  This task data area is initialized to zero returned to the caller to be filled with whatever data is required.  The data is then passed to the task function when the task is executed.  This task-data area is automatically freed after execution of the task.

returns: a pointer to the task data area or NULL if there is not enough memory to create and queue the task.

ddlref

struct ddlprop_s * ddlref(struct rootdev_s *root,
struct ddlprop_s *pp,
const ddlchar_t *path)

Trace a DDL property cross reference using xml IDs and the syntax specified in the DDL spec.

arguments

rootthe root device structure
pppointer to the starting property (usually the one containing or specifying the reference.
paththe string specifying the path.

bset_start

static void bset_start(struct dcxt_s *dcxp,
const ddlchar_t **atta)

Process a behaviorset.

bset_start

Process a behaviorset.

lset_start

static void lset_start(struct dcxt_s *dcxp,
const ddlchar_t **atta)

Process a languageset.  This is currently just a stub that skips the entire set.

lset_end

static void lset_end(struct dcxt_s *dcxp)

We’ve accumulated the full language array in dcxp.  Now we know how many languages and keys are in the set we can consolidate.  Allocate all our strings and languages in one big block.  We can then free the rather wasteful string structures and the languages are part of dxcp which gets wiped for the next module.

pprop_start

static void pprop_start(struct dcxt_s *dcxp,
const ddlchar_t **atta)

Start tag for property or pseudo property (includedev, propertypointer)

prop_wrapup

static int prop_wrapup(struct dcxt_s *dcxp)

Call after initial elements within property (label, behavior, value, protocol) but before children or included properties.

findaddr

static int findaddr(union addrmap_u *amap,
uint32_t addr)

search the map for our insertion point

Returns the region in the map where addr belongs (our address is less than the current low address at this point, or this point overlaps our low address).  If ours is a sparse array it may also overlap zero or more higher regions.

mapprop

static void mapprop(struct dcxt_s *dcxp,
struct ddlprop_s *prop)

Add a dmp property to the property map.

During parsing property map is always a search type.

Make a single pass up the array tree (if any) filling in the dimensions in the netprop.dim[] array.  At the same time calculate: total number of addresses and address span.

Determine whether the array is packed or has overlapping indexes.

The dim[] array is in sorted order from largest increment at dim[0] to smallest at dim[n].  This code anticipates that larger increments will be higher up the tree so we will find the smaller ones first, but whilst this is the most likely it is not guaranteed so we need to sort as we go.

propref_start

static void propref_start(struct dcxt_s *dcxp,
const ddlchar_t **atta)

queue_module

void queue_module(struct dcxt_s *dcxp,
tok_t modtype,
const ddlchar_t *name,
void *ref)

Add a DDL module to the queue for future processing.

The structure of DDL would naturally encourage recursive processing but that could easily be exessively resource hungry in lightweight components so when a reference requiring processing af a subsiduary module is encountered the module is added to the queue together with a reference to the point at which it applies.  Modules can then be processed in sequence instead of recursively.

parseroot

struct rootdev_s * parseroot(const char *name)

Parse the root device of a component.  This is the main entry point for the device parser.  It is passed a name which must resolve to a DDL document (see resolve.c).  It initalizes the root, enters the na,med module as first in the queue and starts the parser.  As subdevices or other modules are found, during construction of the property tree, they are added to the queue and processed in turn.  This function returns once the entire queue has been processed.  As parsing progresses, both the property tree (DDL properties) and the address map (DMP properties) are constructed.

returns:pointer to the resulting rootdev structure (<struct rootdev_s>) or NULL if unrecoverable errors are encountered.

The rootdev_s should be freed with freerootdev when it is no longer needed.

freerootdev

void freerootdev(struct rootdev_s *dev)

Free all the resources used by a rootdev_s.

struct rootdev_s * parseroot(const char *name)
Parse the root device of a component.
static tok_t tokmatchtok(const ddlchar_t *str,
const struct allowtok_s *allowed)
static int tokmatchofs(const ddlchar_t *str,
const struct allowtok_s *allowed)
char * flagnames(uint32_t flags,
const char **names,
char *buf,
const char *format)
Utility function that prints the names of those flags which are set in a word into a string.
const ddlchar_t * resolveuuid(struct dcxt_s *dcxp,
const ddlchar_t *name,
uint8_t *dcid)
Resolve a UUID alias to a full UUID using currently scoped UUIDname definitions.
const struct bv_s * findbv(const uint8_t *uuid,
const ddlchar_t *name,
struct bvset_s **bvset)
Find the struct bv_s corresponding to a behavior in a set.
static void init_behaviors(struct dcxt_s *dcxp)
Add our defined behaviorsets to the kbehaviors structure where we can find them quickly.
void setlang(const ddlchar_t **ltags)
const char * propname(struct ddlprop_s *pp,
enum pname_flags_e flags)
Generate a property name derived from its xml ID if it has one, otherwise from its property number which is its position in document order.
void * add_devtask(struct dcxt_s *dcxp,
devtask_fn *task,
size_t size)
Add a task (during parsing) for execution once the entire tree has been constructed but before returning it to the caller.
struct ddlprop_s * ddlref(struct rootdev_s *root,
struct ddlprop_s *pp,
const ddlchar_t *path)
Trace a DDL property cross reference using xml IDs and the syntax specified in the DDL spec.
static void bset_start(struct dcxt_s *dcxp,
const ddlchar_t **atta)
Process a behaviorset.
static void lset_start(struct dcxt_s *dcxp,
const ddlchar_t **atta)
Process a languageset.
static void lset_end(struct dcxt_s *dcxp)
We’ve accumulated the full language array in dcxp.
static void pprop_start(struct dcxt_s *dcxp,
const ddlchar_t **atta)
Start tag for property or pseudo property (includedev, propertypointer)
static int prop_wrapup(struct dcxt_s *dcxp)
Call after initial elements within property (label, behavior, value, protocol) but before children or included properties.
static int findaddr(union addrmap_u *amap,
uint32_t addr)
search the map for our insertion point
static void mapprop(struct dcxt_s *dcxp,
struct ddlprop_s *prop)
Add a dmp property to the property map.
static void propref_start(struct dcxt_s *dcxp,
const ddlchar_t **atta)
void queue_module(struct dcxt_s *dcxp,
tok_t modtype,
const ddlchar_t *name,
void *ref)
Add a DDL module to the queue for future processing.
void freerootdev(struct rootdev_s *dev)
Free all the resources used by a rootdev_s.
Mapgen generates the address map and property tables for an ACN device from its DDL description.
Resolve a UUID into a DDL file
Close