197bd480fSBaptiste Daroussin# LIBUCL 297bd480fSBaptiste Daroussin 3*a0409676SBaptiste Daroussin[](https://circleci.com/gh/vstakhov/libucl) 411dd9ed6SBaptiste Daroussin[](https://scan.coverity.com/projects/4138) 511dd9ed6SBaptiste Daroussin[](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