Изменения в версии 0.3
This commit is contained in:
+25
-12
@@ -1,14 +1,27 @@
|
|||||||
{
|
int coins = 100;
|
||||||
print("=== Welcome to FoxLang ===");
|
string playerName = "Alex";
|
||||||
|
|
||||||
print("Math test:");
|
|
||||||
print(20 + 30 * 2);
|
|
||||||
|
|
||||||
print("Type something:");
|
|
||||||
print(input());
|
|
||||||
|
|
||||||
{
|
|
||||||
print("I am inside a block!");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Коментприй
|
||||||
|
void showStats() {
|
||||||
|
print("--- Stats ---");
|
||||||
|
print("Player: " + playerName);
|
||||||
|
print("Coins: " + coins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mainLogic() {
|
||||||
|
print("Welcome to Fox Game!");
|
||||||
|
showStats();
|
||||||
|
|
||||||
|
print("You found a chest! Adding 50 coins.");
|
||||||
|
coins = coins + 50;
|
||||||
|
|
||||||
|
showStats();
|
||||||
|
|
||||||
|
print("Enter new name:");
|
||||||
|
playerName = input();
|
||||||
|
|
||||||
|
print("Updated profile:");
|
||||||
|
showStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
mainLogic();
|
||||||
@@ -2,75 +2,197 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
// Хелпер для красивого вывода чисел
|
// Предварительное объявление
|
||||||
|
struct Node;
|
||||||
|
|
||||||
|
// --- ПАМЯТЬ ЯЗЫКА ---
|
||||||
|
struct Context {
|
||||||
|
// Переменные: Имя -> Значение
|
||||||
|
std::map<std::string, std::string> variables;
|
||||||
|
// Типы переменных: Имя -> Тип ("int", "string")
|
||||||
|
std::map<std::string, std::string> varTypes;
|
||||||
|
// Функции: Имя -> Узел тела функции (BlockNode)
|
||||||
|
std::map<std::string, std::shared_ptr<Node>> functions;
|
||||||
|
|
||||||
|
bool exists(const std::string& name) {
|
||||||
|
return variables.find(name) != variables.end();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static std::string formatNumber(double val) {
|
static std::string formatNumber(double val) {
|
||||||
std::string s = std::to_string(val);
|
std::string s = std::to_string(val);
|
||||||
// Удаляем лишние нули и точку, если число целое
|
|
||||||
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
|
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
|
||||||
if (s.back() == '.') s.pop_back();
|
if (s.back() == '.') s.pop_back();
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- УЗЛЫ ---
|
||||||
struct Node {
|
struct Node {
|
||||||
virtual ~Node() = default;
|
virtual ~Node() = default;
|
||||||
virtual std::string eval() = 0;
|
virtual std::string eval(Context& ctx) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 1. Базовые значения
|
||||||
struct NumberNode : Node {
|
struct NumberNode : Node {
|
||||||
std::string val;
|
std::string val;
|
||||||
NumberNode(std::string v) : val(v) {}
|
NumberNode(std::string v) : val(v) {}
|
||||||
std::string eval() override { return val; }
|
std::string eval(Context& ctx) override { return val; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct StringNode : Node {
|
||||||
|
std::string val;
|
||||||
|
StringNode(std::string v) : val(v) {}
|
||||||
|
std::string eval(Context& ctx) override { return val; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. Доступ к переменной (x)
|
||||||
|
struct VarAccessNode : Node {
|
||||||
|
std::string name;
|
||||||
|
VarAccessNode(std::string n) : name(n) {}
|
||||||
|
std::string eval(Context& ctx) override {
|
||||||
|
if (!ctx.exists(name)) {
|
||||||
|
std::cerr << "Runtime Error: Variable '" << name << "' is not defined." << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return ctx.variables[name];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 3. Объявление переменной (int x = 5;)
|
||||||
|
struct VarDeclNode : Node {
|
||||||
|
std::string type;
|
||||||
|
std::string name;
|
||||||
|
std::unique_ptr<Node> expr;
|
||||||
|
VarDeclNode(std::string t, std::string n, std::unique_ptr<Node> e)
|
||||||
|
: type(t), name(n), expr(std::move(e)) {}
|
||||||
|
|
||||||
|
std::string eval(Context& ctx) override {
|
||||||
|
if (ctx.exists(name)) {
|
||||||
|
std::cerr << "Error: Variable '" << name << "' already exists." << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
std::string val = expr->eval(ctx);
|
||||||
|
// Простая проверка типа (можно усложнить)
|
||||||
|
if (type == "int") {
|
||||||
|
try { std::stod(val); } catch(...) {
|
||||||
|
std::cerr << "Type Error: Cannot assign string to int '" << name << "'" << std::endl; exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.variables[name] = val;
|
||||||
|
ctx.varTypes[name] = type;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 4. Присваивание (x = 10;)
|
||||||
|
struct AssignNode : Node {
|
||||||
|
std::string name;
|
||||||
|
std::unique_ptr<Node> expr;
|
||||||
|
AssignNode(std::string n, std::unique_ptr<Node> e) : name(n), expr(std::move(e)) {}
|
||||||
|
|
||||||
|
std::string eval(Context& ctx) override {
|
||||||
|
if (!ctx.exists(name)) {
|
||||||
|
std::cerr << "Error: Variable '" << name << "' not declared." << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
std::string val = expr->eval(ctx);
|
||||||
|
ctx.variables[name] = val; // Тут можно добавить проверку типа снова
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 5. Математика
|
||||||
struct BinOpNode : Node {
|
struct BinOpNode : Node {
|
||||||
char op;
|
char op;
|
||||||
std::unique_ptr<Node> left, right;
|
std::unique_ptr<Node> left, right;
|
||||||
BinOpNode(char o, std::unique_ptr<Node> l, std::unique_ptr<Node> r)
|
BinOpNode(char o, std::unique_ptr<Node> l, std::unique_ptr<Node> r)
|
||||||
: op(o), left(std::move(l)), right(std::move(r)) {}
|
: op(o), left(std::move(l)), right(std::move(r)) {}
|
||||||
|
|
||||||
|
std::string eval(Context& ctx) override {
|
||||||
|
std::string lStr = left->eval(ctx);
|
||||||
|
std::string rStr = right->eval(ctx);
|
||||||
|
|
||||||
|
// Если это строки и операция +, конкатенируем
|
||||||
|
bool isStr = (lStr.find_first_not_of("0123456789.-") != std::string::npos) ||
|
||||||
|
(rStr.find_first_not_of("0123456789.-") != std::string::npos);
|
||||||
|
|
||||||
|
if (op == '+' && isStr) return lStr + rStr;
|
||||||
|
|
||||||
std::string eval() override {
|
double l = std::stod(lStr);
|
||||||
double l = std::stod(left->eval());
|
double r = std::stod(rStr);
|
||||||
double r = std::stod(right->eval());
|
if (op == '+') return formatNumber(l + r);
|
||||||
double res = 0;
|
if (op == '-') return formatNumber(l - r);
|
||||||
if (op == '+') res = l + r;
|
if (op == '*') return formatNumber(l * r);
|
||||||
else if (op == '-') res = l - r;
|
if (op == '/') return formatNumber((r!=0)? l/r : 0);
|
||||||
else if (op == '*') res = l * r;
|
return "0";
|
||||||
else if (op == '/') res = (r != 0) ? l / r : 0;
|
|
||||||
return formatNumber(res);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RoundNode : Node {
|
// 6. Блок кода { ... }
|
||||||
std::unique_ptr<Node> expr;
|
struct BlockNode : Node {
|
||||||
RoundNode(std::unique_ptr<Node> e) : expr(std::move(e)) {}
|
std::vector<std::unique_ptr<Node>> statements;
|
||||||
std::string eval() override {
|
std::string eval(Context& ctx) override {
|
||||||
double val = std::stod(expr->eval());
|
std::string lastVal = "";
|
||||||
return formatNumber(std::round(val));
|
for (auto& stmt : statements) {
|
||||||
|
lastVal = stmt->eval(ctx);
|
||||||
|
}
|
||||||
|
return lastVal;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RandomNode : Node {
|
// 7. Объявление функции (void myFunc() { ... })
|
||||||
std::string eval() override {
|
// Мы не выполняем тело, а СОХРАНЯЕМ его в память.
|
||||||
return std::to_string(rand() % 100); // Случайное от 0 до 99
|
struct FuncDefNode : Node {
|
||||||
|
std::string name;
|
||||||
|
std::shared_ptr<Node> body; // Используем shared_ptr, чтобы хранить в мапе
|
||||||
|
FuncDefNode(std::string n, std::shared_ptr<Node> b) : name(n), body(b) {}
|
||||||
|
|
||||||
|
std::string eval(Context& ctx) override {
|
||||||
|
ctx.functions[name] = body;
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 8. Вызов функции (myFunc())
|
||||||
|
struct FuncCallNode : Node {
|
||||||
|
std::string name;
|
||||||
|
FuncCallNode(std::string n) : name(n) {}
|
||||||
|
|
||||||
|
std::string eval(Context& ctx) override {
|
||||||
|
if (ctx.functions.find(name) == ctx.functions.end()) {
|
||||||
|
std::cerr << "Error: Function '" << name << "' is not defined." << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
// Выполняем сохраненное тело
|
||||||
|
return ctx.functions[name]->eval(ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Стандартные функции
|
||||||
struct PrintNode : Node {
|
struct PrintNode : Node {
|
||||||
std::unique_ptr<Node> expr;
|
std::unique_ptr<Node> expr;
|
||||||
PrintNode(std::unique_ptr<Node> e) : expr(std::move(e)) {}
|
PrintNode(std::unique_ptr<Node> e) : expr(std::move(e)) {}
|
||||||
std::string eval() override {
|
std::string eval(Context& ctx) override {
|
||||||
std::cout << expr->eval() << std::endl;
|
std::cout << expr->eval(ctx) << std::endl;
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InputNode : Node {
|
struct InputNode : Node {
|
||||||
std::string eval() override {
|
std::string eval(Context& ctx) override {
|
||||||
std::string buffer;
|
std::string b; std::getline(std::cin, b); return b;
|
||||||
std::getline(std::cin, buffer);
|
|
||||||
return buffer;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
struct RoundNode : Node {
|
||||||
|
std::unique_ptr<Node> expr;
|
||||||
|
RoundNode(std::unique_ptr<Node> e) : expr(std::move(e)) {}
|
||||||
|
std::string eval(Context& ctx) override { return formatNumber(std::round(std::stod(expr->eval(ctx)))); }
|
||||||
|
};
|
||||||
|
struct RandomNode : Node {
|
||||||
|
std::string eval(Context& ctx) override { return std::to_string(rand() % 100); }
|
||||||
|
};
|
||||||
+34
-14
@@ -8,50 +8,70 @@ std::vector<Token> Lexer::tokenize() {
|
|||||||
std::vector<Token> tokens;
|
std::vector<Token> tokens;
|
||||||
while (pos < source.length()) {
|
while (pos < source.length()) {
|
||||||
char current = source[pos];
|
char current = source[pos];
|
||||||
|
|
||||||
|
// 1. Пропуск пробелов
|
||||||
if (isspace(current)) {
|
if (isspace(current)) {
|
||||||
if (current == '\n') line++;
|
if (current == '\n') line++;
|
||||||
pos++;
|
pos++;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
else if (isdigit(current)) {
|
|
||||||
|
// 2. ОБРАБОТКА КОММЕНТАРИЕВ (НОВОЕ)
|
||||||
|
// Если видим '/', и следующий символ тоже '/', то это комментарий
|
||||||
|
if (current == '/' && pos + 1 < source.length() && source[pos + 1] == '/') {
|
||||||
|
while (pos < source.length() && source[pos] != '\n') {
|
||||||
|
pos++; // Пропускаем всё до конца строки
|
||||||
|
}
|
||||||
|
continue; // Переходим к следующей итерации цикла
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Числа
|
||||||
|
if (isdigit(current)) {
|
||||||
std::string num;
|
std::string num;
|
||||||
while (pos < source.length() && isdigit(source[pos])) num += source[pos++];
|
while (pos < source.length() && isdigit(source[pos])) num += source[pos++];
|
||||||
// Точка для дробных чисел (например, 3.14)
|
|
||||||
if (pos < source.length() && source[pos] == '.') {
|
if (pos < source.length() && source[pos] == '.') {
|
||||||
num += source[pos++];
|
num += source[pos++];
|
||||||
while (pos < source.length() && isdigit(source[pos])) num += source[pos++];
|
while (pos < source.length() && isdigit(source[pos])) num += source[pos++];
|
||||||
}
|
}
|
||||||
tokens.push_back({TokenType::NUMBER, num, line});
|
tokens.push_back({TokenType::NUMBER, num, line});
|
||||||
}
|
}
|
||||||
|
// 4. Строки
|
||||||
else if (current == '"') {
|
else if (current == '"') {
|
||||||
pos++; std::string str;
|
pos++; std::string str;
|
||||||
while (pos < source.length() && source[pos] != '"') str += source[pos++];
|
while (pos < source.length() && source[pos] != '"') str += source[pos++];
|
||||||
pos++;
|
pos++;
|
||||||
tokens.push_back({TokenType::STRING, str, line});
|
tokens.push_back({TokenType::STRING_LITERAL, str, line});
|
||||||
}
|
}
|
||||||
|
// 5. Идентификаторы и Ключевые слова
|
||||||
else if (isalpha(current)) {
|
else if (isalpha(current)) {
|
||||||
std::string id;
|
std::string id;
|
||||||
while (pos < source.length() && isalpha(source[pos])) id += source[pos++];
|
while (pos < source.length() && isalnum(source[pos])) id += source[pos++];
|
||||||
|
|
||||||
if (id == "print") tokens.push_back({TokenType::PRINT, id, line});
|
if (id == "print") tokens.push_back({TokenType::PRINT, id, line});
|
||||||
else if (id == "input") tokens.push_back({TokenType::INPUT, id, line});
|
else if (id == "input") tokens.push_back({TokenType::INPUT, id, line});
|
||||||
else if (id == "round") tokens.push_back({TokenType::ROUND, id, line});
|
else if (id == "round") tokens.push_back({TokenType::ROUND, id, line});
|
||||||
else if (id == "random") tokens.push_back({TokenType::RANDOM, id, line});
|
else if (id == "random") tokens.push_back({TokenType::RANDOM, id, line});
|
||||||
else if (id == "fox") tokens.push_back({TokenType::FOX, id, line});
|
else if (id == "fox") tokens.push_back({TokenType::FOX, id, line});
|
||||||
else tokens.push_back({TokenType::IDENTIFIER, id, line}); // Неизвестное слово
|
else if (id == "int") tokens.push_back({TokenType::INT_KW, id, line});
|
||||||
}
|
else if (id == "string") tokens.push_back({TokenType::STRING_KW, id, line});
|
||||||
|
else if (id == "void") tokens.push_back({TokenType::VOID_KW, id, line});
|
||||||
|
else tokens.push_back({TokenType::IDENTIFIER, id, line});
|
||||||
|
}
|
||||||
|
// 6. Операторы
|
||||||
else {
|
else {
|
||||||
switch (current) {
|
switch (current) {
|
||||||
case '+': tokens.push_back({TokenType::PLUS, "+", line}); break;
|
case '+': tokens.push_back({TokenType::PLUS, "+", line}); break;
|
||||||
case '-': tokens.push_back({TokenType::MINUS, "-", line}); break;
|
case '-': tokens.push_back({TokenType::MINUS, "-", line}); break;
|
||||||
case '*': tokens.push_back({TokenType::STAR, "*", line}); break;
|
case '*': tokens.push_back({TokenType::STAR, "*", line}); break;
|
||||||
case '/': tokens.push_back({TokenType::SLASH, "/", line}); break;
|
case '/': tokens.push_back({TokenType::SLASH, "/", line}); break; // Это деление, если не сработало условие выше
|
||||||
case '(': tokens.push_back({TokenType::LPAREN, "(", line}); break;
|
case '(': tokens.push_back({TokenType::LPAREN, "(", line}); break;
|
||||||
case ')': tokens.push_back({TokenType::RPAREN, ")", line}); break;
|
case ')': tokens.push_back({TokenType::RPAREN, ")", line}); break;
|
||||||
case '{': tokens.push_back({TokenType::LBRACE, "{", line}); break;
|
case '{': tokens.push_back({TokenType::LBRACE, "{", line}); break;
|
||||||
case '}': tokens.push_back({TokenType::RBRACE, "}", line}); break;
|
case '}': tokens.push_back({TokenType::RBRACE, "}", line}); break;
|
||||||
case ';': tokens.push_back({TokenType::SEMICOLON, ";", line}); break;
|
case ';': tokens.push_back({TokenType::SEMICOLON, ";", line}); break;
|
||||||
default:
|
case ',': tokens.push_back({TokenType::COMMA, ",", line}); break;
|
||||||
|
case '=': tokens.push_back({TokenType::ASSIGN, "=", line}); break;
|
||||||
|
default:
|
||||||
std::cerr << "Lexer Error: Unknown char '" << current << "' at line " << line << std::endl;
|
std::cerr << "Lexer Error: Unknown char '" << current << "' at line " << line << std::endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@@ -60,4 +80,4 @@ std::vector<Token> Lexer::tokenize() {
|
|||||||
}
|
}
|
||||||
tokens.push_back({TokenType::END, "", line});
|
tokens.push_back({TokenType::END, "", line});
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
+86
-51
@@ -5,46 +5,46 @@ Parser::Parser(std::vector<Token> t) : tokens(t) {}
|
|||||||
|
|
||||||
Token Parser::consume(TokenType type) {
|
Token Parser::consume(TokenType type) {
|
||||||
if (tokens[pos].type == type) return tokens[pos++];
|
if (tokens[pos].type == type) return tokens[pos++];
|
||||||
std::cerr << "Syntax Error: Expected token type " << (int)type
|
std::cerr << "Syntax Error: Expected token type " << (int)type
|
||||||
<< " but found '" << tokens[pos].value
|
<< " but found '" << tokens[pos].value
|
||||||
<< "' on line " << tokens[pos].line << std::endl;
|
<< "' on line " << tokens[pos].line << std::endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Выражения (возвращают значение)
|
|
||||||
std::unique_ptr<Node> Parser::primary() {
|
std::unique_ptr<Node> Parser::primary() {
|
||||||
if (tokens[pos].type == TokenType::NUMBER) return std::make_unique<NumberNode>(consume(TokenType::NUMBER).value);
|
if (tokens[pos].type == TokenType::NUMBER)
|
||||||
if (tokens[pos].type == TokenType::STRING) return std::make_unique<NumberNode>(consume(TokenType::STRING).value);
|
return std::make_unique<NumberNode>(consume(TokenType::NUMBER).value);
|
||||||
|
|
||||||
// input()
|
if (tokens[pos].type == TokenType::STRING_LITERAL)
|
||||||
if (tokens[pos].type == TokenType::INPUT) {
|
return std::make_unique<StringNode>(consume(TokenType::STRING_LITERAL).value);
|
||||||
consume(TokenType::INPUT); consume(TokenType::LPAREN); consume(TokenType::RPAREN);
|
|
||||||
return std::make_unique<InputNode>();
|
if (tokens[pos].type == TokenType::IDENTIFIER) {
|
||||||
|
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||||
|
if (tokens[pos].type == TokenType::LPAREN) {
|
||||||
|
consume(TokenType::LPAREN);
|
||||||
|
consume(TokenType::RPAREN);
|
||||||
|
return std::make_unique<FuncCallNode>(name);
|
||||||
|
}
|
||||||
|
return std::make_unique<VarAccessNode>(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// round( expr )
|
if (tokens[pos].type == TokenType::INPUT) {
|
||||||
|
consume(TokenType::INPUT); consume(TokenType::LPAREN); consume(TokenType::RPAREN);
|
||||||
|
return std::make_unique<InputNode>();
|
||||||
|
}
|
||||||
if (tokens[pos].type == TokenType::ROUND) {
|
if (tokens[pos].type == TokenType::ROUND) {
|
||||||
consume(TokenType::ROUND);
|
consume(TokenType::ROUND); consume(TokenType::LPAREN); auto n = expression(); consume(TokenType::RPAREN);
|
||||||
consume(TokenType::LPAREN);
|
return std::make_unique<RoundNode>(std::move(n));
|
||||||
auto node = expression();
|
|
||||||
consume(TokenType::RPAREN);
|
|
||||||
return std::make_unique<RoundNode>(std::move(node));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// random()
|
|
||||||
if (tokens[pos].type == TokenType::RANDOM) {
|
if (tokens[pos].type == TokenType::RANDOM) {
|
||||||
consume(TokenType::RANDOM); consume(TokenType::LPAREN); consume(TokenType::RPAREN);
|
consume(TokenType::RANDOM); consume(TokenType::LPAREN); consume(TokenType::RPAREN);
|
||||||
return std::make_unique<RandomNode>();
|
return std::make_unique<RandomNode>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokens[pos].type == TokenType::LPAREN) {
|
if (tokens[pos].type == TokenType::LPAREN) {
|
||||||
consume(TokenType::LPAREN);
|
consume(TokenType::LPAREN); auto n = expression(); consume(TokenType::RPAREN); return n;
|
||||||
auto node = expression();
|
|
||||||
consume(TokenType::RPAREN);
|
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cerr << "Error: Unexpected token '" << tokens[pos].value << "' in expression at line " << tokens[pos].line << std::endl;
|
std::cerr << "Error: Unexpected token '" << tokens[pos].value << "' line " << tokens[pos].line << std::endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,46 +66,81 @@ std::unique_ptr<Node> Parser::expression() {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Блоки кода
|
// ИЗМЕНЕНИЕ: Возвращаем unique_ptr и используем make_unique
|
||||||
void Parser::block() {
|
std::unique_ptr<BlockNode> Parser::parseBlock() {
|
||||||
consume(TokenType::LBRACE);
|
consume(TokenType::LBRACE);
|
||||||
|
auto block = std::make_unique<BlockNode>(); // Здесь было make_shared
|
||||||
while (tokens[pos].type != TokenType::RBRACE && tokens[pos].type != TokenType::END) {
|
while (tokens[pos].type != TokenType::RBRACE && tokens[pos].type != TokenType::END) {
|
||||||
statement();
|
block->statements.push_back(statement());
|
||||||
}
|
}
|
||||||
consume(TokenType::RBRACE);
|
consume(TokenType::RBRACE);
|
||||||
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Инструкции (заканчиваются ;)
|
std::unique_ptr<Node> Parser::statement() {
|
||||||
void Parser::statement() {
|
// 1. Объявление переменных
|
||||||
if (tokens[pos].type == TokenType::PRINT) {
|
if (tokens[pos].type == TokenType::INT_KW || tokens[pos].type == TokenType::STRING_KW) {
|
||||||
consume(TokenType::PRINT);
|
std::string type = tokens[pos].value;
|
||||||
consume(TokenType::LPAREN);
|
pos++;
|
||||||
|
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||||
|
consume(TokenType::ASSIGN);
|
||||||
auto expr = expression();
|
auto expr = expression();
|
||||||
consume(TokenType::RPAREN);
|
|
||||||
consume(TokenType::SEMICOLON);
|
consume(TokenType::SEMICOLON);
|
||||||
auto pNode = std::make_unique<PrintNode>(std::move(expr));
|
return std::make_unique<VarDeclNode>(type, name, std::move(expr));
|
||||||
pNode->eval();
|
|
||||||
}
|
}
|
||||||
else if (tokens[pos].type == TokenType::FOX) {
|
|
||||||
consume(TokenType::FOX);
|
// 2. Объявление функций
|
||||||
consume(TokenType::LPAREN); // fox (
|
if (tokens[pos].type == TokenType::VOID_KW) {
|
||||||
consume(TokenType::RPAREN); // )
|
consume(TokenType::VOID_KW);
|
||||||
consume(TokenType::SEMICOLON); // ;
|
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||||
std::cout << " (\\_/) \n (o.o) \n (> <) OFOX!" << std::endl;
|
consume(TokenType::LPAREN);
|
||||||
|
consume(TokenType::RPAREN);
|
||||||
|
|
||||||
|
// Тут магия: unique_ptr автоматически превратится в shared_ptr при создании FuncDefNode
|
||||||
|
auto body = parseBlock();
|
||||||
|
return std::make_unique<FuncDefNode>(name, std::move(body));
|
||||||
}
|
}
|
||||||
else if (tokens[pos].type == TokenType::LBRACE) {
|
|
||||||
block();
|
// 3. Команда PRINT
|
||||||
|
if (tokens[pos].type == TokenType::PRINT) {
|
||||||
|
consume(TokenType::PRINT); consume(TokenType::LPAREN);
|
||||||
|
auto expr = expression();
|
||||||
|
consume(TokenType::RPAREN); consume(TokenType::SEMICOLON);
|
||||||
|
return std::make_unique<PrintNode>(std::move(expr));
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// ОБРАБОТКА ОШИБОК: Если встретили слово, которое не является командой
|
if (tokens[pos].type == TokenType::FOX) {
|
||||||
std::cerr << "Runtime Error: Unknown command or function '" << tokens[pos].value
|
consume(TokenType::FOX); consume(TokenType::LPAREN); consume(TokenType::RPAREN); consume(TokenType::SEMICOLON);
|
||||||
<< "' at line " << tokens[pos].line << std::endl;
|
return std::make_unique<StringNode>("Fox says: Hello!");
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tokens[pos].type == TokenType::IDENTIFIER) {
|
||||||
|
if (tokens[pos+1].type == TokenType::ASSIGN) {
|
||||||
|
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||||
|
consume(TokenType::ASSIGN);
|
||||||
|
auto expr = expression();
|
||||||
|
consume(TokenType::SEMICOLON);
|
||||||
|
return std::make_unique<AssignNode>(name, std::move(expr));
|
||||||
|
}
|
||||||
|
if (tokens[pos+1].type == TokenType::LPAREN) {
|
||||||
|
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||||
|
consume(TokenType::LPAREN); consume(TokenType::RPAREN); consume(TokenType::SEMICOLON);
|
||||||
|
return std::make_unique<FuncCallNode>(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ИЗМЕНЕНИЕ: Просто возвращаем результат parseBlock, никаких преобразований не нужно
|
||||||
|
if (tokens[pos].type == TokenType::LBRACE) {
|
||||||
|
return parseBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Syntax Error: Unknown statement '" << tokens[pos].value << "' at line " << tokens[pos].line << std::endl;
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::run() {
|
void Parser::run() {
|
||||||
while (tokens[pos].type != TokenType::END) {
|
while (tokens[pos].type != TokenType::END) {
|
||||||
statement();
|
auto stmt = statement();
|
||||||
|
stmt->eval(globalContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+7
-4
@@ -7,15 +7,18 @@
|
|||||||
class Parser {
|
class Parser {
|
||||||
std::vector<Token> tokens;
|
std::vector<Token> tokens;
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
|
Context globalContext;
|
||||||
|
|
||||||
Token consume(TokenType type);
|
Token consume(TokenType type);
|
||||||
std::unique_ptr<Node> primary();
|
std::unique_ptr<Node> primary();
|
||||||
std::unique_ptr<Node> multiplication();
|
std::unique_ptr<Node> multiplication();
|
||||||
std::unique_ptr<Node> expression();
|
std::unique_ptr<Node> expression();
|
||||||
void statement(); // Обработка отдельной команды с точкой с запятой
|
|
||||||
void block(); // Обработка блока { ... }
|
std::unique_ptr<Node> statement();
|
||||||
|
// ИЗМЕНЕНИЕ: теперь возвращает unique_ptr
|
||||||
|
std::unique_ptr<BlockNode> parseBlock();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Parser(std::vector<Token> t);
|
Parser(std::vector<Token> t);
|
||||||
void run();
|
void run();
|
||||||
};
|
};
|
||||||
+11
-8
@@ -2,13 +2,16 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
enum class TokenType {
|
enum class TokenType {
|
||||||
NUMBER, STRING,
|
NUMBER, STRING_LITERAL,
|
||||||
PLUS, MINUS, STAR, SLASH,
|
PLUS, MINUS, STAR, SLASH,
|
||||||
LPAREN, RPAREN,
|
LPAREN, RPAREN, LBRACE, RBRACE,
|
||||||
LBRACE, RBRACE, SEMICOLON,
|
SEMICOLON, COMMA, ASSIGN, // =, , ;
|
||||||
PRINT, INPUT,
|
|
||||||
ROUND, FOX, RANDOM, // Новые команды
|
// Ключевые слова
|
||||||
IDENTIFIER, // Для неизвестных слов
|
PRINT, INPUT, ROUND, RANDOM, FOX,
|
||||||
|
INT_KW, STRING_KW, VOID_KW, // int, string, void
|
||||||
|
|
||||||
|
IDENTIFIER, // Имена переменных и функций
|
||||||
END, ERROR
|
END, ERROR
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -16,4 +19,4 @@ struct Token {
|
|||||||
TokenType type;
|
TokenType type;
|
||||||
std::string value;
|
std::string value;
|
||||||
int line;
|
int line;
|
||||||
};
|
};
|
||||||
BIN
Binary file not shown.
Reference in New Issue
Block a user