
ЛР3 Саляхов
.docxМинистерство образования и науки Российской Федерации
Федеральное государственное бюджетное образовательное учреждение высшего образования
«Уфимский университет науки и технологий»
Кафедра ТК
Отчет по лабораторной работе № 3
«Системное программное обеспечение»
Тема: «Построение простейшего дерева вывода» Вариант 11
Выполнил:
студент группы ИВТ-429Б
Саляхов А.Ф.
Проверил:
доцент кафедры ТК
Ракипова А.С.
Уфа 2024
Цель работы:
Изучение основных принципов генерации компилятором объектного кода, ознакомление с методами оптимизации результирующего объектного кода для линейного участка программы с помощью свертки и исключения лишних операций.
Задание:
Написать программу, которая на основании дерева синтаксического разбора порождает объектный код и выполняет затем его оптимизацию методом свертки объектного кода и методом исключения лишних операций. Результатом работы должна быть построенная на основе заданного предложения грамматики должна быть построенная на основе заданного предложения грамматики программа на объектном языке или построенная последовательность триад.
Вариант:
Входной язык содержит операторы условия типа if ... then .. else и if .. then, разделённые символом ; (точка с запятой). Операторы условия содержат идентификаторы, знаки сравнения <, >, =, шестнадцатеричные, знак присваивания (:=)
Запись грамматики входного языка в форме Бэкуса- Наура:
G ({if, then, else, i, ;, <, >, =, O, :=},{S, F, T, E }, P, S)
P:
S→ F;
F→ if E then T else F | if E then F | i := O
T→ if E then T else T | i := O
E→ i < i | i > i | i = i
Множества крайних правых и крайних левых символов с указанием шагов построения
Таблица 1 – Шаг 1
Символ U |
L(U) |
R(U) |
S |
F |
; |
F |
if, i |
F, O |
T |
if, i |
T, O |
E |
i |
<, >, = |
Таблица 2 – Шаг 2
Символ U |
L(U) |
R(U) |
S |
F, if, i |
; |
F |
if, i |
F, O |
T |
if, i |
T, O |
E |
i |
<, >, = |
Таблица 3 – Шаг 3
Символ U |
L(U) |
R(U) |
S |
F, if, i |
; |
F |
if, i |
F, O |
T |
if, i |
T, O |
E |
i |
<, >, = |
Таблица 4 – Шаг 4
Символ U |
L(U) |
R(U) |
S |
F, if, i |
; |
F |
if, i |
F, O |
T |
if, i |
T, O |
E |
i |
<, >, = |
Таблица не изменилась, построение закончено.
Множества крайних правых и крайних левых терминальных символов
Таблица 5 – Шаг 1
Символ U |
Lt(U) |
Rt(U) |
S |
; |
; |
F |
if, i |
F, O |
T |
if, i |
T, O |
E |
i |
<, >, = |
Таблица 6 – Шаг 2
Символ U |
Lt(U) |
Rt(U) |
S |
;, if, i |
; |
F |
if, i |
else, then, := |
T |
if, i |
else, := |
E |
i |
<, >, = |
Заполненная матрица предшествования для грамматики
Таблица 7 – матрица операторного предшествования
Символы |
; |
if |
then |
else |
i |
:= |
< |
> |
= |
O |
⊥к |
; |
|
|
|
|
|
|
|
|
|
|
⸱> |
if |
|
|
=⸱ |
|
<⸱ |
|
<⸱ |
<⸱ |
<⸱ |
|
|
then |
⸱> |
<⸱ |
|
=⸱ |
<⸱ |
|
|
|
|
|
|
else |
⸱> |
<⸱ |
|
|
<⸱ |
|
|
|
|
|
|
i |
⸱> |
|
⸱> |
⸱> |
|
=⸱ |
⸱> |
⸱> |
⸱> |
|
|
:= |
⸱> |
|
⸱> |
<⸱ |
|
|
<⸱ |
<⸱ |
<⸱ |
<⸱ |
|
< |
⸱> |
|
⸱> |
⸱> |
<⸱ |
|
⸱> |
⸱> |
⸱> |
<⸱ |
|
> |
⸱> |
|
⸱> |
⸱> |
<⸱ |
|
⸱> |
⸱> |
⸱> |
<⸱ |
|
= |
⸱> |
|
⸱> |
⸱> |
<⸱ |
|
⸱> |
⸱> |
⸱> |
<⸱ |
|
O |
⸱> |
|
⸱> |
<⸱ |
<⸱ |
|
<⸱ |
<⸱ |
<⸱ |
|
|
⊥н |
<⸱ |
<⸱ |
|
|
<⸱ |
|
|
|
|
|
|
Остовная грамматика G'({for, do, :=, a, ;, <, >, =}, {S, F, T, E}, P', S):
E→ E; – правило 1
E→ if E then E else E | if E then E | i := O – правило 2, 3, 4
E→ if E then E else E | i := O – правило 5, 6
E→ i < i | i > i | i = i – правило 7, 8, 9
Пример выполнения разбора простейшего предложения входного языка
Рассматриваемый МП-автомат имеет только одно состояние. Тогда для иллюстрации работы МП-автомата будем записывать каждую его конфигурацию в виде трех составляющих {𝛼 |𝛽 |𝛾}, где:
𝛼 — непрочитанная часть входной цепочки;
𝛽 — содержимое стека МП-автомата;
𝛾 — последовательность номеров примененных правил.
В начальном состоянии вся входная цепочка не прочитана, стек автомата содержит только лексему типа «начало строки», последовательность номеров правил пуста. Для удобства чтения стек МП-автомата будем заполнять в порядке справа налево, тогда находящимся на верхушке стека будет считаться крайний правый символ в цепочке 𝛽.
if A < B then K := 123AF else if A > B then L := 32FF;
После выполнения лексического анализа, если все лексемы типа «идентификатор» обозначить как «i», а «шестнадцатеричные числа» обозначить как «O» получим цепочку: if i < i then i := O else if i > i then i := O;
{ if i < i then i := O else if i > i then i := O; ⊥к | ⊥н | λ }
{ i < i then i := O else if i > i then i := O; ⊥к | ⊥н if | λ } ÷п
{ < i then i := O else if i > i then i := O; ⊥к | ⊥н if i | λ } ÷п
{ i then i := O else if i > i then i := O; ⊥к | ⊥н if i < | λ } ÷п
{ then i := O else if i > i then i := O; ⊥к | ⊥н if i < i | 7 } ÷c
{ then i := O else if i > i then i := O; ⊥к | ⊥н if E | 7 } ÷п
{ i := O else if i > i then i := O; ⊥к | ⊥н if E then | 7 } ÷п
{ := O else if i > i then i := O; ⊥к | ⊥н if E then i | 7 } ÷п
{ O else if i > i then i := O; ⊥к | ⊥н if E then i := | 7 } ÷п
{ else if i > i then i := O; ⊥к | ⊥н if E then i := O | 7 4} ÷c
{ else if i > i then i := O; ⊥к | ⊥н if E then E | 7 4 } ÷п
{ if i > i then i := O; ⊥к | ⊥н if E then E else | 7 4 } ÷п
{ i > i then i := O; ⊥к | ⊥н if E then E else if | 7 4 } ÷п
{ > i then i := O; ⊥к | ⊥н if E then E else if i | 7 4 } ÷п
{ i then i := O; ⊥к | ⊥н if E then E else if i > | 7 4 } ÷п
{ then i := O; ⊥к | ⊥н if E then E else if i > i | 7 4 8} ÷c
{ then i := O; ⊥к | ⊥н if E then E else if E | 7 4 8 } ÷п
{ i := O; ⊥к | ⊥н if E then E else if E then | 7 4 8 } ÷п
{ := O; ⊥к | ⊥н if E then E else if E then i | 7 4 8 } ÷п
{ O; ⊥к | ⊥н if E then E else if E then i := | 7 4 8 } ÷п
{; ⊥к | ⊥н if E then E else if E then i := O | 7 4 8 4} ÷c
{; ⊥к | ⊥н if E then E else if E then E | 7 4 8 4 3} ÷c
{; ⊥к | ⊥н if E then E else E | 7 4 8 4 3 2} ÷c
{; ⊥к | ⊥н E | 7 4 8 4 3 2} ÷п
{ ⊥к | ⊥н E; | 7 4 8 4 3 2 1} ÷c
{⊥к | ⊥н E | 7 4 8 4 3 2 1} ÷п
{| ⊥н E ⊥к | 7 4 8 4 3 2 1} ÷п
Разбор закончен, МП-автомат перешел в конечную конфигурацию, цепочка принята. В результате получим последовательность правил: 7 4 8 4 3 2 1. Этой последовательности будет соответствовать цепочка вывода на основе остовной грамматики G':
S→F(1)→if E then T else F(2)→if E then T else if E then F(3)→if E then T else if E then i := O(6)→if E then T else if E then i := O(8)→if E then T else if i > i then i := O(6)→ if E then i := O else if i > i then i := O(7)→ if i < i then i := O else if i > i then i := O;
Рисунок 1– Дерево синтаксического разбора
Текст программы
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cctype>
#include <stack>
#include <unordered_map>
#include <algorithm>
using namespace std;
bool hasUnknownLexeme = false;
struct Lexeme {
string value;
string type;
string chain;
int lineNumber;
};
string determineLexemeType(const string& lexeme) {
if (!lexeme.empty() && isdigit(lexeme[0])) {
bool isHex = true;
for (char ch : lexeme) {
if (!isxdigit(ch)) { // Проверяем, является ли символ цифрой или буквой A-F/a-f
isHex = false;
break;
}
}
if (isHex) return "Шестнадцатеричное число";
}
// Проверки для других типов
if (lexeme == "if" || lexeme == "then" || lexeme == "else") return "Оператор условия";
if (lexeme == "<" || lexeme == ">" || lexeme == "=") return "Знак сравнения";
if (lexeme == ":=") return "Оператор присваивания";
if (lexeme == ";") return "Разделитель";
if (isalpha(lexeme[0])) return "Идентификатор";
return "Неизвестная лексема";
}
string generateChain(const string& lexeme) {
string chain = "h";
char State = 'h';
for (char ch : lexeme) {
switch (State) {
case 'h':
if (ch == 'i') State = 'A';
else if (ch == 't') State = 'C';
else if (ch == ':') State = 'M';
else if (ch == 'e') State = 'G';
else if (ch == ';') State = 'K';
else if (ch == '>' || ch == '<' || ch == '=') State = 'L';
else if (isdigit(ch)) State = 'O'; // Обработка для чисел
else State = 'i';
break;
case 'A':
if (ch == 'f') State = 'B';
else State = 'i';
break;
case 'C':
if (ch == 'h') State = 'D';
else State = 'i';
break;
case 'M':
if (ch == '=') State = 'N';
else State = 'e';
break;
case 'O':
if (isdigit(ch) || (toupper(ch) >= 'A' && toupper(ch) <= 'F')) State = 'O'; // Обработка для шестнадцатеричных чисел
break;
default:
break;
}
chain += State;
}
chain += "s";
return chain;
}
void reportUnknownLexeme(const string& lexeme, int lineNumber) {
cout << "Неизвестная лексема \"" << lexeme << "\" в строке " << lineNumber << endl;
}
vector<Lexeme> lexicalAnalysis(const string& text) {
vector<Lexeme> lexemes;
string current;
bool inComment = false;
int lineNumber = 0;
for (size_t i = 0; i < text.length(); i++) {
char ch = text[i];
if (ch == '\n') lineNumber++;
if (!inComment && ch == '/' && text[i + 1] == '*') {
inComment = true;
i++;
continue;
}
if (inComment && ch == '*' && text[i + 1] == '/') {
inComment = false;
i++;
continue;
}
if (inComment) continue;
if (isspace(ch) || ch == ';' || ch == '<' || ch == '>' || ch == '=' || ch == ':') {
if (!current.empty()) {
string type = determineLexemeType(current);
if (type == "Неизвестная лексема") {
reportUnknownLexeme(current, lineNumber);
}
else {
lexemes.push_back({ current, type, generateChain(current), lineNumber });
}
current.clear();
}
if (ch == ';' || ch == '<' || ch == '>' || ch == '=') {
string lexemeStr(1, ch);
string type = determineLexemeType(lexemeStr);
if (type == "Неизвестная лексема") {
reportUnknownLexeme(lexemeStr, lineNumber);
}
else {
lexemes.push_back({ lexemeStr, type, generateChain(lexemeStr), lineNumber });
}
}
if (ch == ':' && text[i + 1] == '=') {
lexemes.push_back({ ":=", "Оператор присваивания", generateChain(":="), lineNumber });
i++;
}
}
else {
current += ch;
}
}
if (!current.empty()) {
string type = determineLexemeType(current);
if (type == "Неизвестная лексема") {
reportUnknownLexeme(current, lineNumber);
}
else {
lexemes.push_back({ current, type, generateChain(current), lineNumber });
}
}
return lexemes;
}
bool isValidSequence(const string& previousLexeme, const string& currentLexeme) {
static unordered_map<string, vector<string>> precedenceTable = {
{"if", {"i"}},
{"then", {"i"}},
{"else", {"i", "if"}},
{"i", {"<", ">", "=", "then", ":="}},
{"<", {"i"}},
{">", {"i"}},
{"=", {"i"}},
{":=", {"O"}},
{"O", {";", "else"}}
};
auto it = precedenceTable.find(previousLexeme);
if (it != precedenceTable.end()) {
const auto& allowedNextLexemes = it->second;
return find(allowedNextLexemes.begin(), allowedNextLexemes.end(), currentLexeme) != allowedNextLexemes.end();
}
return false;
}
void syntaxAnalysis(const vector<Lexeme>& lexemes) {
stack<string> automatonStack;
automatonStack.push(".н");
vector<string> lexemeArray;
vector<int> appliedRules;
size_t index = 0;
string previousLexeme = "";
for (const auto& lexeme : lexemes) {
string valueToAdd = lexeme.value;
if (lexeme.type == "Идентификатор") {
valueToAdd = "i";
}
else if (lexeme.type == "Шестнадцатеричное число") {
valueToAdd = "O";
}
lexemeArray.push_back(valueToAdd);
if (!previousLexeme.empty() && !isValidSequence(previousLexeme, valueToAdd)) {
cout << "Синтаксическая ошибка, разбор невозможен: после " << previousLexeme
<< " ожидалась другая лексема." << endl;
return;
}
if (valueToAdd == "i" && previousLexeme == "") {
cout << "Синтаксическая ошибка, разбор невозможен: перед i ожидалась лексема" << endl;
return;
}
previousLexeme = valueToAdd;
}
lexemeArray.push_back(".к");
auto printState = [&](const string& rule, const string& action = "", const vector<int>& rulesApplied = {}) {
cout << "{ ";
for (size_t i = index; i < lexemeArray.size(); ++i) {
cout << lexemeArray[i] << " ";
}
cout << "| ";
stack<string> tempStack = automatonStack;
vector<string> rightStack;
while (!tempStack.empty()) {
rightStack.push_back(tempStack.top());
tempStack.pop();
}
for (auto it = rightStack.rbegin(); it != rightStack.rend(); ++it) {
cout << *it << " ";
}
if (!action.empty()) {
cout << "| " << action;
}
if (!rulesApplied.empty()) {
for (int ruleNum : rulesApplied) {
cout << " " << ruleNum;
}
}
cout << " }" << rule << endl;
};
auto reduce = [&](int ruleNumber, const vector<string>& pattern, const string& replacement) {
stack<string> tempStack;
for (auto it = pattern.rbegin(); it != pattern.rend(); ++it) {
if (!automatonStack.empty() && automatonStack.top() == *it) {
tempStack.push(automatonStack.top());
automatonStack.pop();
}
else {
while (!tempStack.empty()) {
automatonStack.push(tempStack.top());
tempStack.pop();
}
return false;
}
}
automatonStack.push(replacement);
appliedRules.push_back(ruleNumber);
return true;
};
string rules = "";
printState(rules);
while (index < lexemeArray.size()) {
string current = lexemeArray[index];
automatonStack.push(current);
index++;
printState(rules, "%п", appliedRules);
bool reduced;
do {
reduced = false;
vector<int> currentAppliedRules;
if (index < lexemeArray.size() && lexemeArray[index] == "else") {
if (reduce(2, { "if", "E", "then", "F", "else", "F" }, "F")) {
reduced = true;
currentAppliedRules.push_back(2);
printState(rules, "%с", currentAppliedRules);
continue;
}
}
else if (reduce(3, { "if", "E", "then", "F" }, "F")) {
reduced = true;
currentAppliedRules.push_back(3);
printState(rules, "%с", currentAppliedRules);
}
if (reduce(7, { "i", "<", "i" }, "E")) {
reduced = true;
currentAppliedRules.push_back(7);
printState(rules, "%с", currentAppliedRules);
}
else if (reduce(8, { "i", ">", "i" }, "E")) {
reduced = true;
currentAppliedRules.push_back(8);
printState(rules, "%с", currentAppliedRules);
}
else if (reduce(9, { "i", "=", "i" }, "E")) {
reduced = true;
currentAppliedRules.push_back(9);
printState(rules, "%с", currentAppliedRules);
}
else if (reduce(4, { "i", ":=", "O" }, "F")) {
reduced = true;
currentAppliedRules.push_back(4);
printState(rules, "%с", currentAppliedRules);
}
else if (reduce(1, { "F", ";" }, "S")) {
reduced = true;
currentAppliedRules.push_back(1);
printState(rules, "%с", currentAppliedRules);
}
else if (reduce(2, { "if", "E", "then", "F", "else", "F" }, "F")) {
reduced = true;
currentAppliedRules.push_back(2);
printState(rules, "%с", currentAppliedRules);
}
if (reduced) {
continue;
}
} while (reduced);
}
cout << "Применённые правила: ";
for (int rule : appliedRules) {
cout << rule << " ";
}
cout << endl;
}
int main() {
setlocale(LC_ALL, "Russian");
ifstream file("D:/EclipseWorkspace/12345/src/input.txt");
if (!file) {
cerr << "Не удалось открыть файл." << endl;
return 1;
}
string line, text;
while (getline(file, line)) {
text += line + '\n';
}
file.close();
vector<Lexeme> lexemes = lexicalAnalysis(text);
cout << "Лексема\t\tТип лексемы\t\tЦепочка" << endl;
cout << "--------------------------------------------------------" << endl;
for (const auto& lex : lexemes) {
cout << lex.value << "\t\t" << lex.type << "\t\t" << lex.chain << endl;
}
if (!hasUnknownLexeme)
{
cout << "\nРезультаты синтаксического анализа:" << endl;
syntaxAnalysis(lexemes);
}
else
{
cout << "\nВ файле найдены ошибки, невозможно запустить синтаксический анализ" << endl;
}
return 0;
}
Результаты работы
На Рисунок 2 – представлен результат работы программы
Рисунок 2 – результат работы программы
Сравним последовательность правил, которую вывела программа с той последовательностью правил, которую мы получили после разбора простейшего предложения входного языка: «7 4 8 4 3 2 1» = «7 4 8 4 3 2 1»
Последовательности правил совпадают, это означает, что программа работает правильно.