Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Программная реализация языкового процессора




В соответствии с разработанной грамматикой была разработана программная реализация языкового процессора, осуществляющая ввод и поддерживающая лексический и синтаксический анализ текста. При реализации был использован язык C++, графический интерфейс был реализован в среде RAD Studio XE.

Общий вид программы приведен на рис. 4.

Рис. 4. Общий вид программы

В окне «Исходный код» в построчном режиме отображается исходный код из загружаемых файлов. Оно также доступно для ввода кода с клавиатуры. В окне «Сообщения» отображается информация о ошибках ввода оператора scanf.

Раздел меню Файл содержит функции, связанные с загрузкой и сохранением файлов, расширения которых соответствуют языкам C/C++. Общий вид меню Файл представлен на рис. 5.

Рис. 5. Общий вид меню Файл

При выборе команды Файл > Создать происходит очистка окна «Исходный код». Его область становится доступна для ввода и загрузки.

При выборе команды Файл > Загрузить появляется диалоговое окно загрузки файла с выбором расширения *.c или *.cpp. Загруженный файл отображается в окне «Исходный код».

При выборе команды Файл > Сохранить появляется диалоговое окно сохранения файла с выбором расширения *.c или *.cpp. Выполняется сохранение файла в заданный файл.

Меню Редактирование содержит функции вырезания, копирования и вставки выделенных фрагментов текста в окно.

Запуск анализа происходит по команде из меню Запуск > Анализ кода. При наличии ошибок в тексте кода или вводе операторов, формат которых отличен от scanf, будут выводиться сообщения. Пример работы программы приведен на рис. 6.

Рис. 6. Общий вид меню Файл

Как видно из рис. 6, единственным верным вариантом является вторая строка. В остальных случаях выдается сообщение об ошибке.


 

Заключение

В работе выполнен анализ понятий языка из его грамматического представления. Дана классификация языковых грамматик, показаны взаимосвязи между ними. Сформулированы принципы лексического и синтаксического анализа. Разработана программа, описывающая синтаксический и лексический анализ оператора языка С scanf. Для этой цели была разработана грамматика, представленная в форме Бэкуса-Нара. Программная реализация, выполненная на языке C++ в среде RAD Studio XE, показала корректность разработанной грамматики для решения поставленной задачи

 


 

СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ

 

1. Ахо А., Сети Р., Лам М., Ульман Д. Компиляторы: принципы, технологии и инструментарий.: Пер. с англ. — М.: ООО «И. Д. Вильямс», 2008. — 1184 с.: ил.

2. Сычев О. А. Синтаксический анализ транслируемой программы: метод. указания / сост. Сычев О. А.; ВолгГТУ. – Волгоград, 2009. – 36 с.

3. Свердлов С.З. Введение в методы трансляции: Учебное пособие. - Вологда: Издательство "Русь", 1994. - 80 с.

4. Вирт Н. Построение компиляторов/ Пер. с англ. Борисов Е. В., Чернышов Л. Н. – М.: ДМК Пресс, 2010. – 192 с.

5. Д.Грис. Конструирование компиляторов для цифровых вычислительных машин. - М., Мир, 1975.

6. Ф.Льюис, Д.Розенкранц, Р.Стирнз. Теоретические основы проектирования компиляторов. - М., Мир, 1979.


 

ПРИЛОЖЕНИЕ

Main.h

//---------------------------------------------------------------------------

 

#ifndef mainH

#define mainH

//---------------------------------------------------------------------------

#include <Classes.hpp>

#include <Controls.hpp>

#include <StdCtrls.hpp>

#include <Forms.hpp>

#include <Menus.hpp>

#include "Parser.h"

#include <vector.h>

#include <Dialogs.hpp>

//---------------------------------------------------------------------------

class TForm1: public TForm

{

__published: // IDE-managed Components

TMemo *Memo1;

TMemo *Memo2;

TMainMenu *MainMenu1;

TMenuItem *N1;

TMenuItem *N2;

TMenuItem *N3;

TMenuItem *N4;

TMenuItem *N5;

TMenuItem *N6;

TMenuItem *N7;

TMenuItem *N8;

TMenuItem *N9;

TLabel *Label1;

TLabel *Label2;

TOpenDialog *LoadFiles;

TMenuItem *N10;

TSaveDialog *SaveFiles;

void __fastcall N9Click(TObject *Sender);

void __fastcall N6Click(TObject *Sender);

void __fastcall N7Click(TObject *Sender);

void __fastcall N5Click(TObject *Sender);

void __fastcall N2Click(TObject *Sender);

void __fastcall N3Click(TObject *Sender);

void __fastcall N10Click(TObject *Sender);

private: // User declarations

public: // User declarations

__fastcall TForm1(TComponent* Owner);

};

