I have programing language - BetterCookie961. Here is it syntax:
```
# BetterCookie961 Instructions

* `k` - move the pointer right
* `i` - move the pointer left
* `K` - move the pointer right and copy the value of the left cookie
* `I` - move the pointer left and copy the value of the right cookie
* `c` - increment the current cookie value
* `C` - Adds 10 to current cookie value
* `o` - decrement the current cookie value
* `O` - decreases the current cookie value by 10
* `L` - erase current cookie value
* `9` - output the value of the current cookie (Outputs only chars)
* `a` - debug(ASCII) output. Output the current cookie value
* `e` - **replace** the value of the current cookie with input
* `6` - jump to the **matching** `1` instruction if the current cookie value is zero
* `1` - jump to the **matching** `6` instruction if the current cookie value is **not** zero
* `(` - IF construction starts
* `)` - IF construction ends
* `N` - Return to the initial cookie
* `n` - print current cookie value
* `>` - Go to next line(in output)
* `R` - Write a random value in the range 0-100 to a cookie
* `{` - Beginning of an arithmetic expression
* `}` - End of arithmetic expression
* `r` - Run file from user input
* `B[filename]` - Run file
* `S[milliseconds]` - Sleep function
* `M[WhereToMove(int)]` - Move current cookie value to another cookie
* `-` - Stop executing program
* `g` - output in which cookie we are located
* `X[Offset(int)]` - Copy current cookie value to another cookie
* `/* {your comment here} */` - Comment
* `P` - Move to the cookie whose number corresponds to the value of the current cookie and transfer the number of the current cookie to the one to which we move (for example, in order to go back).
* `J` - Move to the cookie whose number corresponds to the value of the current cookie. (without transferring the number of the current cookie to the one to which we move).
* `_` - Exit from interpretation iteration.
* `G` - Write current cookie's address into current cookie.
* `x[+/-0/1]` - Copy the value of the current cookie to the address that lies in the left or right cookie (depending on -(left) or +(right)), and also moves the cursor to the address (if 1) and does not move (if 0).
* `m[+/-0/1]` - Move the value of the current cookie to the address that lies in the left or right cookie (depending on -(left) or +(right)), and also moves the cursor to the address (if 1) and does not move (if 0).
# Some Examples

* `M[3]` - Moves the value of this cookie 3 steps to the right.
* `M[-1]` - Moves the value of this cookie 1 step to the left.
* `x[-0]` - Copy the value of the current cookie to the address from the left cookie without moving the cursor.
* `m[+1]` - Move the value of the current cookie to the address from the right cookie and move the cursor to the address from the right cookie.

# How to write IF constructions

IF construction operators:
* `>` - bigger
* `<` - less
* `=` - equal
* `~` - not equal

1) IF always starts with an expression. (k > i ! cca)

2) Always after the expression comes !, after which the body is written. {k > i - expression} {! - delimiter} {cca - body}

3) IF has no variables, so numeric values are passed like this:
* `i` - left cookie value
* `k` - right cookie value
* `t` - current cookie value
Example: (k > i ! cca) = {if (k > i): do cca}


ATTENTION! Before calling the IF, "create" all the cookies you need (move to them)!
Example: cc(k > i ! cca) - ERROR, because right cookie does not exist. kkki(k > i ! cca) - SUCCESS, right and left cookies exist.
More examples:
* `kkk(k > i ! cca)` - ERROR, right cookie does not exist.
* `ki(k < i ! cca)` - ERROR, left cookie does not exist.

You can also write IF inside the BODY of another IF.
Examples:
* `cckccckcccci(k > i ! kkcckccckcci(i > k ! cca))` - SUCCESS.
* `cckccckcccci(k > i ! (i < k ! cca))` - SUCCESS.
* `cckccckcccci(k > (i < k ! cca))` - ERROR, no body.

**IF constructs ALWAYS have !, otherwise you will get an error.**

# How to write arithmetic expressions

Arithmetic expressions have 4 operators:
* `+` - addition
* `-` - subtraction
* `*` - multiplication
* `%` - division

Numeric values are passed through:
* `k` - right cookie value
* `t` - current cookie value
* `i` - left cookie value

**After executing an arithmetic expression, the result is written to the current cookie**

Examples:

`{t + k}` - current cookie value + right cookie value
`{t - i}` - current cookie value - left cookie value

**ATTENTION! The arithmetic expression only uses 2 values and 1 operator!**

`{t + k - i}` = `{t + k}`
`{i * k - t + k}` = `{i * k}`

Program Examples:

`ekei{t + k}n` - Calculates the sum of two numbers
`ekekei{t + k}{t + i}n` - Calculates the sum of three numbers
```

