summaryrefslogtreecommitdiffstats
path: root/mmss/lex.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mmss/lex.cpp')
-rw-r--r--mmss/lex.cpp399
1 files changed, 399 insertions, 0 deletions
diff --git a/mmss/lex.cpp b/mmss/lex.cpp
new file mode 100644
index 0000000..0ef205e
--- /dev/null
+++ b/mmss/lex.cpp
@@ -0,0 +1,399 @@
+/*
+ Copyright (c) 2012-2014, Matthias Schiffer <mschiffer@universe-factory.net>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include "lex.hpp"
+
+#include <cstdlib>
+
+
+#define array_size(array) (sizeof(array)/sizeof((array)[0]))
+
+
+namespace MMSS {
+
+
+struct keyword_t {
+ const char *keyword;
+ int token;
+};
+
+/* the keyword list must be sorted */
+static const keyword_t keywords[] = {
+ { "address", TOK_ADDRESS },
+ { "const", TOK_CONST },
+ { "days", TOK_DAYS },
+ { "default", TOK_DEFAULT },
+ { "etx", TOK_ETX },
+ { "h", TOK_H },
+ { "interface", TOK_INTERFACE },
+ { "load", TOK_LOAD },
+ { "m", TOK_M },
+ { "max", TOK_MAX },
+ { "min", TOK_MIN },
+ { "network", TOK_NETWORK },
+ { "no", TOK_NO },
+ { "node", TOK_NODE },
+ { "period", TOK_PERIOD },
+ { "phase", TOK_PHASE },
+ { "protocol", TOK_PROTOCOL },
+ { "s", TOK_S },
+ { "sine", TOK_SINE },
+ { "yes", TOK_YES },
+};
+
+static int compare_keywords(const void *v1, const void *v2) {
+ const keyword_t *k1 = static_cast<const keyword_t*>(v1), *k2 = static_cast<const keyword_t*>(v2);
+ return std::strcmp(k1->keyword, k2->keyword);
+}
+
+
+bool lex_t::advance() {
+ if (start > 0) {
+ std::memmove(buffer, buffer+start, end - start);
+ end -= start;
+ start = 0;
+ }
+
+ if (end == sizeof(buffer))
+ return false;
+
+ size_t l = std::fread(buffer+end, 1, sizeof(buffer) - end, file);
+
+ end += l;
+ return l;
+}
+
+bool lex_t::next(YYLTYPE *yylloc, bool move) {
+ if (start + tok_len >= end)
+ return false;
+
+ if (current() == '\n') {
+ yylloc->last_column = 0;
+ yylloc->last_line++;
+ }
+ else {
+ yylloc->last_column++;
+ }
+
+ if (move)
+ start++;
+ else
+ tok_len++;
+
+
+ if (start + tok_len >= end)
+ return advance();
+
+ return true;
+}
+
+void lex_t::consume(bool consume_needspace) {
+ start += tok_len;
+ tok_len = 0;
+
+ needspace = consume_needspace;
+}
+
+int lex_t::io_error(YYSTYPE *yylval) {
+ yylval->error = "I/O error";
+ return -1;
+}
+
+int lex_t::syntax_error(YYSTYPE *yylval) {
+ if (ferror(file))
+ return io_error(yylval);
+
+ yylval->error = "syntax error";
+ return -1;
+}
+
+int lex_t::consume_comment(YYSTYPE *yylval, YYLTYPE *yylloc) {
+ char prev = 0;
+
+ while (next(yylloc, true)) {
+ if (prev == '*' && current() == '/') {
+ next(yylloc, true);
+ consume(false);
+ return 0;
+ }
+
+ prev = current();
+ }
+
+ if (ferror(file))
+ return io_error(yylval);
+
+ yylval->error = "unterminated block comment";
+ return -1;
+}
+
+int lex_t::unterminated_string(YYSTYPE *yylval) {
+ if (ferror(file))
+ return io_error(yylval);
+
+ yylval->error = "unterminated string";
+ return -1;
+}
+
+int lex_t::parse_string(YYSTYPE *yylval, YYLTYPE *yylloc) {
+ char *buf = NULL;
+ size_t len = 1024;
+ size_t pos = 0;
+
+ if (needspace)
+ return syntax_error(yylval);
+
+ buf = static_cast<char*>(std::malloc(len));
+
+ while (true) {
+ if (!next(yylloc, true)) {
+ free(buf);
+ return unterminated_string(yylval);
+ }
+
+ char cur = current();
+
+ if (cur == '"')
+ break;
+
+ if (cur == '\\') {
+ if (!next(yylloc, true)) {
+ free(buf);
+ return unterminated_string(yylval);
+ }
+
+ cur = current();
+
+ if (cur == '\n')
+ continue;
+ }
+
+ if (pos >= len) {
+ len *= 2;
+ buf = static_cast<char*>(std::realloc(buf, len));
+ }
+
+ buf[pos++] = cur;
+ }
+
+ yylval->str = strndup(buf, pos);
+ std::free(buf);
+
+ next(yylloc, true);
+ consume(true);
+
+ return TOK_STRING;
+}
+
+int lex_t::parse_address(YYSTYPE *yylval, YYLTYPE *yylloc) {
+ if (needspace)
+ return syntax_error(yylval);
+
+ while (next(yylloc, false)) {
+ char cur = current();
+
+ if (!((cur >= '0' && cur <= '9') || (cur >= 'a' && cur <= 'f') || (cur >= 'A' && cur <= 'F') || (cur == ':')))
+ break;
+ }
+
+ char *token = get_token();
+ int len;
+ bool ok = (std::strlen(token) == 23) &&
+ (std::sscanf(token, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx%n",
+ &yylval->addr.d[0], &yylval->addr.d[1], &yylval->addr.d[2], &yylval->addr.d[3],
+ &yylval->addr.d[4], &yylval->addr.d[5], &yylval->addr.d[6], &yylval->addr.d[7],
+ &len) >= 8) &&
+ (len == 23);
+
+ std::free(token);
+
+ if (!ok)
+ return syntax_error(yylval);
+
+ consume(true);
+
+ return TOK_GMRF_ADDRESS;
+}
+
+int lex_t::parse_float(YYSTYPE *yylval, YYLTYPE *yylloc) {
+ while (next(yylloc, false)) {
+ char cur = current();
+
+ if (!(cur >= '0' && cur <= '9'))
+ break;
+ }
+
+ char *endptr, *token = get_token();
+ yylval->fnum = std::strtof(token, &endptr);
+
+ bool ok = !*endptr;
+ free(token);
+
+ if (!ok)
+ return syntax_error(yylval);
+
+ consume(true);
+
+ return TOK_FLOAT;
+}
+
+int lex_t::parse_number(YYSTYPE *yylval, YYLTYPE *yylloc) {
+ bool digitonly = true;
+
+ if (needspace)
+ return syntax_error(yylval);
+
+ while (next(yylloc, false)) {
+ char cur = current();
+
+ if (cur == ':')
+ return parse_address(yylval, yylloc);
+
+ if (cur == '.' && digitonly)
+ return parse_float(yylval, yylloc);
+
+ if (!(cur >= '0' && cur <= '9')) {
+ if (cur == 'x' || (cur >= 'a' && cur <= 'f') || (cur >= 'A' && cur <= 'F'))
+ digitonly = false;
+ else
+ break;
+ }
+ }
+
+ char *endptr, *token = get_token();
+ yylval->uint64 = std::strtoull(token, &endptr, 0);
+
+ bool ok = !*endptr;
+ free(token);
+
+ if (!ok)
+ return syntax_error(yylval);
+
+ consume(true);
+
+ return TOK_UINT;
+}
+
+int lex_t::parse_keyword(YYSTYPE *yylval, YYLTYPE *yylloc) {
+ if (needspace)
+ return syntax_error(yylval);
+
+ while (next(yylloc, false)) {
+ char cur = current();
+
+ if (!((cur >= 'a' && cur <= 'z') || (cur >= '0' && cur <= '9') || cur == '-'))
+ break;
+ }
+
+ char *token = get_token();
+ const keyword_t key = { .keyword = token };
+ const keyword_t *ret = static_cast<const keyword_t*>(bsearch(&key, keywords, array_size(keywords), sizeof(keyword_t), compare_keywords));
+ free(token);
+
+ if (!ret)
+ return syntax_error(yylval);
+
+ consume(true);
+
+ return ret->token;
+}
+
+lex_t::lex_t(FILE *file0) : file(file0), needspace(false), start(0), end(0), tok_len(0) {
+ advance();
+}
+
+int lex_t::lex(YYSTYPE *yylval, YYLTYPE *yylloc) {
+ int token;
+
+ while (end > start) {
+ yylloc->first_line = yylloc->last_line;
+ yylloc->first_column = yylloc->last_column+1;
+
+ switch (current()) {
+ case ' ':
+ case '\n':
+ case '\t':
+ case '\r':
+ next(yylloc, true);
+ consume(false);
+ continue;
+
+ case ';':
+ case ':':
+ case '{':
+ case '}':
+ token = current();
+ next(yylloc, true);
+ consume(false);
+ return token;
+
+ case '/':
+ if (!next(yylloc, true))
+ return syntax_error(yylval);
+
+ if (current() == '*') {
+ token = consume_comment(yylval, yylloc);
+ if (token)
+ return token;
+
+ continue;
+ }
+
+ if (current() != '/')
+ return syntax_error(yylval);
+
+ /* fall-through */
+ case '#':
+ while (next(yylloc, true)) {
+ if (current() == '\n')
+ break;
+ }
+
+ next(yylloc, true);
+ consume(false);
+ continue;
+
+ case '"':
+ return parse_string(yylval, yylloc);
+
+ case '0' ... '9':
+ return parse_number(yylval, yylloc);
+
+ case 'a' ... 'z':
+ return parse_keyword(yylval, yylloc);
+
+ default:
+ return syntax_error(yylval);
+ }
+ }
+
+ if (ferror(file))
+ return io_error(yylval);
+
+ return 0;
+}
+
+}