========================== ``libkdl``: the ckdl C API ========================== .. highlight:: c :: #include Basic types ----------- Strings ^^^^^^^ ckdl uses two distinct string types, one owned and one non-owned: if you have a :c:type:`kdl_owned_string` object, you are responsible for managing the memory, while if you have a :c:type:`kdl_str` object, you may read the string data for a limited time, but someone else (i.e., the library) is going to take care of freeing the memory. In that case, you must be aware of the string's lifetime and only access it as long as the pointer is still valid. The owned/non-owned distinction is akin to ``String`` vs ``str`` in Rust or ``std::u8string`` vs ``std::u8string_view`` in C++, though of course in C memory management is more manual than in those languages. All string are UTF-8. The ckdl API mostly exposes :c:type:`kdl_str` objects with a limited lifetime. To use the string data in your program or library, you must normally copy the strings into a :c:type:`kdl_owned_string` using :c:func:`kdl_clone_str`, or into your own string type. .. c:type:: struct kdl_str kdl_str A non-owned ("borrowed") string. .. c:member:: char const* data A pointer to the actual string data. .. warning:: The string is not, in general, null-terminated. It may contain null characters. This pointer may be ``NULL``, which signifies the absense of a value. An empty string is represented by a non-NULL pointer and a :c:member:`len` of zero. (In either case the pointer must not be dereferenced). .. c:member:: size_t len The length of the string in bytes (not Unicode characters) .. c:type:: struct kdl_owned_string kdl_owned_string An owned string object. .. c:member:: char* data The pointer to the string data. .. note:: The string is null-terminated, but may also contain null characters. .. c:member:: size_t len The length of the string in bytes (not Unicode characters), not including the terminating nul byte. String handling functions """"""""""""""""""""""""" .. c:function:: kdl_str kdl_borrow_str(kdl_owned_string const* str) Create a "borrowed" :c:type:`kdl_str` from an owned string. :param str: The owned string to "borrow" :return: The string, borrowed .. c:function:: kdl_str kdl_str_from_cstr(char const* s) Create a :c:type:`kdl_str` from a nul-terminated C string :param s: The C string to use :return: The string, as a :c:type:`kdl_str` .. c:function:: kdl_owned_string kdl_clone_str(kdl_str const* s) Create an owned string from a borrowed string (think ``strdup()``). :param s: The string to duplicate :return: A string owned by the caller with the same content .. c:function:: void kdl_free_string(kdl_owned_string* s) Free a string created by :c:func:`kdl_clone_str`. This function also replaces the data pointer by ``NULL``. :param s: The string to destroy String escaping """"""""""""""" ckdl provides a couple of functions to parse and to generate backslash escape sequences as defined by the KDL spec. For generating the escapes, there are a few options: .. c:type:: enum kdl_escape_mode kdl_escape_mode .. c:enumerator:: KDL_ESCAPE_MINIMAL = 0 Only escape what *must* be escaped: ``"``, ``\``, and (in KDLv2) any characters which aren't allowed to appear in KDL files. .. c:enumerator:: KDL_ESCAPE_CONTROL = 0x10 Escape ASCII control characters .. c:enumerator:: KDL_ESCAPE_NEWLINE = 0x20 Escape newline characters .. c:enumerator:: KDL_ESCAPE_TAB = 0x40 Escape tabs .. c:enumerator:: KDL_ESCAPE_ASCII_MODE =0x170 Escape all non-ASCII charscters .. c:enumerator:: KDL_ESCAPE_DEFAULT = KDL_ESCAPE_CONTROL | KDL_ESCAPE_NEWLINE | KDL_ESCAPE_TAB "Sensible" default: escape tabs, newlines, and other control characters, but leave most non-ASCII Unicode intact. String behaviour varies by KDL version: .. c:type:: enum kdl_version kdl_version .. c:enumerator:: KDL_VERSION_1 KDL 1.0.0 rules (e.g. ``\/`` is a valid escape sequence) .. c:enumerator:: KDL_VERSION_2 KDL 2.0.0 rules (e.g. ``\s`` escapes the space character) .. c:function:: kdl_owned_string kdl_escape_v(kdl_version version, kdl_str const* s, kdl_escape_mode mode) Escape special characters in a string. :param version: The KDL version to use :param s: The original string :param mode: How to escape :return: A string that could be surrounded by ``""`` in a KDL file .. c:function:: kdl_owned_string kdl_unescape_v(kdl_version version, kdl_str const* s) Resolve backslash escape sequences. :param version: The KDL version to use :param s: A string that might have been surrounded by ``"`` in a KDL file :return: The string with all backslash escapes replaced .. c:function:: kdl_owned_string kdl_unescape_multi_line(kdl_version version, kdl_str const* s) Dedent the multi-line string and resolve backslash escape sequences. :param version: The KDL version to use :param s: A string that might have been surrounded by ``"""`` in a KDL file (in KDLv1: ``"``) :return: The dedented string with all backslash escapes replaced KDL Values ^^^^^^^^^^ In a KDL document, an argument or property of a node has a value of one of four distinct data types: null, boolean, number, or string, optionally paired with a type annotation which may be represented as a string. In ckdl, a value (with a optional type annotation) is represented as: .. c:type:: struct kdl_value kdl_value .. c:member:: kdl_type type The data type of the value .. c:member:: kdl_str type_annotation The type annotation, if any. The *lack* of a type annotation is represented by a ``NULL`` pointer in the string. .. c:union:: @value_union .. c:member:: bool boolean If a boolean, the value ``true`` or ``false`` .. c:member:: kdl_number number If a number, the value of the number .. c:member:: kdl_str string If a string, the value of the string Note that the string value is borrrowed, not owned. The types are: .. c:type:: enum kdl_type kdl_type .. c:enumerator:: KDL_TYPE_NULL .. c:enumerator:: KDL_TYPE_BOOLEAN .. c:enumerator:: KDL_TYPE_NUMBER .. c:enumerator:: KDL_TYPE_STRING KDL has one "number" type which does not map cleanly onto one fundamental type in C, so numbers require some special treatment: .. c:type:: struct kdl_number kdl_number .. c:member:: kdl_number_type type Enum indicating how the number is stored .. c:union:: @number_union .. c:member:: long long integer The number represented as a signed integer (probably 64 bits) .. c:member:: double floating_point The number represented as a double-precision floating point number (probably 64 bits) .. c:member:: kdl_str string The number represented as a string (used when the number cannot be represented exactly in a either long long or an double). .. c:type:: enum kdl_number_type kdl_number_type .. c:enumerator:: KDL_NUMBER_TYPE_INTEGER .. c:enumerator:: KDL_NUMBER_TYPE_FLOATING_POINT .. c:enumerator:: KDL_NUMBER_TYPE_STRING_ENCODED .. _parser: The ckdl Parser --------------- The ckdl parser is an event-based "streaming" parser, inspired by `SAX`_. The workflow is, at the highest level: #. Create a parser object for a document #. Read all events generated by the parser until EOF #. Destroy the parser Typically, you will want to build up some kind of document structure in your program while reading the parsing events. The goal of this approach is to make it easy and efficient to use ckdl to load KDL into structures native to some other programming language or framework. .. _SAX: https://en.wikipedia.org/wiki/Simple_API_for_XML .. _parse events: Parse Events ^^^^^^^^^^^^ The events produced by the ckdl parser are: .. c:type:: enum kdl_event kdl_event .. c:enumerator:: KDL_EVENT_EOF Regular end of document (do not continue reading) .. c:enumerator:: KDL_EVENT_PARSE_ERROR Parse error (do not continue reading) .. c:enumerator:: KDL_EVENT_START_NODE Start of a node .. c:enumerator:: KDL_EVENT_END_NODE End of a node. Every :c:enumerator:`KDL_EVENT_START_NODE` is followed, some time later, by a :c:enumerator:`KDL_EVENT_END_NODE` (barring a parse error) .. c:enumerator:: KDL_EVENT_ARGUMENT An argument to the most recently started node .. c:enumerator:: KDL_EVENT_PROPERTY A property of the most recently started node .. c:enumerator:: KDL_EVENT_COMMENT = 0x10000 Normally not produced. If :c:enumerator:`KDL_EMIT_COMMENTS` is enabled, regular comments give a :c:enumerator:`KDL_EVENT_COMMENT` event, and any arguments, properties, or nodes that have been commented out using a slashdash (``/-``) are rendered as their original type ORed with :c:enumerator:`KDL_EVENT_COMMENT` (e.g., ``KDL_EVENT_COMMENT | KDL_EVENT_START_NODE``) Each event is associated with certain event data: .. c:type:: struct kdl_event_data kdl_event_data .. c:member:: kdl_event event The event type .. c:member:: kdl_str name The name of the node (with :c:enumerator:`KDL_EVENT_START_NODE`), or the name of the property (with :c:enumerator:`KDL_EVENT_PROPERTY`) .. c:member:: kdl_value value The value of the argument (with :c:enumerator:`KDL_EVENT_ARGUMENT`) or of the property (with :c:enumerator:`KDL_EVENT_PROPERTY`), including any potential type annotation. With :c:enumerator:`KDL_EVENT_START_NODE`, if the node has a type annotation, that annotation is encoded here: a node ``(type)name`` is represented as ``name="name"`` and ``value=(type)null``. The value itself is always ``null`` for a node. To get a feel for what exact events are generated during parsing, you may want to use the :ref:`ckdl-parse-events` tool. Parser Objects ^^^^^^^^^^^^^^ You can create a parser either for a document that you have in the form of a UTF-8 string .. c:function:: kdl_parser* kdl_create_string_parser(kdl_str doc, kdl_parse_option opt) :param doc: The KDL document text :param opt: Options for the parser :return: A :c:type:`kdl_parser` object ready to produce parse events or from any other data source by supplying a ``read`` function .. c:type:: size_t (*kdl_read_func)(void* user_data, char* buf, size_t bufsize) Pointer to a function that reads from some source, such as a file. Should return the actual number of bytes read, or zero on EOF. .. c:function:: kdl_parser* kdl_create_stream_parser(kdl_read_func read_func, void* user_data, kdl_parse_option opt) :param read_func: Function to use to read the KDL text :param user_data: opaque pointer to pass to ``read_func`` :param opt: Options for the parser :return: A :c:type:`kdl_parser` object ready to produce parse events You always interact with the parser through an otherwise opaque pointer .. c:type:: struct _kdl_parser kdl_parser Opaque struct containing parser internals which you must free once you're done with it, using .. c:function:: void kdl_destroy_parser(kdl_parser* parser) :param parser: Parser to destroy If you wish, you may configure the parser to emit comments in addition to "regular" events .. c:type:: enum kdl_parse_option kdl_parse_option .. c:enumerator:: KDL_DEFAULTS By default, support KDL 1.0.0 only and ignore all comments .. c:enumerator:: KDL_EMIT_COMMENTS Produce events for comments and events deleted using ``/-`` .. c:enumerator:: KDL_READ_VERSION_1 Use KDL version 1.0.0 .. c:enumerator:: KDL_READ_VERSION_2 Use KDL version 2.0.0 .. c:enumerator:: KDL_DETECT_VERSION Allow both KDL v2 and KDL v1. This is the default. This mode aims to produce entirely correct output for all KDLv2 documents as well as for *almost* all KDLv1 documents. If you need complete KDLv1 compliance, use ``KDL_READ_VERSION_1`` instead. If you're not reading from a stream (and can afford to parse the document twice), consider running the parser in both ``KDL_READ_VERSION_2`` mode and ``KDL_READ_VERSION_1`` mode for maximum standard compliance. The parser object provides one method: .. c:function:: kdl_event_data* kdl_parser_next_event(kdl_parser* parser) Get the next event in the document from a KDL parser :param parser: The parser :return: A pointer to a parse event structure. This pointer is valid until the next call to :c:func:`kdl_parser_next_event` for this parser. The next call also invalidates all :c:type:`kdl_str` pointers which may be contained in the event data. .. _emitter: The ckdl Emitter ---------------- The ckdl emitter can help you produce nicely formatted KDL documents programmatically. Similarly to :ref:`the parser `, the emitter operates in a streaming mode: after creating the emitter, you feed nodes, arguments and properties to the emitter one-by-one, and they will be written in the order you supply them. Like the parser, the emitter supports two IO models: it can either write to an internal buffer and give you a string at the end, or it can write its data on the fly by calling a writer function you supply. .. c:function:: kdl_emitter* kdl_create_buffering_emitter(kdl_emitter_options const* opt) Create an emitter than writes into an internal buffer. Read the buffer using :c:func:`kdl_get_emitter_buffer` after calling :c:func:`kdl_emit_end`. :param opt: Emitter configuration .. c:type:: size_t (*kdl_write_func)(void* user_data, char const* data, size_t nbytes) Pointer to a function that writes to some destination, such as a file. It should return exactly ``nbytes``, or 0 in case of an error. .. c:function:: kdl_emitter* kdl_create_stream_emitter(kdl_write_func write_func, void* user_data, kdl_emitter_options const* opt) Create an emitter that writes by calling a user-supplied function :param write_func: Function to call for writing :param user_data: First argument of write_func :param opt: Emitter configuration You will interact with the emitter through a pointer to an opaque :c:type:`kdl_emitter` structure. .. c:type:: struct _kdl_emitter kdl_emitter Opaque structure containing emitter internals Once you're finished, you must clean up and free the memory used by the emitter by calling :c:func:`kdl_destroy_emitter`. .. c:function:: void kdl_destroy_emitter(kdl_emitter* emitter) :param emitter: Emitter to destroy The emitter supports some configuration, allowing you to specify some details of the generated KDL text. .. c:type:: struct kdl_emitter_options kdl_emitter_options .. c:member:: int indent Number of spaces to indent child nodes by (default: 4) .. c:member:: kdl_escape_mode escape_mode Configuration for :c:func:`kdl_escape`: which characters should be escaped in regular strings? (default: :c:enumerator:`KDL_ESCAPE_DEFAULT`) .. c:member:: kdl_identifier_emission_mode identifier_mode How should identifiers (i.e., node names, type annotations and property keys) be rendered? (default: :c:enumerator:`KDL_PREFER_BARE_IDENTIFIERS`) .. c:member:: kdl_float_printing_options float_mode How exactly should doubles be formatted? .. c:member:: kdl_version version KDL version to use. (default: :c:enumerator:`KDL_VERSION_2`) .. c:type:: enum kdl_identifier_emission_mode kdl_identifier_emission_mode .. c:enumerator:: KDL_PREFER_BARE_IDENTIFIERS Traditional: quote identifiers only if absolutely necessary .. c:enumerator:: KDL_QUOTE_ALL_IDENTIFIERS Express *all* identifiers as strings .. c:enumerator:: KDL_ASCII_IDENTIFIERS Allow only ASCII in bare identifiers .. c:type:: struct kdl_float_printing_options kdl_float_printing_options .. c:member:: bool always_write_decimal_point Write ".0" if there would otherwise be no decimal point (default: false) .. c:member:: bool always_write_decimal_point_or_exponent Write ".0" if there would otherwise be neither a decimal point nor an exponent (i.e., 1.0 is written as "1.0", not "1", but 1.0e+6 is written as "1e6") (default: true) .. c:member:: bool capital_e Use ``E`` instead of ``e`` to introduce the exponent. (default: false) .. c:member:: bool exponent_plus Always write the exponent with a sign: ``1e+6`` instead of ``1e6`` (default: false) .. c:member:: bool plus Always write a sign in front of the number: ``+1.0`` instead of ``1.0`` (default: false) .. c:member:: int min_exponent The absolute power of 10 above which to use scientific notation (default: 4) .. c:var:: extern const kdl_emitter_options KDL_DEFAULT_EMITTER_OPTIONS Default configuration for the emitter. The emitter has a number of methods to write KDL nodes, arguments and properties: .. c:function:: bool kdl_emit_node(kdl_emitter* emitter, kdl_str name) Write a node tag .. c:function:: bool kdl_emit_node_with_type(kdl_emitter* emitter, kdl_str type, kdl_str name) Write a node tag including a type annotation .. c:function:: bool kdl_emit_arg(kdl_emitter* emitter, kdl_value const* value) Write an argument for a node .. c:function:: bool kdl_emit_property(kdl_emitter* emitter, kdl_str name, kdl_value const* value) Write a property for a node .. c:function:: bool kdl_start_emitting_children(kdl_emitter* emitter) Start a list of children for the previous node (``{``) .. c:function:: bool kdl_finish_emitting_children(kdl_emitter* emitter) End the list of children (``}``) To end the KDL document, you must call one final method .. c:function:: bool kdl_emit_end(kdl_emitter* emitter) Finish the document: write a final newline if required To get the actual KDL document text (assuming the emitter was created with :c:func:`kdl_create_buffering_emitter`), call: .. c:function:: kdl_str kdl_get_emitter_buffer(kdl_emitter* emitter) Get the internal buffer of the emitter, containing the document generated so far.