//---------------------------------------------------------------------------

extern PACKAGE TForm1 *Form1;

//---------------------------------------------------------------------------

#endif

 

Main.cpp

#include <vcl.h>

#pragma hdrstop

#include "windows.h"

#include "main.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)

#pragma resource "*.dfm"

TForm1 *Form1;

//---------------------------------------------------------------------------

__fastcall TForm1::TForm1(TComponent* Owner)

: TForm(Owner)

{

}

//---------------------------------------------------------------------------

void __fastcall TForm1::N9Click(TObject *Sender)

{

vector<string> errs;

try

{

//

Memo2->Clear();

//

for(int i=0;i<Memo1->Lines->Count;i++)

{

errs.clear();

//Длина строки кода

int size_=Memo1->Lines->Strings[i].Length();

//Берем текущую строку

wchar_t *s=Memo1->Lines->Strings[i].t_str();

char *s1=new char[size_+1];

//Преобразуем ее во внутреннее представление

for(int j=0;j<size_;j++)

s1[j]=s[j];

s1[size_]='\0';

//Парсим строку из компонента

parseScanf(s1,errs);

//Если есть ошибки

if(errs.size()!=0)

{

//Формируем из них отчет

TStringList* lst = new TStringList;

//Формируем отчет

lst->Add("Строка № "+IntToStr(i+1)+"\n");

//Добавляем строки из вектора в список

for(int j=0;j<errs.size();j++)

lst->Add(errs[j].c_str());

//Сводим все в единую строку

UnicodeString str=lst->Text;

//Добавляем ее в второе окно

Memo2->Lines->Add(str);

 

}

 

}

}

catch(Exception &ex)

{

//Вывод формы ошибки

Application->MessageBox((const wchar_t *)"Ошибка", (const wchar_t *)"Ошибка программы", MB_OK);

 

}

 

}

 

 

void __fastcall TForm1::N6Click(TObject *Sender)

{

//Копирование текста

Memo1->CopyToClipboard();

}

//---------------------------------------------------------------------------

 

void __fastcall TForm1::N7Click(TObject *Sender)

{

//Вставка текста из буфера

Memo1->PasteFromClipboard();

}

//---------------------------------------------------------------------------

 

void __fastcall TForm1::N5Click(TObject *Sender)

{

//Вырезание фрагмента в буфер

Memo1->CutToClipboard();

}

//---------------------------------------------------------------------------

 

 

//Загрузка из файла

 

void __fastcall TForm1::N2Click(TObject *Sender)

{

//Если диалог открыт и файл выбран

if(LoadFiles->Execute())

{

//Загрузка из файла на компонент

Memo1->Lines->LoadFromFile(LoadFiles->FileName);

}

}

//Сохранение в файл

 

void __fastcall TForm1::N3Click(TObject *Sender)

{

//Если диалог открыт и файл выбран

if(SaveFiles->Execute())

{

//Сохранение в файл из окна кода

Memo1->Lines->SaveToFile(SaveFiles->FileName);

}

}

//Создание файла

void __fastcall TForm1::N10Click(TObject *Sender)

{

//Очитска окна

Memo1->Clear();

}

//---------------------------------------------------------------------------

 

 

Parser.h

#ifndef PARSER_H

#define PARSER_H

#define _CRT_SECURE_NO_WARNINGS

 

#include <string.h>

#include <ctype.h>

#include <math.h>

#include <vector>

using namespace std;

//Переменные массива имен функций

const int FuncCount = 1; const int FuncMaxLen = 8;

//Переменные массива символов

const int SymCount = 4; const int SymsMaxLen = 2; // 6 для arcsin плюс 1 для '\0' const char

//Список имен функций

const char FuncNameList [FuncCount][FuncMaxLen+1] = {"scanf"};

//Массив управляющих символов

const char FormatSymbolslSet[SymCount][SymsMaxLen+1] = { "%d", "%c", "%n", "%f" };

 

// типы лексем

enum TLexType { LEX_ERROR, // ошибочная лексема

LEX_DELIM, // разделитель

LEX_VARIABLE, // переменная

LEX_CSYM, // управляющий символ

LEX_LINK, // переменная-ссылка

LEX_FUNCTION, // имя функции

LEX_LEFT, // левая скобка

LEX_RIGHT, // правая скобка ---

LEX_END // конец выражения ---

};

