- infos = Information about the yanlr plugin is in keys below
- infos/author = René Schwaiger <sanssecours@me.com>
- infos/licence = BSD
- infos/needs = directoryvalue yamlsmith
- infos/provides = storage/yaml
- infos/recommends =
- infos/placements = getstorage
- infos/status = maintained unittest preview experimental unfinished nodoc concept discouraged
- infos/metadata =
- infos/description = This storage plugin use a parser generated by ANTLR to read YAML files

# Yan LR

## Introduction

This plugin uses ANTLR to generate a parser for the [YAML](http://yaml.org) serialization format. Apart from the hand written [lexer](yaml_lexer.cpp), all other parts of the plugin use standard ANTLR tools, such as

- an ANTLR [parser grammar](YAML.g4),
- a [listener](listener.cpp), that reacts to matches in the grammar building a key set, and
- an [error listener](error_listener.cpp) that reacts to syntax errors

.

## Dependencies

The plugin requires

- [ANTLR](https://www.antlr.org) `4.6` or later, and
- [ANTLR 4’s C++ runtime](https://github.com/antlr/antlr4/tree/master/runtime/Cpp).

If packages for those libraries are not available for your system, you can install them manually. For more information about that please
take a look [at ANTLR’s homepage](https://www.antlr.org) and at the
[ReadMe of the ANTLR C++ runtime](https://github.com/antlr/antlr4/tree/master/runtime/Cpp).

Please note that we only tested the plugin with ANTLR `4.7.1` (and later versions of ANTLR).

### Arch Linux

In [Arch Linux](https://www.archlinux.org) the required packages are called [`antlr4`](https://www.archlinux.org/packages/extra/any/antlr4) and [`antlr4-runtime`](https://www.archlinux.org/packages/community/x86_64/antlr4-runtime).

```
pacman -Sy --noconfirm antlr4 antlr4-runtime
```

### Debian Linux

In Debian Linux you need the packages [`antlr4`](https://packages.debian.org/sid/antlr4) and [`libantlr4-runtime-dev`](https://packages.debian.org/sid/libantlr4-runtime-dev).

```
apt-get -y install antlr4 libantlr4-runtime-dev
```

### macOS

To install the dependencies of the plugin using [Homebrew](https://brew.sh), please use the command below.

```
brew install antlr antlr4-cpp-runtime
```

## Examples

### Mappings

```sh
# Mount plugin to `user/tests/yanlr`
sudo kdb mount config.yaml user/tests/yanlr yanlr

# Manually add some mappings to the configuration file
printf 'all : circles presuppose\n' >  `kdb file user/tests/yanlr`
printf 'hello : world\n'            >> `kdb file user/tests/yanlr`

kdb ls /tests/yanlr
#> user/tests/yanlr/all
#> user/tests/yanlr/hello

kdb get user/tests/yanlr/all
#> circles presuppose

# Store value at root of mountpoint
kdb set user/tests/yanlr 'Mount Eerie'
kdb get user/tests/yanlr
#> Mount Eerie

# Add new key-value pairs
# Yan LR actually uses the YAML Smith plugin to write data
kdb set user/tests/yanlr/brand new
kdb set user/tests/yanlr/brand/new eyes
kdb set user/tests/yanlr/dance/gavin 'Dance!'

kdb ls /tests/yanlr
#> user/tests/yanlr
#> user/tests/yanlr/all
#> user/tests/yanlr/brand
#> user/tests/yanlr/brand/new
#> user/tests/yanlr/dance/gavin
#> user/tests/yanlr/hello

kdb get /tests/yanlr/hello
#> world
kdb get user/tests/yanlr/brand
#> new
kdb get /tests/yanlr/dance/gavin
#> Dance!

# Undo modifications to the key database
kdb rm -r user/tests/yanlr
sudo kdb umount user/tests/yanlr
```

### Arrays

```sh
# Mount plugin to `/tests/yanlr`
sudo kdb mount config.yaml user/tests/yanlr yanlr

# Manually add a sequences to the configuration file
printf 'primes:\n'   >  `kdb file user/tests/yanlr`
printf '  - two\n'   >> `kdb file user/tests/yanlr`
printf '  - three\n' >> `kdb file user/tests/yanlr`
printf '  - five\n'  >> `kdb file user/tests/yanlr`

kdb ls user/tests/yanlr
#> user/tests/yanlr/primes
#> user/tests/yanlr/primes/#0
#> user/tests/yanlr/primes/#1
#> user/tests/yanlr/primes/#2

kdb set user/tests/yanlr/primes/#3 seven

# Retrieve index of last array element
kdb meta-get user/tests/yanlr/primes array
#> #3

# Undo modifications to the key database
kdb rm -r user/tests/yanlr
sudo kdb umount user/tests/yanlr
```

### Boolean Values

```sh
# Mount plugin to `/tests/yanlr`
sudo kdb mount config.yaml user/tests/yanlr yanlr

# Manually add a boolean value to the database
printf 'boolean: true' > `kdb file user/tests/yanlr`

# Elektra stores boolean values as `0` and `1`
kdb get user/tests/yanlr/boolean
#> 1

# Undo modifications to the key database
kdb rm -r user/tests/yanlr
sudo kdb umount user/tests/yanlr
```

### Null Values

```sh
# Mount plugin to `/tests/yanlr`
sudo kdb mount config.yaml user/tests/yanlr yanlr

# Manually add a null value to the database
printf '"null":' > `kdb file user/tests/yanlr`

# Elektra adds the metakey `binary` for empty keys
kdb meta-ls user/tests/yanlr/null
#> binary

# Undo modifications to the key database
kdb rm -r user/tests/yanlr
sudo kdb umount user/tests/yanlr
```

### Error Messages

```sh
# Mount plugin
sudo kdb mount config.yaml user/tests/yanlr yanlr

# Manually add syntactically incorrect data
printf -- 'key: - element 1\n'                   >  `kdb file user/tests/yanlr`
printf -- '- element 2 # Incorrect Indentation!' >> `kdb file user/tests/yanlr`

# The plugin reports the location of the error
kdb ls user/tests/yanlr
# RET: 5
# STDERR: .*/config.yaml:2:1: mismatched input '- ' expecting end of map.*

# Let us look at the error message more closely.
# Since the location of `config.yaml` depends on the current user and OS,
# we store the text before `config.yaml` as `user/tests/error/prefix`.
kdb set user/tests/error "$(2>&1 kdb ls user/tests/yanlr)"
kdb set user/tests/error/prefix "$(kdb get user/tests/error | grep 'config.yaml' | head -1 | sed -E 's/(.*)config.yaml.*/\1/')"
# We also store the length of the prefix, so we can remove it from every
# line of the error message.
kdb set user/tests/error/prefix/length "$(kdb get user/tests/error/prefix | wc -c | sed 's/[ ]*//g')"

# Since we only want to look at the “reason” of the error, we
# remove the other part of the error message with `head` and `tail`.
kdb get user/tests/error | tail -n6 | cut -c"$(kdb get user/tests/error/prefix/length | tr -d '\n')"-
#> config.yaml:2:1: mismatched input '- ' expecting end of map
#>                  - element 2 # Incorrect Indentation!
#>                  ^^
#> config.yaml:2:37: extraneous input 'end of map' expecting end of document
#>                   - element 2 # Incorrect Indentation!
#>                                                       ^

# Fix syntax error
printf -- 'key: - element 1\n'        >  `kdb file user/tests/yanlr`
printf -- '     - element 2 # Fixed!' >> `kdb file user/tests/yanlr`

kdb ls user/tests/yanlr
#> user/tests/yanlr/key
#> user/tests/yanlr/key/#0
#> user/tests/yanlr/key/#1

# Undo modifications
kdb rm -r user/tests/error
kdb rm -r user/tests/yanlr
sudo kdb umount user/tests/yanlr
```

## Limitations

- The plugin does **not support**

  - plain scalars that span **multiple lines**,
  - **special characters** inside double quoted scalars,
  - other **line endings** than `\n` (Unix line endings),
  - **block scalars**,
  - **flow collections**,
  - **tags**,
  - **anchors & aliases**,
  - **multiple documents**, and
  - document **start and end markers**

  .

- Yan LR does not provide write support for data. Please use the [YAML Smith](../yamlsmith/) plugin for that purpose.

### Input Restrictions

The plugin should, but does not, limit the amount

- of **nesting levels**,
- the **length of numbers**, and
- the **length of scalars**

. These restrictions would make it less easy to crash the parser, by feeding it unrestricted data.

### Duplicate Keys

Currently the plugin parses the input

```yaml
duplicate: one
duplicate: two
```

storing the value `two`. According to the YAML specification the parser should not allow duplicated keys, and instead fail with an error, for the input above.

### Comments

The [lexer](yaml_lexer.cpp) does currently tokenize comments. Consequently the [plugin grammar](YAML.g4) of the plugin does also match comments. However, the [listener](listener.cpp) does currently **ignore comments**.

### Indentation

The lexer does not check for incorrect indentation. Consequently the following YAML data:

<!-- prettier-ignore-start -->

```yaml
	value
```

<!-- prettier-ignore-end -->

will produce a plain scalar containing a tab character followed by the text `value`. The correct behavior would be to report an error, since YAML does not allow tab characters in indentation.

### Error Messages

Visualized error messages (containing `^` markers) might not point to the correct error location, if the input contains tabs or unicode characters with a length other than `1`.
