Скачиваний:
11
Добавлен:
01.05.2014
Размер:
12.79 Кб
Скачать

//   Реализация класса 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