Lexeme Lexal::readNumber(int startLine, int startPos, wchar_t first) { std::wstring number; number += first; Lexeme::Type type = Lexeme::Integer; while (! reader.isEof()) { wchar_t ch = reader.getNextChar(); pos++; if (isDigit(ch)) number += ch; else if (L'.' == ch) { if (Lexeme::Integer == type) { type = Lexeme::Float; number += ch; } else throw Exception(L"To many dots in number at " + posToStr(line, pos)); } else if ((! isSymbol(ch)) && (! isWhiteSpace(ch))) throw Exception(L"invalid number at " + posToStr(line, pos)); else { pos--; reader.ungetChar(ch); break; } } if (L'.' == number[number.length() - 1]) throw Exception(L"Missing digit after dot at " + posToStr(line, pos)); return Lexeme(type, number, startLine, startPos); }
/* Теперь сама функция для лексического анализа, выделяющая из входного потока символов очередную лексему. Она предполагает, что при ее вызове текущий символ - первый символ лексемы, после ее завершения текущий символ - следующий за лексемой. Возвращает она как раз найденную лексему. А это регулярная грамматика, на основе которой производится разбор, здесь \w обозначает букву, а \s пробельный символ (по аналогии с регулярными выражениями): S -> \w W | [()] D | ! | ) | ( | $ | \s S W -> \w W | eps D -> & Первая альтернатива в S - выделение констант (из алфавитных символов), вторая - двусимвольного разделителя (&&), далее - разделители (отрицание, скобки) и конец ввода. Последняя альтернатива в S - это правило для пропуска пробелов. W выделяет константы (цепочки букв), а D - двухсимвольный разделитель (&&). */ Lexeme readNextLexeme() { enum State { S, W, D }; // Итак, у нас есть три состояния - дополнительных здесь объявлять не будем State currentState = S; // Переменная для состояния, начальное состояние - S std::string buf; // Это строка-буфер, в котором будут накапливаться символы, образующие лексему while (true) { // Цикл обработки switch (currentState) { // В зависимости от текущего состояния case S: // Если мы в начальном состоянии // Если у нас здесь пробельный символ, то мы его просто пропускаем if ( currentChar == ' ' ) { gc(); // То есть считываем следующий currentState = S; // И остаемся в том же состоянии } else if ( isalpha( currentChar ) ) { // Если текущий символ - буква buf += currentChar; // Тогда добавляем ее в буфер-накопитель лексемы gc(); // Считываем следующий символ currentState = W; // И переходим в состояние W - для констант } else if ( currentChar == '&' ) { // Если символ - & - то есть часть операции "И" buf += currentChar; // Добавляем его в строку-буфер gc(); // Считываем следующий символ currentState = D; // И переходим в состояние D } else if ( currentChar == '!' || currentChar == '(' || currentChar == ')' ) { // Если же это односимвольный разделитель buf += currentChar; // То добавляем его в строку-буфер gc(); // Считываем следующий символ int index = find( buf, LEX_DELIMS ); // Находим этот разделитель в таблице return Lexeme( LEX_DELIM, index, buf ); // И возвращаем соответствующую лексему } else if ( currentChar == '\n' || currentChar == '\r' ) { // Если символ - конец ввода return Lexeme( LEX_EOF, LEX_NULL, "$" ); // То возвращаем лексему конца ввода } else { // Иначе - какой-то непонятный символ buf += currentChar; // Добавляем его в строку-буфер return Lexeme( LEX_NULL, LEX_NULL, buf ); // И возвращаем ошибочную лексему } break; case W: // Состояние распознавания констант if ( isalpha( currentChar ) ) { // Если символ - буква buf += currentChar; // То добавляем его в строку-буфер gc(); // Считываем следующий currentState = W; // И остаемся в том же состоянии } else { // Другой символ - значит константа уже закончилась int index = find( buf, LEX_CONSTS ); // Находим ее в таблице return Lexeme( LEX_CONST, index, buf ); // Просто возвращаем соответствующую константе лексему } break; case D: // Состояние распознавания двухсимвольного разделителя if ( currentChar == '&' ) { // Он у нас один - &&, и если это он gc(); // То, считываем следующий символ return Lexeme( LEX_DELIM, LEX_DEL_AND, "&&" ); // И возвращаем соответствующую лексему } else { // Иначе buf += currentChar; // Добавляем символ в строку-буфер return Lexeme( LEX_NULL, LEX_NULL, buf ); // И возвращаем ошибочную лексему } break; }; } }
Lexeme Lexal::getNext() { skipSpaces(); if (reader.isEof()) return Lexeme(Lexeme::Eof, L"", line, pos); int startLine = line; int startPos = pos; wchar_t ch = reader.getNextChar(); pos++; if (isIdentStart(ch)) return readIdent(startLine, startPos, ch); else if (isDigit(ch)) return readNumber(startLine, startPos, ch); else if (isQuote(ch)) return readString(startLine, startPos, ch); else if (isSymbol(ch)) return Lexeme(Lexeme::Symbol, toString(ch), startLine, startPos); throw Exception(L"Invalid character at "+ posToStr(startLine, startPos)); }
Lexeme Lexal::readIdent(int startLine, int startPos, wchar_t first) { std::wstring ident; ident += first; while (! reader.isEof()) { wchar_t ch = reader.getNextChar(); if (! isIdentCont(ch)) { reader.ungetChar(ch); break; } ident += ch; pos++; } return Lexeme(Lexeme::Ident, ident, startLine, startPos); }
Lexeme Lexal::readString(int startLine, int startPos, wchar_t quote) { std::wstring str; bool closed = false; while (! reader.isEof()) { wchar_t ch = reader.getNextChar(); pos++; if ('\n' == ch) { line++; pos = 0; str += ch; } else if ('\\' == ch) { if (! reader.isEof()) { wchar_t nextCh = reader.getNextChar(); if (isWhiteSpace(nextCh)) throw Exception(L"Invalid escape sequence at " + posToStr(line, pos)); pos++; switch (nextCh) { case L'\t': str += L'\t'; break; case L'\n': str += L'\n'; break; case L'\r': str += L'\r'; break; default: str += nextCh; } } } else if (quote == ch) { closed = true; break; } else str += ch; } if (! closed) throw Exception(L"String at " + posToStr(startLine, startPos) + L" doesn't closed"); return Lexeme(Lexeme::String, str, startLine, startPos); }
//************************************************************************************ Lexeme CBCGPOutlineParser::GetNext (const CString& strIn, int& nOffset, const int nSearchTo) { while (nOffset >= 0 && nOffset < strIn.GetLength () && nOffset <= nSearchTo) { if (IsEscapeSequence (strIn, nOffset)) { continue; } for (int i = 0; i <= m_arrBlockTypes.GetUpperBound (); i++) { BlockType* pBlockType = m_arrBlockTypes [i]; ASSERT (pBlockType != NULL); // Nested blocks: if (pBlockType->m_bAllowNestedBlocks) { int nEndOffset; if (Compare (strIn, nOffset, pBlockType->m_strOpen, nEndOffset)) { Lexeme lexem (i, LT_BlockStart, nOffset, nEndOffset); nOffset += pBlockType->m_strOpen.GetLength (); return lexem; } else if (Compare (strIn, nOffset, pBlockType->m_strClose, nEndOffset)) { Lexeme lexem (i, LT_BlockEnd, nOffset, nEndOffset); nOffset += pBlockType->m_strClose.GetLength (); return lexem; } } // NonNested blocks: else { int nEndOffset; if (Compare (strIn, nOffset, pBlockType->m_strOpen, nEndOffset)) { Lexeme lexem (i, LT_CompleteBlock, nOffset, nEndOffset); nOffset += pBlockType->m_strOpen.GetLength (); if (!pBlockType->m_strClose.IsEmpty ()) { // find close token skipping escape sequences: while (nOffset >= 0 && nOffset < strIn.GetLength () && nOffset <= nSearchTo) { if (IsEscapeSequence (strIn, nOffset)) { continue; } if (Compare (strIn, nOffset, pBlockType->m_strClose, nEndOffset)) { nOffset += pBlockType->m_strClose.GetLength (); if (pBlockType->m_strClose == _T("\n")) { nOffset--; } lexem.m_nEnd = nOffset - 1; return lexem; } nOffset++; } } if (pBlockType->m_bIgnore) { nOffset = lexem.m_nStart; } else { lexem.m_nEnd = strIn.GetLength () - 1; return lexem; } } } } nOffset++; } return Lexeme (0, LT_EndOfText, 0, 0); }
Lexeme Scanner::get_lex() { int number, j; current_state = START; type_of_lex tmp; do { switch(current_state) { case START: if(next == ' ' || next == '\n' || next == '\t') { if(next == '\n') line++; get_next(); } else if(isalpha(next)) { current_state = IDENT; clear_buffer(); add_next_to_buffer(); get_next(); } else if(isdigit(next)) { current_state = NUMB; number = next - '0'; get_next(); } else if(next == '\'') { current_state = STRI1; clear_buffer(); get_next(); } else if(next == '\"') { current_state = STRI2; clear_buffer(); get_next(); } else if(next == '>' || next == '<' || next == '=' || next == '&' || next == '|' || next == '!' || next == '/' || next == '-' || next == '+') { current_state = DELIM2; clear_buffer(); add_next_to_buffer(); get_next(); } else if(next == -1) { return Lexeme(LEX_FIN); } else { current_state = DELIM; clear_buffer(); add_next_to_buffer(); } break; case IDENT: if(isalpha(next) || isdigit(next)) { add_next_to_buffer(); get_next(); } else if(MJSWords.find(scan_buffer) != MJSWords.end()) { tmp = MJSWords[scan_buffer]; if(tmp == LEX_TRUE) { return Lexeme(LEX_BOOL, true); } else if(tmp == LEX_FALSE) { return Lexeme(LEX_BOOL, false); } else { return Lexeme(tmp); } } else if(find(MJSFields.begin(), MJSFields.end(), scan_buffer) != MJSFields.end()) { return Lexeme(LEX_FIELDS, string(scan_buffer)); } else if(find(MJSMethods.begin(), MJSMethods.end(), scan_buffer) != MJSMethods.end()) { return Lexeme(LEX_METHODS, string(scan_buffer)); } else { return Lexeme(LEX_ID, TOI.put(scan_buffer)); } break; case NUMB: if(isdigit(next)) { number = number * 10 + (next - '0'); get_next(); } else { return Lexeme(LEX_NUM, number); } break; case STRI1: if(next == '\'') { get_next(); return Lexeme(LEX_STR, string(scan_buffer)); } else if(next == -1) { throw lexical_exception(next, line, "Unexpected EOF in LEX_STR"); } else if(next == '\\') { get_next(); if(next == 'n') add_to_buffer('\n'); get_next(); } else { add_next_to_buffer(); get_next(); } break; case STRI2: if(next == '\"') { get_next(); return Lexeme(LEX_STR, string(scan_buffer)); } else if(next == -1) { throw lexical_exception(next, line, "Unexpected EOF in LEX_STR"); } else if(next == '\\') { get_next(); if(next == 'n') { add_to_buffer('\n'); } else { add_next_to_buffer(); } get_next(); } else { add_next_to_buffer(); get_next(); } break; case DELIM: if(MJSDelims.find(scan_buffer) != MJSDelims.end()) { get_next(); return Lexeme(MJSDelims[scan_buffer]); } else { throw lexical_exception(next, line, "Forbidden symbol"); } break; case DELIM2: add_next_to_buffer(); if(scan_buffer[0] == '=' && next == '=') { current_state = DELIM3; get_next(); } else if(MJSDelims.find(scan_buffer) != MJSDelims.end()) { get_next(); if(MJSDelims[scan_buffer] == LEX_COMM) { current_state = COMM; } else { return Lexeme(MJSDelims[scan_buffer]); } } else { scan_buffer[1] = '\0'; if(MJSDelims.find(scan_buffer) != MJSDelims.end()) { return Lexeme(MJSDelims[scan_buffer]); } else { throw lexical_exception(next, line, "Forbidden symbol"); } } break; case DELIM3: if(next == '=') { get_next(); return Lexeme(LEX_IDENT); } else { return Lexeme(LEX_EQ); } break; case COMM: if(next == '\n' || next == -1) { line++; current_state = START; clear_buffer(); get_next(); } else { get_next(); } default: break; } } while(true); }