Index: trunk/src_3rd/libuhpgl/libuhpgl.h =================================================================== --- trunk/src_3rd/libuhpgl/libuhpgl.h (revision 11176) +++ trunk/src_3rd/libuhpgl/libuhpgl.h (revision 11177) @@ -120,6 +120,10 @@ int pen; /* selected pen [0..255] */ uhpgl_point_t at; /* last known coordinate of the pen */ int ct; /* Chord Tolerance */ + + /* last char parsed */ + size_t offs; + size_t line, col; } state; /* parser error report: written by the lib, read-only for the caller */ @@ -129,6 +133,7 @@ const char *msg; } error; + void *parser; /* opaque parser data */ void *user_data; /* only used by the caller */ }; Index: trunk/src_3rd/libuhpgl/parse.c =================================================================== --- trunk/src_3rd/libuhpgl/parse.c (revision 11176) +++ trunk/src_3rd/libuhpgl/parse.c (revision 11177) @@ -20,6 +20,178 @@ This library is part of pcb-rnd: http://repo.hu/projects/pcb-rnd */ +#include +#include #include "libuhpgl.h" #include "parse.h" +typedef enum state_e { + ST_IDLE, + ST_INST, + ST_INST_END, + ST_COORDS +} state_t; + +typedef struct { + size_t token_offs; + size_t token_line, token_col; + char token[32]; + int len; /* token length */ + state_t state; + unsigned error:1; + unsigned eof:1; +} parse_t; + +static int error(uhpgl_ctx_t *ctx, const char *msg) +{ + parse_t *p = ctx->parser; + + ctx->error.offs = p->token_offs; + ctx->error.line = p->token_line; + ctx->error.col = p->token_col; + ctx->error.msg = msg; + + p->error = 1; + return -1; +} + +static const char *err_beyond_end = "Character after EOF"; +static const char *err_not_open = "Parser is not open"; + +#define decl_parser_ctx \ + parse_t *p = ctx->parser; \ + if (p == NULL) {\ + ctx->error.msg = err_not_open; \ + return -1; \ + } \ + if (p->error) \ + return -1; \ + if (p->eof) \ + return error(ctx, err_beyond_end); + +int uhpgl_parse_str(uhpgl_ctx_t *ctx, const char *str) +{ + int ret; + decl_parser_ctx; + + for(;*str != '\0'; str++) { + ret = uhpgl_parse_char(ctx, *str); + if (ret != 0) + return ret; + } + return 0; +} + +int uhpgl_parse_file(uhpgl_ctx_t *ctx, FILE *f) +{ + int ret, c; + decl_parser_ctx; + + while((c = fgetc(f)) != EOF) { + ret = uhpgl_parse_char(ctx, c); + if (ret != 0) + return ret; + } + return 0; +} + +int uhpgl_parse_open(uhpgl_ctx_t *ctx) +{ + if (ctx->parser != NULL) { + ctx->error.msg = "Parser already open"; + return -1; + } + ctx->parser = calloc(sizeof(parse_t), 1); + ctx->state.offs = 0; + ctx->state.line = 0; + ctx->state.col = 0; + return 0; +} + +int uhpgl_parse_close(uhpgl_ctx_t *ctx) +{ + decl_parser_ctx; + if (p->state != ST_IDLE) { + error(ctx, "premature end of stream"); + free(p); + ctx->parser = NULL; + return -1; + } + + free(p); + ctx->parser = NULL; + return 0; +} + +/*** the actual parser: high level (grammar) ***/ +static int parse_inst(uhpgl_ctx_t *ctx) +{ + parse_t *p = ctx->parser; + return error(ctx, "unimplemented instruction"); +} + +static int parse_coord(uhpgl_ctx_t *ctx, long int coord, int is_last) +{ + parse_t *p = ctx->parser; + return error(ctx, "unimplemented coord instruction"); +} + +/*** the actual parser: low level (tokens) ***/ +int uhpgl_parse_char(uhpgl_ctx_t *ctx, int c) +{ + decl_parser_ctx; + + ctx->state.col++; + switch(c) { + case EOF: + p->eof = 1; + return 0; + case '\n': + ctx->state.line++; + ctx->state.col = 1; + return 0; + case '\r': + case '\t': + case ' ': + return 0; + } + + switch(p->state) { + case ST_IDLE: + if (c == ';') /* be liberal: accept multiple terminators or empty instructions */ + return 0; + p->state = ST_INST; + p->len = 0; + /* fall through to read the first char */ + case ST_INST: + if (!isalpha(c)) + return error(ctx, "Invalid character in instruction (must be alpha)"); + p->token[p->len] = toupper(c); + p->len++; + if (p->len == 2) { + p->len = 0; + return parse_inst(ctx); + } + return 0; + case ST_INST_END: + if (c != ';') + return error(ctx, "Expected semicolon to terminate instruction"); + p->state = ST_IDLE; + return 0; + case ST_COORDS: + if ((c == ',') || (c == ';')) { + int last = (c == ';'); + p->token[p->len] = '\0'; + p->len = 0; + if (last) + p->state = ST_IDLE; + return parse_coord(ctx, strtol(p->token, NULL, 10), last); + } + if (!isdigit(c)) + return error(ctx, "Expected digit or separator in coordinate"); + p->token[p->len] = c; + p->len++; + return 0; + } + return error(ctx, "Internal error: broken state machine"); +} Index: trunk/src_3rd/libuhpgl/parse.h =================================================================== --- trunk/src_3rd/libuhpgl/parse.h (revision 11176) +++ trunk/src_3rd/libuhpgl/parse.h (revision 11177) @@ -24,7 +24,22 @@ #ifndef LIBUHPGL_PRASE_H #define LIBUHPGL_PRASE_H +#include #include "libuhpgl.h" +/* Shall be called before parsing starts (call uhpgl_parse_open() at the end) */ +int uhpgl_parse_open(uhpgl_ctx_t *ctx); +/* Parse next character of a stream; c may be EOF; returns 0 on success */ +int uhpgl_parse_char(uhpgl_ctx_t *ctx, int c); + +/* Parse next portion of a stream from a string; returns 0 on success */ +int uhpgl_parse_str(uhpgl_ctx_t *ctx, const char *str); + +/* Parse next portion of a stream from a file; returns 0 on success */ +int uhpgl_parse_file(uhpgl_ctx_t *ctx, FILE *f); + +/* Shall be called after the last portion parsed; returns 0 on success */ +int uhpgl_parse_close(uhpgl_ctx_t *ctx); + #endif