Syntax Reference

ZOTI-YAML sources are files following the YAML 1.1 language specification. To aid organizing information into separate libraries and modules, ZOTI-YAML provides a set of language extensions, such as references and a module system.

Modules

Modules are the main organization units in ZOTI-YAML projects. Each module name needs to reflect the path of its associated source file. Assuming that a path NY_PATH is in the search path, then the file MY_PATH/path/to/MyModule.yaml would correspond to the qualified module name path.to.MyModule. The search path can be controlled via:

Each module consists in a YAML source file formatted as:

preamble
---
document

where preamble contains directives, such as import declarations, descriptions or other document-wide definitions, whereas document contains the actual source code.

Preamble

A module’s peramble is for the most part a free-form dictionary containing metadata preserved and used by downstream tools. ZOTI-YAML uses two of these entries:

module (string)

Mandatory field containing the qualified module name. It needs to reflect its path relative to the loading path, otherwise an error is raised upon its loading.

import (list)

List of imports. Each entry is a dictionay:

module:

(string) qualified name of module being imported.

as:

(string) short alias used in the document.

Document

Commands

ZOTI-YAML documents are regular YAML files that might contain some additional keywords followed by certain arguments, as documented below.

!default [defaults, originals]

This keyword is followed by a list of exactly 2 YAML objects (i.e., trees), defaults and original. When the document tree is being resolved, it recursivvely fills in the contents of original according to the values in defaults recursively, based on the active merge policy (see !policy:<merge_policy>). The default merge policy is !policy:union.

A policy is a marked YAML node in the defaults tree, and is active from that node to all its childred until the last leaf node or until a node with a policy changing marker. E.g.:

!default
- !policy:A
  root:          # policy A holds
  - foo: bar     # policy A holds
  - !policy:B
    biz:         # policy B holds
    - baz        # policy B holds
    - buzz       # policy B holds
  - bam: blep    # policy A holds
- root: ...

Example:

!default
- root:
  - !policy:intersect
    foo:
      a: this is superseded by original
      b: !policy:union+replace this supersedes the original
      c: this will be ignored
      d: !policy:union this is created
  - bar: this is ignored (only the first element in a list in !defaults matters)
- root:
  - foo:
      a: this supersedes the default value
      b: this is superseded by the default value
  - foo:
      d: this supersedes the default value

resolves to:

root:
  - foo:
      a: this supersedes the default value
      b: this supersedes the original
      d: this is created
  - foo:
      b: this supersedes the original
      d: this supersedes the default value

!policy:<merge_policy> any

Policy dictating how the defaults object is to be recursively merged in originals (see !default). The current policies are:

union

performs the union between originals and defaults where originals have priority if the same key is found.

union+replace

performs the union between originals and defaults where defaults have priority if the same key is found.

intersect

ignores fields from defaults whose keys are not explicitly found in originals. originals have priority when the same key is found.

intersect+replace

ignores fields from defaults whose keys are not explicitly found in originals. defaults have priority when the same key is found.

!ref {module, path | name}

Reference to an element in a module. When resolved, this object contains the qualified name or path to a (unique) element. It may have the following keyword arguments:

module: (optional, string)

A module name or an alias. It resolves to an explicit name. If none is specified it is assumed to be the current module.

path: (mutually exlusive with name, string)

A path to an element in the tree (see zoti_yaml.core.TreePath).

name: (mutually exlusive with path, string)

An identifier of the element in a certain module.

The type of the reference depends on which tool in the downstream uses it. If the tool works with, e.g. JSON trees (similar to ZOTI-YAML) then specifying path is more reasonable as it is being constructed into a zoti_yaml.core.TreePath handler. However, if the tool works with other type of element identifiers, name should be used, as it preserves the string for downstream manipulation.

Example:

module: Foo
import: {module: Foo.Bar, as: Baz}
---
root:
  - ref1: !ref {path: ../../ref2}
  - ref2: !ref {module: Baz, name: "egg"}

resolves to:

module: Foo
import: {module: Foo.Bar, as: Baz}
---
root:
  - ref1: {module: Foo, path: ../../ref2}
  - ref2: {module: Foo.Bar, name: "egg"}