// хранение данных лексемы

struct TLexem {

//Тип лексемы

TLexType type;

//Имя фрагмента,отвечающего лексеме как часть кода

char *iData;

};

 

//Извлечение лексемы

TLexem GetLexem(const char*, unsigned &);

//Собираем чписок лексем,отвечающих строке

vector<TLexem >BuildLexems(const char *);

//Пропуск пробелов

void GetSpace(const char*, unsigned &);

//Синтаксический разбор возможного scanf

bool parseScanf(char *str, vector<string> &);

#endif

 

Parser.cpp

#include "Parser.h"

void GetSpace(const char* str, unsigned &i)

{

if (str!= 0)

while ((str[i] == ' ') || (str[i] == '\t') || (str[i] == '\n') || (str[i] == '\r')||(str[i] == ','))

i++;

}

 

int GetName(const char* source, unsigned &i, char* name, int CountChar) {

if (source == NULL) return 0;

int k = i; //Запоминаем начало имени

//Первый символ буква или символ подчеркивания, или % для управляющих символов

if (isalpha(source[i]) || (source[i] == '_') || (source[i] == '%') || (source[i] == '&'))

{

i++;

i--;

}

else

return 0;

//Остальные символы: буквы, цифры и символы подчеркивания

while (isalnum(source[i]) || (source[i] == '_') || (source[i] == '%') || (source[i] == '&'))

i++;

//Копируем в строку

if ((CountChar>0) && (name!=NULL))

{ int Len = ((int)i-k < CountChar-1)? i-k: CountChar-1;

// -1 для '\0'

strncpy(name,&source[k],Len);

name[Len] = '\0';

}

return i-k;

}

 

TLexem GetLexem(const char *source, unsigned &i) {

 

const int MAX_NAME_LEN = 64;

TLexem result = { LEX_ERROR, 0 };

GetSpace(source, i); //Пропуск пробелов

 

switch (source[i]) {

case '%':

{ //Собираем управляющий символ в str-lex

char str_lex[3];

str_lex[0] = source[i];

str_lex[1] = source[i+1];

str_lex[2] = '\0';

int k = 0;

//Цикл по управляющим символам

while (k<SymCount)

//Если строка отвечает одному из них

if (strcmp(FormatSymbolslSet[k], str_lex) == 0)

//Прерываем цикл

break;

else k++;

//Если нашли соответствие

if (k < SymCount) {

//Составляем лексему символа

result.type = LEX_CSYM;

result.iData = str_lex;

i = i + 2;

return result;

}

 

}

 

 

//Лексема: операция

case ',':

result.type = LEX_DELIM;

result.iData = (char *)source[i];

i++; return result;

//Лексема: левая скобка

case '(':

result.type = LEX_LEFT;

result.iData = (char *)source[i];

i++;

return result;

//Лексема: правая скобка

case ')':

result.type = LEX_RIGHT;

result.iData = (char *)source[i];

i++;

return result;

//Лексема: конец выражения

case '\0':

result.type = LEX_END;

result.iData = 0; //не используется

i++;

return result;

//Лексема: ошибка

default:

result.type = LEX_ERROR;

result.iData = 0;

//найден символ, не принадлежащий алфавиту

i++;

}

 

//Лексема: имя функции или переменная, или управляющий символ(% вначале),или ссылка

if (isalpha(source[i]) || (source[i] == '_') || (source[i] == '%') || (source[i] == '&'))

{ char name[MAX_NAME_LEN+1];

//Получаем имя по строке

GetName(source,i,name,MAX_NAME_LEN+1);

//Понижаем регистр

strlwr(name);

int k = 0;

//Проверяем, является ли строка именем функции

while (k<FuncCount) //Цикл по массиву имен функций

//Если есть совпадение

if (strcmp(FuncNameList[k], name) == 0)

//Прерываем цикл

break;

else k++;

//Если найдено совпадение

if (k<FuncCount) {

//Формируем лексему-функцию

result.type = LEX_FUNCTION;

result.iData = name;

return result;

}

//Если имя, найденное по правилам идентификатора,существует

//Получаем лексему-переменную

result.type = LEX_VARIABLE;

result.iData = name;

//Неизвестный идентификатор

return result;

}

return result;

}

vector<TLexem > BuildLexems(const char *str)

