Любой написанный код является просто набором байтов в памяти, но с компилятором наш код обретает логическую интерпретацию. Данное превращение из набора символов в набор команд делится на 8 этапов:
1. Лексический анализ (Lexical analysis)
2. Синтаксический анализ (Parsing)
3. Семантический анализ
4. Построение промежуточного представления (IR)
5. Оптимизации (Compile-time optimizations)
6. Генерация кода
7. Линковка (Linking)
8. Бинарник / байткод
Каждый из этапов мы отдельно рассмотрим, и пример реализации каждого слоя будет представлен на Node.js или Python.
1. Лексический анализ (Lexical analysis)
2. Синтаксический анализ (Parsing)
3. Семантический анализ
4. Построение промежуточного представления (IR)
5. Оптимизации (Compile-time optimizations)
6. Генерация кода
7. Линковка (Linking)
8. Бинарник / байткод
Каждый из этапов мы отдельно рассмотрим, и пример реализации каждого слоя будет представлен на Node.js или Python.
🤯1
Лексический анализ — это первый этап компиляции.
На этом этапе исходный код разбивается на последовательность токенов.
Лексер читает поток символов, группирует их в лексемы и классифицирует согласно правилам языка.
Пробелы, табуляции, переносы строк и комментарии обычно игнорируются.
Результатом работы является поток токенов, который передаётся на этап синтаксического анализа.
Пример:
выходит:
На этом этапе исходный код разбивается на последовательность токенов.
Лексер читает поток символов, группирует их в лексемы и классифицирует согласно правилам языка.
Пробелы, табуляции, переносы строк и комментарии обычно игнорируются.
Результатом работы является поток токенов, который передаётся на этап синтаксического анализа.
Пример:
int a = 10 + b
выходит:
KEYWORD(int)
IDENTIFIER(a)
ASSIGN
NUMBER(10)
PLUS
IDENTIFIER(b)
SEMICOLON
Full stack dev
Лексический анализ — это первый этап компиляции. На этом этапе исходный код разбивается на последовательность токенов. Лексер читает поток символов, группирует их в лексемы и классифицирует согласно правилам языка. Пробелы, табуляции, переносы строк и комментарии…
Пример кода на python:
При запуске:
Token(type=<Вид токена>, value=<Значение>, position=<метоположение токена в строке>)
python main.py
from core.lexer import Lexer
def main():
code = "int a = 10 + b;"
lexer = Lexer(code)
tokens = lexer.tokenize()
for token in tokens:
print(token)
if __name__ == "__main__":
main()
python core.lexer.py
import re
from dataclasses import dataclass
@dataclass
class Token:
type: str
value: str
position: int
class Lexer:
def __init__(self, source: str):
self.source = source
self.position = 0
self.tokens = []
self.token_specification = [
("NUMBER", r"\d+"),
("IDENTIFIER", r"[a-zA-Z_]\w*"),
("PLUS", r"\+"),
("MINUS", r"-"),
("MULTIPLY", r"\*"),
("DIVIDE", r"/"),
("ASSIGN", r"="),
("LPAREN", r"\("),
("RPAREN", r"\)"),
("SEMICOLON", r";"),
("SKIP", r"[ \t\n]+"),
("MISMATCH", r"."),
]
self.regex = re.compile(
"|".join(
f"(?P<{name}>{pattern})" for name, pattern in self.token_specification
)
)
def tokenize(self) -> list[Token]:
for match in self.regex.finditer(self.source):
kind = match.lastgroup
value = match.group()
position = match.start()
if kind == "SKIP":
continue
elif kind == "MISMATCH":
raise SyntaxError(f"Unexpected character {value} at {position}")
else:
# Проверка на ключевое слово
if kind == "IDENTIFIER" and value == "int":
kind = "KEYWORD"
self.tokens.append(Token(kind, value, position))
return self.tokens
При запуске:
Token(type='KEYWORD', value='int', position=0)
Token(type='IDENTIFIER', value='a', position=4)
Token(type='ASSIGN', value='=', position=6)
Token(type='NUMBER', value='10', position=8)
Token(type='PLUS', value='+', position=11)
Token(type='IDENTIFIER', value='b', position=13)
Token(type='SEMICOLON', value=';', position=14)
Token(type=<Вид токена>, value=<Значение>, position=<метоположение токена в строке>)
Синтаксический анализ (Parsing) - исходня из названию, данный слой получает на вход набор токенов, которые мы получили с слоя lexer и на выходе получаем AST.
Parsing:
- корректен ли порядок токенов
- соблюдены ли правила языка
- можно ли из этого построить осмысленную конструкцию
Данный слой позволяет нам связать token в структуру под названием AST
AST(Абстрактное сентаксическое дерево) - это структура данных, позволяющия в данной задаче, описавать связи между токенами
Parsing:
- корректен ли порядок токенов
- соблюдены ли правила языка
- можно ли из этого построить осмысленную конструкцию
Данный слой позволяет нам связать token в структуру под названием AST
AST(Абстрактное сентаксическое дерево) - это структура данных, позволяющия в данной задаче, описавать связи между токенами
Full stack dev
Синтаксический анализ (Parsing) - исходня из названию, данный слой получает на вход набор токенов, которые мы получили с слоя lexer и на выходе получаем AST. Parsing: - корректен ли порядок токенов - соблюдены ли правила языка - можно ли из этого построить…
реализация на python:
main.py
ast.py
parser.py
output
main.py
from core.lexer import Lexer
from core.parser import Parser
def main():
code = """
int a = 10 + b;
int c = a * 2;
"""
lexer = Lexer(code)
tokens = lexer.tokenize()
for token in tokens:
print(token)
parser = Parser(tokens)
ast = parser.parse()
print(ast)
if __name__ == "__main__":
main()
ast.py
from dataclasses import dataclass
class ASTNode:
pass
@dataclass
class Number(ASTNode):
value: int
@dataclass
class Identifier(ASTNode):
name: str
@dataclass
class BinaryOp(ASTNode):
left: ASTNode
operator: str
right: ASTNode
@dataclass
class VarDeclaration(ASTNode):
var_type: str
name: str
value: ASTNode
parser.py
from core.ast import BinaryOp, Identifier, Number, VarDeclaration
from core.lexer import Token
class Parser:
def __init__(self, tokens):
self.tokens = tokens
self.position = 0
def current(self) -> Token | None:
if self.position < len(self.tokens):
return self.tokens[self.position]
return None
def eat(self, token_type):
token = self.current()
if token and token.type == token_type:
self.position += 1
return token
raise SyntaxError(f"Expected {token_type}, got {token}")
def parse(self):
declarations = []
while self.current() is not None:
declarations.append(self.parse_declaration())
return declarations
def parse_declaration(self) -> VarDeclaration:
self.eat("KEYWORD")
identifier = self.eat("IDENTIFIER")
self.eat("ASSIGN")
expr = self.parse_expression()
self.eat("SEMICOLON")
return VarDeclaration(var_type="int", name=identifier.value, value=expr)
def parse_expression(self):
return self.parse_term()
def parse_term(self) -> BinaryOp:
node = self.parse_factor()
while self.current() and self.current().type in ("PLUS", "MINUS"):
operator = self.eat(self.current().type)
right = self.parse_factor()
node = BinaryOp(node, operator.value, right)
return node
def parse_factor(self):
node = self.parse_primary()
while self.current() and self.current().type in ("MULTIPLY", "DIVIDE"):
operator = self.eat(self.current().type)
right = self.parse_primary()
node = BinaryOp(node, operator.value, right)
return node
def parse_primary(self):
token = self.current()
if token.type == "NUMBER":
self.eat("NUMBER")
return Number(int(token.value))
elif token.type == "IDENTIFIER":
self.eat("IDENTIFIER")
return Identifier(token.value)
elif token.type == "LPAREN":
self.eat("LPAREN")
expr = self.parse_expression()
self.eat("RPAREN")
return expr
else:
raise SyntaxError(f"Unexpected token {token}")
output
Token(type='KEYWORD', value='int', position=5)
Token(type='IDENTIFIER', value='a', position=9)
Token(type='ASSIGN', value='=', position=11)
Token(type='NUMBER', value='10', position=13)
Token(type='PLUS', value='+', position=16)
Token(type='IDENTIFIER', value='b', position=18)
Token(type='SEMICOLON', value=';', position=19)
Token(type='KEYWORD', value='int', position=25)
Token(type='IDENTIFIER', value='c', position=29)
Token(type='ASSIGN', value='=', position=31)
Token(type='IDENTIFIER', value='a', position=33)
Token(type='MULTIPLY', value='*', position=35)
Token(type='NUMBER', value='2', position=37)
Token(type='SEMICOLON', value=';', position=38)
[VarDeclaration(var_type='int', name='a', value=BinaryOp(left=Number(value=10), operator='+', right=Identifier(name='b'))), VarDeclaration(var_type='int', name='c', value=BinaryOp(left=Identifier(name='a'), operator='*', right=Number(value=2)))]
❤2
После проверки структуры кода и создания AST идёт этап Семантического анализа(Semantic Analyzer)
Semantic Analyzer - это процесс когда код программы, проверяется на осмысленность программы, к примеру:
————
Код:
Ошибка
————
Код:
Ошибка
————
и еще довольно большой раяд подобных проверок.
На выходе данного layer мы получаем таблицу символов(Symbol Table):
Код:
Выходные данные(в Symbol Table):
Порядок исполнения: AST -> Semantic Analyzer -> Validated AST -> Symbol Table
Semantic Analyzer - это процесс когда код программы, проверяется на осмысленность программы, к примеру:
————
Код:
int a = b + 10;
Ошибка
Variable 'b' not defined
————
Код:
int a = 10;
int a = 20;
Ошибка
Variable 'a' already declared
————
и еще довольно большой раяд подобных проверок.
На выходе данного layer мы получаем таблицу символов(Symbol Table):
Код:
int a = 5;
int b = 10;
Выходные данные(в Symbol Table):
{
"a": { type: "int" },
"c": { type: "int" }
}Порядок исполнения: AST -> Semantic Analyzer -> Validated AST -> Symbol Table
👍3
Full stack dev
После проверки структуры кода и создания AST идёт этап Семантического анализа(Semantic Analyzer) Semantic Analyzer - это процесс когда код программы, проверяется на осмысленность программы, к примеру: ———— Код: int a = b + 10; Ошибка Variable 'b' not defined…
Реализация на python
core.semantic.py
main.py
Вывод:
core.semantic.py
class SemanticAnalyzer:
def __init__(self):
self.symbol_table = {}
def analyze(self, nodes):
for node in nodes:
self.visit(node)
def visit(self, node):
method_name = f"visit_{type(node).__name__}"
method = getattr(self, method_name, self.generic_visit)
return method(node)
def generic_visit(self, node):
raise Exception(f"No visit method for {type(node).__name__}")
def visit_VarDeclaration(self, node):
name = node.name
if name in self.symbol_table:
raise Exception(f"Variable '{name}' already declared")
self.symbol_table[name] = node.var_type
self.visit(node.value)
def visit_BinaryOp(self, node):
self.visit(node.left)
self.visit(node.right)
def visit_Identifier(self, node):
if node.name not in self.symbol_table:
raise Exception(f"Variable '{node.name}' not defined")
def visit_Number(self, node):
pass
main.py
from core.lexer import Lexer
from core.parser import Parser
from core.semantic import SemanticAnalyzer
def main():
code = """
int b = 3;
int a = 10 + b;
int c = a * 2;
"""
lexer = Lexer(code)
tokens = lexer.tokenize()
for token in tokens:
print(token)
parser = Parser(tokens)
ast = parser.parse()
print(ast)
semantic = SemanticAnalyzer()
semantic.analyze(ast)
print("Semantic analysis passed")
print("Symbol table:", semantic.symbol_table)
if __name__ == "__main__":
main()
Вывод:
...
Semantic analysis passed
Symbol table: {'b': 'int', 'a': 'int', 'c': 'int'}
🤯2👍1
После проверки программы на осмысленные конструкции, идёт следующий этап
Intermediate Representation (IR) - данный слой решает проблему, связанную с тем что AST является удобным для анализа
Напомню AST:
Но является совершенно не удобным для оптимизаций и генерации кода. Данный слой испровляет данную проблему и AST преобразует в IR(промежуточное престовление)
Пример:
Source code
AST to IR
Резюме:
Данный слой принимет на вхоид AST и на выходе IR. Нужно для того чтобы можно было сделать оптимизации и генерации кода.
Intermediate Representation (IR) - данный слой решает проблему, связанную с тем что AST является удобным для анализа
Напомню AST:
[VarDeclaration(var_type='int', name='b', value=Number(value=3)), VarDeclaration(var_type='int', name='a', value=BinaryOp(left=Number(value=10), operator='+', right=Identifier(name='b'))), VarDeclaration(var_type='int', name='c', value=BinaryOp(left=Identifier(name='a'), operator='*', right=Number(value=2)))]
Но является совершенно не удобным для оптимизаций и генерации кода. Данный слой испровляет данную проблему и AST преобразует в IR(промежуточное престовление)
Пример:
Source code
int c = a * 2 + 10;
AST to IR
t1 = a * 2
t2 = t1 + 10
c = t2
Резюме:
Данный слой принимет на вхоид AST и на выходе IR. Нужно для того чтобы можно было сделать оптимизации и генерации кода.
🔥1
Full stack dev
После проверки программы на осмысленные конструкции, идёт следующий этап Intermediate Representation (IR) - данный слой решает проблему, связанную с тем что AST является удобным для анализа Напомню AST: [VarDeclaration(var_type='int', name='b', value=Number(value=3))…
Реализация на python
core.ir.py
core.ir.py
main.py
При запуске на выходе:
core.ir.py
@dataclass
class IRInstruction:
op: str
arg1: str | None = None
arg2: str | None = None
result: str | None = None
core.ir.py
class IRBuilder:
def __init__(self):
self.instructions = []
self.temp_count = 0
def new_temp(self):
self.temp_count += 1
return f"t{self.temp_count}"
def build(self, nodes):
for node in nodes:
self.visit(node)
return self.instructions
def visit(self, node):
method_name = f"visit_{type(node).__name__}"
method = getattr(self, method_name)
return method(node)
def visit_VarDeclaration(self, node):
value = self.visit(node.value)
self.instructions.append(
IRInstruction(
op="STORE",
arg1=value,
result=node.name
)
)
def visit_BinaryOp(self, node):
left = self.visit(node.left)
right = self.visit(node.right)
temp = self.new_temp()
op_map = {
"+": "ADD",
"-": "SUB",
"*": "MUL",
"/": "DIV",
}
self.instructions.append(
IRInstruction(
op=op_map[node.operator],
arg1=left,
arg2=right,
result=temp
)
)
return temp
def visit_Number(self, node):
temp = self.new_temp()
self.instructions.append(
IRInstruction(
op="LOAD_CONST",
arg1=node.value,
result=temp
)
)
return temp
def visit_Identifier(self, node):
return node.name
main.py
from core.ir import IRBuilder
from core.lexer import Lexer
from core.parser import Parser
from core.semantic import SemanticAnalyzer
def main():
code = """
int b = 3;
int a = 10 + b;
int c = a * 2;
"""
lexer = Lexer(code)
tokens = lexer.tokenize()
for token in tokens:
print(token)
parser = Parser(tokens)
ast = parser.parse()
print(ast)
semantic = SemanticAnalyzer()
semantic.analyze(ast)
print("Semantic analysis passed")
print("Symbol table:", semantic.symbol_table)
ir_builder = IRBuilder()
ir = ir_builder.build(ast)
for instr in ir:
print(instr)
if __name__ == "__main__":
main()
При запуске на выходе:
...
IRInstruction(op='LOAD_CONST', arg1=3, arg2=None, result='t1')
IRInstruction(op='STORE', arg1='t1', arg2=None, result='b')
IRInstruction(op='LOAD_CONST', arg1=10, arg2=None, result='t2')
IRInstruction(op='ADD', arg1='t2', arg2='b', result='t3')
IRInstruction(op='STORE', arg1='t3', arg2=None, result='a')
IRInstruction(op='LOAD_CONST', arg1=2, arg2=None, result='t4')
IRInstruction(op='MUL', arg1='a', arg2='t4', result='t5')
IRInstruction(op='STORE', arg1='t5', arg2=None, result='c')
❤1👍1🔥1🤔1
Следующий этап "Оптимизация".
Оптимизации (Compile-time optimizations) - это процесс когда мы, улучшаем скорость работы программы, на уровне компиляции, при этом сохраняем правельность работы программы.
Это преобразования программы, которые:
* выполняются во время компиляции
* не меняют результат программы
* улучшают производительность / размер / читаемость IR
Мы проведём самые базовые 3 вида оптимизаций в нашем мини языке:
1. Constant Folding
2. Constant Propagation
3. Dead Code Elimination
Продолжение ниже⏬
Оптимизации (Compile-time optimizations) - это процесс когда мы, улучшаем скорость работы программы, на уровне компиляции, при этом сохраняем правельность работы программы.
Это преобразования программы, которые:
* выполняются во время компиляции
* не меняют результат программы
* улучшают производительность / размер / читаемость IR
Мы проведём самые базовые 3 вида оптимизаций в нашем мини языке:
1. Constant Folding
2. Constant Propagation
3. Dead Code Elimination
Продолжение ниже⏬
🔥1
Full stack dev
Следующий этап "Оптимизация". Оптимизации (Compile-time optimizations) - это процесс когда мы, улучшаем скорость работы программы, на уровне компиляции, при этом сохраняем правельность работы программы. Это преобразования программы, которые: * выполняются…
1. Constant Folding
тут мы заранее оптимизируем константы и их вычисления
до код:
до IR
После код:
После IR
2. Constant Propagation
Если переменная известна как константа — распространяем её.
До
После
3. Dead Code Elimination
Удаляем вычисления, которые никогда не используются.
до
t3 никогда не используется.
После
Pipeline оптимизаций
Соединяем все оптимизации и прогоняем их по нашем инструкциям.
тут мы заранее оптимизируем константы и их вычисления
до код:
int a = 2 * 3
до IR
IRInstruction(op='LOAD_CONST', arg1=3, arg2=None, result='t1')
IRInstruction(op='LOAD_CONST', arg1=2, arg2=None, result='t4')
IRInstruction(op='MUL', arg1='t1', arg2='t2', result='t3')
После код:
int a = 6
После IR
IRInstruction(op='LOAD_CONST', arg1=6, arg2=None, result='t1')
2. Constant Propagation
Если переменная известна как константа — распространяем её.
До
a = 5
b = a + 2
После
b = 5 + 2
3. Dead Code Elimination
Удаляем вычисления, которые никогда не используются.
до
t1 = 10
t2 = t1 + 5
t3 = 2 * 3
STORE t2 → a
t3 никогда не используется.
После
t1 = 10
t2 = t1 + 5
STORE t2 → a
Pipeline оптимизаций
Соединяем все оптимизации и прогоняем их по нашем инструкциям.
Full stack dev
1. Constant Folding тут мы заранее оптимизируем константы и их вычисления до код: int a = 2 * 3 до IR IRInstruction(op='LOAD_CONST', arg1=3, arg2=None, result='t1') IRInstruction(op='LOAD_CONST', arg1=2, arg2=None, result='t4') IRInstruction(op='MUL', arg1='t1'…
Кодавая реализация на python:
1. Constant Folding
2. Constant Propagation
3. Dead Code Elimination
Pipeline оптимизаций
Точка входа программы:
——————————
Исходный код:
1. Constant Folding
core.optimizations.constant_folder.pyfrom core.ir import IRInstruction
class ConstantFolder:
def optimize(self, instructions):
constants = {}
optimized = []
for instr in instructions:
if instr.op == "LOAD_CONST":
constants[instr.result] = instr.arg1
optimized.append(instr)
elif instr.op in ("ADD", "SUB", "MUL", "DIV"):
left = constants.get(instr.arg1)
right = constants.get(instr.arg2)
if left is not None and right is not None:
if instr.op == "ADD":
value = left + right
elif instr.op == "SUB":
value = left - right
elif instr.op == "MUL":
value = left * right
elif instr.op == "DIV":
value = left / right
constants[instr.result] = value
optimized.append(
IRInstruction(op="LOAD_CONST", arg1=value, result=instr.result)
)
else:
optimized.append(instr)
else:
optimized.append(instr)
return optimized
2. Constant Propagation
core.optimizations.constant_propagation.pyclass ConstantPropagation:
def optimize(self, instructions):
constants = {}
optimized = []
for instr in instructions:
if instr.op == "LOAD_CONST":
constants[instr.result] = instr.arg1
if instr.arg1 in constants:
instr.arg1 = constants[instr.arg1]
if instr.arg2 in constants:
instr.arg2 = constants[instr.arg2]
optimized.append(instr)
return optimized
3. Dead Code Elimination
core.optimizations.dead_code_elimination.pyclass DeadCodeElimination:
def optimize(self, instructions):
used = set()
for instr in instructions:
if instr.arg1:
used.add(instr.arg1)
if instr.arg2:
used.add(instr.arg2)
optimized = []
for instr in instructions:
if instr.result and instr.result.startswith("t"):
if instr.result not in used:
continue
optimized.append(instr)
return optimized
Pipeline оптимизаций
core.optimizations.pipeline.pyfrom core.optimizations.constant_folder import ConstantFolder
from core.optimizations.constant_propagation import ConstantPropagation
from core.optimizations.dead_code_elimination import DeadCodeElimination
class Optimizer:
def __init__(self):
self.passes = [ConstantPropagation(), ConstantFolder(), DeadCodeElimination()]
def optimize(self, instructions):
for p in self.passes:
instructions = p.optimize(instructions)
return instructions
Точка входа программы:
main.pyfrom core.ir import IRBuilder
from core.lexer import Lexer
from core.optimizations.pipline import Optimizer
from core.parser import Parser
from core.semantic import SemanticAnalyzer
def main():
code = """
int b = 3;
int a = 10 + b;
int c = a * 2;
"""
lexer = Lexer(code)
tokens = lexer.tokenize()
for token in tokens:
print(token)
parser = Parser(tokens)
ast = parser.parse()
print(ast)
semantic = SemanticAnalyzer()
semantic.analyze(ast)
print("Semantic analysis passed")
print("Symbol table:", semantic.symbol_table)
ir_builder = IRBuilder()
ir = ir_builder.build(ast)
print("IR:")
for instr in ir:
print(instr)
optimizer = Optimizer()
ir = optimizer.optimize(ir)
print("Optimized IR:")
for instr in ir:
print(instr)
if __name__ == "__main__":
main()
——————————
Исходный код:
int b = 3;
int a = 10 + b;
int c = a * 2;
👍1🔥1🤯1
Full stack dev
1. Constant Folding тут мы заранее оптимизируем константы и их вычисления до код: int a = 2 * 3 до IR IRInstruction(op='LOAD_CONST', arg1=3, arg2=None, result='t1') IRInstruction(op='LOAD_CONST', arg1=2, arg2=None, result='t4') IRInstruction(op='MUL', arg1='t1'…
Origin IR:
Optimized IR:
IRInstruction(op='LOAD_CONST', arg1=3, arg2=None, result='t1')
IRInstruction(op='STORE', arg1='t1', arg2=None, result='b')
IRInstruction(op='LOAD_CONST', arg1=10, arg2=None, result='t2')
IRInstruction(op='ADD', arg1='t2', arg2='b', result='t3')
IRInstruction(op='STORE', arg1='t3', arg2=None, result='a')
IRInstruction(op='LOAD_CONST', arg1=2, arg2=None, result='t4')
IRInstruction(op='MUL', arg1='a', arg2='t4', result='t5')
IRInstruction(op='STORE', arg1='t5', arg2=None, result='c')
Optimized IR:
IRInstruction(op='STORE', arg1=3, arg2=None, result='b')
IRInstruction(op='ADD', arg1=10, arg2='b', result='t3')
IRInstruction(op='STORE', arg1='t3', arg2=None, result='a')
IRInstruction(op='MUL', arg1='a', arg2=2, result='t5')
IRInstruction(op='STORE', arg1='t5', arg2=None, result='c')
❤1👍1🔥1🤯1
Этап Code Generation - самый интересный этап, давайте освежим в памяти весь pipline:
Есть несколько вариантов генерации:
-
-
-
-
Мы возьмём вариант
IR Interpreter:
выполняет инструкции по типу таких IRInstruction(op='STORE', arg1=3, arg2=None, result='b') и подерживает память переменных.
Нам необходимо релизовать две структуры:
IRInterpreter - класс отвечающий за логику генерации кода
variables - память переменных
temps - временные регистры
Итог:
Генерация кода - эта тот этап, где мы уже можем увидеть наглядно работоспособность кода.
Пример и реализация 🔽
0. Source code
1. Lexer
2. Parser
3. AST
4. Semantic Analysis
5. IR
6. Optimization
7. Code Generation *
Есть несколько вариантов генерации:
-
Assembly (x86-64)-
Bytecode (как у JVM)-
WebAssembly-
Интерпретация IRМы возьмём вариант
Интерпретация IR.IR Interpreter:
выполняет инструкции по типу таких IRInstruction(op='STORE', arg1=3, arg2=None, result='b') и подерживает память переменных.
Нам необходимо релизовать две структуры:
IRInterpreter - класс отвечающий за логику генерации кода
variables - память переменных
temps - временные регистры
Итог:
Генерация кода - эта тот этап, где мы уже можем увидеть наглядно работоспособность кода.
Пример и реализация 🔽
🔥1
Full stack dev
Этап Code Generation - самый интересный этап, давайте освежим в памяти весь pipline: 0. Source code 1. Lexer 2. Parser 3. AST 4. Semantic Analysis 5. IR 6. Optimization 7. Code Generation * Есть несколько вариантов генерации: - Assembly (x86-64) - Bytecode…
Реализация в коде:
Интепритатор(core.intepritator.py):
self.variables: Dict = {} и
self.temps: Dict = {} выглядят примерно так:
Точка входа:
main.py
Тест:
IR Optimize:
Результат:
Интепритатор(core.intepritator.py):
from typing import Dict
class IRInterpreter:
def __init__(self):
self.variables: Dict = {}
self.temps: Dict = {}
def get_value(self, name):
if isinstance(name, (int, float)):
return name
if name in self.temps:
return self.temps[name]
if name in self.variables:
return self.variables[name]
raise Exception(f"Unknown value {name}")
def execute(self, instructions):
for instr in instructions:
op = instr.op
if op == "LOAD_CONST":
self.temps[instr.result] = instr.arg1
elif op == "ADD":
self.temps[instr.result] = self.get_value(instr.arg1) + self.get_value(
instr.arg2
)
elif op == "SUB":
self.temps[instr.result] = self.get_value(instr.arg1) - self.get_value(
instr.arg2
)
elif op == "MUL":
self.temps[instr.result] = self.get_value(instr.arg1) * self.get_value(
instr.arg2
)
elif op == "DIV":
self.temps[instr.result] = self.get_value(instr.arg1) / self.get_value(
instr.arg2
)
elif op == "STORE":
self.variables[instr.result] = self.get_value(instr.arg1)
else:
raise Exception(f"Unknown op {op}")
return self.variables
self.variables: Dict = {} и
self.temps: Dict = {} выглядят примерно так:
variables = {
"a": 12
}
temps = {
"t1": 10,
"t2": 12
}Точка входа:
main.py
from core.interpreter import IRInterpreter
from core.ir import IRBuilder
from core.lexer import Lexer
from core.optimizations.pipline import Optimizer
from core.parser import Parser
from core.semantic import SemanticAnalyzer
def main():
code = """
int b = 3;
int a = 10 + b;
int c = a * 2;
"""
lexer = Lexer(code)
tokens = lexer.tokenize()
for token in tokens:
print(token)
parser = Parser(tokens)
ast = parser.parse()
print(ast)
semantic = SemanticAnalyzer()
semantic.analyze(ast)
print("Semantic analysis passed")
print("Symbol table:", semantic.symbol_table)
ir_builder = IRBuilder()
ir = ir_builder.build(ast)
print("IR:")
for instr in ir:
print(instr)
optimizer = Optimizer()
ir = optimizer.optimize(ir)
print("Optimized IR:")
for instr in ir:
print(instr)
print("Generate code")
interpreter = IRInterpreter()
result = interpreter.execute(ir)
print("Program result:")
print(result)
if __name__ == "__main__":
main()
Тест:
source code
int b = 3;
int a = 10 + b;
int c = a * 2;
IR Optimize:
IRInstruction(op='STORE', arg1=3, arg2=None, result='b')
IRInstruction(op='ADD', arg1=10, arg2='b', result='t3')
IRInstruction(op='STORE', arg1='t3', arg2=None, result='a')
IRInstruction(op='MUL', arg1='a', arg2=2, result='t5')
IRInstruction(op='STORE', arg1='t5', arg2=None, result='c')
Результат:
...
Generate code
Program result:
{'b': 3, 'a': 13, 'c': 26}
🔥2❤1
Full stack dev
Работа языко программирования №1 Многие из вас, сейчас читающих данный пост, умеют писать код на разных языках. Но немногие понимают и знают работу этих языков, то, как они реализованы, и то, что происходит, когда вы запускаете свой код. Данный пост открывает…
Конец ? Да, но только для этого канала. Данный ряд постов, была тестовая для этого канала, и подобного кантетна тут больше не будет, подобное перемещается на другой канал https://t.me/freyzanIT. Тут же возвращается контет связанный именно по full stack разработке.
👍2
Весь контент переходит сюда: https://t.me/morilogos
Telegram
morilogos
youtube: https://www.youtube.com/channel/UCzl6mFsCUU1i33QJUAIVQ-Q
❤2