Full stack dev
61 subscribers
356 photos
50 videos
4 files
99 links
"Full Stack Dev & Computer Science" – Канал для разработчиков, которые хотят
расширить свои знания в Full Stack и углубиться в основы и новейшие тренды Computer Science.
Здесь вы найдете материалы по frontend и backend разработке, работе с базами данных,
Download Telegram
👍3
Работа языко программирования №1

Многие из вас, сейчас читающих данный пост, умеют писать код на разных языках. Но немногие понимают и знают работу этих языков, то, как они реализованы, и то, что происходит, когда вы запускаете свой код. Данный пост открывает арку постов, связанных с этой темой; все последующие посты — 3–4 шт.
Любой написанный код является просто набором байтов в памяти, но с компилятором наш код обретает логическую интерпретацию. Данное превращение из набора символов в набор команд делится на 8 этапов:

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:

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(Абстрактное сентаксическое дерево) - это структура данных, позволяющия в данной задаче, описавать связи между токенами
Full stack dev
Синтаксический анализ (Parsing) - исходня из названию, данный слой получает на вход набор токенов, которые мы получили с слоя lexer и на выходе получаем AST. Parsing: - корректен ли порядок токенов - соблюдены ли правила языка - можно ли из этого построить…
реализация на python:

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 - это процесс когда код программы, проверяется на осмысленность программы, к примеру:
————
Код:
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
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:
[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
@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

Продолжение ниже
🔥1
Full stack dev
Следующий этап "Оптимизация". Оптимизации (Compile-time optimizations) - это процесс когда мы, улучшаем скорость работы программы, на уровне компиляции, при этом сохраняем правельность работы программы. Это преобразования программы, которые: * выполняются…
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', 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
core.optimizations.constant_folder.py
from 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.py
class 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.py
class 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.py
from 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.py
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)


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:
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:
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):
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}
🔥21
Full stack dev
Работа языко программирования №1 Многие из вас, сейчас читающих данный пост, умеют писать код на разных языках. Но немногие понимают и знают работу этих языков, то, как они реализованы, и то, что происходит, когда вы запускаете свой код. Данный пост открывает…
Конец ? Да, но только для этого канала. Данный ряд постов, была тестовая для этого канала, и подобного кантетна тут больше не будет, подобное перемещается на другой канал https://t.me/freyzanIT. Тут же возвращается контет связанный именно по full stack разработке.
👍2