diff --git a/script.fox b/script.fox index 08fe969..880e854 100644 --- a/script.fox +++ b/script.fox @@ -1,14 +1,27 @@ -{ - print("=== Welcome to FoxLang ==="); - - print("Math test:"); - print(20 + 30 * 2); - - print("Type something:"); - print(input()); - - { - print("I am inside a block!"); - } +int coins = 100; +string playerName = "Alex"; +// Коментприй +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(); \ No newline at end of file diff --git a/src/AST.h b/src/AST.h index 01bb260..921cc02 100644 --- a/src/AST.h +++ b/src/AST.h @@ -2,75 +2,197 @@ #include #include #include +#include +#include #include #include -// Хелпер для красивого вывода чисел +// Предварительное объявление +struct Node; + +// --- ПАМЯТЬ ЯЗЫКА --- +struct Context { + // Переменные: Имя -> Значение + std::map variables; + // Типы переменных: Имя -> Тип ("int", "string") + std::map varTypes; + // Функции: Имя -> Узел тела функции (BlockNode) + std::map> functions; + + bool exists(const std::string& name) { + return variables.find(name) != variables.end(); + } +}; + static std::string formatNumber(double val) { std::string s = std::to_string(val); - // Удаляем лишние нули и точку, если число целое s.erase(s.find_last_not_of('0') + 1, std::string::npos); if (s.back() == '.') s.pop_back(); return s; } +// --- УЗЛЫ --- struct Node { virtual ~Node() = default; - virtual std::string eval() = 0; + virtual std::string eval(Context& ctx) = 0; }; +// 1. Базовые значения struct NumberNode : Node { std::string val; 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 expr; + VarDeclNode(std::string t, std::string n, std::unique_ptr 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 expr; + AssignNode(std::string n, std::unique_ptr 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 { char op; std::unique_ptr left, right; - BinOpNode(char o, std::unique_ptr l, std::unique_ptr r) - : op(o), left(std::move(l)), right(std::move(r)) {} + BinOpNode(char o, std::unique_ptr l, std::unique_ptr 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(left->eval()); - double r = std::stod(right->eval()); - double res = 0; - if (op == '+') res = l + r; - else if (op == '-') res = l - r; - else if (op == '*') res = l * r; - else if (op == '/') res = (r != 0) ? l / r : 0; - return formatNumber(res); + double l = std::stod(lStr); + double r = std::stod(rStr); + if (op == '+') return formatNumber(l + r); + if (op == '-') return formatNumber(l - r); + if (op == '*') return formatNumber(l * r); + if (op == '/') return formatNumber((r!=0)? l/r : 0); + return "0"; } }; -struct RoundNode : Node { - std::unique_ptr expr; - RoundNode(std::unique_ptr e) : expr(std::move(e)) {} - std::string eval() override { - double val = std::stod(expr->eval()); - return formatNumber(std::round(val)); +// 6. Блок кода { ... } +struct BlockNode : Node { + std::vector> statements; + std::string eval(Context& ctx) override { + std::string lastVal = ""; + for (auto& stmt : statements) { + lastVal = stmt->eval(ctx); + } + return lastVal; } }; -struct RandomNode : Node { - std::string eval() override { - return std::to_string(rand() % 100); // Случайное от 0 до 99 +// 7. Объявление функции (void myFunc() { ... }) +// Мы не выполняем тело, а СОХРАНЯЕМ его в память. +struct FuncDefNode : Node { + std::string name; + std::shared_ptr body; // Используем shared_ptr, чтобы хранить в мапе + FuncDefNode(std::string n, std::shared_ptr 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 { std::unique_ptr expr; PrintNode(std::unique_ptr e) : expr(std::move(e)) {} - std::string eval() override { - std::cout << expr->eval() << std::endl; + std::string eval(Context& ctx) override { + std::cout << expr->eval(ctx) << std::endl; return ""; } }; struct InputNode : Node { - std::string eval() override { - std::string buffer; - std::getline(std::cin, buffer); - return buffer; + std::string eval(Context& ctx) override { + std::string b; std::getline(std::cin, b); return b; } }; +struct RoundNode : Node { + std::unique_ptr expr; + RoundNode(std::unique_ptr 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); } +}; \ No newline at end of file diff --git a/src/Lexer.cpp b/src/Lexer.cpp index 049b842..bd7cf4f 100644 --- a/src/Lexer.cpp +++ b/src/Lexer.cpp @@ -8,50 +8,70 @@ std::vector Lexer::tokenize() { std::vector tokens; while (pos < source.length()) { char current = source[pos]; - + + // 1. Пропуск пробелов if (isspace(current)) { if (current == '\n') line++; 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; while (pos < source.length() && isdigit(source[pos])) num += source[pos++]; - // Точка для дробных чисел (например, 3.14) if (pos < source.length() && source[pos] == '.') { num += source[pos++]; while (pos < source.length() && isdigit(source[pos])) num += source[pos++]; } tokens.push_back({TokenType::NUMBER, num, line}); - } + } + // 4. Строки else if (current == '"') { pos++; std::string str; while (pos < source.length() && source[pos] != '"') str += source[pos++]; - pos++; - tokens.push_back({TokenType::STRING, str, line}); - } + pos++; + tokens.push_back({TokenType::STRING_LITERAL, str, line}); + } + // 5. Идентификаторы и Ключевые слова else if (isalpha(current)) { 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}); 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 == "random") tokens.push_back({TokenType::RANDOM, 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 { switch (current) { case '+': tokens.push_back({TokenType::PLUS, "+", line}); break; case '-': tokens.push_back({TokenType::MINUS, "-", 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::RPAREN, ")", line}); break; case '{': tokens.push_back({TokenType::LBRACE, "{", line}); break; case '}': tokens.push_back({TokenType::RBRACE, "}", 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; exit(1); } @@ -60,4 +80,4 @@ std::vector Lexer::tokenize() { } tokens.push_back({TokenType::END, "", line}); return tokens; -} +} \ No newline at end of file diff --git a/src/Parser.cpp b/src/Parser.cpp index b507e5d..6613b95 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -5,46 +5,46 @@ Parser::Parser(std::vector t) : tokens(t) {} Token Parser::consume(TokenType type) { if (tokens[pos].type == type) return tokens[pos++]; - std::cerr << "Syntax Error: Expected token type " << (int)type - << " but found '" << tokens[pos].value - << "' on line " << tokens[pos].line << std::endl; + std::cerr << "Syntax Error: Expected token type " << (int)type + << " but found '" << tokens[pos].value + << "' on line " << tokens[pos].line << std::endl; exit(1); } -// Выражения (возвращают значение) std::unique_ptr Parser::primary() { - if (tokens[pos].type == TokenType::NUMBER) return std::make_unique(consume(TokenType::NUMBER).value); - if (tokens[pos].type == TokenType::STRING) return std::make_unique(consume(TokenType::STRING).value); - - // input() - if (tokens[pos].type == TokenType::INPUT) { - consume(TokenType::INPUT); consume(TokenType::LPAREN); consume(TokenType::RPAREN); - return std::make_unique(); + if (tokens[pos].type == TokenType::NUMBER) + return std::make_unique(consume(TokenType::NUMBER).value); + + if (tokens[pos].type == TokenType::STRING_LITERAL) + return std::make_unique(consume(TokenType::STRING_LITERAL).value); + + 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(name); + } + return std::make_unique(name); } - // round( expr ) + if (tokens[pos].type == TokenType::INPUT) { + consume(TokenType::INPUT); consume(TokenType::LPAREN); consume(TokenType::RPAREN); + return std::make_unique(); + } if (tokens[pos].type == TokenType::ROUND) { - consume(TokenType::ROUND); - consume(TokenType::LPAREN); - auto node = expression(); - consume(TokenType::RPAREN); - return std::make_unique(std::move(node)); + consume(TokenType::ROUND); consume(TokenType::LPAREN); auto n = expression(); consume(TokenType::RPAREN); + return std::make_unique(std::move(n)); } - - // random() if (tokens[pos].type == TokenType::RANDOM) { consume(TokenType::RANDOM); consume(TokenType::LPAREN); consume(TokenType::RPAREN); return std::make_unique(); } - if (tokens[pos].type == TokenType::LPAREN) { - consume(TokenType::LPAREN); - auto node = expression(); - consume(TokenType::RPAREN); - return node; + consume(TokenType::LPAREN); auto n = expression(); consume(TokenType::RPAREN); return n; } - 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); } @@ -66,46 +66,81 @@ std::unique_ptr Parser::expression() { return node; } -// Блоки кода -void Parser::block() { +// ИЗМЕНЕНИЕ: Возвращаем unique_ptr и используем make_unique +std::unique_ptr Parser::parseBlock() { consume(TokenType::LBRACE); + auto block = std::make_unique(); // Здесь было make_shared while (tokens[pos].type != TokenType::RBRACE && tokens[pos].type != TokenType::END) { - statement(); + block->statements.push_back(statement()); } consume(TokenType::RBRACE); + return block; } -// Инструкции (заканчиваются ;) -void Parser::statement() { - if (tokens[pos].type == TokenType::PRINT) { - consume(TokenType::PRINT); - consume(TokenType::LPAREN); +std::unique_ptr Parser::statement() { + // 1. Объявление переменных + if (tokens[pos].type == TokenType::INT_KW || tokens[pos].type == TokenType::STRING_KW) { + std::string type = tokens[pos].value; + pos++; + std::string name = consume(TokenType::IDENTIFIER).value; + consume(TokenType::ASSIGN); auto expr = expression(); - consume(TokenType::RPAREN); consume(TokenType::SEMICOLON); - auto pNode = std::make_unique(std::move(expr)); - pNode->eval(); + return std::make_unique(type, name, std::move(expr)); } - else if (tokens[pos].type == TokenType::FOX) { - consume(TokenType::FOX); - consume(TokenType::LPAREN); // fox ( - consume(TokenType::RPAREN); // ) - consume(TokenType::SEMICOLON); // ; - std::cout << " (\\_/) \n (o.o) \n (> <) OFOX!" << std::endl; + + // 2. Объявление функций + if (tokens[pos].type == TokenType::VOID_KW) { + consume(TokenType::VOID_KW); + std::string name = consume(TokenType::IDENTIFIER).value; + consume(TokenType::LPAREN); + consume(TokenType::RPAREN); + + // Тут магия: unique_ptr автоматически превратится в shared_ptr при создании FuncDefNode + auto body = parseBlock(); + return std::make_unique(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(std::move(expr)); } - else { - // ОБРАБОТКА ОШИБОК: Если встретили слово, которое не является командой - std::cerr << "Runtime Error: Unknown command or function '" << tokens[pos].value - << "' at line " << tokens[pos].line << std::endl; - exit(1); + + if (tokens[pos].type == TokenType::FOX) { + consume(TokenType::FOX); consume(TokenType::LPAREN); consume(TokenType::RPAREN); consume(TokenType::SEMICOLON); + return std::make_unique("Fox says: Hello!"); } + + 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(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(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() { while (tokens[pos].type != TokenType::END) { - statement(); + auto stmt = statement(); + stmt->eval(globalContext); } -} +} \ No newline at end of file diff --git a/src/Parser.h b/src/Parser.h index 0655779..5d21fd2 100644 --- a/src/Parser.h +++ b/src/Parser.h @@ -7,15 +7,18 @@ class Parser { std::vector tokens; size_t pos = 0; + Context globalContext; Token consume(TokenType type); std::unique_ptr primary(); std::unique_ptr multiplication(); std::unique_ptr expression(); - void statement(); // Обработка отдельной команды с точкой с запятой - void block(); // Обработка блока { ... } + + std::unique_ptr statement(); + // ИЗМЕНЕНИЕ: теперь возвращает unique_ptr + std::unique_ptr parseBlock(); public: Parser(std::vector t); - void run(); -}; + void run(); +}; \ No newline at end of file diff --git a/src/Token.h b/src/Token.h index 9934423..2283c9b 100644 --- a/src/Token.h +++ b/src/Token.h @@ -2,13 +2,16 @@ #include enum class TokenType { - NUMBER, STRING, - PLUS, MINUS, STAR, SLASH, - LPAREN, RPAREN, - LBRACE, RBRACE, SEMICOLON, - PRINT, INPUT, - ROUND, FOX, RANDOM, // Новые команды - IDENTIFIER, // Для неизвестных слов + NUMBER, STRING_LITERAL, + PLUS, MINUS, STAR, SLASH, + LPAREN, RPAREN, LBRACE, RBRACE, + SEMICOLON, COMMA, ASSIGN, // =, , ; + + // Ключевые слова + PRINT, INPUT, ROUND, RANDOM, FOX, + INT_KW, STRING_KW, VOID_KW, // int, string, void + + IDENTIFIER, // Имена переменных и функций END, ERROR }; @@ -16,4 +19,4 @@ struct Token { TokenType type; std::string value; int line; -}; +}; \ No newline at end of file diff --git a/src/foxlang b/src/foxlang index 1e8d6e7..2145ed2 100755 Binary files a/src/foxlang and b/src/foxlang differ