From: Clark C . E. <cc...@cl...> - 2001-08-01 18:31:09
|
/************************************************************************** ** Revision: $Id: yaml.h,v 1.20 2001/07/09 16:15:37 cce Exp $ ** Author: Clark C. Evans ** Credits: Brian Ingerson and Oren Ben-Kiki ** ** Copyright (c) 2001, Xgenda, Inc. ** All rights reserved. ** ** Permission is hereby granted, free of charge, to any person obtaining ** a copy of this software and associated documentation files (the ** "Software"), to deal in the Software without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be ** included in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *************************************************************************/ #ifndef __YAML_H #define __YAML_H #include <stddef.h> #include <stdio.h> /* Classes * * The YAML library exports several classes of objects. For each class, * there is a global unique identifier (GUID) which complies with * Microsoft's GUID structure and can be used for OLE interfaces. * * For each interface, a _vtbl structure defines the methods which * are included in the interface. For each object, there is a structure * with exactly one item, a pointer to its method dispatch table. * And then, since pointers to these structures are common, a typedef * is issued for a pointer to the given object. A method can then * be called on an object in a way similar to: * * result = object->tbl->method(object,...); * * In practice, the first argument to each method is called self * and is a pointer to the object's structure. Most methods * return yaml_result_t so that error propigation can occur. The * exception to this last rule include addref() and release(), which * return the current reference count (for debugging I guess...) * and most boolean methods which just return the boolean. * * To enable easy calling of object methods, YAML_CALL can be used * in general, and specific versions of YAML_EXEC are created to * do macro based exception propigation. These macros are side-effect * free as long as the object passed is not an expression. In general, * arguments *can* be expressions. */ typedef struct { unsigned long p1; unsigned short p2; unsigned short p3; unsigned char p4[8]; } yaml_guid_t; struct yaml_base_vtbl; extern const yaml_guid_t yaml_guid_base; struct yaml_node_vtbl; extern const yaml_guid_t yaml_guid_node; struct yaml_iterator_vtbl; extern const yaml_guid_t yaml_guid_iterator; struct yaml_visitor_vtbl; extern const yaml_guid_t yaml_guid_visitor; struct yaml_reader_vtbl; extern const yaml_guid_t yaml_guid_reader; struct yaml_writer_vtbl; extern const yaml_guid_t yaml_guid_writer; struct yaml_builder_vtbl; extern const yaml_guid_t yaml_guid_builder; struct yaml_factory_vtbl; extern const yaml_guid_t yaml_guid_factory; struct yaml_base_s { struct yaml_base_vtbl * vtbl; }; struct yaml_node_s { struct yaml_node_vtbl * vtbl; }; struct yaml_iterator_s { struct yaml_iterator_vtbl * vtbl; }; struct yaml_visitor_s { struct yaml_visitor_vtbl * vtbl; }; struct yaml_reader_s { struct yaml_reader_vtbl * vtbl; }; struct yaml_writer_s { struct yaml_writer_vtbl * vtbl; }; struct yaml_builder_s { struct yaml_builder_vtbl * vtbl; }; struct yaml_factory_s { struct yaml_factory_vtbl * vtbl; }; typedef struct yaml_base_s * yaml_base_t; typedef struct yaml_node_s * yaml_node_t; typedef struct yaml_iterator_s * yaml_iterator_t; typedef struct yaml_visitor_s * yaml_visitor_t; typedef struct yaml_reader_s * yaml_reader_t; typedef struct yaml_writer_s * yaml_writer_t; typedef struct yaml_builder_s * yaml_builder_t; typedef struct yaml_factory_s * yaml_factory_t; #define YAML_CALL0(obj,mthd) ((obj)->vtbl->mthd((obj))) #define YAML_CALL1(obj,mthd,a) ((obj)->vtbl->mthd((obj),(a))) #define YAML_CALL2(obj,mthd,a,b) ((obj)->vtbl->mthd((obj),(a),(b))) #define YAML_CALL3(obj,mthd,a,b,c) ((obj)->vtbl->mthd((obj),(a),(b),(c))) #define YAML_CALL4(obj,mthd,a,b,c,d) \ ((obj)->vtbl->mthd((obj),(a),(b),(c),(d))) #define YAML_CALL5(obj,mthd,a,b,c,d,e) \ ((obj)->vtbl->mthd((obj),(a),(b),(c),(d),(e))) #define YAML_EXEC0(obj,mthd) YAML_EXEC(YAML_CALL0(obj,mthd)) #define YAML_EXEC1(obj,mthd,a) YAML_EXEC(YAML_CALL1(obj,mthd,a)) #define YAML_EXEC2(obj,mthd,a,b) YAML_EXEC(YAML_CALL2(obj,mthd,a,b)) #define YAML_EXEC3(obj,mthd,a,b,c) YAML_EXEC(YAML_CALL3(obj,mthd,a,b,c)) /* Results * * Since we do not have exceptions, each function may return a success * code. This enumeration is a subset of the Win32 HRESULT (winerror.h) * Also given are macros to help with error detection and propigation. */ typedef enum { YAML_OK = 0x00000000L, /* success, with results */ YAML_STOP = 0x00000001L, /* success, but no results */ YAML_E_PARAM = 0x80000003L, /* config or parameter problem */ YAML_E_MEMORY = 0x8007000EL, /* out of memory or similar */ YAML_E_IO = 0x80070005L, /* socket or file handle error */ YAML_E_UNAVAILABLE = 0x8000000AL, /* data pending or not avail. */ YAML_E_NOTIMPL = 0x80004001L, /* method is not implemented */ YAML_E_UNEXPECTED = 0x8000FFFFL, /* catrastrophic failure */ YAML_E_SIZETOOBIG = 0x800288C5L, /* buffer is not large enough */ YAML_E_NOINTERFACE = 0x80004002L, /* interface is not supported */ YAML_E_INVALIDSTATE = 0x80028029L, /* object's state prevents good */ YAML_E_WRONGTYPEKIND = 0x8002802AL, /* wrong typeof object detected */ YAML_E_FAIL = 0x80004005L /* general failure */ } yaml_result_t; #define YAML_SUCCEEDED(x) ((signed long)(x) >= 0) #define YAML_FAILED(x) ((signed long)(x) < 0) #define YAML_IS_OK(x) (YAML_OK == (x)) #define YAML_CHECK(x,e) do {if(!(x)) return (e); } while(0) #define YAML_CHECK_PARAM(x) YAML_CHECK(x,YAML_E_PARAM) #define YAML_CHECK_FAIL(res) do { if(YAML_FAILED(res)) \ return (res); } while(0) #define YAML_EXEC(x) do { yaml_result_t res = (x); \ if(YAML_FAILED(res)) \ return (res); } while(0) /* Types * * This defines common data types used by YAML. They include a unicode * character type, a byte type for binary data, boolean data type, and * enumerations specific to YAML's information model. There are a few * types defined here worthy of explanation: * * yaml_char_t This type defines the internal encoding used by the * yaml library. Currently it can be UTF8 or UTF16 * (although UTF16 may not be implemented yet). Note * that LE or BE is not important at this level since * the unit used (short) hides the ordering issue * for the in-memory case. Only during serialization, * or when looking at the value as a stream of bytes * (casting it to char *) does byte ordering matter. * * yaml_byte_t This type is used for binary values. It is defined * as an unsigned 8 bit quantity on all platforms and * unicode is stored in using an array of bytes, UTF8 * is always used. Note UTF16 cannot be used for this * purpose since the byte ordering issues would cause * two internal representations. * * yaml_supplement This contains information about the node's * serialization context, which contains items that * are not in the YAML information model, but are * helpful when dealing with the parser/printer. * Specific items in the context include: * * style The style used during serialization, block, * quoted, or simple. See the YAML * specification for details. * * anchor The anchor value for the node. Note that * after all reference nodes have been * normalized by replacing them with their * referant, the anchor is no longer needed. * * line The line in which the node appears in the * input file. This is useful for debugging * and/or validation purposes so that the * source of an error can be reported. * * parent YAML is a directed graph structure, thus the * node cannot have a "parent" (since it may * have more than one node which references it). * Furthermore, a random access structure is * not required to store backward pointers, as * this may be hard to keep up-to-date. * * However, when parsing or printing, YAML is * serialized as a tree, and in this context * a parent is available. And since this * information may be helpful, it is included. * * After parsing this pointer can become stale * very quickly. Thus, the user of this info * should be careful; and the implementation * should make this pointer NULL at the first * available moment (or simply don't provide * the sequential context). */ #ifdef YAML_CHAR_IS_UTF16 typedef unsigned short yaml_char_t; /* UTF-16 Unicode Characters */ #elif YAML_CHAR_IS_UTF8 typedef unsigned char yaml_char_t; /* UTF-8 Unicode Characters */ #else #error Must define either YAML_CHAR_IS_UTF16 or YAML_CHAR_IS_UTF8 #endif typedef unsigned long yaml_index_t; /* Index of a node in a list */ typedef unsigned char yaml_byte_t; /* Used for binary scalars */ typedef enum { YAML_FALSE = 0, YAML_TRUE = 1 } yaml_bool_t; typedef enum { YAML_NULL = 0, /* An degenerate value (~) */ YAML_MAP = 1, /* Named unordered sequence (%)*/ YAML_LIST = 2, /* Anonymous ordered seq. (@) */ YAML_SCALAR = 3, /* Scalar is an atomic unit */ YAML_REFERENCE = 4 /* Pointer to another node (*) */ } yaml_kind_t; typedef enum { YAML_DEFAULT = 0, /* Undetermined representation */ YAML_BLOCK = 1, /* Block (|) method is used. */ YAML_QUOTED = 2, /* Quoted (") method is used. */ YAML_SIMPLE = 3 /* Unquoted style is used. */ } yaml_style_t; typedef enum { YAML_UNAVAILABLE = 0, /* Requested is not available */ YAML_SEQUENTIAL = 1, /* Only single pass available */ YAML_RANDOM = 2 /* Multiple passes are allowed */ } yaml_access_t; typedef enum { YAML_GUESS = 0, YAML_UTF8 = 1, YAML_UTF16BE = 2, YAML_UTF16LE = 3, YAML_UTF32BE = 4, YAML_UTF32LE = 5, YAML_ISO88591 = 6 } yaml_encoding_t; typedef struct { yaml_node_t parent; /* Serialized parent node. */ yaml_style_t style; /* Style used for scalar. */ unsigned long anchor; /* Used to serialize graphs. */ unsigned long line; /* Line number of node. */ } yaml_supplement_t; /* Base * * In general, YAML uses a object mechanism which should be binary * compatible with Microsoft's COM. Each object is represented via a * structure. The first item in the structure is a pointer to a * function table. Remaining items in the object's structure are * implementation detail and are not part of the interface. The * following methods are always the first three methods in the * function table. * * query_interface() This is the equivalent of COM's QueryInterface. It * is passed in a GUID and the corresponding object * is returned. Since the classes here do not support * multiple interfaces, each object should only return * with itself when its interface is queried. * * addref() This can be used to increase the reference count on * a given object. * * release() This function reduces the reference count on a * given object. If the reference count reaches zero * then it may be deleted. * * When an object is returned from a method invocation, * one can assume that addref() has been called on the * object at least once. In this case, release() * should be called to free the object when the caller * is done with the returned object. * * Note: These methods need not be used/exposed for bindings * to scripting languages. */ typedef yaml_result_t (*yaml_query_interface_t)(/* in */ yaml_base_t self, /* in */ const yaml_guid_t *guid, /* out */ yaml_base_t *out); typedef unsigned long (*yaml_addref_t) (/* in */ yaml_base_t self); typedef unsigned long (*yaml_release_t) (/* in */ yaml_base_t self); struct yaml_base_vtbl { yaml_query_interface_t query_interface; /* Obtain another interface. */ yaml_addref_t addref; /* Increment reference count. */ yaml_release_t release; /* Decrement reference count. */ }; #define YAML_ADDREF(obj) YAML_CALL0((yaml_base_t)(obj),addref) #define YAML_RELEASE(obj) YAML_CALL0((yaml_base_t)(obj),release) #define YAML_QUERY_INTERFACE(obj,guid,out) \ YAML_CALL2((yaml_base_t)(obj),query_interface,(guid),(yaml_base_t*)&(out)) #define YAML_CAST(obj,type,out) \ YAML_IS_OK(YAML_QUERY_INTERFACE((obj),yaml_guid_##type,(out))) /* Node * * A Node can be NULL, MAP, LIST, SCALAR, or REFERENCE. They are all * modeled within this class, where particular operations may be * unavailable depending upon the kind of node and the access state. * The methods below contain data structure markup and container * operations. Note that Key is not provided as part of a node as YAML's * information model is not a labeled list. A node's key is given * through a map context (see iterator or visitor below). Every node * has a few core properties: * * kind() Returns the kind of node, can be YAML_NULL, YAML_MAP, * YAML_LIST, YAML_SCALAR, or YAML_REFERENCE. Note that * an in-memory representation should not have any nodes * of YAML_REFERENCE kind(). * * supplement() Information regarding the node's serialization * which may be available. This information is provided * primarly as a way to handle re-constructing a graph * from a serialized tree structure and to provide other * serialization specific information. The caller must * provide a pointer to the structure to be filled-in. * * access() A node may only be available by a sequential (single * pass) access mode. Due to this access constraint * certain operations may be unavailable, and depending * upon the circumstances may return YAML_E_UNAVAILABLE. * To help the user grok these constraints, the current * access state of the node is provided. There are * three access states: * * YAML_RANDOM The nodes operations are not * restricted in any way by access() * * YAML_SEQUENTIAL Generally, operations which require * multiple passes are not possible. * Functionality which only requires * a single pass may operate, but will * cause the node's access to drop to * YAML_UNAVAILABLE unless the method * is documented otherwise. * * YAML_UNAVAILABLE This is the most restrictive level, * and in general, most functions will * return YAML_E_UNAVAILABLE or zero. * * A REFERENCE node is specific to a serialization context and is not * part of the official information model. It is provided here so that * the node interface can be used by the Parser, but if the tree is * moved into random access memory, all reference nodes should be * replaced by their referant. As for the scalar, branch, map or list * methods described below, they should return YAML_E_WRONGTYPEKIND when * possible, otherwise YAML_FALSE or 0 depending on the circumstance. * */ typedef yaml_kind_t (*yaml_get_kind_t) (/* in */ yaml_node_t self); typedef yaml_access_t (*yaml_get_access_t ) (/* in */ yaml_node_t self); typedef yaml_result_t (*yaml_get_supplement_t) (/* in */ yaml_node_t self, /* i/o */ yaml_supplement_t *fill); #define YAML_KIND(obj) YAML_CALL0((obj),get_kind) #define YAML_ACCESS(obj) YAML_CALL0((obj),get_access) #define YAML_SUPPLEMENT(obj,data) YAML_EXEC1((obj),get_supplement,&(data)) /* Scalar * * A scalar node is an atomic value, it is either a unicode string or a * binary object (or both). The differences are encapsulated within * this class. So that the scalar may be shared efficiently, reference * counting is employed. Scalars are immutable. Once they are * initialized, their content never changes (although one can replace * a scalar with another scalar). * * is_binary() This flag indicates if the scalar is binary or if * it is a unicode string. * * is_equal() This function can be used to perform scalar * comparisons safely without requesting the string * or a reader/writer. An implementation may store * store a hash value or employ some other mechanism * to make this a quick operation. * * string() Both the string() and chars() methods return a * chars() sequence of unicode characters using the native * encoding chosen at compile time (UTF-8, UTF-16, * UTF-32). They are identical with the exception * of memory management policy: * * chars() The scalar object owns the return value, * which is const. If the incoming pointer * is valid, it is ignored. The scope of * the char array returned is the life of * the scalar object, but no further. * * string() The caller owns the memory returned, * and if a valid pointer is passed in * that pointer is reallocated. Under * Windows this uses BSTR (SysAlloc, etc.) * Under OLE, this is also the default * property for the scalar object. * * Binary values must be accessed through a Reader * or pushed to a Writer. If this method is called * on a binary, YAML_E_WRONGTYPEKIND is returned. * * NOTE: In scripting languages where memory management * is part of the binding, the "chars()" method * need not be part of the interface. The * chars() method is thus a feature of the "C" * binding. In Java the string() method would * give a java.lang.String. In Python, string() * would return a unicode string. And in Perl, * string() returns a unicode scalar. * * In Java this method returns a java.lang.String * In Python, this method returns a unicode string. * In Perl, this method returns a unicode scalar. * * strlen() This is used to obtain the length of the string(). * The return value is in sizeof(yaml_char_t) units. * If is_binary() is true, then this method must also * result in a YAML_E_WRONGTYPEKIND. * * as_reader() This method pulls the Scalar's content via a Reader, * allowing for sequential access to the Scalar. Either * as_reader() or to_writer() must be used if the * scalar is_binary(). Note that the reader returned * must be released when the caller is finished! * * to_writer() This method pushes the Scalar's content to a Writer. * This method as an additional parameter to indicate * if strings should be sent as byte events rather than * character events. Binary scalars are always sent * as byte events. * * For the scalar methods above, certain access() restrictions apply. * All of them, with exception of is_binary() require either YAML_RANDOM * or YAML_SEQUENTIAL. And if any of them, with the exception of chars(), * are used when access is YAML_SEQUENTIAL, the node's access level drops * to YAML_UNAVAILABLE. When chars() is called, a copy of the string * value must be maintained by the node, thus access is promoted to * YAML_RANDOM. * * A NULL node is considered a degenerate scalar. For this node type, * is_binary() and is_equal() always returns YAML_FALSE, string() returns * an empty character array, and strlen() returs 0. Also, as_reader() * and to_writer() are both degenerate, but fully functional. */ typedef yaml_bool_t (*yaml_is_binary_t) (/* in */ yaml_node_t self); typedef yaml_result_t (*yaml_get_chars_t) (/* in */ yaml_node_t self, /* out */ const yaml_char_t* *chars); typedef yaml_result_t (*yaml_get_string_t) (/* in */ yaml_node_t self, /* out */ yaml_char_t* *chars); typedef yaml_result_t (*yaml_get_strlen_t) (/* in */ yaml_node_t self, /* out */ size_t *len); typedef yaml_result_t (*yaml_as_reader_t) (/* in */ yaml_node_t self, /* out */ yaml_reader_t *reader); typedef yaml_result_t (*yaml_to_writer_t) (/* in */ yaml_node_t self, /* in */ yaml_writer_t writer, /* in */ yaml_bool_t send_bytes); typedef yaml_bool_t (*yaml_is_equal_t) (/* in */ yaml_node_t self, /* in */ yaml_node_t other); #define YAML_IS_BINARY(obj) YAML_CALL0((obj),is_binary) #define YAML_STRING(obj,str) YAML_EXEC1((obj),string,&(str)) #define YAML_CHARS(obj,str) YAML_EXEC1((obj),chars,&(str)) #define YAML_STRLEN(obj,len) YAML_EXEC1((obj),strlen,&(len)) #define YAML_IS_EQUAL(obj,other) YAML_CALL1((obj),is_equal,(other)) #define YAML_AS_READER(obj,re) YAML_EXEC1((obj),as_reader,&(re)) #define YAML_TO_WRITER(obj,wt,sb) YAML_EXEC2((obj),to_writer,&(wt),(sb)) /* Branch * * For MAP and LIST nodes (hereafter refered to as branch nodes), the * following additional methods are available. * * children() This returns an iterator which can be used to visit * the container's children. * * Barring exceptions, children() must return YAML_STOP * if the map or list is empty or if called on a scalar * or null node. Otherwise, YAML_OK is returned to * indicate that the output parameter is provided and * that the caller must now be responsible for releasing * the iterator returned. * * apply() This method applies a visitor to each one of the * node's children recursively. See children() above. * * count() Returns the number of items in the list or map. * * The ability to treat a MAP or LIST nodes as a SCALAR value enables * forward-compatible programs which can be resillient to structural * changes that substitute a map or list where a scalar is expected. * For a MAP node, the scalar methods are resolved as if the * implementation had recursed into the sequence of children and invoked * the same method on the node having the key value of "=". If such a * node is not found, then the method exhibits behavior as if the node * type is NULL. For a LIST node, the behavior is similar to a MAP, only * that the recursive behavior uses the first node in the list, defaulting * to the NULL behavior if the node is empty. * * If the access() is YAML_SEQUENTIAL and if a LIST or a MAP calls a * scalar method, then the type of node is downgraded to a SCALAR, or * if the LIST is empty, or the MAP does not have a "=" key, the node * is downgraded to a NULL node, the special class of SCALAR. In general, * during sequential access if a node is treated as a scalar then it * becomes a scalar. Of course, if access() is YAML_RANDOM, then the * node kind remains a LIST or MAP, and children(), apply(), and the * map or list specific functions below remain available. */ typedef yaml_result_t (*yaml_children_t) (/* in */ yaml_node_t self, /* out */ yaml_iterator_t *iter); typedef yaml_result_t (*yaml_apply_t) (/* in */ yaml_node_t self, /* in */ yaml_visitor_t visitor); typedef yaml_index_t (*yaml_count_t) (/* in */ yaml_node_t self); #define YAML_APPLY(obj,visitor) YAML_EXEC2((obj),apply,(visitor)) #define YAML_CHILDREN(node,iter,result) \ YAML_IS_OK((result) = YAML_CALL1((node),children,&(iter))) #define YAML_COUNT(node) YAML_CALL0((node),count) /* Map (Random Access) * * A map is a unordered sequence of (key,value) pairs. For purposes * of this API, a key is a node treated as a scalar. Thus the only * difference between a key and a node, is where it is used. To this * end, yaml_key_t has been type defined as a yaml_key_t . * The methods below, specific to maps, are only available if the * node has YAML_RANDOM access(). * * get() Returns the node with a given key. Where the key * is any node, but one treated as a scalar value. * * set() Puts a node with a given key. If the output * parameter is not NULL, then it will be updated * to reflect a pointer to the node replaced. * * del() Removes a node with a given key. If the output * parameter is not NULL, then it will be updated * to reflect a pointer to the node replaced. */ typedef yaml_key_t yaml_key_t; /* Node used as a key in a map */ typedef yaml_result_t (*yaml_get_t) (/* in */ yaml_node_t self, /* in */ yaml_key_t key, /* out */ yaml_node_t *value); typedef yaml_result_t (*yaml_set_t) (/* in */ yaml_node_t self, /* in */ yaml_key_t key, /* in */ yaml_node_t value, /* out */ yaml_node_t *retired); typedef yaml_result_t (*yaml_del_t) (/* in */ yaml_node_t self, /* in */ yaml_key_t key, /* out */ yaml_node_t *retired); /* List (Random Access) * * If the methods above, or the methods below are called on a node that * is not a MAP or list, then they must return YAML_E_WRONGTYPEKIND. * If the methods are called on a node that is not YAML_RANDOM access(), * then YAML_E_UNAVAILABLE should be returned. Following are methods * available to LIST nodes: * * append() Adds a child to the end of the sequence. This * method should be funtionally equivalent to calling * insert with an index of count(). * * insert() Adds a node before a given index shifting all * later nodes up one index. If an index of count() * is given, then the node is appended to the end. * * item() Fetches a node with a given index. Indexing is zero * based for the "C" implementation, although scripting * languages are free to start at 1 depending upon that * language's conventions. * * remove() Deletes a node with a given index. If the output * parameter is not NULL, then it will be updated to * reflect a pointer to the node removed. * * replace() Updates a node at a given index. If the output * parameter is not NULL, then it will be updated to * reflect a pointer to the node replaced. * */ typedef yaml_result_t (*yaml_append_t) (/* in */ yaml_node_t self, /* in */ yaml_node_t value); typedef yaml_result_t (*yaml_insert_t) (/* in */ yaml_node_t self, /* in */ yaml_index_t index, /* in */ yaml_node_t value); typedef yaml_result_t (*yaml_item_t) (/* in */ yaml_node_t self, /* in */ yaml_index_t index, /* out */ yaml_node_t *out); typedef yaml_result_t (*yaml_remove_t) (/* in */ yaml_node_t self, /* in */ yaml_index_t index, /* out */ yaml_node_t *oldval); typedef yaml_result_t (*yaml_replace_t) (/* in */ yaml_node_t self, /* in */ yaml_index_t index, /* in */ yaml_node_t value, /* out */ yaml_node_t *oldval); /* Node (Complete Interface) * * The "C" binding of the YAML library uses one large interface * rather than several smaller interfaces due to type casting * and OLE QueryInterface inefficency for treating inheritance. * As specifically described above, some of the methods will * return YAML_E_WRONGTYPEKING if they are used on a node of the * wrong type, or will return YAML_E_UNAVAILABLE if they require * stronger access() then the node currently has. * * The library provides a default implementation of this entire * interface in the form of a static vtbl implemenation which can be used * to extract specific static methods as needed. Many of the methods * are implemented in terms of each other, so they should not be used * without careful inspection of their implementation. */ const extern struct yaml_node_vtbl *yaml_node_vtbl_defimpl; struct yaml_node_vtbl { /* Base */ yaml_query_interface_t query_interface; /* Obtain another interface. */ yaml_addref_t addref; /* Increment reference count. */ yaml_release_t release; /* Decrement reference count. */ /* General */ yaml_get_kind_t kind; /* Map, List, Scalar, etc. */ yaml_get_access_t access; /* What type of access */ yaml_get_supplement_t supplement; /* Serilization context. */ /* Scalar */ yaml_is_binary_t is_binary; /* Is Unicode or Binary? */ yaml_is_equal_t is_equal; /* Are two scalars equal? */ yaml_get_chars_t chars; /* Access to scalar's buffer. */ yaml_get_string_t string; /* Access as unicode string. */ yaml_get_strlen_t strlen; /* Length of the string array. */ yaml_as_reader_t as_reader; /* Pull content from reader. */ yaml_to_writer_t to_writer; /* Push content to writer. */ /* Sequence */ yaml_children_t children; /* Obtain a child iterator */ yaml_apply_t apply; /* Run a visitor over children */ yaml_count_t count; /* Returns number of children. */ /* Map */ yaml_get_t get; /* Gets a map entry for a key. */ yaml_set_t set; /* Sets a map entry for a key. */ yaml_del_t del; /* Removes a map entry. */ /* List */ yaml_append_t append; /* Inserts at end of the list. */ yaml_insert_t insert; /* Adds a node before an index.*/ yaml_item_t item; /* Retrieves node at an index. */ yaml_remove_t remove; /* Removes a node at an index. */ yaml_replace_t replace; /* Replaces a node at an index.*/ }; /* Constants * * In many cases the nodes needed are static in nature, in this section * are macros used to construct static node objects. * * YAML_CONST Used to construct a constant scalar tag which * has the value "str". */ #define YAML_CONST(tag,str) const struct yaml_const_scalar_s tag##_i = \ {&yaml_const_scalar_vtbl,(yaml_char_t *) str , sizeof(str)}; \ yaml_node_t tag = (yaml_node_t) & tag##_i typedef struct yaml_const_scalar_s { struct yaml_node_vtbl * vtbl; const yaml_char_t * chararr; const size_t charlen; }; extern struct yaml_node_vtbl yaml_const_scalar_vtbl ; /* TODO: Figure out how to construct a list or map */ #define YAML_CONST_BEGIN(tag) #define YAML_CONST_ENTRY(tag) #define YAML_CONST_END(tag) /* Iterator * * This is a pull based interface for accessing a sequence of nodes. * * A basic tenant of this interface is that the flow control is * owned by the consumer. This interface allows a sequence of YAML * nodes to be transferred. This interface is basically flat, hierarchy * is supported through subordinate, or child iterators. * * next() Gives the next node in the sequence of nodes. * If an node is not available, then this method * should return YAML_STOP, otherwise YAML_OK should * be returned if a node was provided. If YAML_STOP * is returned, there is no current node. * * A scalar key, if any, and a node object are * acquired through this method. These objects must * be released by the caller when the caller has * finished with them. Also, if them sequence is * part of a list, then the key is NULL. * * Note: If the access to the parent node who owns the sequence is * YAML_SEQUENTIAL then certain restrictions may apply. It may be * necessary for any node returned to be released or to have an * UNAVAILABLE access state before next() can be called again. * Along this line of reasoning, if a descendent iterator is created * or decendent nodes are accessed they may also need to be released * before next() can be called. If these restrictions are not met, then * the iterator's next() method is free to return a YAML_E_UNAVAILABLE * error condition. Do to garbage collection lag, the above may have * to be loosened to the case where all subordinate iterators have * been closed and all subordinate nodes have become YAML_E_UNAVAILABLE * through use. This requires a bit more research before the exact * requirements can be spelled out. */ typedef yaml_result_t (*yaml_next_t) (/* in */ yaml_iterator_t self, /* out */ yaml_node_t *node, /* out */ yaml_key_t *key); struct yaml_iterator_vtbl { /* Base Methods */ yaml_query_interface_t query_interface; /* Obtain another interface. */ yaml_addref_t addref; /* Increment reference count. */ yaml_release_t release; /* Decrement reference count. */ /* Node Methods */ yaml_next_t next; }; #define YAML_NEXT(iter,key,node,result) \ YAML_IS_OK( (result) = YAML_CALL2((iter),next,&(key),&(node))) /* Reader * * This is the character / byte level interface for pulling data from * a scalar or input stream. * * read_chars() This method is passed a properly allocated * buffer capable of holding 'len' yaml_char_t units. * If data exists to be read, then this function * fills in the 'buff' and then sets the 'out' * variable to the number of characters read. * If this function is successful, YAML_OK is * returned. If no more data is available * then YAML_STOP is returned. * * If the Reader is created from a Scalar, and * the Scalar is_binary() then using this method * must result with a YAML_E_WRONGTYPEKIND exception. * * read_bytes() This method is similar to read_chars(), only that * yaml_byte_t units are read rather than yaml_char_t. * Thus, the len and out parameters are specified in * sizeof(yaml_byte_t) units rather than units of * sizeof(yaml_char_t). * * When a string scalar is read using this method, the * string is returned using UTF-8 encoding regardless * of the normal internal encoding or the encoding of * the input/output stream. This restriction gives * a canonical form so that binary values (which may * happen to be valid unicode) can be saved and used * without being flagged as binary (with base64 * wrapping). Note that UTF-16 or UTF-32 cannot be * used for this purpose due to byte ordering issues. */ typedef yaml_result_t (*yaml_read_chars_t)( /* in */ yaml_reader_t self, /* in */ yaml_char_t * buff, /* in */ size_t len, /* out */ size_t * out ); typedef yaml_result_t (*yaml_read_bytes_t)( /* in */ yaml_reader_t self, /* in */ yaml_byte_t * buff, /* in */ size_t len, /* out */ size_t * out ); struct yaml_reader_vtbl { /* Base Methods */ yaml_query_interface_t query_interface; /* Obtain another interface. */ yaml_addref_t addref; /* Increment reference count. */ yaml_release_t release; /* Decrement reference count. */ /* Reader Methods */ yaml_read_chars_t read_chars; /* Read input as unicode. */ yaml_read_bytes_t read_bytes; /* Read input as binary (UTF8) */ }; const extern struct yaml_node_vtbl *yaml_reader_vtbl_defimpl; #define YAML_READ_CHARS(obj,buff,len,out) \ YAML_EXEC3((obj),read_chars,(buff), (len), &(out)) #define YAML_READ_BYTES(obj,buff,len,out) \ YAML_EXEC3((obj),read_chars,(buff), (len), &(out)) /* Writer * * This is the character / byte level interface for pushing data * from a scalar or to an input stream. This objject acts as a * acceptor allowing multiple character or byte array chunks * to be concatinated (or appended depening on your perspective). * * write_chars() This method pushes zero or more unicode * characters to the output. It is passed the * begining of the character array, and the length * of the character in sizeof(yaml_char_t) units. * * write_bytes() This method pushes bytes (binary) to the output with * length is specified as sizeof(yaml_byte_t) units. * * When a string scalar is sent to a writer, and * send_bytes is true, then the unicode characters * are written using the UTF-8 encoding regardless of * internal encoding or the output encoding. This * restriction is necessary to garuentee a canonical * form allowing binary values to round trip through * a YAML processor without being particularly marked * as binary values. */ typedef yaml_result_t (*yaml_write_chars_t)( /* in */ yaml_writer_t self, /* in */ const yaml_char_t * buff, /* in */ size_t len ); typedef yaml_result_t (*yaml_write_bytes_t)( /* in */ yaml_writer_t self, /* in */ const yaml_byte_t * buff, /* in */ size_t len ); struct yaml_writer_vtbl { /* Base Methods */ yaml_query_interface_t query_interface; /* Obtain another interface. */ yaml_addref_t addref; /* Increment reference count. */ yaml_release_t release; /* Decrement reference count. */ /* Writer Methods */ yaml_write_chars_t write_chars; /* Write as unicode chars. */ yaml_write_bytes_t write_bytes; /* Write as binary (UTF8) */ }; const extern struct yaml_node_vtbl *yaml_writer_vtbl_defimpl; #define YAML_WRITE_CHARS(obj,buff,len,out) \ YAML_EXEC2((obj),write_chars,(buff), (len)) #define YAML_WRITE_BYTES(obj,buff,len,out) \ YAML_EXEC2((obj),write_chars,(buff), (len)) /* Builder * * This encapsulates the programmatic construction of nodes. The * destination of the nodes is not specified by this interface, it * could be a map or list object returned to the user or it could * send the nodes to a sequential output stream. * * begin() Indicates to the builder that the next node * is to begin. The type of the node, a possible * key (if there is a parent node that is a map) * are the sole arguments. * * inject() This includes a given node in the current stream * of nodes. This is only valid when the parent * call to begin() returns This recursively appends a * given node, if the parent context is a map, then * a key is allowed. * * end() Marks the ending of a given node. This returns * a random-access node, unless the builder is attached * to a visitor, in which case NULL is the output. * * key() This is a helper method which can be used to * find/construct a scalar to be used as a key. * Since the scalar returned is immutable, this * operation may reuse an existing scalar rather * then allocate a new scalar. * * attach() Links the output of the builder to a visitor * (such as a printer, or a next stage). * * This class inherits from writer so that scalar values can be * constructed. begin()/inject() and write_chars()/write_bytes() * are mutually exclusive depending upon the type of node in the * outermost begin/end. */ typedef yaml_result_t (*yaml_begin_t) (/* in */ yaml_builder_t self, /* in */ yaml_kind_t kind, /* in */ yaml_key_t key); typedef yaml_result_t (*yaml_inject_t) (/* in */ yaml_builder_t self, /* in */ yaml_node_t node, /* in */ yaml_key_t key); typedef yaml_result_t (*yaml_end_t) (/* in */ yaml_builder_t self); typedef yaml_result_t (*yaml_get_key_t) (/* in */ yaml_builder_t self, /* in */ const yaml_char_t * keystr, /* out */ yaml_key_t *out); typedef yaml_result_t (*yaml_attach_t) (/* in */ yaml_builder_t self, /* in */ yaml_visitor_t visitor); struct yaml_builder_vtbl { /* Base Methods */ yaml_query_interface_t query_interface; /* Obtain another interface. */ yaml_addref_t addref; /* Increment reference count. */ yaml_release_t release; /* Decrement reference count. */ /* Writer Methods */ yaml_write_chars_t write_chars; /* Write as unicode chars. */ yaml_write_bytes_t write_bytes; /* Write as binary (UTF8) */ /* Builder Methods */ yaml_begin_t begin; /* Begin of a node. */ yaml_inject_t inject; /* Appends an existing node. */ yaml_end_t end; /* Ending of a node. */ yaml_get_key_t key; /* Looks up a key scalar. */ yaml_attach_t attach; /* Connects to a Visitor. */ }; #define YAML_BLD_START(obj,kind,context,key) \ YAML_EXEC3((obj),start,(kind),(context),(key)) #define YAML_BLD_FINISH(obj) \ YAML_EXEC2((obj),finish) #define YAML_BLD_INJECT(obj,alt) \ YAML_EXEC1((obj),inject,alt) #define YAML_ATTACH(obj,visit) \ YAML_EXEC2((obj),attach,(visit)) #define YAML_ATTACH(obj,visit) \ YAML_EXEC2((obj),attach,(visit)) #define YAML_KEY(obj,str,out) \ YAML_EXEC3((obj),attach,(str),&(out)) /* Macros for calling write methods in a type-safe manner */ #define YAML_B do {yaml_builder_t f=(o); yaml_writer_t w=(yaml_writer_t)f; #define YAML_E ;} while(0) #define YAML_BLD_WRITE_CHARS(o,b,l) YAML_B YAML_WRITE_CHARS(w,b,l) YAML_E #define YAML_BLD_WRITE_BYTES(o,b,l) YAML_B YAML_WRITE_BYTES(w,b,l) YAML_E #undef YAML_B #undef YAML_E /* Obtain a new built-in Builder, or inherit from it */ yaml_builder_t yaml_builder(); const extern struct yaml_builder_vtbl *yaml_builder_vtbl_defimpl; /* Visitor * * This is a push based interface for accessing a sequence of nodes. * This interface is like the iterator, only that the producer, not the * consumer owns the call stack (flow control). The visitor addresses * the following requirements: * * 1. Provides a technique for pushing previously * constructed nodes to the next processing stage * such such that producer is in control of the * node stream. * * 2. Enables a pull style to be used by the consumer * in the case that the nodes are random access. * In particular, the visitor construct should not * require node copying, or large amount of events * to pass a composite with many children. * * 3. Enables both flat and recursive visititation, where * the consumer can be notified of descendent nodes. * This is done through a nested visitor to enable * fine-grained dispatching. * * 4. Enables a feedback mechanism whereby the consumer * can express disinterest in a particular sub-tree * or can ask for a sub-tree to be returned as a * scalar instead of a sequence of nodes. * * 5. This does not provide for the construction of * nodes. Furthermore it requires some rather detailed * semantics on the part of the producer, which is * not always useful. For a simpler "push" interface, * see the builder object below. * * visit() This is method is used to notify the visitor about * the availability of a node. The primary input to * this function is the node structure, and a key for * the case that the parent context is a map. A third * output argument, 'handler' allows the visitor to * provide a writer for scalar output or a suborinate * visitor to receive the current node's children. * * If this handler is provided, then the visitor * has the option of returning one of the following: * * a) NULL to indicate that the content for the * given node does not interest the consumer. * * b) A Writer to indicate that the consumer is * expecting a scalar value. * * c) A Visitor which the producer can use to * enumerate the node's children. * * The exact semantics of this exchange are to be * defined by each visitor. The printer visitor, for * example, returns a Writer for each SCALAR node and * a subordinate Visitor for each MAP or LIST node. * For the push2pull visitor, the return value can take * on any of the three values above depending upon * what the next stage requests. Thus, a producer * sending information to a push2pull visitor must be * prepared to handle the three possibilities. * * If the return value is provided, the caller must * use queryinterface to determine which type of object * was returned and behave appropriately. If the parent * is a sequence, the caller must check for a child * visitor before it checks for a writer. Note that * this flexibility would allow a single object to act * as a visitor and writer. It even allows for the * same object to re-use itself recursively. * * If a visitor for the children is provided, then * the caller should use this visitor for each * child node in sequence, if any. Afterward, * the caller must release() the child visitor * before any further interaction with the current * visitor is carried out. * * If a writer is provided, then the caller should * provide for the node's content as either a sequence * of bytes or characters as appropriate. In this * case, if the current node is a map then the * scalar value of its "=" child should be provided. * Else, if the current node is a list, then the * scalar value of its first child should be * provided. In any case, the writer must be * released before any further interaction with the * current visitor can be carried out. * * NOTE: This library provides two a reference function which * implements the required recursive behavior. * * THOUGHTS: This is just _too_ complicated, even for my convoluted mind. * Or is it? The problem is a processor pipeline, where the * output of one stage (using visitor) is converted into the * input of the next stage (using iterator) via the push2pull * converter. In this case, a call to next() must result in * in a block for the next node to be visited. Then, when * the second stage resumes, it has one of the three options. * The first option (NULL) is equivalent to calling next() * again, and skipping the content. The second option (Writer) * is correlated with to_writer(). And the third option * (Visitor) is correlated with children(). Thus each of the * three have well defined correlaries to the use of the * node class under the iterator. I'm wondering. Could some * of this functionality move to the Node class? Or an * interaction with the node class and the Visitor? * */ typedef yaml_result_t (*yaml_visit_t) (/* in */ yaml_visitor_t self, /* in */ yaml_node_t node, /* in */ yaml_key_t key, /* out */ yaml_base_t *handler); struct yaml_visitor_vtbl { /* Base Methods */ yaml_query_interface_t query_interface; /* Obtain another interface. */ yaml_addref_t addref; /* Increment reference count. */ yaml_release_t release; /* Decrement reference count. */ /* Node Methods */ yaml_visit_t visit; /* Visit a node in a sequence. */ }; #define YAML_VISIT(obj,key,node,handler,result) \ YAML_IS_OK( (result) = YAML_CALL3((obj),visit,(key),(node),&(handler))) #define YAML_CAST_WRITER(obj) ((yaml_writer_t)(obj)) /* TODO: verify */ #define YAML_CAST_VISITOR(obj) ((yaml_writer_t)(obj)) /* TODO: verify */ /* Parser and Printer * * The YAML library starts and ends with a serialized text * representation of the information model. The parser converts * the text into a YAML_SEQUENTIAL access() iterator. The * printer converts a visitor's events into a serialized text. */ /* TODO: Check signature, is yaml_builder_t or yaml_arena_t needed? */ yaml_result_t yaml_parse( yaml_reader_t input, yaml_builder_t arena, yaml_encoding_t encoding, yaml_iterator_t * out ); yaml_result_t yaml_print( yaml_writer_t output, yaml_encoding_t encoding, yaml_visitor_t * out ); yaml_result_t yaml_file_reader( FILE * file, yaml_reader_t *out ); yaml_result_t yaml_file_writer( FILE * file, yaml_writer_t *out ); yaml_result_t yaml_byte_reader( yaml_byte_t *bytes, size_t len ); yaml_result_t yaml_byte_writer( /* allocates a byte array, user must free */ yaml_byte_t **bytes, size_t *len, yaml_reader_t *out ); /* Interface Conversion * * Many times one needs to connect interfaces of different types. * This provides to functions, push2pull and pull2push to do the * conversion. pull2push() is the easier of the two, and is very * useful for driving a process, such as connecting the parser's * iterator to the printer's visitor. push2pull() is much harder, * it returns a iterator and visitor pair that are connected via * a worker thread process. The latter function is also very * useful for building a processor pipeline of filters that uses * a pull2push() model. */ /* TODO: verify that push2pull and pull2push is needed as they are somewhat redundant with children() and apply() in the node interface */ yaml_result_t yaml_push2pull( yaml_builder_t arena, yaml_iterator_t in, yaml_visitor_t out ); yaml_result_t yaml_pull2push( yaml_builder_t arena, yaml_iterator_t *in, yaml_visitor_t *out); /* This function normalizes all scalar values so that they can be compared by pointer. */ yaml_result_t yaml_intern( yaml_builder_t arena, yaml_iterator_t in, yaml_iterator_t *out ); /*************************************************************************/ #ifdef YAML_IMPL enum { YAML_C_BANG = 33, YAML_C_QUOT = 34, YAML_C_POUND = 35, YAML_C_PCT = 37, YAML_C_AMP = 38, YAML_C_COLON = 58, YAML_C_EQUAL = 61, YAML_C_QUEST = 63, YAML_C_BKSL = 92, YAML_C_HAT = 94 }; /* Arena * * This encapsulates memory management policies in a single * interface which is passed about as required. * * alloc() Allocates a block of memory * realloc() Reallocates a block of memory. * free() Releases a block of memory. * */ /* TODO: Verify that this is even needed... I think so */ typedef yaml_result_t (*yaml_alloc_t) (/* in */ yaml_arena_t self, /* in */ size_t size, /* out */ void * *block); typedef yaml_result_t (*yaml_realloc_t) (/* in */ yaml_arena_t self, /* in */ size_t size, /* i/o */ void * *block); typedef yaml_result_t (*yaml_free_t) (/* in */ yaml_arena_t self, /* in */ void * block); struct yaml_arena_vtbl { /* Base Methods */ yaml_query_interface_t query_interface; /* Obtain another interface. */ yaml_addref_t addref; /* Increment reference count. */ yaml_release_t release; /* Decrement reference count. */ /* Arena Methods */ yaml_alloc_t alloc; /* Allocates arena block. */ yaml_realloc_t realloc; /* Resizes a arena block. */ yaml_free_t free; /* Returns a arena block. */ }; #define YAML_ALLOC(mem,size,pblk) YAML_EXEC2((mem),alloc,(size),(pblk)) #define YAML_REALLOC(mem,size,pblk) YAML_EXEC2((mem),realloc,(size),(pblk)) #define YAML_FREE(mem,pblk) YAML_EXEC2((mem),free,(pblk)) yaml_arena_t yaml_arena(); #ifdef YAML_MAIN // {00000000-0000-0000-C000-000000000046} static const yaml_guid_t yaml_guid_base = { 0x00000000, 0x000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } }; // {E8A25BD8-81F8-45e4-AE33-20A25328415E} static const yaml_guid_t yaml_guid_writer = { 0xe8a25bd8, 0x81f8, 0x45e4, { 0xae, 0x33, 0x20, 0xa2, 0x53, 0x28, 0x41, 0x5e } }; // {E8A25BD8-187A-47de-9539-5360362D66D0} static const yaml_guid_t yaml_guid_reader = { 0xe8a25bd8, 0x187a, 0x47de, { 0x95, 0x39, 0x53, 0x60, 0x36, 0x2d, 0x66, 0xd0 } }; // {E8A25BD8-06EE-4086-A323-39E254630BB0} static const yaml_guid_t yaml_guid_visitor = { 0xe8a25bd8, 0x6ee, 0x4086, { 0xa3, 0x23, 0x39, 0xe2, 0x54, 0x63, 0xb, 0xb0 } }; // {E8A25BD8-3A9D-4f27-8F8F-DEFCE54824F7} static const yaml_guid_t yaml_guid_iterator = { 0xe8a25bd8, 0x3a9d, 0x4f27, { 0x8f, 0x8f, 0xde, 0xfc, 0xe5, 0x48, 0x24, 0xf7 } }; // {E8A25BD8-F5E8-43ef-823A-CFB162D983C5} static const yaml_guid_t yaml_guid_node = { 0xe8a25bd8, 0xf5e8, 0x43ef, { 0x82, 0x3a, 0xcf, 0xb1, 0x62, 0xd9, 0x83, 0xc5 } }; // {E8A25BD8-1A7E-4933-B4B4-07294CDA27F4} stat... [truncated message content] |