{

//Лексема

TLexem lexem;

//Вектор-список лексем строки

vector<TLexem > vec;

unsigned int i = 0;

do {

lexem = GetLexem(str, i); //считываем очередную лексему

//Добавляем ее в список

vec.push_back(lexem);

}

while (lexem.type!=LEX_END);

return vec;

 

}

 

bool parseScanf(char *str, vector<string> &Errors)

{

vector<char *> obj;

//Вектор лексем управляющей строки

vector<TLexem> ControlStringLexems;

//Вектор лексем

vector<TLexem> ValueLexems;

char * pch;

char *fun_ = "scanf";

try

{

//Просматриваем имя функции

pch = strstr(str, fun_);

//Если оно вначале, то вхождение совпадает с исходной строкой

if (strcmp(pch, str) == 0)

{

//Убираем из строки название функции

for (int i = 0; i < strlen(fun_); i++)

*pch++;

//Разбиваем строку на токены по запятой

//Первая операция

char *pch_ = strtok(pch, ",");

//Рекурсия

while (pch_!= 0)

{

//Токены-результаты помещаем в вектор

obj.push_back(pch_);

//Следующий шаг извлечения токенов

pch_ = strtok(NULL, ",");

}

 

if(obj.size()!=0)

{

//Рассматриваем первую строку как управляющую

//Рассматриваем наличие скобок

pch = strtok(obj[0], "(");

//Выделение кавычек

if (pch!= NULL)

{

pch = strtok(pch, "\"");

pch_ = pch;

//Выполняем анализ кавычек

int balance = 1; //Счетчик числа кавычек в управляющей строке

while (pch_!= NULL)

{

balance++;

pch_ = strtok(NULL, "\"");

}

//Если нет открывающей или закрывающей кавычки

if (balance!= 2)

//Пишем лог ошибок

Errors.push_back(string("Ошибка баланса кавычек"));

//Получаем список лексем из строки

ControlStringLexems = BuildLexems(pch);

//Сопоставляем управляющие параметры и идентификаторы по числу

//Если нет соответствия

if (ControlStringLexems.size()-1!= obj.size() - 1)

//Пишем лог ошибок

Errors.push_back("Число управляющих символов не отвечает числу идентификаторов ввода");

//Цикл по лексемам

for (int j = 0; j<ControlStringLexems.size();j++)

//Если среди управляющих символов нет такового

if (((int)ControlStringLexems[j].type!= (int)LEX_CSYM)&&(ControlStringLexems[j].type!= LEX_END))

{

char *str = new char[2000];

strcpy(str, "Обнаружена лексема ");

//Если информация о имени лексемы есть

if(ControlStringLexems[j].iData!=NULL)

//Добавляем ее

strcat(str, ControlStringLexems[j].iData);

//Дописываем строку ошибки

strcat(str, " не являющаяся управляющим символом");

//Пишем лог ошибок

Errors.push_back(string(str));

}

 

}

else

//Если нет скобки левой скобки

Errors.push_back("Ошибка баланса скобок в управляющей строке");

//Преобразование последнего элемента

obj[obj.size() - 1] = strtok(obj[obj.size() - 1], ")");

//Проверка идентификаторов во второй части ввода

for (int i = 1; i < obj.size(); i++)

{

ValueLexems.clear();

//Список лексем идентификатора

ValueLexems = BuildLexems(obj[i]);

//Цикл по списку лексем

for (int j = 0; j < ValueLexems.size(); j++)

{

//Если лексема - не переменная и не ссылка

if ((ValueLexems[j].type!= LEX_VARIABLE)&&(ValueLexems[j].type!= LEX_LINK) && (ValueLexems[j].type!= LEX_END))

{

//Пишем сообщение о ошибке в лог

char *str = new char[200];

strcpy(str, "Обнаружена лексема ");

//Если информация о имени лексемы есть

if(ValueLexems[j].iData!=NULL)

strcat(str, ValueLexems[j].iData);

strcat(str, " не являющаяся идентификатором\n или ссылкой");

//Пишем лог ошибок

Errors.push_back(string(str));

}

}

}

}

else

//Если распознавание не на чем осуществлять

Errors.push_back(string("Оператор записан ошибочно или неполно. Ошибка анализа"));

 

}

 

}

//Перехват исключения

catch(...)

{

//Запись в логе

Errors.push_back(string("Оператор записан ошибочно или неполно. Ошибка анализа"));

return 0;

}

 

return 0;

}

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...