Добавил:
Studfiles2
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Курсовая работа2 / LexAn
.cpp
// Реализация класса CLexAn - синтаксического анализатора входного языка
/********************************************************************************************/
#include "StdAfx.h"
#include "LexAn.h"
#include "ErrorHandler.h"
/********************************************************************************************/
// Проверяем: является ли символ одиночным символьным разделителем (коих всего 11: знаки
// операций, круглые и квадратные скобки, точка с запятой, запятая и равно). Не самая
// подходящая работа для метода класса, ну да ладно...
// Ну как-нибудь так... Вот все наши разделители
const int DelimsCount = 11;
char CLexAn::Delims[DelimsCount] = {'+', '-', '*', '/', '[', ']', '(', ')', ';', ',', '='};
bool CLexAn::IsDelimiter( char Ch )
{
// Пытаемся найти соответствие линейным поиском
for (int I = 0; I < DelimsCount; I++) if (Delims[I] == Ch) return true;
// Ни одного соответствия - возвращаем false
return false;
}
/********************************************************************************************/
// Всего пара операций: поместить текущий символ в буффер и сдвинуть на единицу указатель
// текущего символа. Очень полезно при накапливании строк в буффере...
void CLexAn::ToBuffer()
{
m_Buff += *m_Iter;
m_Iter++;
}
/********************************************************************************************/
// "Сливает" буфер в текущем состоянии во заданную строку, завершая его содержимое концом
// строки. Предположительно, это гарантирует передачу в выходную строку ровно одной
// лекскммы. После "слива" буфер очищается...
void CLexAn::FlushBuffer( std::string &Target, ENUM_TOKEN_TYPES tot )
{
// Добавление буфера к выходной строке. Проверки на пустоту нет.
Target += m_Buff;
Target += "\r\n";
// Добавление нового токена
g_TableMgr.AddToken(tot, m_Buff, m_LineNumber);
// Очищение содержимого буффера
m_Buff = "";
// А ещё здесь можно проанализировать буффер ошибочной последовательности m_ErrBuf
LookForErrors();
}
/********************************************************************************************/
// Читает из заданного файла комментарий. Комментарий не заносится в буфер и не включается
// в выходную последовательность. Единственное назначение процедуры - перевести глобальный
// итератор текущего символа на символ, следующий за комментарием...
void CLexAn::ExtractComment( std::string &Rcv )
{
// нам надо либо найти символ конца комментария, либо упереться в Src.end();
while ( (m_Iter != m_Src.end()) && (*m_Iter != '}') ) m_Iter++;
// в случае всё-таки конца комментария, поставить итератор на след. символ
if (m_Iter != m_Src.end()) m_Iter++;
}
/********************************************************************************************/
// Читает из текста программы строку, пока не встретит ещё одну кавычку, либо не упрётся
// в конец текста программы... Результирующая строка накапливается в буффере и сливается
// по факту обнаружения её конца...
void CLexAn::ExtractString( std::string &Rcv )
{
// Текущая открывающая кавычка нам не нужна
m_Iter++;
// Читаем строку, пока не обнаружим кавычку, либо конец текста
while ( (m_Iter != m_Src.end()) && (*m_Iter != '\'') && (*m_Iter != '\n') ) ToBuffer();
if (*m_Iter == '\n')
{
g_ErrorHandler.Error(CError(ERR_UNTERM_STRING, m_LineNumber));
}
// Закрывающая кавычка нам тоже не нужна
if (m_Iter != m_Src.end()) m_Iter++;
// сливаем аккумулированную строку в результир. текст
if (m_Buff.length() != 1)
FlushBuffer( Rcv, TT_STRING );
else
FlushBuffer( Rcv, TT_CHAR );
}
/********************************************************************************************/
// Читаем из текста программы идентификатор, либо ключевое слово. В любом случае, читаем
// лексемму, состоящую из букв и цифр. Корректно отрабатываем конец файла. По результатам
// чтения лексемма отправляется в выходную последовательность... Чуть не забыл: идентификатор
// может содержать и начинаться с подчёркивания...
void CLexAn::ExtractIdentifier( std::string &Rcv )
{
// Текущая буква отправляется в буффер
ToBuffer();
// Читаем буквы и цифры, пока можем
while ( (m_Iter != m_Src.end()) && ((isalpha(*m_Iter) || isdigit(*m_Iter) || ('_' == *m_Iter))) )
ToBuffer();
// Неазвисимо от причины остановки, заносим лесемму в результат
FlushBuffer( Rcv, TT_ID );
}
/********************************************************************************************/
// Встретив в начале лексеммы цифру, мы однозначно понимаем, что читать придётся число. Кстати,
// именно поэтому идентификатор не может начинаться с цифры. Число бывает двух сортов: целое
// (DDD) и дробное (DDD.DDD). Никакого инженерного формата реализовывать желания нет!
void CLexAn::ExtractNumber( std::string &Rcv )
{
ENUM_TOKEN_TYPES TokenType = TT_INT;
// Текущую цифру положено занести в буффер
ToBuffer();
// Читаем цифры, пока они - цифры
while ( (m_Iter != m_Src.end()) && isdigit(*m_Iter) ) ToBuffer();
// Если встретили точку, то число надо ещё почитать...
// Если точка одна, то это вещественно число,
// а вот если их две подряд, то это нижняя граница диапазона.
if ( ((m_Src.end() != m_Iter ) && ('.' == *m_Iter)) &&
((m_Src.end() == (m_Iter + 1)) || ('.' != *(m_Iter + 1))) )
{
TokenType = TT_REAL;
// Точку заносим в буффер
ToBuffer();
// Читаем дробную часть. Её, кстати, может и не быть
while ( (m_Iter != m_Src.end()) && isdigit(*m_Iter) ) ToBuffer();
}
// для извращенцев типа меня, кто использует инженерный (научный) формат
if ( (m_Iter != m_Src.end()) && (('e' == *m_Iter) || ('E' == *m_Iter)) )
{
TokenType = TT_REAL;
ToBuffer();
if ((m_Iter != m_Src.end()) && (('-' == *m_Iter) || ('+' == *m_Iter)))
ToBuffer();
while ( (m_Iter != m_Src.end()) && isdigit(*m_Iter) ) ToBuffer();
}
FlushBuffer( Rcv , TokenType );
}
/********************************************************************************************/
// Извлекаем в качестве лексеммы символьный разделитель. Премудрость, в общем-то говоря,
// небольшая - заносим его в буффер и сразу сливаем
void CLexAn::ExtractDelimiter( std::string &Rcv )
{
// Заносим наш разделитель в буфер
ToBuffer();
// И, как заявлено, сливаем
FlushBuffer( Rcv , TT_SPEC_SYMB );
}
/********************************************************************************************/
// Colon (англ.) - двоеточие. Может являться как самостоятельной лексеммой, так и входить
// в состав присваивания (:=)...
void CLexAn::ExtractColon( std::string &Rcv )
{
// Само двоеточие заносится в буффер
ToBuffer();
// Если след. символ - равно (и текст ещё есть), то и его - в буффер
if ( (m_Iter != m_Src.end()) && ('=' == *m_Iter) ) ToBuffer();
// Итоговая лексемма сливается в результат
FlushBuffer( Rcv, TT_SPEC_SYMB );
}
/********************************************************************************************/
// Period (или Fullstop) - это точка как знак препинания (а никакая не dot). Точка может
// стоять одна (в конце файла) или с последующей точкой для обозначения диапазона значений
void CLexAn::ExtractPeriod( std::string &Rcv )
{
// Сама точка заносится в буффер
ToBuffer();
// Если след. символ - точка (и текст ещё есть), то и её - в буффер
if ( (m_Iter != m_Src.end()) && ('.' == *m_Iter) ) ToBuffer();
// Итоговая лексемма сливается в результат
FlushBuffer( Rcv, TT_SPEC_SYMB );
}
/********************************************************************************************/
// Больше... С этого значка может начинаться как "больше", так и "больше или равно".
void CLexAn::ExtractGreater( std::string &Rcv )
{
// Сам символ заносится в буффер
ToBuffer();
// Если след. символ - равно (и текст ещё есть), то и его - в буффер
if ( (m_Iter != m_Src.end()) && ('=' == *m_Iter) ) ToBuffer();
// Итоговая лексемма сливается в результат
FlushBuffer( Rcv, TT_SPEC_SYMB );
}
/********************************************************************************************/
// Меньше... С этого значка могут начинаться лексеммы "меньше", "меньше или равно" и "не равно"
void CLexAn::ExtractLess( std::string &Rcv )
{
// Сам символ заносится в буффер
ToBuffer();
// Если след. символ - равно или больше (и текст ещё есть), то и его - в буффер
if ( (m_Iter != m_Src.end()) && (('=' == *m_Iter) || ('>' == *m_Iter)) ) ToBuffer();
// Итоговая лексемма сливается в результат
FlushBuffer( Rcv, TT_SPEC_SYMB );
}
/********************************************************************************************/
// Определение границ лексемм во входном тексте. Вход: inText - текст программы на нашем
// "недоПаскале". Выход: outText - тест программы, разделённый на строки, каждая из которых
// содержит ровно одну лексемму...
void CLexAn::ProcessText( std::string inText , std::string &outText )
{
// Подготавливаем буффер и итератор текущего символа
m_Buff = "";
m_ErrBuf = "";
m_Src = inText;
m_Iter = m_Src.begin();
m_LineNumber = 1;
g_TableMgr.Clear();
// Читаем содержимое входной строки посимвольно
while ( m_Iter != m_Src.end() )
{
// Возможно, придётся читать комментарий
if ( '{' == *m_Iter )
{
ExtractComment( outText );
continue;
};
// Если не комментарий, тогда, может быть, строку
if ( '\'' == *m_Iter )
{
ExtractString( outText );
continue;
};
// А если не строку, то, наверное, идентификатор
if ( isalpha(*m_Iter) || ('_' == *m_Iter) )
{
ExtractIdentifier( outText );
continue;
};
// А если не идентификатор, то, пожалуй, число
if ( isdigit(*m_Iter) )
{
ExtractNumber( outText );
continue;
};
// А если не число, то, наверняка, символьный разделитель
if ( IsDelimiter(*m_Iter) )
{
ExtractDelimiter( outText );
continue;
};
// Теперь начинается самое скучное - двусимвольные разделители
if ( ':' == *m_Iter )
{
ExtractColon( outText );
continue;
}
// Этот кусок кода посвящён точкам и многоточиям
if ( '.' == *m_Iter )
{
ExtractPeriod( outText );
continue;
}
// Здесь обрабатывается символ "больше"
if ( '>' == *m_Iter )
{
ExtractGreater( outText );
continue;
}
// Последним в программе выступает символ меньше
if ( '<' == *m_Iter )
{
ExtractLess( outText );
continue;
}
// Если встретили перевод конец строки
if ( '\n' == *m_Iter )
{
LookForErrors(); // Ошибки надо посмотреть до перевода строки
m_LineNumber++;
m_Iter++;
continue;
}
// Если ничего не подошло - крутим шарманку
// Точнее если это не пробел и не табуляция, то это что-то левое...
if (( ' ' != *m_Iter ) && ( '\t' != *m_Iter) && ( '\r' != *m_Iter) )
{
// Еггог... :)
// И вот тут надо построить максимальную последовательность этого левого... хм...
m_ErrBuf += *m_Iter;
}
m_Iter++;
}
g_TableMgr.AddToken(TT_EPS, "", m_LineNumber);
}
//-----------------------------------------------------------------------------
void CLexAn::LookForErrors()
{
if (m_ErrBuf.length() > 0)
{
// Выдаём сообщение об ошибке
// Лексическний анализатор может распознать только один тип ошибок - неизвестные символы
g_ErrorHandler.Error( CError(ERR_UNKNOWN_SYMBOL, m_LineNumber, m_ErrBuf) );
m_ErrBuf = ""; // почему-то тут в std::string нет метода clear...
}
}
/********************************************************************************************/
Соседние файлы в папке Курсовая работа2