/* * * The contents of this file are subject to the Initial * Developer's Public License Version 1.0 (the "License"); * you may not use this file except in compliance with the * License. You may obtain a copy of the License at * http://www.ibphoenix.com/idpl.html. * * Software distributed under the License is distributed on * an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either * express or implied. See the License for the specific * language governing rights and limitations under the License. * * The contents of this file or any work derived from this file * may not be distributed under any other license whatsoever * without the express prior written permission of the original * author. * * * The Original Code was created by James A. Starkey for IBPhoenix. * * Copyright (c) 1997 - 2000, 2001, 2003 James A. Starkey * Copyright (c) 1997 - 2000, 2001, 2003 Netfrastructure, Inc. * All Rights Reserved. */ // Lex.cpp: implementation of the Lex class. // ////////////////////////////////////////////////////////////////////// #include #include #include #include "firebird.h" #include "Lex.h" #include "AdminException.h" #include "InputStream.h" #define WHITE_SPACE " \t\n\r" #define PUNCTUATION_CHARS "<>=" #define MULTI_CHARS "" //"+=*/%!~<>~^|&=" #define MAX_TOKEN 1024 #define UPCASE(c) ((c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c) #define WHITE 1 #define PUNCT 2 #define MULTI_CHAR 4 #define DIGIT 8 #define TERM (WHITE | PUNCT) ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// Lex::Lex(const char *punctuation, int debugFlags) { lineComment = NULL; commentStart = NULL; memset (charTable, 0, sizeof (charTable)); setCharacters (PUNCT, punctuation); setCharacters (WHITE, WHITE_SPACE); setCharacters (DIGIT, "0123456789"); ptr = end = NULL; inputStream = NULL; tokenType = NONE; lineNumber = 0; continuationChar = 0; captureStart = captureEnd = 0; flags = debugFlags; } Lex::~Lex() { if (inputStream) inputStream->release(); } void Lex::skipWhite() { for (;;) { while (ptr >= end) if (!getSegment()) return; while (ptr < end) if (lineComment && lineComment [0] == *ptr && match (lineComment, ptr)) { while (ptr < end && *ptr++ != '\n') ; ++inputStream->lineNumber; } else if (commentStart && commentStart [0] == *ptr && match (commentStart, ptr)) { ptr += strlen (commentStart); while (ptr < end) if (commentEnd [0] == *ptr && match (commentEnd, ptr)) { ptr += strlen (commentEnd); break; } else if (*ptr++ == '\n') ++inputStream->lineNumber; } else if (*ptr == continuationChar && ptr [1] == '\n') { ptr += 2; ++inputStream->lineNumber; } else if (charTable [*ptr] & WHITE) { if (*ptr++ == '\n') { eol = true; ++inputStream->lineNumber; } } else return; } } // Just another custom memcmp-like routine. bool Lex::match(const char *pattern, const char *string) { while (*pattern && *string) if (*pattern++ != *string++) return false; return *pattern == 0; } void Lex::getToken() { priorInputStream = tokenInputStream; priorLineNumber = tokenLineNumber; if (tokenType == END_OF_STREAM) throw AdminException ("expected token, got end-of-file"); eol = false; skipWhite(); if (tokenInputStream = inputStream) tokenLineNumber = inputStream->lineNumber; if (ptr >= end) { tokenType = END_OF_STREAM; strcpy (token, "-end-of-file-"); return; } tokenOffset = inputStream->getOffset (ptr); char *p = token; char *endToken = token + sizeof (token); char c = *p++ = *ptr++; if (charTable [c] & PUNCT) tokenType = PUNCT; else if (c == '\'' || c == '"') { p = token; for (;;) { if (ptr >= end) { if (!getSegment()) throw AdminException ("end of file in quoted string"); } else if (*ptr == c) break; else { if (p >= endToken) throw AdminException ("token overflow in quoted string"); *p++ = *ptr++; } } ++ptr; tokenType = (c == '"') ? QUOTED_STRING : SINGLE_QUOTED_STRING; } else if (charTable [c] & DIGIT) { tokenType = NUMBER; while (ptr < end && (charTable [*ptr] & DIGIT)) *p++ = *ptr++; } else { tokenType = NAME; if (flags & LEX_upcase) { p [-1] = UPCASE(c); while (ptr < end && !(charTable [*ptr] & (WHITE | PUNCT))) { c = *ptr++; *p++ = UPCASE(c); } } else while (ptr < end && !(charTable [*ptr] & (WHITE | PUNCT))) *p++ = *ptr++; } *p = 0; } void Lex::setCharacters(int type, const char *characters) { for (const char *p = characters; *p; ++p) charTable [*p] |= type; } /*** void Lex::openFile(const char *fileName) { inputStream = new InputFile (fileName, inputStream); } ***/ void Lex::setLineComment(const char *string) { lineComment = string; } void Lex::setCommentString(const char *start, const char *cend) { commentStart = start; commentEnd = cend; } bool Lex::isKeyword(const char *word) const { return strcmp (token, word) == 0; } bool Lex::match(const char *word) { if (!isKeyword (word)) return false; if (*word == captureStart) captureStuff(); getToken(); return true; } JString Lex::reparseFilename() { char *p = token; while (*p) ++p; while (ptr < end && *ptr != '>' && !(charTable [*ptr] & WHITE)) *p++ = *ptr++; *p = 0; JString string = token; //getToken(); return string; } JString Lex::getName() { if (tokenType != NAME) syntaxError ("name"); JString name = token; getToken(); return name; } void Lex::syntaxError(const char *expected) { AdminException exception ("expected %s, got \"%s\"", expected, token); if (tokenInputStream) exception.setLocation (tokenInputStream->getFileName(), tokenLineNumber); throw exception; } /*** InputFile* Lex::pushFile(const char *fileName) { if (inputStream) inputStream->ptr = ptr; InputFile *inputFile = new InputFile (fileName, inputStream); inputStream = inputFile; ptr = end = NULL; tokenType = NONE; return inputFile; } ***/ void Lex::setContinuationChar(char c) { continuationChar = c; } void Lex::pushStream(InputStream *stream) { stream->addRef(); if (flags & LEX_trace) { const char *fileName = stream->getFileName(); if (fileName) printf ("Opening %s\n", fileName); } if (inputStream) inputStream->ptr = ptr; stream->prior = inputStream; inputStream = stream; ptr = end = NULL; tokenType = NONE; } bool Lex::getSegment() { if (!inputStream) { tokenType = END_OF_STREAM; eol = true; return false; } if (!(ptr = inputStream->getSegment())) { end = ptr; InputStream *prior = inputStream->prior; inputStream->close(); inputStream->release(); if (!(inputStream = prior)) return false; ptr = inputStream->ptr; } end = (ptr) ? inputStream->getEnd() : NULL; if (end && (flags & LEX_list)) printf (" %s", ptr); return true; } void Lex::captureStuff() { stuff.clear(); for (;;) { if (ptr >= end) { if (!getSegment()) return; continue; } if (*ptr == captureEnd) return; stuff.putCharacter (*ptr++); } }