And I want to translate C-language without pointers (for now) into this language. I have:
tokenizer.hpp:
```
#pragma once

enum TokenType
{
	T_KEYWORD,
	T_IDENTIFIER,
	T_NUMBER,
	T_OPERATOR,
	T_PUNCTUATION,
	T_BUILTIN,
	T_STRING,
	T_EOF
};

namespace std
{
	string to_string(const TokenType& tokenType);
}

struct Token
{
	TokenType type = T_EOF;
	std::string value;
	int line = 0;
	int column = 0;
};

class ITokenizer
{
public:
	virtual ~ITokenizer() = default;

	static std::shared_ptr<ITokenizer> GetTokenizer(const std::string& input, const uint32_t version = 1);

	virtual Token nextToken() = 0;
	virtual Token peekNextToken() = 0;
	virtual void resetCurrent() = 0;
};
```
tokenizer.cpp:
```
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <cctype>
#include <unordered_set>

#include "tokenizer.hpp"

std::string std::to_string(const TokenType& tokenType)
{
	switch (tokenType)
	{
		case TokenType::T_KEYWORD:
			return "T_KEYWORD";
		case TokenType::T_IDENTIFIER:
			return "T_IDENTIFIER";
		case TokenType::T_NUMBER:
			return "T_NUMBER";
		case TokenType::T_OPERATOR:
			return "T_OPERATOR";
		case TokenType::T_PUNCTUATION:
			return "T_PUNCTUATION";
		case TokenType::T_BUILTIN:
			return "T_BUILTIN";
		case TokenType::T_STRING:
			return "T_STRING";
		case TokenType::T_EOF:
			return "T_EOF";
		default:
			return "UnknownTokenType";
	}
}

class CTokenizer : public ITokenizer
{
	friend class ASTBuilder;

public:
	CTokenizer(const std::string& input) : input(input), current(0), line(1), column(1) {}

	virtual Token nextToken() override
	{
		skipWhitespace();

		if (current >= input.size()) { return {T_EOF, "", line, column}; }

		char ch = input[current];
		if (isalpha(ch) || ch == '_') { return readIdentifier(); }
		else if (isdigit(ch)) { return readNumber(); }
		else if (isOperator(ch)) { return readOperator(); }
		else if (isPunctuation(ch)) { return readPunctuation(); }
		else if (ch == '"') { return readString(); }

		std::cerr << "Error: Unexpected character '" << ch << "' at line " << line << ", column " << column
				  << std::endl;
		return {T_EOF, "", line, column};
	}

	virtual Token peekNextToken() override
	{ // Function to peek at the next token without consuming it
		int originalCurrent = current;
		int originalLine = line;
		int originalColumn = column;

		Token next = nextToken();

		current = originalCurrent;
		line = originalLine;
		column = originalColumn;

		return next;
	}

	virtual void resetCurrent() override
	{
		current = 0;
		line = 0;
		column = 0;
	}

private:
	void skipWhitespace()
	{
		while (current < input.size() && isspace(input[current]))
		{
			if (input[current] == '\n')
			{
				line++;
				column = 1;
			}
			else { column++; }
			current++;
		}
	}

	Token readIdentifier()
	{
		std::string identifier;
		while (current < input.size() && (isalnum(input[current]) || input[current] == '_'))
		{
			identifier += input[current];
			current++;
		}

		if (keywords.count(identifier) > 0) { return {T_KEYWORD, identifier, line, column}; }
		else if (builtins.count(identifier) > 0) { return {T_BUILTIN, identifier, line, column}; }
		else { return {T_IDENTIFIER, identifier, line, column}; }
	}

	Token readNumber()
	{
		std::string number;
		while (current < input.size() && isdigit(input[current]))
		{
			number += input[current];
			current++;
		}
		return {T_NUMBER, number, line, column};
	}

	Token readOperator()
	{
		std::string op;
		op += input[current];
		current++;

		// Check for "==", "!=", "++", "--", "<=", ">=" operators
		if (current < input.size() && ((op == "+" && input[current] == '+') || (op == "-" && input[current] == '-') ||
									   (op == "=" && input[current] == '=') || (op == "!" && input[current] == '=') ||
									   (op == "<" && input[current] == '=') || (op == ">" && input[current] == '=')))
		{
			op += input[current];
			current++;
		}
		return {T_OPERATOR, op, line, column};
	}

	Token readPunctuation()
	{
		std::string punct;
		punct += input[current];
		current++;
		return {T_PUNCTUATION, punct, line, column};
	}

	Token readString()
	{
		std::string str;
		current++; // Skip the opening quote
		while (current < input.size() && input[current] != '"')
		{
			str += input[current];
			current++;
		}
		if (current >= input.size())
		{
			std::cerr << "Error: Unterminated string literal at line " << line << ", column " << column << std::endl;
			return {T_EOF, "", line, column};
		}
		current++; // Skip the closing quote
		return {T_STRING, str, line, column};
	}

	bool isOperator(char ch)
	{
		return (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '%' || ch == '<' || ch == '>' || ch == '=' ||
				ch == '!' || ch == '&' || ch == '|' || ch == '^');
	}

	bool isPunctuation(char ch)
	{
		return (ch == '(' || ch == ')' || ch == '{' || ch == '}' || ch == '[' || ch == ']' || ch == ',' || ch == ';' ||
				ch == ':');
	}

	std::string input;
	int current;
	int line;
	int column;

	std::unordered_set<std::string> keywords = {"cookie", "void", "return", "if",    "else",
												"while",  "for",  "do",     "break", "continue"};

	std::unordered_set<std::string> builtins = {"__builtin_input", "__builtin_dprint"};
};

std::shared_ptr<ITokenizer> ITokenizer::GetTokenizer(const std::string& input, const uint32_t version)
{
	return std::make_shared<CTokenizer>(input);
}
```
ast.hpp:
```
#pragma once

enum ASTNodeType
{
	AST_PROGRAM,

	AST_FUNCTION_DECLARATION,
	AST_VARIABLE_DECLARATION,

	AST_UNARY_EXPRESSION,
	AST_BINARY_EXPRESSION,

	AST_BUILTIN_CALL,

	AST_FUNCTION_CALL,
	AST_FUNCTION_ARGUMENT,

	AST_IF_STATEMENT,
	AST_WHILE_STATEMENT,
	AST_RETURN_STATEMENT,
	AST_EXPRESSION_STATEMENT,

	AST_NUMBER,
	AST_STRING,
	AST_IDENTIFIER,
};

namespace std
{
	string to_string(const ASTNodeType& astNodeType);
}

struct ASTNode
{
	ASTNodeType type;
	std::string value;
	int line;
	int column;
	std::vector<std::shared_ptr<ASTNode>> children;

	ASTNode(ASTNodeType aType, const std::string& aValue, int aLine, int aColumn,
			const std::vector<std::shared_ptr<ASTNode>>& aChildren)
		: type(aType), value(aValue), line(aLine), column(aColumn), children(aChildren)
	{
	}
};

class IASTBuilder
{
public:
	virtual ~IASTBuilder() = default;

	static std::shared_ptr<IASTBuilder> GetASTBuilder(std::shared_ptr<ITokenizer> tokenizer,
													  const uint32_t version = 1);

	virtual std::shared_ptr<ASTNode> buildAST() = 0;
};
```
ast.cpp:
```
#include <iostream>
#include <string>
#include <vector>
#include <cctype>
#include <unordered_set>

#include "tokenizer.hpp"
#include "ast.hpp"

std::string std::to_string(const ASTNodeType& astNodeType)
{
	switch (astNodeType)
	{
		case ASTNodeType::AST_PROGRAM:
			return "AST_PROGRAM";
		case ASTNodeType::AST_FUNCTION_DECLARATION:
			return "AST_FUNCTION_DECLARATION";
		case ASTNodeType::AST_VARIABLE_DECLARATION:
			return "AST_VARIABLE_DECLARATION";
		case ASTNodeType::AST_RETURN_STATEMENT:
			return "AST_RETURN_STATEMENT";
		case ASTNodeType::AST_EXPRESSION_STATEMENT:
			return "AST_EXPRESSION_STATEMENT";
		case ASTNodeType::AST_UNARY_EXPRESSION:
			return "AST_UNARY_EXPRESSION";
		case ASTNodeType::AST_BINARY_EXPRESSION:
			return "AST_BINARY_EXPRESSION";
		case ASTNodeType::AST_IDENTIFIER:
			return "AST_IDENTIFIER";
		case ASTNodeType::AST_NUMBER:
			return "AST_NUMBER";
		case ASTNodeType::AST_BUILTIN_CALL:
			return "AST_BUILTIN_CALL";
		case ASTNodeType::AST_FUNCTION_CALL:
			return "AST_FUNCTION_CALL";
		case ASTNodeType::AST_FUNCTION_ARGUMENT:
			return "AST_FUNCTION_ARGUMENT";
		case ASTNodeType::AST_STRING:
			return "AST_STRING";
		default:
			return "UnknownASTNodeType";
	}
}

class CASTBuilder : public IASTBuilder
{
public:
	CASTBuilder(std::shared_ptr<ITokenizer>& aTokenizer) : tokenizer(aTokenizer)
	{
		currentToken = tokenizer->nextToken();
	}

	std::shared_ptr<ASTNode> buildAST()
	{
		std::shared_ptr<ASTNode> program =
			std::make_shared<ASTNode>(AST_PROGRAM, "program", 0, 0, std::vector<std::shared_ptr<ASTNode>>());
		while (currentToken.type != T_EOF)
		{
			program->children.push_back(parseDeclaration());
		}
		return program;
	}

private:
	std::shared_ptr<ASTNode> parseDeclaration()
	{
		if (currentToken.type == T_KEYWORD && (currentToken.value == "cookie" || currentToken.value == "void"))
		{
			return parseFunctionDeclaration();
		}
		else
		{
			throw std::runtime_error("Error: Expected declaration at line " + std::to_string(currentToken.line) +
									 ", column " + std::to_string(currentToken.column));
		}
	}

	std::shared_ptr<ASTNode> parseFunctionDeclaration()
	{
		Token returnType = currentToken;
		consumeToken();
		Token functionName = currentToken;
		consumeToken();

		if (currentToken.type != T_PUNCTUATION || currentToken.value != "(")
		{
			throw std::runtime_error("Error: Expected '(' at line " + std::to_string(currentToken.line) + ", column " +
									 std::to_string(currentToken.column));
		}
		consumeToken();

		std::vector<std::shared_ptr<ASTNode>> parameters;
		if (currentToken.type != T_PUNCTUATION || currentToken.value != ")") { parameters = parseParameterList(); }

		if (currentToken.type != T_PUNCTUATION || currentToken.value != ")")
		{
			throw std::runtime_error("Error: Expected ')' at line " + std::to_string(currentToken.line) + ", column " +
									 std::to_string(currentToken.column));
		}
		consumeToken();

		if (currentToken.type != T_PUNCTUATION || currentToken.value != "{")
		{
			throw std::runtime_error("Error: Expected '{' at line " + std::to_string(currentToken.line) + ", column " +
									 std::to_string(currentToken.column));
		}
		consumeToken();

		std::vector<std::shared_ptr<ASTNode>> body;
		while (currentToken.type != T_PUNCTUATION || currentToken.value != "}")
		{
			body.push_back(parseStatement());
		}
		consumeToken();

		std::shared_ptr<ASTNode> functionDeclaration =
			std::make_shared<ASTNode>(AST_FUNCTION_DECLARATION, functionName.value, functionName.line,
									  functionName.column, std::vector<std::shared_ptr<ASTNode>>());
		functionDeclaration->children.push_back(std::make_shared<ASTNode>(AST_IDENTIFIER, returnType.value,
																		  returnType.line, returnType.column,
																		  std::vector<std::shared_ptr<ASTNode>>()));
		for (const auto& parameter : parameters)
		{
			functionDeclaration->children.push_back(parameter);
		}
		for (const auto& statement : body)
		{
			functionDeclaration->children.push_back(statement);
		}

		return functionDeclaration;
	}

	std::vector<std::shared_ptr<ASTNode>> parseParameterList()
	{
		std::vector<std::shared_ptr<ASTNode>> parameters;

		while (true)
		{
			if (currentToken.type != T_KEYWORD || currentToken.value != "cookie")
			{
				throw std::runtime_error("Error: Expected 'cookie' at line " + std::to_string(currentToken.line) +
										 ", column " + std::to_string(currentToken.column));
			}
			consumeToken();

			Token parameterName = currentToken;
			if (parameterName.type != T_IDENTIFIER)
			{
				throw std::runtime_error("Error: Expected identifier at line " + std::to_string(currentToken.line) +
										 ", column " + std::to_string(currentToken.column));
			}
			consumeToken();

			parameters.push_back(std::make_shared<ASTNode>(AST_FUNCTION_ARGUMENT, parameterName.value,
														   parameterName.line, parameterName.column,
														   std::vector<std::shared_ptr<ASTNode>>()));

			if (currentToken.type == T_PUNCTUATION && currentToken.value == ",") { consumeToken(); }
			else { break; }
		}
		return parameters;
	}

	std::shared_ptr<ASTNode> parseStatement()
	{
		if (currentToken.type == T_KEYWORD && currentToken.value == "return") { return parseReturnStatement(); }
		else if (currentToken.type == T_KEYWORD && currentToken.value == "cookie")
		{
			return parseVariableDeclaration();
		}
		else if (currentToken.type == T_KEYWORD && currentToken.value == "if")
		{
			return parseIfStatement(); // Parse if statements
		}
		else if (currentToken.type == T_KEYWORD && currentToken.value == "while")
		{
			return parseWhileStatement(); // Parse while statements
		}
		else if (currentToken.type == T_IDENTIFIER) { return parseAssignmentOrExpressionStatement(); }
		else { return parseExpressionStatement(); }
	}

	std::shared_ptr<ASTNode> parseIfStatement()
	{
		Token ifKeyword = currentToken;
		consumeToken(); // Consume "if"

		if (currentToken.type != T_PUNCTUATION || currentToken.value != "(")
		{
			throw std::runtime_error("Error: Expected '(' after 'if' at line " + std::to_string(currentToken.line) +
									 ", column " + std::to_string(currentToken.column));
		}
		consumeToken(); // Consume "("

		std::shared_ptr<ASTNode> condition = parseExpression();

		if (currentToken.type != T_PUNCTUATION || currentToken.value != ")")
		{
			throw std::runtime_error("Error: Expected ')' after if condition at line " +
									 std::to_string(currentToken.line) + ", column " +
									 std::to_string(currentToken.column));
		}
		consumeToken(); // Consume ")"

		std::shared_ptr<ASTNode> thenBlock = parseBlock(); // Parse the block following the if condition

		std::shared_ptr<ASTNode> elseBlock = nullptr; // Initialize else block as nullptr
		if (currentToken.type == T_KEYWORD && currentToken.value == "else")
		{
			consumeToken();           // Consume "else"
			elseBlock = parseBlock(); // Parse the else block
		}

		std::shared_ptr<ASTNode> ifStatement =
			std::make_shared<ASTNode>(AST_IF_STATEMENT, ifKeyword.value, ifKeyword.line, ifKeyword.column,
									  std::vector<std::shared_ptr<ASTNode>>());
		ifStatement->children.push_back(condition);
		ifStatement->children.push_back(thenBlock);
		if (elseBlock != nullptr) { ifStatement->children.push_back(elseBlock); }
		return ifStatement;
	}

	std::shared_ptr<ASTNode> parseWhileStatement()
	{
		Token whileKeyword = currentToken;
		consumeToken(); // Consume "while"

		if (currentToken.type != T_PUNCTUATION || currentToken.value != "(")
		{
			throw std::runtime_error("Error: Expected '(' after 'while' at line " + std::to_string(currentToken.line) +
									 ", column " + std::to_string(currentToken.column));
		}
		consumeToken(); // Consume "("

		std::shared_ptr<ASTNode> condition = parseExpression();

		if (currentToken.type != T_PUNCTUATION || currentToken.value != ")")
		{
			throw std::runtime_error("Error: Expected ')' after while condition at line " +
									 std::to_string(currentToken.line) + ", column " +
									 std::to_string(currentToken.column));
		}
		consumeToken(); // Consume ")"

		std::shared_ptr<ASTNode> body = parseBlock(); // Parse the block following the while condition

		std::shared_ptr<ASTNode> whileStatement =
			std::make_shared<ASTNode>(AST_WHILE_STATEMENT, whileKeyword.value, whileKeyword.line, whileKeyword.column,
									  std::vector<std::shared_ptr<ASTNode>>());
		whileStatement->children.push_back(condition);
		whileStatement->children.push_back(body);
		return whileStatement;
	}

	std::shared_ptr<ASTNode> parseBlock()
	{
		if (currentToken.type != T_PUNCTUATION || currentToken.value != "{")
		{
			throw std::runtime_error("Error: Expected '{' at line " + std::to_string(currentToken.line) + ", column " +
									 std::to_string(currentToken.column));
		}
		consumeToken(); // Consume "{"

		std::shared_ptr<ASTNode> block = std::make_shared<ASTNode>(
			AST_PROGRAM, "block", currentToken.line, currentToken.column, std::vector<std::shared_ptr<ASTNode>>());

		while (currentToken.type != T_PUNCTUATION || currentToken.value != "}")
		{
			block->children.push_back(parseStatement());
		}
		consumeToken(); // Consume "}"
		return block;
	}

	std::shared_ptr<ASTNode> parseAssignmentOrExpressionStatement()
	{
		Token identifier = currentToken;
		Token canditate = tokenizer->peekNextToken(); // consumeToken();

		if (canditate.type == T_OPERATOR && canditate.value == "=")
		{
			consumeToken();
			return parseAssignment(identifier);
		}
		else if (canditate.type == T_PUNCTUATION && canditate.value == "(")
		{
			// It's a function call, backtrack and parse it
			return parseExpressionStatement();
		}
		else
		{
			// It's a regular identifier, no backtracking needed
			consumeToken();
			return parseExpressionStatement();
		}
	}

	std::shared_ptr<ASTNode> parseAssignment(const Token& identifier)
	{
		consumeToken(); // Consume '='
		std::shared_ptr<ASTNode> right = parseExpression();
		if (currentToken.type != T_PUNCTUATION || currentToken.value != ";")
		{
			throw std::runtime_error("Error: Expected ';' at line " + std::to_string(currentToken.line) + ", column " +
									 std::to_string(currentToken.column));
		}
		consumeToken();

		std::shared_ptr<ASTNode> binaryExpression = std::make_shared<ASTNode>(
			AST_BINARY_EXPRESSION, "=", identifier.line, identifier.column, std::vector<std::shared_ptr<ASTNode>>());
		binaryExpression->children.push_back(std::make_shared<ASTNode>(AST_IDENTIFIER, identifier.value,
																	   identifier.line, identifier.column,
																	   std::vector<std::shared_ptr<ASTNode>>()));
		binaryExpression->children.push_back(right);

		std::shared_ptr<ASTNode> expressionStatement =
			std::make_shared<ASTNode>(AST_EXPRESSION_STATEMENT, "expression", identifier.line, identifier.column,
									  std::vector<std::shared_ptr<ASTNode>>());
		expressionStatement->children.push_back(binaryExpression);
		return expressionStatement;
	}

	std::shared_ptr<ASTNode> parseReturnStatement()
	{
		consumeToken();
		std::shared_ptr<ASTNode> expression = parseExpression();
		if (currentToken.type != T_PUNCTUATION || currentToken.value != ";")
		{
			throw std::runtime_error("Error: Expected ';' at line " + std::to_string(currentToken.line) + ", column " +
									 std::to_string(currentToken.column));
		}
		consumeToken();

		std::shared_ptr<ASTNode> returnStatement =
			std::make_shared<ASTNode>(AST_RETURN_STATEMENT, "return", currentToken.line, currentToken.column,
									  std::vector<std::shared_ptr<ASTNode>>());
		returnStatement->children.push_back(expression);
		return returnStatement;
	}

	std::shared_ptr<ASTNode> parseVariableDeclaration()
	{
		consumeToken();
		Token variableName = currentToken;
		consumeToken();

		if (currentToken.type != T_OPERATOR || currentToken.value != "=")
		{
			throw std::runtime_error("Error: Expected '=' at line " + std::to_string(currentToken.line) + ", column " +
									 std::to_string(currentToken.column));
		}
		consumeToken();
		std::shared_ptr<ASTNode> expression = parseExpression();

		if (currentToken.type != T_PUNCTUATION || currentToken.value != ";")
		{
			throw std::runtime_error("Error: Expected ';' at line " + std::to_string(currentToken.line) + ", column " +
									 std::to_string(currentToken.column));
		}
		consumeToken();

		std::shared_ptr<ASTNode> variableDeclaration =
			std::make_shared<ASTNode>(AST_VARIABLE_DECLARATION, variableName.value, variableName.line,
									  variableName.column, std::vector<std::shared_ptr<ASTNode>>());
		variableDeclaration->children.push_back(expression);
		return variableDeclaration;
	}

	std::shared_ptr<ASTNode> parseExpressionStatement()
	{
		std::shared_ptr<ASTNode> expression = parseExpression(); // This will now parse the call expression correctly
		if (currentToken.type != T_PUNCTUATION || currentToken.value != ";")
		{
			throw std::runtime_error("Error: Expected ';' at line " + std::to_string(currentToken.line) + ", column " +
									 std::to_string(currentToken.column));
		}
		consumeToken();

		std::shared_ptr<ASTNode> expressionStatement =
			std::make_shared<ASTNode>(AST_EXPRESSION_STATEMENT, "expression", currentToken.line, currentToken.column,
									  std::vector<std::shared_ptr<ASTNode>>());
		expressionStatement->children.push_back(expression);
		return expressionStatement;
	}

	std::shared_ptr<ASTNode> parseExpression() { return parseAdditiveExpression(); }

	std::shared_ptr<ASTNode> parseAdditiveExpression()
	{
		std::shared_ptr<ASTNode> left = parseMultiplicativeExpression();
		while (currentToken.type == T_OPERATOR && (currentToken.value == "+" || currentToken.value == "-"))
		{
			Token op = currentToken;
			consumeToken();
			std::shared_ptr<ASTNode> right = parseMultiplicativeExpression();
			left = std::make_shared<ASTNode>(AST_BINARY_EXPRESSION, op.value, op.line, op.column,
											 std::vector<std::shared_ptr<ASTNode>>{left, right});
		}
		return left;
	}

	std::shared_ptr<ASTNode> parseEqualityExpression()
	{
		std::shared_ptr<ASTNode> left = parseRelationalExpression();
		while (currentToken.type == T_OPERATOR && (currentToken.value == "==" || currentToken.value == "!="))
		{
			Token op = currentToken;
			consumeToken();
			std::shared_ptr<ASTNode> right = parseRelationalExpression();
			left = std::make_shared<ASTNode>(AST_BINARY_EXPRESSION, op.value, op.line, op.column,
											 std::vector<std::shared_ptr<ASTNode>>{left, right});
		}
		return left;
	}

	std::shared_ptr<ASTNode> parseRelationalExpression()
	{
		std::shared_ptr<ASTNode> left = parseAdditiveExpression();
		while (currentToken.type == T_OPERATOR && (currentToken.value == "<" || currentToken.value == ">" ||
												   currentToken.value == "<=" || currentToken.value == ">="))
		{
			Token op = currentToken;
			consumeToken();
			std::shared_ptr<ASTNode> right = parseAdditiveExpression();
			left = std::make_shared<ASTNode>(AST_BINARY_EXPRESSION, op.value, op.line, op.column,
											 std::vector<std::shared_ptr<ASTNode>>{left, right});
		}
		return left;
	}

	std::shared_ptr<ASTNode> parseMultiplicativeExpression()
	{
		std::shared_ptr<ASTNode> left = parseUnaryExpression(); // Changed to parseUnaryExpression
		while (currentToken.type == T_OPERATOR && (currentToken.value == "*" || currentToken.value == "/"))
		{
			Token op = currentToken;
			consumeToken();
			std::shared_ptr<ASTNode> right = parseUnaryExpression(); // Changed to parseUnaryExpression
			left = std::make_shared<ASTNode>(AST_BINARY_EXPRESSION, op.value, op.line, op.column,
											 std::vector<std::shared_ptr<ASTNode>>{left, right});
		}
		return left;
	}

	std::shared_ptr<ASTNode> parseUnaryExpression()
	{
		if (currentToken.type == T_OPERATOR && (currentToken.value == "-" || currentToken.value == "+"))
		{
			Token op = currentToken;
			consumeToken();
			std::shared_ptr<ASTNode> operand = parsePrimaryExpression();
			return std::make_shared<ASTNode>(AST_UNARY_EXPRESSION, op.value, op.line, op.column,
											 std::vector<std::shared_ptr<ASTNode>>{operand});
		}
		else if (currentToken.type == T_IDENTIFIER)
		{
			Token nextToken = tokenizer->peekNextToken();
			if (nextToken.type == T_OPERATOR && (nextToken.value == "++" || nextToken.value == "--"))
			{
				auto ret = parseIncrementDecrementOperator();
				nextToken = tokenizer->peekNextToken();
				if (nextToken.type == T_OPERATOR && (nextToken.value == "++" || nextToken.value == "--"))
					throw std::runtime_error("Error: Increment/Decrement operator can used only prefix or only postfix "
											 "variant in one expression. Line " +
											 std::to_string(nextToken.line) + ", column " +
											 std::to_string(nextToken.column));
				return ret;
			}
		}
		else if (currentToken.type == T_OPERATOR && (currentToken.value == "++" || currentToken.value == "--"))
		{
			auto ret = parseIncrementDecrementOperator();
			Token nextToken = tokenizer->peekNextToken();
			if (nextToken.type == T_OPERATOR && (nextToken.value == "++" || nextToken.value == "--"))
				throw std::runtime_error("Error: Increment/Decrement operator can used only prefix or only postfix "
										 "variant in one expression. Line " +
										 std::to_string(nextToken.line) + ", column " +
										 std::to_string(nextToken.column));
			return ret;
		}
		return parsePrimaryExpression();
	}

	std::shared_ptr<ASTNode> parseIncrementDecrementOperator()
	{
		Token operatorToken = currentToken;
		if (operatorToken.type == T_OPERATOR) consumeToken();
		std::shared_ptr<ASTNode> operand = parsePrimaryExpression();

		if (currentToken.type == T_OPERATOR && (currentToken.value == "++" || currentToken.value == "--") &&
			operand->type == AST_IDENTIFIER)
		{
			operatorToken = currentToken;
			consumeToken();
			return std::make_shared<ASTNode>(
				AST_UNARY_EXPRESSION, operatorToken.value, operatorToken.line, operatorToken.column,
				std::vector<std::shared_ptr<ASTNode>>{
					operand, std::make_shared<ASTNode>(AST_NUMBER, "POSTFIX", operatorToken.line, operatorToken.column,
													   std::vector<std::shared_ptr<ASTNode>>())});
		}
		else if (operand->type != AST_IDENTIFIER)
		{
			throw std::runtime_error("Error: Increment/Decrement operator can only be applied to identifiers at line " +
									 std::to_string(operatorToken.line) + ", column " +
									 std::to_string(operatorToken.column));
		}

		return std::make_shared<ASTNode>(
			AST_UNARY_EXPRESSION, operatorToken.value, operatorToken.line, operatorToken.column,
			std::vector<std::shared_ptr<ASTNode>>{
				operand, std::make_shared<ASTNode>(AST_STRING, "PREFIX", operatorToken.line, operatorToken.column,
												   std::vector<std::shared_ptr<ASTNode>>())});
	}

	std::shared_ptr<ASTNode> parsePrimaryExpression()
	{
		if (currentToken.type == T_IDENTIFIER)
		{
			Token identifier = currentToken;
			consumeToken();

			if (currentToken.type == T_PUNCTUATION && currentToken.value == "(")
			{
				return parseCallExpression(identifier);
			}
			else
			{
				return std::make_shared<ASTNode>(AST_IDENTIFIER, identifier.value, identifier.line, identifier.column,
												 std::vector<std::shared_ptr<ASTNode>>());
			}
		}
		else if (currentToken.type == T_NUMBER)
		{
			Token number = currentToken;
			consumeToken();
			return std::make_shared<ASTNode>(AST_NUMBER, number.value, number.line, number.column,
											 std::vector<std::shared_ptr<ASTNode>>());
		}
		else if (currentToken.type == T_BUILTIN)
		{
			Token identifier = currentToken;
			consumeToken();

			if (currentToken.type != T_PUNCTUATION || currentToken.value != "(")
				throw std::runtime_error("Error: Expected '(' in builtin call at line " +
										 std::to_string(currentToken.line) + ", column " +
										 std::to_string(currentToken.column));
			return parseBuiltinCall(identifier);
		}
		else if (currentToken.type == T_STRING)
		{
			Token stringLiteral = currentToken;
			consumeToken();
			return std::make_shared<ASTNode>(AST_STRING, stringLiteral.value, stringLiteral.line, stringLiteral.column,
											 std::vector<std::shared_ptr<ASTNode>>());
		}
		else if (currentToken.type == T_PUNCTUATION && currentToken.value == "(")
		{
			consumeToken();
			std::shared_ptr<ASTNode> expression = parseExpression();
			if (currentToken.type != T_PUNCTUATION || currentToken.value != ")")
			{
				throw std::runtime_error("Error: Expected ')' at line " + std::to_string(currentToken.line) +
										 ", column " + std::to_string(currentToken.column));
			}
			consumeToken();
			return expression;
		}
		else
		{
			throw std::runtime_error("Error: Unexpected token at line " + std::to_string(currentToken.line) +
									 ", column " + std::to_string(currentToken.column));
		}
	}

	std::shared_ptr<ASTNode> parseCallExpression(const Token& identifier)
	{
		consumeToken(); // Consume the '('
		std::vector<std::shared_ptr<ASTNode>> arguments;
		if (currentToken.type != T_PUNCTUATION || currentToken.value != ")") { arguments = parseArgumentList(); }

		if (currentToken.type != T_PUNCTUATION || currentToken.value != ")")
		{
			throw std::runtime_error("Error: Expected ')' at line " + std::to_string(currentToken.line) + ", column " +
									 std::to_string(currentToken.column));
		}
		consumeToken();

		std::shared_ptr<ASTNode> callExpression =
			std::make_shared<ASTNode>(AST_FUNCTION_CALL, identifier.value, identifier.line, identifier.column,
									  std::vector<std::shared_ptr<ASTNode>>());
		for (const auto& argument : arguments)
		{
			callExpression->children.push_back(argument);
		}

		return callExpression;
	}

	std::shared_ptr<ASTNode> parseBuiltinCall(const Token& identifier)
	{
		consumeToken(); // Consume the '('
		std::vector<std::shared_ptr<ASTNode>> arguments;
		if (currentToken.type != T_PUNCTUATION || currentToken.value != ")") { arguments = parseArgumentList(); }

		if (currentToken.type != T_PUNCTUATION || currentToken.value != ")")
		{
			throw std::runtime_error("Error: Expected ')' at line " + std::to_string(currentToken.line) + ", column " +
									 std::to_string(currentToken.column));
		}
		consumeToken();

		std::shared_ptr<ASTNode> callExpression =
			std::make_shared<ASTNode>(AST_BUILTIN_CALL, identifier.value, identifier.line, identifier.column,
									  std::vector<std::shared_ptr<ASTNode>>());
		for (const auto& argument : arguments)
		{
			callExpression->children.push_back(argument);
		}

		return callExpression;
	}

	std::vector<std::shared_ptr<ASTNode>> parseArgumentList()
	{
		std::vector<std::shared_ptr<ASTNode>> arguments;
		while (true)
		{
			arguments.push_back(parseExpression());
			if (currentToken.type == T_PUNCTUATION && currentToken.value == ",") { consumeToken(); }
			else { break; }
		}
		return arguments;
	}

	void consumeToken() { currentToken = tokenizer->nextToken(); }

	std::shared_ptr<ITokenizer> tokenizer;
	Token currentToken;
};

std::shared_ptr<IASTBuilder> IASTBuilder::GetASTBuilder(std::shared_ptr<ITokenizer> tokenizer, const uint32_t version)
{
	return std::make_shared<CASTBuilder>(tokenizer);
}
```
main.cpp:
```
#include <iostream>
#include <string>
#include <unordered_map>
#include <iomanip>
#include <sstream>

#include "tokenizer.hpp"
#include "ast.hpp"
#include "bytecode.hpp"

static void printAST(std::shared_ptr<ASTNode> node, int level)
{
	for (int i = 0; i < level; i++)
		std::cout << "\t";
	
	std::cout << std::to_string(node->type) << " (" << node->value << ")" << std::endl;
	for (auto& child : node->children)
		printAST(child, level + 1);
}

int main()
{
	std::string code = /*"cookie my_func()\n"
					   "{\n"
					   "    return __builtin_input();\n"
					   "}\n"
					   "\n"*/
					   "cookie my_func_with_args(cookie arg1, cookie arg2)\n"
					   "{\n"
					   "    return arg1 + arg2;\n"
					   "}\n"
					   /*"\n"
					   "void main()\n"
					   "{\n"
					   "    cookie my_val = 0;\n"
					   "    __builtin_cprint(my_val);\n"
					   "    my_val = my_func() + 32;\n"
					   "    __builtin_iprint(my_val);\n"
					   "    __builtin_iprint(my_func_with_args(my_val, 16));\n"
					   "}\n"*/;

	auto tokenizer = ITokenizer::GetTokenizer(code);
	{
		std::cout << "Tokens: " << std::endl;
		Token token;
		do
		{
			token = tokenizer->nextToken();
			std::cout << "Token: " << std::to_string(token.type) << ", Value: \"" << token.value
					  << "\", Line: " << token.line << ", Column: " << token.column << std::endl;
		} while (token.type != T_EOF);
		tokenizer->resetCurrent();
	}

	auto builder = IASTBuilder::GetASTBuilder(tokenizer);
	auto ast = builder->buildAST();
	{
		std::cout << std::endl << "Abstract Syntax Tree: " << std::endl;
		printAST(ast, 0);
	}

	auto bytecode_gen = IBytecodeGenerator::GetBytecodeGenerator(ast);
	auto bytecode = bytecode_gen->getBytecode();
	{
		std::cout << translateBytecodeToText(bytecode) << std::endl;
	}

	return 0;
}
```
Write ABI for this language and write bytecode.hpp and bytecode.cpp to translate that AST to BC961 code. About calling conventions and stack - I was having idea about virtual registers (for example first 10 cells was registers) and function calling via B[<function_name>.bc961], but you can create something by yourself. Response format: 
```
<ABI>
---
<code>
---
<additional info>
```