From: Clark C . E. <cc...@cl...> - 2001-05-20 00:16:18
|
/************************************************************************* ** RCS-ID: $Id: yaml.h,v 1.3 2001/05/15 16:03:13 cce Exp $ ** Copyright: (c) 2001 Clark Evans (cc...@cl...) ** License: You may use under the Python, Perl, or LGPL license ** Credits: Brian Ingerson and Oren Ben-Kiki ** Version: This is a prototype API and is not yet ** ready for implementing ************************************************************************/ /************************************************************************* * CONTEXT AND CONSTANTS ************************************************************************/ struct yaml_context; typedef char yaml_char_t; /* typedef for unicode */ typedef size_t yaml_size_t /* buffer size in bytes */ typedef void * yaml_user_t; /* user defined ptr */ typedef const yaml_char_t * yaml_key_t; /* null terminated str */ typedef const yaml_char_t * yaml_name_t; /* null terminated str */ typedef struct yaml_context * yaml_context_t; /* iter/visit context */ typedef void * yaml_reference_t; /* opaque handle */ typedef enum { YAML_ERROR = -1, YAML_SUCCESS = 0, } yaml_result_t; typedef enum { YAML_NODE_SCALAR = 1, YAML_NODE_LIST = 2, YAML_NODE_MAP = 3, YAML_NODE_REF = 4 } yaml_type_t; struct yaml_context /* this structure holds the current iterator or visitor context, which is the path up to the root node. */ { yaml_context_t parent; /* the parent context, maintained for you */ yaml_type_t type; /* the type of node, this is Map, List, */ yaml_key_t key; /* if an entry in a map, this is the key */ yaml_reference_t reference; /* the reference for the value's content */ yaml_name_t name; /* the class name or data type */ yaml_user_t user; /* user defined object for this context */ }; /************************************************************************* * VISITOR INTERFACE ************************************************************************/ struct yaml_visitor; /* the visitor event handler and data pointer */ typedef struct yaml_visitor * yaml_visitor_t; typedef enum { YAML_VISIT_ERROR = YAML_ERROR, YAML_VISIT_CONTINUE = YAML_SUCCESS, /* continue visiting */ YAML_VISIT_STOP, /* stop visiting, free visitor */ YAML_VISIT_SKIP /* skip children */ } yaml_visit_t; typedef /* when entering a node (map, list, or scalar) */ yaml_visit_t (*yaml_visit_enter_t)( yaml_visitor_t visitor, yaml_context_t context); typedef /* when exiting node (map, list, or scalar) */ yaml_visit_t (*yaml_visit_exit_t)( yaml_visitor_t visitor, yaml_context_t context); typedef /* for scalar content, can be called more than once per node */ yaml_visit_t (*yaml_visit_chars_t)( yaml_visitor_t visitor, yaml_context_t context, const yaml_char_t* data, yaml_size_t size ); typedef /* reference is encountered, note this does not dereference */ yaml_visit_t(*yaml_visit_ref_t) (yaml_visitor_t visitor, yaml_context_t context); struct yaml_visitor { yaml_visit_ref_t ref; /* function to handle reference events */ yaml_visit_enter_t enter; /* function to handle begin node event */ yaml_visit_exit_t exit; /* function to handle end node event */ yaml_visit_chars_t chars; /* function to handle scalar content */ yaml_user_t user; /* user data for the consumer */ }; /************************************************************************* * ITERATOR INTERFACE ************************************************************************/ struct yaml_iterator; /* the iterator data producer and user data ptr */ typedef struct yaml_iterator * yaml_iterator_t; typedef enum { YAML_ITER_ERROR = YAML_ERROR, /* bad, some error occurred */ YAML_ITER_NONE = YAML_SUCCESS, /* good, but no node encountered */ YAML_ITER_SCALAR = YAML_TYPE_SCALAR, /* good, scalar node encountered */ YAML_ITER_LIST = YAML_TYPE_LIST, /* good, list node encountered */ YAML_ITER_MAP = YAML_TYPE_MAP, /* good, map encountered */ YAML_ITER_REF = YAML_TYPE_REF /* good, reference encountered */ } yaml_iter_t; typedef /* iterates through list of siblings in a list or map */ yaml_iter_t (*yaml_iter_next_t)( yaml_iterator_t iterator, yaml_context_t *context); typedef /* iterates into the first child of a list or map */ yaml_context_t *context); typedef /* allows the return of scalar data, returns amount read */ yaml_size_t (*yaml_iter_chars_t)( yaml_iterator_t iterator, yaml_char_t* data, yaml_size_t size); typedef /* stops the iteration and cleans up iterator */ yaml_iter_t (*yaml_iter_stop_t)( yaml_iterator_t iterator); struct yaml_iterator { yaml_iter_next_t next; /* function to return next yaml node */ yaml_iter_first_t first; /* function to return first yaml node */ yaml_iter_chars_t chars; /* function to return scalar content */ yaml_iter_stop_t stop; /* function to stop the iteration */ yaml_user_t user; /* user data for the producer */ }; /************************************************************************* * PARSER/EMITTER API ************************************************************************/ yaml_iterator_t yaml_parse( FILE * ); yaml_visitor_t yaml_emit( FILE * ); yaml_result_t yaml_exec(yaml_iterator_t,yaml_visitor_t); /* To stream a document though the system: * * yaml_exec( yaml_parse(fopen("file.in","r")), * yaml_emit(fopen("file.out","w"))); */ /************************************************************************* * PARSER/EMITTER API ************************************************************************/ typedef struct { yaml_iterator_t iterator, yaml_visitor_t visitor } * yaml_convert_t; yaml_convert_t yaml_convert(void); |
From: Clark C . E. <cc...@cl...> - 2001-05-20 00:42:45
|
I talked with Brian earlier today, and he expresed "over engineering" concerns about this strawman. I can understand the concern, but I'd like to explain, and hopefully we can work this out. Our "C" level API will be very important to our long term growth. It must be "just right", if it is too simple, it won't do enough work to be useful or if it is too complicated, it will not be useable. ... This API is targeted for two users, both of which will be C programmers: a) Those who are writing the native langauge representation glue for Python, Perl, etc. b) Those who are building YAML processing filters. For those in the first class, the host programming langauge will call "load" or some other similar one-liner that loads the entire YAML document into the in-memory, language specific structures. And then when the host programming langauge calls "save", the entire document is serilized from a random access memory image into a disk image. Those users in second class are slightly different, as they will require "streaming" based processing of YAML, and thus minimal in-memory storage. I see many YAML filters being created and provided as a "yaml processing library". This API is targeted at the latter class of users, since their requirements are a bit rougher. Further, for python at least, this streaming based API may also be exposed... ... Before we dive into a commentary/justification for the API strawman, let me first discuss stream based filter processing. The minimal version of this looks like... TEXT -> [READ] -> API -> [PROCESS] -> API -> [WRITE] -> TEXT Here READ is done by the parser, WRITE is done by the emitter, and PROCESS is done by the application. A filter architecture occurs when the PROCESS has multiple stages (we skip the stuff before the READ and after the WRITE). API -> [A] -> API -> [B] -> API -> [C] -> API Where A, B, and C are stages in the processing, and API is some tokenized stream or perhaps in-memory representation of the YAML text. ... Anyway... I have to go, so I'll finish this later. However, there are two points to be made: a) If API is sequential, it must be pull based (see the iterator pattern, with a .next() method) or push based (see visitor pattern, with an .accept() event handler). b) The difference between push and pull is very important beacuse it dictates the point of control, who has the main loop. Given a process X, with input API I and output API O, this is a useful chart: Input API | Output API | Comments -------------------------------------------- PULL PUSH Very Easy To Implement PULL PULL Not too hard PUSH PUSH Not too hard PUSH PULL Very hard, requires threads co-routines, or random access. Thus... we need both a push and a pull interface. The parser has a pull and emitter has a push interface. More on this later.... Clark |