• M
    json: Redesign the callback to consume JSON values · 62815d85
    Markus Armbruster 提交于
    The classical way to structure parser and lexer is to have the client
    call the parser to get an abstract syntax tree, the parser call the
    lexer to get the next token, and the lexer call some function to get
    input characters.
    
    Another way to structure them would be to have the client feed
    characters to the lexer, the lexer feed tokens to the parser, and the
    parser feed abstract syntax trees to some callback provided by the
    client.  This way is more easily integrated into an event loop that
    dispatches input characters as they arrive.
    
    Our JSON parser is kind of between the two.  The lexer feeds tokens to
    a "streamer" instead of a real parser.  The streamer accumulates
    tokens until it got the sequence of tokens that comprise a single JSON
    value (it counts curly braces and square brackets to decide).  It
    feeds those token sequences to a callback provided by the client.  The
    callback passes each token sequence to the parser, and gets back an
    abstract syntax tree.
    
    I figure it was done that way to make a straightforward recursive
    descent parser possible.  "Get next token" becomes "pop the first
    token off the token sequence".  Drawback: we need to store a complete
    token sequence.  Each token eats 13 + input characters + malloc
    overhead bytes.
    
    Observations:
    
    1. This is not the only way to use recursive descent.  If we replaced
       "get next token" by a coroutine yield, we could do without a
       streamer.
    
    2. The lexer reports errors by passing a JSON_ERROR token to the
       streamer.  This communicates the offending input characters and
       their location, but no more.
    
    3. The streamer reports errors by passing a null token sequence to the
       callback.  The (already poor) lexical error information is thrown
       away.
    
    4. Having the callback receive a token sequence duplicates the code to
       convert token sequence to abstract syntax tree in every callback.
    
    5. Known bug: the streamer silently drops incomplete token sequences.
    
    This commit rectifies 4. by lifting the call of the parser from the
    callbacks into the streamer.  Later commits will address 3. and 5.
    
    The lifting removes a bug from qjson.c's parse_json(): it passed a
    pointer to a non-null Error * in certain cases, as demonstrated by
    check-qjson.c.
    
    json_parser_parse() is now unused.  It's a stupid wrapper around
    json_parser_parse_err().  Drop it, and rename json_parser_parse_err()
    to json_parser_parse().
    Signed-off-by: NMarkus Armbruster <armbru@redhat.com>
    Reviewed-by: NEric Blake <eblake@redhat.com>
    Message-Id: <20180823164025.12553-35-armbru@redhat.com>
    62815d85
libqtest.c 28.0 KB