mirror of
https://github.com/valitydev/yamerl.git
synced 2024-11-06 02:45:23 +00:00
238 lines
6.7 KiB
Plaintext
238 lines
6.7 KiB
Plaintext
@author Jean-Sébastien Pédron <jean-sebastien.pedron@dumbbell.fr>
|
|
@copyright
|
|
2012-2014 Yakaz,
|
|
2016-2017 Jean-Sébastien Pédron <jean-sebastien.pedron@dumbbell.fr>
|
|
|
|
@doc
|
|
|
|
== Introduction ==
|
|
|
|
YAML is a human-friendly data serialization format. The specification
|
|
for this language and many examples are available from the <a
|
|
href="http://www.yaml.org/">Official YAML web site</a>. You may also
|
|
want to check the <a href="http://en.wikipedia.org/wiki/YAML">YAML
|
|
Wikipedia article</a>.
|
|
|
|
<strong>`yamerl'</strong> is a pure <a
|
|
href="http://www.erlang.org/">Erlang application</a> which is able
|
|
to parse <a href="http://yaml.org/spec/1.1/">YAML 1.1</a> and <a
|
|
href="http://www.yaml.org/spec/1.2/spec.html">YAML 1.2</a> documents, as
|
|
well as <a href="http://json.org/">JSON</a> documents. It only depends
|
|
on standard Erlang/OTP applications; no external dependency is required.
|
|
It doesn't use native code either (neither port drivers nor NIFs). At
|
|
this time, it has no support to serialize a YAML document.
|
|
|
|
`yamerl' is distributed under the terms of the <strong>2-clause BSD
|
|
license</strong>; see `COPYING'.
|
|
|
|
== When to use yamerl or not ==
|
|
|
|
=== Advantages ===
|
|
|
|
<ul>
|
|
<li>Pure Erlang implementation:
|
|
<ul>
|
|
<li>should scale more easily than a port-driver based implementation;</li>
|
|
<li>won't take the whole VM down in case of a crash.</li>
|
|
</ul></li>
|
|
<li>YAML 1.2 support, which is not widely supported in many other languages.</li>
|
|
</ul>
|
|
|
|
=== Caveats ===
|
|
|
|
<ul>
|
|
<li>Current implementation is slow, compared to yamler (NIF-based) or any JSON-only parsers.</li>
|
|
<li>Adding schemas is not easy.</li>
|
|
<li>No support for YAML serialization.</li>
|
|
</ul>
|
|
|
|
|
|
== Features ==
|
|
|
|
<ul>
|
|
<li>Supports <a href="http://www.yaml.org/spec/1.2/spec.html">YAML 1.2</a> parsing:
|
|
```
|
|
yamerl_constr:string("YAML snippet").
|
|
'''</li>
|
|
<li>Supports <a href="http://yaml.org/spec/1.1/">[YAML 1.1</a> parsing:
|
|
```
|
|
yamerl_constr:string("YAML snippet", [{schema, yaml11}]).
|
|
'''</li>
|
|
<li>Supports <a href="http://json.org/">JSON</a> parsing:
|
|
```
|
|
yamerl_constr:string(<<"JSON snippet">>, [{schema, json}]).
|
|
'''</li>
|
|
<li>Supports <strong>Erlang atom</strong> node type, either when tagged
|
|
as atom, or if autodetected in plain scalars, and, if asked, only if the
|
|
atom already exists:
|
|
```
|
|
% Enable support for Erlang atoms.
|
|
yamerl_app:set_param(node_mods, [yamerl_node_erlang_atom]),
|
|
yamerl_constr:string("!<tag:yamerl,2012:atom> atom").
|
|
|
|
% Autodetect Erlang atoms in plain scalars.
|
|
yamerl_app:set_param(node_mods, [yamerl_node_erlang_atom]),
|
|
yamerl_constr:string("atom", [{erlang_atom_autodetection, true}]).
|
|
|
|
% Atoms must already exist.
|
|
yamerl_app:set_param(node_mods, [yamerl_node_erlang_atom]),
|
|
yamerl_constr:string("atom", [
|
|
{erlang_atom_autodetection, true},
|
|
{erlang_atom_only_if_exist, true}
|
|
]).
|
|
'''</li>
|
|
<li>Supports <strong>Erlang fun()</strong> node type:
|
|
```
|
|
% Enable support for Erlang fun().
|
|
yamerl_app:set_param(node_mods, [yamerl_node_erlang_fun]),
|
|
[Plus_One_Fun] = yamerl_constr:string(<<"!<tag:yamerl,2012:fun> fun(X) -> X + 1 end.">>),
|
|
|
|
Plus_One_Fun(2). % Return 3.
|
|
'''</li>
|
|
<li>Provides a <strong>yamler compatibility layer</strong>:
|
|
```erlang
|
|
% Both calls return the same value.
|
|
yaml:load_file("input.yaml", [{schema, yaml_schema_failsafe}]),
|
|
yamerl_yamler_compat:load_file("input.yaml", [{schema, yaml_schema_failsafe}])
|
|
'''</li>
|
|
</ul>
|
|
|
|
== Prerequisites ==
|
|
|
|
Before using yamerl, the application must be started:
|
|
```
|
|
application:start(yamerl).
|
|
'''
|
|
|
|
This is required so that application environment variables are available.
|
|
|
|
Now, you can use the {@link yamerl_constr} module to parse and construct a
|
|
list of documents from:
|
|
<ul>
|
|
<li>an in-memory document (string or binary);</li>
|
|
<li>a regular file;</li>
|
|
<li>a stream.</li>
|
|
</ul>
|
|
|
|
Because a YAML input stream may contain multiple documents, {@link
|
|
yamerl_constr} always returns a list of documents, even if the input
|
|
stream only contains one.
|
|
|
|
== Examples ==
|
|
|
|
=== Basic parsing ===
|
|
|
|
<ol>
|
|
<li>Start the yamerl application. This is a mandatory step.
|
|
```
|
|
application:start(yamerl).
|
|
'''</li>
|
|
<li>You're now ready to parse a serialized document:
|
|
<ul>
|
|
<li>To parse an in-memory string or binary:
|
|
```
|
|
Documents = yamerl_constr:string("Hello!").
|
|
% Documents is a list of constructed documents.
|
|
'''</li>
|
|
<li>To parse a file:
|
|
```
|
|
Documents = yamerl_constr:file("input.yaml").
|
|
% Documents is a list of constructed documents.
|
|
'''</li>
|
|
<li>To parse a stream:
|
|
```
|
|
% Create a new construction state. The only required argument is an
|
|
% arbitrary term describing the source of the data. Here, we use the
|
|
% same term structure as yamerl_constr:file/{1, 2}.
|
|
Constr_State = yamerl_constr:new({file, "<stdin>"}),
|
|
|
|
% Feed the parser with binary chunks. The developer is responsible for
|
|
% reading the chunk from the backing source.
|
|
%
|
|
% The function returns an updated construction state, which replaces the
|
|
% previous one.
|
|
%
|
|
% yamerl_constr:next_chunk/2 can be called as many times as necessary.
|
|
{continue, Constr_State2} = yamerl_constr:next_chunk(Constr_State, Chunk),
|
|
|
|
% When the last chunk is reached, call yamerl_constr:last_chunk/2.
|
|
Documents = yamerl_constr:last_chunk(Constr_State2, Last_Chunk).
|
|
% Documents is a list of constructed documents.
|
|
'''</li>
|
|
</ul></li>
|
|
</ol>
|
|
|
|
See {@link yamerl_constr} for more informations.
|
|
|
|
=== Error handling ===
|
|
|
|
yamerl <strong>throws an exception when an error occurs</strong>.
|
|
|
|
<ol>
|
|
<li>Start the yamerl application. This is a mandatory step.
|
|
```
|
|
application:start(yamerl).
|
|
'''</li>
|
|
<li>You're now ready to parse a serialized document. To parse an
|
|
in-memory string or binary:
|
|
```
|
|
-include_lib("yamerl/include/yamerl_errors.hrl").
|
|
|
|
% ...
|
|
|
|
try
|
|
Documents = yamerl_constr:string("Hello!"),
|
|
% Documents is a list of constructed documents.
|
|
Documents
|
|
catch
|
|
throw:#yamerl_exception{errors = Errors} ->
|
|
% Do something with the exception.
|
|
Errors
|
|
end.
|
|
'''</li>
|
|
</ol>
|
|
|
|
As you can see, the `#yamerl_exception{}' record embeds all encountered
|
|
errors:
|
|
```
|
|
#yamerl_exception{
|
|
errors = [] % List of errors.
|
|
}.
|
|
'''
|
|
|
|
Errors are records where the two first members are always:
|
|
<ol>
|
|
<li>`type', either `error' or `warning';</li>
|
|
<li>`text', a human-readable error message.</li>
|
|
</ol>
|
|
|
|
Following members depend on the error record. Two records are currently
|
|
defined:
|
|
<ul>
|
|
<li>`#yamerl_invalid_option{}';</li>
|
|
<li>`#yamerl_parsing_error{}'.</li>
|
|
</ul>
|
|
|
|
See {@link yamerl_constr} for more informations.
|
|
|
|
== Alternatives to yamerl ==
|
|
|
|
=== YAML parsers ===
|
|
|
|
<ul>
|
|
<li><a href="https://github.com/goertzenator/yamler">yamler</a>:
|
|
<ul>
|
|
<li>Based on libyaml, wrapped in a NIF</li>
|
|
<li>Supports YAML 1.1</li>
|
|
<li>Faster than yamerl</li>
|
|
<li>Supports Erlang atoms, however, single-quoted scalar are treated as
|
|
atom, which breaks the YAML specifications</li>
|
|
<li>Doesn't support Erlang fun()</li>
|
|
</ul></li>
|
|
</ul>
|
|
|
|
=== JSON parsers ===
|
|
|
|
There are too many to choose from now to list them here. Use your
|
|
preferred search engine :-)
|