sxpr_parser.c

来自「xen虚拟机源代码安装包」· C语言 代码 · 共 1,003 行 · 第 1/2 页

C
1,003
字号
/* * Copyright (C) 2001 - 2005 Mike Wray <mike.wray@hp.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or  (at your option) any later version. This library is  * distributed in the  hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA */#ifdef __KERNEL__#  include <linux/config.h>#  include <linux/module.h>#  include <linux/kernel.h>#  include <linux/string.h>#  include <linux/errno.h>#else#  include <stdlib.h>#  include <errno.h>#endif#include "sys_net.h"#include "iostream.h"#include "lexis.h"#include "sxpr_parser.h"#include "sys_string.h"#include "enum.h"/** @file * Sxpr parsing. * * So that the parser does not leak memory, all sxprs constructed by * the parser must be freed on error.  On successful parse the sxpr * returned becomes the responsibility of the caller. * * @author Mike Wray <mike.wray@hpl.hp.com> */#ifdef DEBUG#define dprintf(fmt, args...) IOStream_print(iostdout, "[DEBUG] %s" fmt, __FUNCTION__, ##args)#else#define dprintf(fmt, args...) do{ }while(0)#endif#undef printf#define printf(fmt, args...)   IOStream_print(iostdout, fmt, ##args)static int state_start(Parser *p, char c);static int begin_start(Parser *p, char c);#if 0/** Print a parse error. * * @param in parser * @param msg format followed by printf arguments */static void eprintf(Parser *in, char *msg, ...){    va_list args;    if(in->error_out){        va_start(args, msg);        IOStream_vprint(in->error_out, msg, args);        va_end(args);    }}/** Print a parse warning. * * @param in parser * @param msg format followed by printf arguments */static void wprintf(Parser *in, char *msg, ...){    va_list args;    if(in->error_out){        va_start(args, msg);        IOStream_vprint(in->error_out, msg, args);        va_end(args);    }}#endif/*============================================================================*//** Record defining the message for a parse error. */typedef struct {    ParseErrorId id;    char *message;} ParseError;/** Format for printing parse error messages. */#define PARSE_ERR_FMT "parse error> line %3d, column %2d: %s"/** Message catalog for the parse error codes. */static ParseError catalog[] = {    { PARSE_ERR_UNSPECIFIED,            "unspecified error" },    { PARSE_ERR_NOMEM,                  "out of memory" },    { PARSE_ERR_UNEXPECTED_EOF,         "unexpected end of input" },    { PARSE_ERR_TOKEN_TOO_LONG,         "token too long" },    { PARSE_ERR_INVALID_SYNTAX,         "syntax error" },    { PARSE_ERR_INVALID_ESCAPE,         "invalid escape" },    { 0, NULL }};/** Number of entries in the message catalog. */const static int catalog_n = sizeof(catalog)/sizeof(ParseError);/** Set the parser error stream. * Parse errors are reported on the the error stream if it is non-null. *  * @param z parser * @param error_out error stream */void Parser_set_error_stream(Parser *z, IOStream *error_out){    z->error_out = error_out;}/** Get the parser error message for an error code. * * @param id error code * @return error message (empty string if the code is unknown) */static char *get_message(ParseErrorId id){    int i;    for(i = 0; i < catalog_n; i++){        if(id == catalog[i].id){            return catalog[i].message;        }    }    return "";}#if 0/** Get the line number. * * @param in parser */static int get_line(Parser *in){    return in->line_no;}/** Get the column number. * * @param in parser */static int get_column(Parser *in){    return in->char_no;}#endif/** Get the line number the current token started on. * * @param in parser */static int get_tok_line(Parser *in){    return in->tok_begin_line;}/** Get the column number the current token started on. * * @param in parser */static int get_tok_column(Parser *in){    return in->tok_begin_char;}/** Return the current token. * The return value points at the internal buffer, so * it must not be modified (or freed). Use copy_token() if you need a copy. * * @param p parser * @return token */char *peek_token(Parser *p){    return p->tok;}int token_len(Parser *p){    return p->tok_end - p->tok;}/** Return a copy of the current token. * The returned value should be freed when finished with. * * @param p parser * @return copy of token */char *copy_token(Parser *p){    int n = token_len(p);    char *buf = allocate(n + 1);    if(buf){        memcpy(buf, peek_token(p), n);        buf[n] = '\0';    }    return buf;}void new_token(Parser *p){    memset(p->buf, 0, p->buf_end - p->buf);    p->tok = p->buf;    p->tok_end = p->tok;    p->tok_begin_line = p->line_no;    p->tok_begin_char = p->char_no;}/** Report a parse error. * Does nothing if the error stream is null or there is no error. * * @param in parser */static void report_error(Parser *in){    if(in->error_out && in->err){        char *msg = get_message(in->err);        char *tok = peek_token(in);        IOStream_print(in->error_out, PARSE_ERR_FMT,                       get_tok_line(in), get_tok_column(in), msg);        if(tok && tok[0]){            IOStream_print(in->error_out, " '%s'", tok);        }        IOStream_print(in->error_out, "\n");    }}/** Get the error message for the current parse error code. * Does nothing if there is no error. * * @param in parser * @param buf where to place the message * @param n maximum number of characters to place in buf * @return current error code (zero for no error) */int Parser_error_message(Parser *in, char *buf, int n){    if(in->err){        char *msg = get_message(in->err);        snprintf(buf, n, PARSE_ERR_FMT, get_tok_line(in),                 get_tok_column(in), msg);    }    return in->err;}/** Flag a parse error. All subsequent reads will fail. * Does not change the parser error code if it is already set. * * @param in parser * @param id error code */int Parser_error_id(Parser *in, ParseErrorId id){    if(!in->err){        in->err = id;        report_error(in);    }    return -EINVAL;}/** Flag an unspecified parse error. * * @param in parser */int Parser_error(Parser *in){    return Parser_error_id(in, PARSE_ERR_INVALID_SYNTAX);}/** Test if the parser's error flag is set. * * @param in parser * @return 1 if set, 0 otherwise */int Parser_has_error(Parser *in){    return (in->err > 0);}/** Test if the parser is at end of input. * * @param in parser * @return 1 if at EOF, 0 otherwise */int Parser_at_eof(Parser *p){    return p->eof;}void ParserState_free(ParserState *z){    if(!z) return;    objfree(z->val);    deallocate(z);}int ParserState_new(ParserStateFn *fn, char *name,                    ParserState *parent, ParserState **val){    int err = -ENOMEM;    ParserState *z;    z = ALLOCATE(ParserState);    if(!z) goto exit;    z->name = name;    z->fn = fn;    z->parent = parent;    z->val = ONULL;    err = 0;  exit:    *val = (err ? NULL : z);    return err;}void Parser_pop(Parser *p){    ParserState *s = p->state;    if(!s) return;    dprintf("Parser_pop> %s\n", s->name);    p->state = s->parent;    if (p->start_state == s) {        p->start_state = NULL;    }    ParserState_free(s);}/** Free a parser. * No-op if the parser is null. * * @param z parser  */void Parser_free(Parser *z){    if(!z) return;    // Hmmm. Need to free states, but careful about double free of values.    while(z->state){        objfree(z->state->val);        Parser_pop(z);    }    if(z->buf) deallocate(z->buf);    objfree(z->val);    z->val = ONONE;    deallocate(z);}int Parser_push(Parser *p, ParserStateFn *fn, char *name){    dprintf("Parser_push> %s\n", name);    return ParserState_new(fn, name, p->state, &p->state);}        int Parser_return(Parser *p){    int err = 0;    Sxpr val = ONONE;    if(!p->state){        err = -EINVAL;        goto exit;    }    val = p->state->val;    p->state->val = ONONE;    Parser_pop(p);    if(p->state){        err = cons_push(&p->state->val, val);    } else {        val = nrev(val);        p->val = val;    }  exit:    if(err){        objfree(val);    }    return err;}/** Reset the fields of a parser to initial values. * * @param z parser */static void reset(Parser *z){    // leave flags    // leave error_out    while(z->state){        Parser_pop(z);    }    z->val = ONONE;    z->eof = 0;    z->err = 0;    z->line_no = 1;    z->char_no = 0;    memset(z->buf, 0, z->buf_end - z->buf);    z->tok = z->buf;    z->tok_end = z->tok;    z->tok_begin_line = 0;    z->tok_begin_char = 0;    z->start_state = NULL;}/** Create a new parser. The error stream defaults to null. */Parser * Parser_new(void){    Parser *z = ALLOCATE(Parser);    int n = PARSER_BUF_SIZE;    int err = -ENOMEM;      if(!z) goto exit;    z->buf = allocate(n);    if(!z->buf) goto exit;    err = 0;    z->buf_end = z->buf + n;    z->begin = begin_start;    reset(z);  exit:    if(err){        Parser_free(z);        z = NULL;    }    return z;}/** Get the next character. * Records the character read in the parser, * and sets the line and character counts. * * @param p parser * @return error flag: 0 on success, non-zero on error */static int input_char(Parser *p, char c){    int err = 0;    if(c=='\n'){        p->line_no++;        p->char_no = 0;    } else {        p->char_no++;    }    return err;}int save_char(Parser *p, char c){    int err = 0;    if(p->tok_end >= p->buf_end){        int buf_n = (p->buf_end - p->buf) + PARSER_BUF_INCREMENT;        char *buf = allocate(buf_n);        if(!buf){            err = -ENOMEM;            goto exit;        }        memcpy(buf, p->buf, p->tok_end - p->buf);        p->buf_end = buf + buf_n;        p->tok     = buf + (p->tok     - p->buf);        p->tok_end = buf + (p->tok_end - p->buf);        deallocate(p->buf);        p->buf = buf;    }    *p->tok_end++ = c;  exit:    return err;}/** Determine if a character is a separator. * * @param p parser * @param c character to test * @return 1 if a separator, 0 otherwise */static int is_separator(Parser *p, char c){    return in_sep_class(c);}int Parser_set_value(Parser *p, Sxpr obj){    int err = 0;    if(NOMEMP(obj)){        err = -ENOMEM;    } else {        p->state->val = obj;    }    return err;}    int Parser_intern(Parser *p){    Sxpr obj = intern(peek_token(p));    return Parser_set_value(p, obj);}int Parser_atom(Parser *p){    Sxpr obj;    long v;    if(Parser_flags(p, PARSE_INT) &&       convert_atol(peek_token(p), &v) == 0){        obj = OINT(v);    } else {        obj = atom_new(peek_token(p));    }    return Parser_set_value(p, obj);}int Parser_string(Parser *p){    Sxpr obj = string_new_n(peek_token(p), token_len(p));    return Parser_set_value(p, obj);}int Parser_data(Parser *p){    Sxpr obj = string_new_n(peek_token(p), token_len(p));    return Parser_set_value(p, obj);}int Parser_uint(Parser *p){    unsigned int x = htonl(*(unsigned int *)peek_token(p));    return Parser_set_value(p, OINT(x));}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?