/* $Id$ */ /* Copyright (c) 2008-2013 Pierre Pronchery */ /* This file is part of DeforaOS System libSystem */ /* This program 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, version 3 of the License. * * This program 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 program. If not, see . */ /* TODO: * - switch parser_scan() and parser_scan_filter() to avoid confusion */ #include #include #include #include #include #include "System/error.h" #include "System/object.h" #include "System/parser.h" #include "token.h" /* Parser */ /* private */ /* types */ typedef struct _ParserFilterData ParserFilterData; typedef struct _ParserCallbackData ParserCallbackData; struct _Parser { /* parsing sources */ String * filename; FILE * fp; char * string; size_t string_cnt; size_t string_pos; /* tracking the position */ unsigned int line; unsigned int col; int last; unsigned int lookahead; ParserFilter scanner; ParserFilterData * filters; size_t filters_cnt; ParserCallbackData * callbacks; size_t callbacks_cnt; }; struct _ParserFilterData { ParserFilter filter; void * data; }; struct _ParserCallbackData { ParserCallback callback; void * data; }; /* prototypes */ static int _parser_scanner_file(int * c, void * data); static int _parser_scanner_string(int * c, void * data); /* functions */ /* parser_scan_filter */ int parser_scan_filter(Parser * parser) { int c = parser->last; size_t i; ParserFilterData * pfd; int l; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%p)\n", __func__, (void *)parser); #endif if(parser->lookahead) parser->lookahead--; else if(parser->scanner(&c, parser) != 0) return EOF; /* FIXME report error */ for(i = 0; i < parser->filters_cnt; i++) { pfd = &parser->filters[i]; if((l = pfd->filter(&c, pfd->data)) < 0) return EOF; parser->lookahead += l; } parser->last = c; return c; } /* parser_scanner_file */ static int _parser_scanner_file(int * c, void * data) { Parser * parser = data; #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif if(parser->last == '\n') { parser->line++; parser->col = 1; } else if(parser->last != EOF) parser->col++; if((*c = fgetc(parser->fp)) == EOF && !feof(parser->fp)) return -1; parser->last = *c; return 0; } /* parser_scanner_string */ static int _parser_scanner_string(int * c, void * data) { Parser * parser = data; #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif if(parser->last == '\n') { parser->line++; parser->col = 1; } else if(parser->last != EOF) parser->col++; if(parser->string_pos == parser->string_cnt) *c = EOF; else *c = parser->string[parser->string_pos]; parser->string_pos++; parser->last = *c; return 0; } /* public */ /* functions */ /* parser_new */ static Parser * _new_do(ParserFilter scanner); Parser * parser_new(char const * pathname) { Parser * parser; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, pathname); #endif if((parser = _new_do(_parser_scanner_file)) == NULL) return NULL; if((parser->filename = string_new(pathname)) == NULL) error_set_code(1, "%s", strerror(errno)); if((parser->fp = fopen(pathname, "r")) == NULL) error_set_code(1, "%s: %s", pathname, strerror(errno)); if(parser->filename == NULL || parser->fp == NULL) { parser_delete(parser); return NULL; } return parser; } static Parser * _new_do(ParserFilter scanner) { Parser * parser; if((parser = object_new(sizeof(*parser))) == NULL) return NULL; parser->filename = NULL; parser->fp = NULL; parser->string = NULL; parser->string_cnt = 0; parser->string_pos = 0; parser->line = 1; parser->col = 1; parser->last = EOF; parser->lookahead = 0; parser->scanner = scanner; parser->filters = NULL; parser->filters_cnt = 0; parser->callbacks = NULL; parser->callbacks_cnt = 0; return parser; } /* parser_new_string */ Parser * parser_new_string(char const * string, size_t length) { Parser * parser; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\", %lu)\n", __func__, string, length); #endif if((parser = _new_do(_parser_scanner_string)) == NULL) return NULL; parser->string = malloc(length); parser->string_cnt = length; if(parser->string == NULL && length != 0) { error_set_code(1, "%s", strerror(errno)); parser_delete(parser); return NULL; } memcpy(parser->string, string, length); return parser; } /* parser_delete */ int parser_delete(Parser * parser) { int ret = 0; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%p) \"%s\"\n", __func__, (void *)parser, parser->filename); #endif if(parser->fp != NULL && fclose(parser->fp) != 0) ret = error_set_code(1, "%s: %s", parser->filename, strerror(errno)); string_delete(parser->filename); free(parser->string); free(parser->filters); free(parser->callbacks); object_delete(parser); return ret; } /* accessors */ /* parser_get_filename */ char const * parser_get_filename(Parser * parser) { return parser->filename; } /* parser_get_token */ int parser_get_token(Parser * parser, Token ** token) { int ret = 0; /* XXX not sure */ size_t i; ParserCallbackData * pcd; if((*token = token_new(parser->filename, parser->line, parser->col)) == NULL) return 1; if(parser->last == EOF) parser_scan_filter(parser); #ifdef DEBUG fprintf(stderr, "DEBUG: %s() %c\n", __func__, parser->last); #endif for(i = 0; i < parser->callbacks_cnt; i++) { pcd = &parser->callbacks[i]; if((ret = pcd->callback(parser, *token, parser->last, pcd->data)) <= 0) break; } if(ret == 0 && i != parser->callbacks_cnt) return 0; /* there is a token and no error */ token_delete(*token); *token = NULL; return (ret >= 0 && parser->last == EOF) ? 0 : 1; } /* useful */ /* parser_add_callback */ int parser_add_callback(Parser * parser, ParserCallback callback, void * data) { ParserCallbackData * p; if((p = realloc(parser->callbacks, sizeof(*p) * (parser->callbacks_cnt + 1))) == NULL) return 1; parser->callbacks = p; p = &parser->callbacks[parser->callbacks_cnt++]; p->callback = callback; p->data = data; return 0; } /* parser_add_filter */ int parser_add_filter(Parser * parser, ParserFilter filter, void * data) { ParserFilterData * p = parser->filters; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%p, %p, %p)\n", __func__, (void *)parser, (void *)filter, (void *)data); #endif if((p = realloc(p, sizeof(*p) * (parser->filters_cnt + 1))) == NULL) return 1; parser->filters = p; p = &parser->filters[parser->filters_cnt++]; p->filter = filter; p->data = data; return 0; } /* parser_remove_callback */ int parser_remove_callback(Parser * parser, ParserCallback callback) /* FIXME untested */ { size_t i; ParserCallbackData * p; for(i = 0; i < parser->callbacks_cnt; i++) if(parser->callbacks[i].callback == callback) break; if(i != parser->callbacks_cnt) return 1; p = &parser->callbacks[i]; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(): memmove(%p, %p, %lu)\n", __func__, (void *)p, (void *)(p + 1), sizeof(*p) * (parser->callbacks_cnt - i)); #endif memmove(p, p + 1, sizeof(*p) * (parser->callbacks_cnt - i)); parser->callbacks_cnt--; return 0; } /* parser_remove_filter */ int parser_remove_filter(Parser * parser, ParserFilter filter) /* FIXME untested */ { size_t i; ParserFilterData * p; for(i = 0; i < parser->filters_cnt; i++) if(parser->filters[i].filter == filter) break; if(i != parser->filters_cnt) return 1; p = &parser->filters[i]; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(): memmove(%p, %p, %lu)\n", __func__, (void *)p, (void *)(p + 1), sizeof(*p) * (parser->callbacks_cnt - i)); #endif memmove(p, p + 1, sizeof(*p) * (parser->filters_cnt - i)); parser->filters_cnt--; return 0; } /* parser_scan */ int parser_scan(Parser * parser) { int c; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%p)\n", __func__, (void *)parser); #endif if(parser->scanner(&c, parser) != 0) return EOF; return c; }