ГоловнаСтаттіComputer Science

Розробка компіляторів

Розробка компіляторів - це галузь комп'ютерних наук, що займається створенням програм, які перетворюють код, написаний на одній мові програмування, в код на іншій мові або в машинний код. Компілятори є основою сучасного програмування та дозволяють розробникам писати код на високорівневих мовах.

mysimulator teamОновлено — липень 2026≈ 4 хв читання▶ Відкрити симуляцію

🎯 Вступ до розробки компіляторів

Розробка компіляторів - це галузь комп'ютерних наук, що займається створенням програм, які перетворюють код, написаний на одній мові програмування, в код на іншій мові або в машинний код. Компілятори є основою сучасного програмування та дозволяють розробникам писати код на високорівневих мовах.

🔑 Основні етапи компіляції:

Лексичний аналіз: Розбиття коду на токени

Синтаксичний аналіз: Побудова дерева розбору

Семантичний аналіз: Перевірка коректності програми

Генерація коду: Створення цільового коду

🏗️ Архітектура компілятора

📊 Основні компоненти

📝 Лексичний аналізатор

Вхідний код → Токени

🌳 Синтаксичний аналізатор

Токени → Абстрактне синтаксичне дерево

🔍 Семантичний аналізатор

AST → Анотоване AST

⚡ Генератор коду

Анотоване AST → Машинний код

🔄 Типи компіляторів

Проходить через код один раз, генеруючи код одразу під час аналізу.

Переваги: Швидкість, простота

Недоліки: Обмежена оптимізація

Виконує кілька проходів через код для різних етапів обробки.

Переваги: Глибока оптимізація

Недоліки: Складність, повільність

Just-In-Time компілятор - компілює код під час виконання.

Приклади: Java JVM, .NET CLR

📖 Лексичний аналіз

🔍 Токенізація

Процес лексичного аналізу

Лексичний аналізатор розбиває вхідний текст на послідовність токенів (лексем).

📋 Регулярні вирази для лексем

if|then|else|while|for|function|return|class|int|float|string

[a-zA-Z_][a-zA-Z0-9_]*