!attach {ref, …}

Attaches a referenced node at the current location. This command can have any number of keyword arguments, of which one needs to be ref.

ref: (object) Qualified zoti_yaml.core.TreePath-based

reference to another node (see !ref). If the path

  • begins with character / , it is absolute (i.e. relative to the root of the module)

  • begins with a character other than /, it is relative to this node.

: (keyword pairs)

Arbitrary entries passed to the attached node in the following conditions:

  • if the attached node is not an object (i.e. dictionary), these entries are ignored;

  • if the attached node is an object but does not contain the said entry, it is ignored;

  • if the attached node is an object and contains the said entry, the argument entry replaces the original one. The original entry is stored at path _info/_prev_attrs underneath the attached node.

OBS1: If both the parent and referenced nodes contain positional information, this will be captured in the _info/_pos entry, whose head will point to the original position of the referenced node.

OBS2: !ref commands are resolved before !attach, thus they can be used to construct references.

OBS3: when exchanging arguments between the caller and callee it is recommended to use a dedicated field (e.g., check the argfields argument of zoti_yaml.handlers.Project)

Example:

module: Foo
---
root:
  !attach
  ref: {module: Bar, path: /root[bar]/refd}
  a: this replaces the original
  b: this is ignored
module: Bar
---
root:
  - name: bar
    refd:
      a: this is replaced
      c: this is carried from the original

resolves to:

module: Foo
---
root:
  a: this replaces the original
  c: this is carried from the original

Module Bar remains unchanged.

!include {file, name | (begin, end)}

One can import raw (chunks of) files using the the !include command. It should be used to attach entire source code to entries of certain nodes. Usage:

!include {file: path/relative/to/module.ext}
!include {file: path/relative/to/module.ext, name: block_name}
!include {file: path/relative/to/module.ext, begin: (keyword|number), end: (keyword|number)}

In the first case an entire file is included as raw text. In the second case it extracts the portion of text between the lines containing BEGIN block_name and END block_name. In the third case the start and stop lines are specified either as line numbers or by arbitrary keywords.

Position Information

In addition to the explicit behavior controlled with commands, ZOTI-YAML implicitly stores positional metadata for certain keys under an entry at _info/_pos. Exactly which nodes are marked for metadata is controlled via:

For each key in the provided list, ZOTI-YAML will mark all first children of its entry, provided that the children are objects themselves. For example, if keys is set to ["root", "node"], here is how a processed yaml file could look like:

root:
- name: a                 # marked, child of "root"
  _info: {_pos: ...}
  foo:
    node:
    - name: b             # marked, child of "node"
      _info: {_pos: ...}
    - bar: baz            # marked, child of "node"
      _info: {_pos: ...}
- name: b                 # marked, child of "root"
  _info: {_pos: ...}
  foo: bar
- baz                     # not marked, not an object

The positional metadata at _info/_pos is presented as a list of entries containing positional information. The last entry in a list corresponds to the current position in the file. Every time a (marked) node is parsed by ZOTI-YAML its current position is appended at the end of the list. This mean that certain nodes can be “handed over” between tools, and ZOTI-YAML will keep the history of their positions (e.g. in intemediate files). In the following example the shown node has been pre-processed by ZOTI-YAML in preparation for two other tools (ZOTI-Graph and ZOTI-Gen), and its information has resided in two different files.

_info:
  _pos:
    - [36, 14, 1066, 1220, app/ProdCons/Src.zog, zoti-graph-0.1.0]
    - [20, 8, 740, 906, gen/graph/ProdCons.yaml, zoti-gen-0.1.0]

Check the API Reference for what each field means.

Configuration

The CLI tool can be run like any Python module depending on how it is distributed. Configuration arguments are documented by passing --help. Each configuration parameter can be specified also in a TOML file called zoticonf.toml found in the current folder, under the following sections:

[zoti]

# global configuration variables, read by all tools

[zoti.yaml]

# configuration variables for ZOTI-YAML. override global ones

[zoti.yaml.<class>]

# specialized configuration loaded with:
#
#      $(ZOML) --spec <class>
#
# where <class> is replaced with an arbitrary name. Overrides 
# parameters in [zoti.yaml]