xref: /freebsd-src/contrib/libucl/README.md (revision a0409676120c1e558d0ade943019934e0f15118d)
197bd480fSBaptiste Daroussin# LIBUCL
297bd480fSBaptiste Daroussin
3*a0409676SBaptiste Daroussin[![CircleCI](https://circleci.com/gh/vstakhov/libucl.svg?style=svg)](https://circleci.com/gh/vstakhov/libucl)
411dd9ed6SBaptiste Daroussin[![Coverity](https://scan.coverity.com/projects/4138/badge.svg)](https://scan.coverity.com/projects/4138)
511dd9ed6SBaptiste Daroussin[![Coverage Status](https://coveralls.io/repos/github/vstakhov/libucl/badge.svg?branch=master)](https://coveralls.io/github/vstakhov/libucl?branch=master)
697bd480fSBaptiste Daroussin
797bd480fSBaptiste Daroussin**Table of Contents**  *generated with [DocToc](http://doctoc.herokuapp.com/)*
897bd480fSBaptiste Daroussin
997bd480fSBaptiste Daroussin- [Introduction](#introduction)
1097bd480fSBaptiste Daroussin- [Basic structure](#basic-structure)
1197bd480fSBaptiste Daroussin- [Improvements to the json notation](#improvements-to-the-json-notation)
1297bd480fSBaptiste Daroussin	- [General syntax sugar](#general-syntax-sugar)
1397bd480fSBaptiste Daroussin	- [Automatic arrays creation](#automatic-arrays-creation)
1497bd480fSBaptiste Daroussin	- [Named keys hierarchy](#named-keys-hierarchy)
1597bd480fSBaptiste Daroussin	- [Convenient numbers and booleans](#convenient-numbers-and-booleans)
1697bd480fSBaptiste Daroussin- [General improvements](#general-improvements)
17273c26a3SBaptiste Daroussin	- [Comments](#comments)
1897bd480fSBaptiste Daroussin	- [Macros support](#macros-support)
1997bd480fSBaptiste Daroussin	- [Variables support](#variables-support)
2097bd480fSBaptiste Daroussin	- [Multiline strings](#multiline-strings)
21*a0409676SBaptiste Daroussin	- [Single quoted strings](#single-quoted-strings)
2297bd480fSBaptiste Daroussin- [Emitter](#emitter)
2397bd480fSBaptiste Daroussin- [Validation](#validation)
2497bd480fSBaptiste Daroussin- [Performance](#performance)
2597bd480fSBaptiste Daroussin- [Conclusion](#conclusion)
2697bd480fSBaptiste Daroussin
27c99fb5f9SBaptiste Daroussin## Introduction
28c99fb5f9SBaptiste Daroussin
29c99fb5f9SBaptiste DaroussinThis document describes the main features and principles of the configuration
30c99fb5f9SBaptiste Daroussinlanguage called `UCL` - universal configuration language.
31c99fb5f9SBaptiste Daroussin
32c99fb5f9SBaptiste DaroussinIf you are looking for the libucl API documentation you can find it at [this page](doc/api.md).
33c99fb5f9SBaptiste Daroussin
34c99fb5f9SBaptiste Daroussin## Basic structure
35c99fb5f9SBaptiste Daroussin
36c99fb5f9SBaptiste DaroussinUCL is heavily infused by `nginx` configuration as the example of a convenient configuration
37c99fb5f9SBaptiste Daroussinsystem. However, UCL is fully compatible with `JSON` format and is able to parse json files.
38c99fb5f9SBaptiste DaroussinFor example, you can write the same configuration in the following ways:
39c99fb5f9SBaptiste Daroussin
40c99fb5f9SBaptiste Daroussin* in nginx like:
41c99fb5f9SBaptiste Daroussin
42c99fb5f9SBaptiste Daroussin```nginx
43c99fb5f9SBaptiste Daroussinparam = value;
44c99fb5f9SBaptiste Daroussinsection {
45c99fb5f9SBaptiste Daroussin    param = value;
46c99fb5f9SBaptiste Daroussin    param1 = value1;
47c99fb5f9SBaptiste Daroussin    flag = true;
48c99fb5f9SBaptiste Daroussin    number = 10k;
49c99fb5f9SBaptiste Daroussin    time = 0.2s;
50c99fb5f9SBaptiste Daroussin    string = "something";
51c99fb5f9SBaptiste Daroussin    subsection {
52c99fb5f9SBaptiste Daroussin        host = {
53c99fb5f9SBaptiste Daroussin            host = "hostname";
54c99fb5f9SBaptiste Daroussin            port = 900;
55c99fb5f9SBaptiste Daroussin        }
56c99fb5f9SBaptiste Daroussin        host = {
57c99fb5f9SBaptiste Daroussin            host = "hostname";
58c99fb5f9SBaptiste Daroussin            port = 901;
59c99fb5f9SBaptiste Daroussin        }
60c99fb5f9SBaptiste Daroussin    }
61c99fb5f9SBaptiste Daroussin}
62c99fb5f9SBaptiste Daroussin```
63c99fb5f9SBaptiste Daroussin
64c99fb5f9SBaptiste Daroussin* or in JSON:
65c99fb5f9SBaptiste Daroussin
66c99fb5f9SBaptiste Daroussin```json
67c99fb5f9SBaptiste Daroussin{
68c99fb5f9SBaptiste Daroussin    "param": "value",
69*a0409676SBaptiste Daroussin    "section": {
70*a0409676SBaptiste Daroussin        "param": "value",
71c99fb5f9SBaptiste Daroussin        "param1": "value1",
72c99fb5f9SBaptiste Daroussin        "flag": true,
73*a0409676SBaptiste Daroussin        "number": 10000,
74*a0409676SBaptiste Daroussin        "time": "0.2s",
75*a0409676SBaptiste Daroussin        "string": "something",
76c99fb5f9SBaptiste Daroussin        "subsection": {
77c99fb5f9SBaptiste Daroussin            "host": [
78c99fb5f9SBaptiste Daroussin                {
79c99fb5f9SBaptiste Daroussin                    "host": "hostname",
80c99fb5f9SBaptiste Daroussin                    "port": 900
81c99fb5f9SBaptiste Daroussin                },
82c99fb5f9SBaptiste Daroussin                {
83c99fb5f9SBaptiste Daroussin                    "host": "hostname",
84c99fb5f9SBaptiste Daroussin                    "port": 901
85c99fb5f9SBaptiste Daroussin                }
86c99fb5f9SBaptiste Daroussin            ]
87c99fb5f9SBaptiste Daroussin        }
88c99fb5f9SBaptiste Daroussin    }
89*a0409676SBaptiste Daroussin}
90c99fb5f9SBaptiste Daroussin```
91c99fb5f9SBaptiste Daroussin
92c99fb5f9SBaptiste Daroussin## Improvements to the json notation.
93c99fb5f9SBaptiste Daroussin
94c99fb5f9SBaptiste DaroussinThere are various things that make ucl configuration more convenient for editing than strict json:
95c99fb5f9SBaptiste Daroussin
96c99fb5f9SBaptiste Daroussin### General syntax sugar
97c99fb5f9SBaptiste Daroussin
98c99fb5f9SBaptiste Daroussin* Braces are not necessary to enclose a top object: it is automatically treated as an object:
99c99fb5f9SBaptiste Daroussin
100c99fb5f9SBaptiste Daroussin```json
101c99fb5f9SBaptiste Daroussin"key": "value"
102c99fb5f9SBaptiste Daroussin```
103c99fb5f9SBaptiste Daroussinis equal to:
104c99fb5f9SBaptiste Daroussin```json
105c99fb5f9SBaptiste Daroussin{"key": "value"}
106c99fb5f9SBaptiste Daroussin```
107c99fb5f9SBaptiste Daroussin
108c99fb5f9SBaptiste Daroussin* There is no requirement of quotes for strings and keys, moreover, `:` may be replaced `=` or even be skipped for objects:
109c99fb5f9SBaptiste Daroussin
110c99fb5f9SBaptiste Daroussin```nginx
111c99fb5f9SBaptiste Daroussinkey = value;
112c99fb5f9SBaptiste Daroussinsection {
113c99fb5f9SBaptiste Daroussin    key = value;
114c99fb5f9SBaptiste Daroussin}
115c99fb5f9SBaptiste Daroussin```
116c99fb5f9SBaptiste Daroussinis equal to:
117c99fb5f9SBaptiste Daroussin```json
118c99fb5f9SBaptiste Daroussin{
119c99fb5f9SBaptiste Daroussin    "key": "value",
120c99fb5f9SBaptiste Daroussin    "section": {
121c99fb5f9SBaptiste Daroussin        "key": "value"
122c99fb5f9SBaptiste Daroussin    }
123c99fb5f9SBaptiste Daroussin}
124c99fb5f9SBaptiste Daroussin```
125c99fb5f9SBaptiste Daroussin
126c99fb5f9SBaptiste Daroussin* No commas mess: you can safely place a comma or semicolon for the last element in an array or an object:
127c99fb5f9SBaptiste Daroussin
128c99fb5f9SBaptiste Daroussin```json
129c99fb5f9SBaptiste Daroussin{
130c99fb5f9SBaptiste Daroussin    "key1": "value",
131c99fb5f9SBaptiste Daroussin    "key2": "value",
132c99fb5f9SBaptiste Daroussin}
133c99fb5f9SBaptiste Daroussin```
134c99fb5f9SBaptiste Daroussin### Automatic arrays creation
135c99fb5f9SBaptiste Daroussin
136c99fb5f9SBaptiste Daroussin* Non-unique keys in an object are allowed and are automatically converted to the arrays internally:
137c99fb5f9SBaptiste Daroussin
138c99fb5f9SBaptiste Daroussin```json
139c99fb5f9SBaptiste Daroussin{
140c99fb5f9SBaptiste Daroussin    "key": "value1",
141c99fb5f9SBaptiste Daroussin    "key": "value2"
142c99fb5f9SBaptiste Daroussin}
143c99fb5f9SBaptiste Daroussin```
144c99fb5f9SBaptiste Daroussinis converted to:
145c99fb5f9SBaptiste Daroussin```json
146c99fb5f9SBaptiste Daroussin{
147c99fb5f9SBaptiste Daroussin    "key": ["value1", "value2"]
148c99fb5f9SBaptiste Daroussin}
149c99fb5f9SBaptiste Daroussin```
150c99fb5f9SBaptiste Daroussin
151c99fb5f9SBaptiste Daroussin### Named keys hierarchy
152c99fb5f9SBaptiste Daroussin
153c99fb5f9SBaptiste DaroussinUCL accepts named keys and organize them into objects hierarchy internally. Here is an example of this process:
154c99fb5f9SBaptiste Daroussin```nginx
155c99fb5f9SBaptiste Daroussinsection "blah" {
156c99fb5f9SBaptiste Daroussin	key = value;
157c99fb5f9SBaptiste Daroussin}
158c99fb5f9SBaptiste Daroussinsection foo {
159c99fb5f9SBaptiste Daroussin	key = value;
160c99fb5f9SBaptiste Daroussin}
161c99fb5f9SBaptiste Daroussin```
162c99fb5f9SBaptiste Daroussin
163c99fb5f9SBaptiste Daroussinis converted to the following object:
164c99fb5f9SBaptiste Daroussin
165c99fb5f9SBaptiste Daroussin```nginx
166c99fb5f9SBaptiste Daroussinsection {
167c99fb5f9SBaptiste Daroussin	blah {
168c99fb5f9SBaptiste Daroussin		key = value;
169c99fb5f9SBaptiste Daroussin	}
170c99fb5f9SBaptiste Daroussin	foo {
171c99fb5f9SBaptiste Daroussin		key = value;
172c99fb5f9SBaptiste Daroussin	}
173c99fb5f9SBaptiste Daroussin}
174c99fb5f9SBaptiste Daroussin```
175c99fb5f9SBaptiste Daroussin
176c99fb5f9SBaptiste DaroussinPlain definitions may be more complex and contain more than a single level of nested objects:
177c99fb5f9SBaptiste Daroussin
178c99fb5f9SBaptiste Daroussin```nginx
179c99fb5f9SBaptiste Daroussinsection "blah" "foo" {
180c99fb5f9SBaptiste Daroussin	key = value;
181c99fb5f9SBaptiste Daroussin}
182c99fb5f9SBaptiste Daroussin```
183c99fb5f9SBaptiste Daroussin
184c99fb5f9SBaptiste Daroussinis presented as:
185c99fb5f9SBaptiste Daroussin
186c99fb5f9SBaptiste Daroussin```nginx
187c99fb5f9SBaptiste Daroussinsection {
188c99fb5f9SBaptiste Daroussin	blah {
189c99fb5f9SBaptiste Daroussin		foo {
190c99fb5f9SBaptiste Daroussin			key = value;
191c99fb5f9SBaptiste Daroussin		}
192c99fb5f9SBaptiste Daroussin	}
193c99fb5f9SBaptiste Daroussin}
194c99fb5f9SBaptiste Daroussin```
195c99fb5f9SBaptiste Daroussin
196c99fb5f9SBaptiste Daroussin### Convenient numbers and booleans
197c99fb5f9SBaptiste Daroussin
198c99fb5f9SBaptiste Daroussin* Numbers can have suffixes to specify standard multipliers:
199c99fb5f9SBaptiste Daroussin    + `[kKmMgG]` - standard 10 base multipliers (so `1k` is translated to 1000)
200c99fb5f9SBaptiste Daroussin    + `[kKmMgG]b` - 2 power multipliers (so `1kb` is translated to 1024)
201c99fb5f9SBaptiste Daroussin    + `[s|min|d|w|y]` - time multipliers, all time values are translated to float number of seconds, for example `10min` is translated to 600.0 and `10ms` is translated to 0.01
202c99fb5f9SBaptiste Daroussin* Hexadecimal integers can be used by `0x` prefix, for example `key = 0xff`. However, floating point values can use decimal base only.
203c99fb5f9SBaptiste Daroussin* Booleans can be specified as `true` or `yes` or `on` and `false` or `no` or `off`.
204c99fb5f9SBaptiste Daroussin* It is still possible to treat numbers and booleans as strings by enclosing them in double quotes.
205c99fb5f9SBaptiste Daroussin
206c99fb5f9SBaptiste Daroussin## General improvements
207c99fb5f9SBaptiste Daroussin
208273c26a3SBaptiste Daroussin### Comments
209c99fb5f9SBaptiste Daroussin
210c99fb5f9SBaptiste DaroussinUCL supports different style of comments:
211c99fb5f9SBaptiste Daroussin
212c99fb5f9SBaptiste Daroussin* single line: `#`
213c99fb5f9SBaptiste Daroussin* multiline: `/* ... */`
214c99fb5f9SBaptiste Daroussin
215c99fb5f9SBaptiste DaroussinMultiline comments may be nested:
216c99fb5f9SBaptiste Daroussin```c
217c99fb5f9SBaptiste Daroussin# Sample single line comment
218c99fb5f9SBaptiste Daroussin/*
219c99fb5f9SBaptiste Daroussin some comment
220c99fb5f9SBaptiste Daroussin /* nested comment */
221c99fb5f9SBaptiste Daroussin end of comment
222c99fb5f9SBaptiste Daroussin*/
223c99fb5f9SBaptiste Daroussin```
224c99fb5f9SBaptiste Daroussin
225c99fb5f9SBaptiste Daroussin### Macros support
226c99fb5f9SBaptiste Daroussin
227c99fb5f9SBaptiste DaroussinUCL supports external macros both multiline and single line ones:
228c99fb5f9SBaptiste Daroussin```nginx
22911dd9ed6SBaptiste Daroussin.macro_name "sometext";
23011dd9ed6SBaptiste Daroussin.macro_name {
231c99fb5f9SBaptiste Daroussin    Some long text
232c99fb5f9SBaptiste Daroussin    ....
233c99fb5f9SBaptiste Daroussin};
234c99fb5f9SBaptiste Daroussin```
235c99fb5f9SBaptiste Daroussin
2364bf54857SBaptiste DaroussinMoreover, each macro can accept an optional list of arguments in braces. These
2374bf54857SBaptiste Daroussinarguments themselves are the UCL object that is parsed and passed to a macro as
2384bf54857SBaptiste Daroussinoptions:
239c99fb5f9SBaptiste Daroussin
2404bf54857SBaptiste Daroussin```nginx
24111dd9ed6SBaptiste Daroussin.macro_name(param=value) "something";
24211dd9ed6SBaptiste Daroussin.macro_name(param={key=value}) "something";
24311dd9ed6SBaptiste Daroussin.macro_name(.include "params.conf") "something";
24411dd9ed6SBaptiste Daroussin.macro_name(#this is multiline macro
2454bf54857SBaptiste Daroussinparam = [value1, value2]) "something";
24611dd9ed6SBaptiste Daroussin.macro_name(key="()") "something";
2474bf54857SBaptiste Daroussin```
2484bf54857SBaptiste Daroussin
2494bf54857SBaptiste DaroussinUCL also provide a convenient `include` macro to load content from another files
2504bf54857SBaptiste Daroussinto the current UCL object. This macro accepts either path to file:
2514bf54857SBaptiste Daroussin
2524bf54857SBaptiste Daroussin```nginx
2534bf54857SBaptiste Daroussin.include "/full/path.conf"
2544bf54857SBaptiste Daroussin.include "./relative/path.conf"
2554bf54857SBaptiste Daroussin.include "${CURDIR}/path.conf"
2564bf54857SBaptiste Daroussin```
2574bf54857SBaptiste Daroussin
2584bf54857SBaptiste Daroussinor URL (if ucl is built with url support provided by either `libcurl` or `libfetch`):
2594bf54857SBaptiste Daroussin
2604bf54857SBaptiste Daroussin	.include "http://example.com/file.conf"
2614bf54857SBaptiste Daroussin
2624bf54857SBaptiste Daroussin`.include` macro supports a set of options:
2634bf54857SBaptiste Daroussin
2644bf54857SBaptiste Daroussin* `try` (default: **false**) - if this option is `true` than UCL treats errors on loading of
2654bf54857SBaptiste Daroussinthis file as non-fatal. For example, such a file can be absent but it won't stop the parsing
2664bf54857SBaptiste Daroussinof the top-level document.
2674bf54857SBaptiste Daroussin* `sign` (default: **false**) - if this option is `true` UCL loads and checks the signature for
2684bf54857SBaptiste Daroussina file from path named `<FILEPATH>.sig`. Trusted public keys should be provided for UCL API after
2694bf54857SBaptiste Daroussinparser is created but before any configurations are parsed.
2704bf54857SBaptiste Daroussin* `glob` (default: **false**) - if this option is `true` UCL treats the filename as GLOB pattern and load
2714bf54857SBaptiste Daroussinall files that matches the specified pattern (normally the format of patterns is defined in `glob` manual page
2724bf54857SBaptiste Daroussinfor your operating system). This option is meaningless for URL includes.
2734bf54857SBaptiste Daroussin* `url` (default: **true**) - allow URL includes.
27439ee7a7aSBaptiste Daroussin* `path` (default: empty) - A UCL_ARRAY of directories to search for the include file.
275273c26a3SBaptiste DaroussinSearch ends after the first match, unless `glob` is true, then all matches are included.
27639ee7a7aSBaptiste Daroussin* `prefix` (default false) - Put included contents inside an object, instead
27739ee7a7aSBaptiste Daroussinof loading them into the root. If no `key` is provided, one is automatically generated based on each files basename()
27839ee7a7aSBaptiste Daroussin* `key` (default: <empty string>) - Key to load contents of include into. If
27939ee7a7aSBaptiste Daroussinthe key already exists, it must be the correct type
28039ee7a7aSBaptiste Daroussin* `target` (default: object) - Specify if the `prefix` `key` should be an
28139ee7a7aSBaptiste Daroussinobject or an array.
2824bf54857SBaptiste Daroussin* `priority` (default: 0) - specify priority for the include (see below).
28339ee7a7aSBaptiste Daroussin* `duplicate` (default: 'append') - specify policy of duplicates resolving:
28439ee7a7aSBaptiste Daroussin	- `append` - default strategy, if we have new object of higher priority then it replaces old one, if we have new object with less priority it is ignored completely, and if we have two duplicate objects with the same priority then we have a multi-value key (implicit array)
285273c26a3SBaptiste Daroussin	- `merge` - if we have object or array, then new keys are merged inside, if we have a plain object then an implicit array is formed (regardless of priorities)
28639ee7a7aSBaptiste Daroussin	- `error` - create error on duplicate keys and stop parsing
28739ee7a7aSBaptiste Daroussin	- `rewrite` - always rewrite an old value with new one (ignoring priorities)
2884bf54857SBaptiste Daroussin
2894bf54857SBaptiste DaroussinPriorities are used by UCL parser to manage the policy of objects rewriting during including other files
2904bf54857SBaptiste Daroussinas following:
2914bf54857SBaptiste Daroussin
2924bf54857SBaptiste Daroussin* If we have two objects with the same priority then we form an implicit array
2934bf54857SBaptiste Daroussin* If a new object has bigger priority then we overwrite an old one
2944bf54857SBaptiste Daroussin* If a new object has lower priority then we ignore it
2954bf54857SBaptiste Daroussin
2964bf54857SBaptiste DaroussinBy default, the priority of top-level object is set to zero (lowest priority). Currently,
2974bf54857SBaptiste Daroussinyou can define up to 16 priorities (from 0 to 15). Includes with bigger priorities will
298*a0409676SBaptiste Daroussinrewrite keys from the objects with lower priorities as specified by the policy. The priority
299*a0409676SBaptiste Daroussinof the top-level or any other object can be changed with the `.priority` macro, which has no
300*a0409676SBaptiste Daroussinoptions and takes the new priority:
301*a0409676SBaptiste Daroussin
302*a0409676SBaptiste Daroussin```
303*a0409676SBaptiste Daroussin# Default priority: 0.
304*a0409676SBaptiste Daroussinfoo = 6
305*a0409676SBaptiste Daroussin.priority 5
306*a0409676SBaptiste Daroussin# The following will have priority 5.
307*a0409676SBaptiste Daroussinbar = 6
308*a0409676SBaptiste Daroussinbaz = 7
309*a0409676SBaptiste Daroussin# The following will be included with a priority of 3, 5, and 6 respectively.
310*a0409676SBaptiste Daroussin.include(priority=3) "path.conf"
311*a0409676SBaptiste Daroussin.include(priority=5) "equivalent-path.conf"
312*a0409676SBaptiste Daroussin.include(priority=6) "highpriority-path.conf"
313*a0409676SBaptiste Daroussin```
314c99fb5f9SBaptiste Daroussin
315c99fb5f9SBaptiste Daroussin### Variables support
316c99fb5f9SBaptiste Daroussin
317c99fb5f9SBaptiste DaroussinUCL supports variables in input. Variables are registered by a user of the UCL parser and can be presented in the following forms:
318c99fb5f9SBaptiste Daroussin
319c99fb5f9SBaptiste Daroussin* `${VARIABLE}`
320c99fb5f9SBaptiste Daroussin* `$VARIABLE`
321c99fb5f9SBaptiste Daroussin
322c99fb5f9SBaptiste DaroussinUCL currently does not support nested variables. To escape variables one could use double dollar signs:
323c99fb5f9SBaptiste Daroussin
324c99fb5f9SBaptiste Daroussin* `$${VARIABLE}` is converted to `${VARIABLE}`
325c99fb5f9SBaptiste Daroussin* `$$VARIABLE` is converted to `$VARIABLE`
326c99fb5f9SBaptiste Daroussin
327c99fb5f9SBaptiste DaroussinHowever, if no valid variables are found in a string, no expansion will be performed (and `$$` thus remains unchanged). This may be a subject
328c99fb5f9SBaptiste Daroussinto change in future libucl releases.
329c99fb5f9SBaptiste Daroussin
330c99fb5f9SBaptiste Daroussin### Multiline strings
331c99fb5f9SBaptiste Daroussin
332c99fb5f9SBaptiste DaroussinUCL can handle multiline strings as well as single line ones. It uses shell/perl like notation for such objects:
333c99fb5f9SBaptiste Daroussin```
334c99fb5f9SBaptiste Daroussinkey = <<EOD
335c99fb5f9SBaptiste Daroussinsome text
336c99fb5f9SBaptiste Daroussinsplitted to
337c99fb5f9SBaptiste Daroussinlines
338c99fb5f9SBaptiste DaroussinEOD
339c99fb5f9SBaptiste Daroussin```
340c99fb5f9SBaptiste Daroussin
341c99fb5f9SBaptiste DaroussinIn this example `key` will be interpreted as the following string: `some text\nsplitted to\nlines`.
342c99fb5f9SBaptiste DaroussinHere are some rules for this syntax:
343c99fb5f9SBaptiste Daroussin
344c99fb5f9SBaptiste Daroussin* Multiline terminator must start just after `<<` symbols and it must consist of capital letters only (e.g. `<<eof` or `<< EOF` won't work);
345c99fb5f9SBaptiste Daroussin* Terminator must end with a single newline character (and no spaces are allowed between terminator and newline character);
346c99fb5f9SBaptiste Daroussin* To finish multiline string you need to include a terminator string just after newline and followed by a newline (no spaces or other characters are allowed as well);
347273c26a3SBaptiste Daroussin* The initial and the final newlines are not inserted to the resulting string, but you can still specify newlines at the beginning and at the end of a value, for example:
348c99fb5f9SBaptiste Daroussin
349c99fb5f9SBaptiste Daroussin```
350c99fb5f9SBaptiste Daroussinkey <<EOD
351c99fb5f9SBaptiste Daroussin
352c99fb5f9SBaptiste Daroussinsome
353c99fb5f9SBaptiste Daroussintext
354c99fb5f9SBaptiste Daroussin
355c99fb5f9SBaptiste DaroussinEOD
356c99fb5f9SBaptiste Daroussin```
357c99fb5f9SBaptiste Daroussin
358*a0409676SBaptiste Daroussin### Single quoted strings
359*a0409676SBaptiste Daroussin
360*a0409676SBaptiste DaroussinIt is possible to use single quoted strings to simplify escaping rules. All values passed in single quoted strings are *NOT* escaped, with two exceptions: a single `'` character just before `\` character, and a newline character just after `\` character that is ignored.
361*a0409676SBaptiste Daroussin
362*a0409676SBaptiste Daroussin```
363*a0409676SBaptiste Daroussinkey = 'value'; # Read as value
364*a0409676SBaptiste Daroussinkey = 'value\n\'; # Read as  value\n\
365*a0409676SBaptiste Daroussinkey = 'value\''; # Read as value'
366*a0409676SBaptiste Daroussinkey = 'value\
367*a0409676SBaptiste Daroussinbla'; # Read as valuebla
368*a0409676SBaptiste Daroussin```
369*a0409676SBaptiste Daroussin
370c99fb5f9SBaptiste Daroussin## Emitter
371c99fb5f9SBaptiste Daroussin
372*a0409676SBaptiste DaroussinEach UCL object can be serialized to one of the four supported formats:
373c99fb5f9SBaptiste Daroussin
374c99fb5f9SBaptiste Daroussin* `JSON` - canonic json notation (with spaces indented structure);
375c99fb5f9SBaptiste Daroussin* `Compacted JSON` - compact json notation (without spaces or newlines);
376c99fb5f9SBaptiste Daroussin* `Configuration` - nginx like notation;
377c99fb5f9SBaptiste Daroussin* `YAML` - yaml inlined notation.
378c99fb5f9SBaptiste Daroussin
37997bd480fSBaptiste Daroussin## Validation
38097bd480fSBaptiste Daroussin
381b04a7a0bSBaptiste DaroussinUCL allows validation of objects. It uses the same schema that is used for json: [json schema v4](http://json-schema.org). UCL supports the full set of json schema with the exception of remote references. This feature is unlikely useful for configuration objects. Of course, a schema definition can be in UCL format instead of JSON that simplifies schemas writing. Moreover, since UCL supports multiple values for keys in an object it is possible to specify generic integer constraints `maxValues` and `minValues` to define the limits of values count in a single key. UCL currently is not absolutely strict about validation schemas themselves, therefore UCL users should supply valid schemas (as it is defined in json-schema draft v4) to ensure that the input objects are validated properly.
38297bd480fSBaptiste Daroussin
383c99fb5f9SBaptiste Daroussin## Performance
384c99fb5f9SBaptiste Daroussin
385c99fb5f9SBaptiste DaroussinAre UCL parser and emitter fast enough? Well, there are some numbers.
386273c26a3SBaptiste DaroussinI got a 19Mb file that consist of ~700 thousand lines of json (obtained via
387c99fb5f9SBaptiste Daroussinhttp://www.json-generator.com/). Then I checked jansson library that performs json
388c99fb5f9SBaptiste Daroussinparsing and emitting and compared it with UCL. Here are results:
389c99fb5f9SBaptiste Daroussin
390c99fb5f9SBaptiste Daroussin```
391c99fb5f9SBaptiste Daroussinjansson: parsed json in 1.3899 seconds
392c99fb5f9SBaptiste Daroussinjansson: emitted object in 0.2609 seconds
393c99fb5f9SBaptiste Daroussin
394c99fb5f9SBaptiste Daroussinucl: parsed input in 0.6649 seconds
395c99fb5f9SBaptiste Daroussinucl: emitted config in 0.2423 seconds
396c99fb5f9SBaptiste Daroussinucl: emitted json in 0.2329 seconds
397c99fb5f9SBaptiste Daroussinucl: emitted compact json in 0.1811 seconds
398c99fb5f9SBaptiste Daroussinucl: emitted yaml in 0.2489 seconds
399c99fb5f9SBaptiste Daroussin```
400c99fb5f9SBaptiste Daroussin
401c99fb5f9SBaptiste DaroussinSo far, UCL seems to be significantly faster than jansson on parsing and slightly faster on emitting. Moreover,
402c99fb5f9SBaptiste DaroussinUCL compiled with optimizations (-O3) performs significantly faster:
403c99fb5f9SBaptiste Daroussin```
404c99fb5f9SBaptiste Daroussinucl: parsed input in 0.3002 seconds
405c99fb5f9SBaptiste Daroussinucl: emitted config in 0.1174 seconds
406c99fb5f9SBaptiste Daroussinucl: emitted json in 0.1174 seconds
407c99fb5f9SBaptiste Daroussinucl: emitted compact json in 0.0991 seconds
408c99fb5f9SBaptiste Daroussinucl: emitted yaml in 0.1354 seconds
409c99fb5f9SBaptiste Daroussin```
410c99fb5f9SBaptiste Daroussin
4114bf54857SBaptiste DaroussinYou can do your own benchmarks by running `make check` in libucl top directory.
412c99fb5f9SBaptiste Daroussin
413c99fb5f9SBaptiste Daroussin## Conclusion
414c99fb5f9SBaptiste Daroussin
415c99fb5f9SBaptiste DaroussinUCL has clear design that should be very convenient for reading and writing. At the same time it is compatible with
416273c26a3SBaptiste DaroussinJSON language and therefore can be used as a simple JSON parser. Macro logic provides an ability to extend configuration
417273c26a3SBaptiste Daroussinlanguage (for example by including some lua code) and comments allow to disable or enable the parts of a configuration
418c99fb5f9SBaptiste Daroussinquickly.
419