[0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?

\+|-|\*|/|==|!=|<=|>=|<|>|&&|\|\|

🔧 Реалізація лексичного аналізатора

class LexicalAnalyzer { private String input; private int position; public Token nextToken() { skipWhitespace(); if (position >= input.length()) { return new Token(TokenType.EOF, ""); } char current = input.charAt(position); if (Character.isLetter(current)) { return readIdentifier(); } else if (Character.isDigit(current)) { return readNumber(); } else if (current == '"') { return readString(); } else { return readOperator(); } } private Token readIdentifier() { StringBuilder sb = new StringBuilder(); while (position < input.length() && Character.isLetterOrDigit(input.charAt(position))) { sb.append(input.charAt(position++)); } String lexeme = sb.toString(); TokenType type = isKeyword(lexeme) ? TokenType.KEYWORD : TokenType.IDENTIFIER; return new Token(type, lexeme); } }

// Вхідний код: int x = 42; if (x > 0) { return x; } // Токени: // KEYWORD(int), IDENTIFIER(x), OPERATOR(=), NUMBER(42), PUNCTUATION(;) // KEYWORD(if), PUNCTUATION(()), IDENTIFIER(x), OPERATOR(>), NUMBER(0), PUNCTUATION()) // PUNCTUATION({), KEYWORD(return), IDENTIFIER(x), PUNCTUATION(;), PUNCTUATION(})

🌳 Синтаксичний аналіз

📊 Абстрактне синтаксичне дерево (AST)

Структура AST

AST представляє синтаксичну структуру програми у вигляді дерева, де кожен вузол відповідає конструкції мови.

🔄 Алгоритми парсингу

Top-down парсер, що використовує рекурсивні функції для кожного нетермінала.

// Для граматики: E → E + T | T Expression parseExpression() { Expression left = parseTerm(); while (currentToken.type == PLUS) { Token op = consume(PLUS); Expression right = parseTerm(); left = new BinaryExpression(left, op, right); } return left; } Term parseTerm() { Term left = parseFactor(); while (currentToken.type == MULTIPLY) { Token op = consume(MULTIPLY); Term right = parseFactor(); left = new BinaryTerm(left, op, right); } return left; }

Bottom-up парсер, що будує дерево знизу вгору.

class LRParser { private Stack stateStack = new Stack<>(); private Stack symbolStack = new Stack<>(); public ASTNode parse() { stateStack.push(initialState); while (true) { State currentState = stateStack.peek(); Action action = actionTable[currentState][currentToken.type]; switch (action.type) { case SHIFT: shift(action.nextState); break; case REDUCE: reduce(action.rule); break; case ACCEPT: return symbolStack.peek().node; case ERROR: throw new ParseException("Syntax error"); } } } }

// Код: int x = 42; // AST: // Declaration // / | \ // Type Name Value // | | | // int x 42 // Код: if (x > 0) { return x; } // AST: // IfStatement // / | \ // Condition Then Else // | | | // BinaryOp Block null // / | \ | // x > 0 Return // | // x

🔍 Семантичний аналіз

📋 Перевірки семантики

Перевіряє сумісність типів у виразах та операціях.

Перевіряє, що всі змінні визначені перед використанням.

// Помилка: x = 42; // x не визначена // Правильно: int x; x = 42;

Перевіряє правила видимості змінних та функцій.

🏗️ Таблиця символів

Структура таблиці символів

Таблиця символів зберігає інформацію про всі ідентифікатори в програмі.

class SymbolTable { private Map symbols = new HashMap<>(); private SymbolTable parent; public void define(String name, Symbol symbol) { symbols.put(name, symbol); } public Symbol lookup(String name) { Symbol symbol = symbols.get(name); if (symbol != null) { return symbol; } if (parent != null) { return parent.lookup(name); } return null; } public void enterScope() { SymbolTable newScope = new SymbolTable(); newScope.parent = this; // ... enter new scope } public void exitScope() { // ... exit current scope } }

// Помилка типів: int x = "hello"; // int ≠ string // Правильно: int x = 42; string y = "hello";
жива демонстрація · пов'язана симуляція● LIVE

⚡ Генерація коду

🎯 Стратегії генерації коду

Генерація коду в проміжну мову (байт-код, LLVM IR).

Генерація коду безпосередньо в машинний код.

// x86-64 асемблер: add: push rbp mov rbp, rsp mov eax, edi ; a add eax, esi ; a + b pop rbp ret

🏗️ Реєстровий алоцізатор

Проблема розподілу реєстрів

Машини мають обмежену кількість реєстрів, тому потрібно ефективно їх використовувати.

Linear Scan: Простий алгоритм для JIT компіляторів

Graph Coloring: Класичний алгоритм для оптимізуючих компіляторів

// Вхідний код: int add(int a, int b) { return a + b; } // LLVM IR: define i32 @add(i32 %a, i32 %b) { entry: %0 = add i32 %a, %b ret i32 %0 }

🚀 Оптимізація коду

⚡ Типи оптимізацій

Оптимізації в межах одного базового блоку.

Постійне згортання

Алгебраїчні спрощення

Видалення мертвого коду

Оптимізації на рівні функції.

Поширення констант

Видалення мертвого коду

Інлайнінг функцій

Оптимізації на рівні всього модуля.

Інлайнінг між модулями

Спеціалізація функцій

Усунення мертвого коду

🔧 Приклади оптимізацій

// До оптимізації: int add(int a, int b) { return a + b; } int main() { int result = add(3, 4); return result; } // Після інлайнінга: int main() { int result = 3 + 4; // 7 return result; }

// До оптимізації: int x = 2 + 3; int y = x * 4; // Після оптимізації: int x = 5; int y = 20;

🛠️ Інструменти розробки компіляторів

⚙️ Генератори парсерів

Потужний генератор парсерів з підтримкою багатьох мов програмування.

Набір інструментів для створення компіляторів з оптимізацією.

// Генерація LLVM IR: Module* module = new Module("my_module", context); Function* func = Function::Create( FunctionType::get(Type::getInt32Ty(context), {Type::getInt32Ty(context)}, false), Function::ExternalLinkage, "my_function", module );

grammar Simple; program: statement+; statement: assignment | ifStatement | returnStatement; assignment: ID '=' expression ';'; ifStatement: 'if' '(' expression ')' statement; returnStatement: 'return' expression ';'; expression: expression ('+'|'-') term | term; term: term ('*'|'/') factor | factor; factor: ID | NUMBER | '(' expression ')'; ID: [a-zA-Z_][a-zA-Z0-9_]*; NUMBER: [0-9]+; WS: [ \t\r\n]+ -> skip;

🎯 Практичні застосування

💻 Мови програмування

C, C++, Java, Python, JavaScript та інші мови потребують компіляторів

🔧 Інструменти розробки

IDE, лінтери, форматувачі коду використовують компіляторні технології

🌐 Веб-технології

TypeScript → JavaScript, SASS → CSS, Babel для транспіляції

🎮 Ігрова розробка

Шейдери, скрипти, оптимізація коду для ігор

📚 Рекомендована література

"Compilers: Principles, Techniques, and Tools" - Aho, Sethi, Ullman (Dragon Book)

"Modern Compiler Implementation in C" - Andrew Appel

"Engineering a Compiler" - Cooper, Torczon

"The Definitive ANTLR 4 Reference" - Terence Parr

Спробуйте наживо

Усе, що вище, працює прямо у вашому браузері — відкрийте Hash Function Avalanche Visualizer і змінюйте параметри під час роботи. Нічого не встановлюється, нічого не завантажується на сервер, уся модель живе в одній вкладці.

▶ Відкрити симуляцію Hash Function Avalanche Visualizer

Що ви знайшли?

Додати кроки відтворення (опційно)