После выполнения вышеуказанных действий получается предварительное представление кода - представление выводится во вторую текстовую область - а также заполненные таблицы классов лексем (рис. 2). На их основании производится формирование дескрипторного кода и псевдокода. Их вывод осуществляется в отдельные текстовые области.
Рис. 1. Блок-схема алгоритма удаления комментариев, незначащие пробелы, пустые строки
Рис. 2. Блок-схема алгоритма записи лексем в таблицу
3.3 Логическое проектирование
3.4 Физическое проектирование
|
Имя модуля |
Заголовок процедуры или функции |
Формальные параметры |
Выполняемое действие |
|
|
Project |
punctz |
wchart |
Проверка, является ли символ знаком пунктуации |
|
|
digit |
Проверка, является ли символ десятичной цифрой |
|||
|
o_perat |
Проверка, является ли символ знаком операции |
|||
|
o_perat1 |
int |
проверяет правильность написания знака операции |
||
|
word |
wchart |
Проверка, является ли символ буквой латинского алфавита или нижним подчёркиванием |
||
|
delet_problem |
- |
Начало обработки кода, записанного в richTextBox1, удаление комментариев и пустых строк |
||
|
programm |
- |
распределение по классам лексем |
||
|
constant, punctuation, ident, o_peratr_sravn, o_peratr, keyWord |
int |
формирование дескрипторного и псевдокодов |
||
|
button2_Click |
object sender EventArgs e |
Проверка и вывод ошибок |
||
|
button1_Click |
object sender EventArgs e |
Начало работы, проверка на ошибки |
В ходе выполнения реализации лексического анализатора были:
создана автоматная модель работы лексического анализатора. Описание модели см. в п. 2.2.2. Граф автомата см. в п. 2.3.4, рис. 1;
на основе автоматной модели создана регулярная грамматика. Описание грамматики см. в п. 2.2.3;
создано приложение - лексический анализатор, осуществляющее лексический анализ исходного кода по автоматной модели. Работу в виде блок-схемы см. в Приложении 2. Приложение выполняет следующие функции:
• исключение из текста исходной программы комментариев;
• исключение из текста исходной программы незначащих пробелов, символов-табуляций и перевода строки;
• выделение лексем следующих типов: идентификаторов, строковых, символьных и числовых, констант, ключевых (служебных) слов входного языка, знаков операций и разделителей;
• выявление ошибок в написании лексем, сообщение об ошибках (с указанием позиции и типа ошибки);
• формирование дескрипторного кода и псевдокода.
Результаты работы программы (см. след. стр.):
Рис.3.Интерфейс приложения.
На рис. 3 изображён интерфейс приложения. В текстовую область слева в верху заносится обрабатываемый код. После нажатия кнопки «start processing» запускается обработка. Результаты следующие:
• в текстовой области, ниже выделенной, выводится первичное представление кода - без комментариев, пустых строк и лишних пробелов;
• в таблицах в нижней части интерфейса выводятся лексемы, распределённые по соответствующим классам (ключевые слова, операторы и операторы сравнения, идентификаторы, знаки пунктуации, константы);
• в текстовых областях в правой части интерфейса выводятся дескрипторный код (верхняя) и псевдокод (нижняя) (чтобы точнее определиться с местоположением текстовых областей они подписываются);
• Также можно выполнить полную диагностику кода на наличие ошибок нажатием на кнопку «error processing» (рисунок 4. в нижнем правом текстовом блоке выводиться информация об ошибках).
Обработка лексических ошибок:
На рисунках 5-8 показан вывод сообщения об ошибке. После нажатия копки «start processing» запускается обработка кода, при нахождении ошибки появляется дополнительное окно с информацией о ошибке, а именно:
1. строка ошибки
2. название ошибки
3. лексема с ошибкой
На рисунках 4, 9-13 показан вывод сообщения об всех имеющихся ошибках. После нажатия копки «error processing» запускается анализ кода на наличие ошибок. В конце обработки кода выводиться сообщение о всех имеющихся ошибках в нижнем правом текстовом блоке.
Рисунок 4 информация об ошибках
Рисунок 5 вывод сообщения об ошибке
Рисунок 6 вывод сообщения об ошибке
Рисунок 7 вывод сообщения об ошибке
Рисунок 8 вывод сообщения об ошибке
Рисунок 9 вывод информации об ошибках
Рисунок 10 вывод информации об ошибках
Рисунок 11 вывод информации об ошибках
Рисунок 12 вывод информации об ошибках
Рисунок 13 вывод информации об ошибках
4. Конструирование модели синтаксического анализа
4.1 Связь между КС-грамматиками и синтаксическим анализом
Синтаксический анализ -- это процесс сопоставления линейной последовательности лексем (слов) естественного или формального языка с его формальной грамматикой. Результатом обычно является дерево разбора (синтаксическое дерево). Обычно применяется совместно с лексическим анализом.
В ходе синтаксического анализа исходный текст преобразуется в структуру данных, обычно -- в дерево, которое отражает синтаксическую структуру входной последовательности и хорошо подходит для дальнейшей обработки. Синтаксический анализ -- это процесс, в котором исследуется таблица лексем и устанавливается, удовлетворяет ли она структурным условиям, явно сформулированным в определении синтаксиса языка.
Синтаксический анализатор (синтаксический разбор) - это часть компилятора, которая отвечает за выявление основных синтаксических конструкций входного языка. В задачу синтаксического анализа входит: найти и выделить основные синтаксические конструкции в тексте входной программы, установить тип и проверить правильность каждой синтаксической конструкции, наконец, представить синтаксические конструкции в виде, удобном для дальнейшей генерации текста результирующей программы.
Основу любого синтаксического анализатора всегда составляет распознаватель, построенный на основе какого-либо класса КС-грамматик. Анализатор воспринимает выход лексического анализатора и разбирает его в соответствии с грамматикой входного языка.
Грамматики обеспечивают значительные преимущества разработчикам языков программирования и создателям компиляторов.
* Грамматика дает точную и при этом простую для понимания синтаксическую спецификацию языка программирования.
* Для некоторых классов грамматик можно автоматически построить эффективный синтаксический анализатор, который определяет, корректна ли структура исходной программы. Дополнительным преимуществом автоматического создания анализатора является возможность обнаружения синтаксических неоднозначностей и других сложных для распознавания конструкций языка, которые иначе могли бы остаться не замеченными на начальных фазах создания языка и его компилятора.
* Правильно построенная грамматика придает языку программирования структуру, которая способствует облегчению трансляции исходной программы в объектный код, выявлению ошибок. Для преобразования описаний трансляции, основанных на грамматике языка, в рабочие программы имеется соответствующий программный инструментарий.
* Со временем языки эволюционируют, обогащаясь новыми конструкциями, и выполняют новые задачи. Добавление конструкций в язык окажется более простой задачей, если существующая реализация языка основана на его грамматическом описании.
4.2 Построение КС-грамматики
V> IV| HV | GV | JV | BV | YV | CV | SV | AV | J | B | A | A[id]V
A > | Include| iostream| string| ctime| void| int| for| new| cout| endl |A id| id
G> using namespace std Y| G1 G
G1># include <A>
I>A*id=new A[id]; |A id=const; | A id[const]; | A id[const]={id}; | id[const]|
A id=”id”;
J> JV|(V)|{V}|”V”|<V>|( )
B> A[V] |Y|C |const
Id >K const |K|* id| & id| id C const| id C id Y
C>S=|S|++|--|>>|<<|%
S >+|-|=|<|>|!
Y>,|.|:|;
K> a..z |A..Z
const >0..9|const 0..9
H> for (A id=const; id C const; id C)|A id J|A(V);|A<<”_”<<I
4.3 Построение дерева разбора
Построение порождени для данной синтаксической конструкции.
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
void opF(int* ar, int* gogo) {
*gogo += *ar;
}
int main()
{
srand(time(NULL));
int m = 0;
int arr[5] = { 3,4,2 };
string mas ="awd";
for (int i = 0; i <2; i++)
{
opF(& arr[i], &m);
}
float *m1 = new float[m];
for (int y = 0; y < m; y++) {
m1[y] = rand() % 10;
cout <<"_"<<m1[y];
}}
V=>GV=>
G1 G V=>
# include <A> G V=>
# include <iostream> G V=>
# include <iostream> G1 G V=>
# include <iostream> # include <A> G V=>
# include <iostream> # include <string> G V=>
# include <iostream> # include <string> G1 G V=>
# include <iostream> # include <string> # include <A> G V=>
# include <iostream> # include <string> # include <ctime> G V=>
# include <iostream> # include <string> # include <ctime> using namespace std Y V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; HV=>
# include <iostream> # include <string> # include <ctime> using namespace std ;A id JV=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void id JV=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF JV=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (AV)V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (A id V)V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (intid V)V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *id V)V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar V )V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar YV)V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , V)V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar ,A)V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , A id )V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int id)V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int*id)V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo)JV=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){V}V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){A}V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){id}V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){ id C id Y }V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){ *id C id Y }V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){ *gogo C id Y }V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){ * gogo S= id Y }V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){* gogo += id Y }V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){* gogo +=*id Y}V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){* gogo +=*ar Y}V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){* gogo +=*ar ;}V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){* gogo +=*ar;}HV=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){* gogo +=*ar;}A id JV=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){* gogo +=*ar;}int id JV=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){* gogo +=*ar;}int main JV=>
# include <iostream> # include <string> # include <ctime> using namespace std ;
Void opF (int *ar , int * gogo){* gogo +=*ar;}int main ()V=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){* gogo +=*ar;}int main ()J=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){* gogo +=*ar;}int main (){V}=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){* gogo +=*ar;}int main (){HV}=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){* gogo +=*ar;}int main (){A (V); V}=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){* gogo +=*ar;}int main (){srand(V);V}=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){* gogo +=*ar;}int main (){srand (AV);V}=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){* gogo +=*ar;}int main (){srand (time V);V}=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){* gogo +=*ar;}int main (){srand (time J);V}=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){* gogo +=*ar;}int main (){srand (time (V));V}=>
# include <iostream> # include <string> # include <ctime> using namespace std ; Void opF (int *ar , int * gogo){* gogo +=*ar;}int main (){srand (time (A));V}=>