diff --git a/foxlang b/foxlang index fb53c70..b943f78 100755 Binary files a/foxlang and b/foxlang differ diff --git a/src/AST.h b/src/AST.h index 8004e19..7963d19 100644 --- a/src/AST.h +++ b/src/AST.h @@ -12,16 +12,68 @@ // Forward declaration struct Node; -// --- ПАМЯТЬ --- +struct FuncParam { + std::string type; + std::string name; +}; + +// Контекст памяти (переменные и функции) struct Context { + Context* parent = nullptr; // Для глобальных переменных std::map variables; - std::map> functions; - // МАССИВЫ: Имя -> Вектор значений + std::map> functions; // Храним функции std::map> arrays; bool exists(const std::string& name) { - return variables.count(name) || arrays.count(name); + if (variables.count(name) || arrays.count(name)) return true; + if (parent) return parent->exists(name); + return false; } + + std::string getVar(const std::string& name) { + if (variables.count(name)) return variables[name]; + if (parent) return parent->getVar(name); + std::cerr << "Runtime Error: Variable '" << name << "' not found." << std::endl; exit(1); + } + + std::vector& getArray(const std::string& name) { + if (arrays.count(name)) return arrays[name]; + if (parent) return parent->getArray(name); + std::cerr << "Runtime Error: Array '" << name << "' not found." << std::endl; exit(1); + } + + void setVar(const std::string& name, const std::string& val) { + if (variables.count(name)) { variables[name] = val; return; } + if (parent) { parent->setVar(name, val); return; } + std::cerr << "Runtime Error: Variable '" << name << "' not defined." << std::endl; exit(1); + } + + void defineVar(const std::string& name, const std::string& val) { + if (variables.count(name)) { + std::cerr << "Runtime Error: Variable '" << name << "' already defined." << std::endl; exit(1); + } + variables[name] = val; + } + + void defineGlobal(const std::string& name, const std::string& val) { + if (parent) parent->defineGlobal(name, val); + else defineVar(name, val); + } + + std::shared_ptr getFunc(const std::string& name) { + if (functions.count(name)) return functions[name]; + if (parent) return parent->getFunc(name); + return nullptr; + } + + void defineFunc(const std::string& name, std::shared_ptr body) { + functions[name] = body; + } +}; + +// Специальный тип для RETURN +struct ReturnValue { + std::string value; }; static std::string formatNumber(double val) { @@ -36,7 +88,77 @@ struct Node { virtual std::string eval(Context& ctx) = 0; }; -// --- БАЗОВЫЕ НОДЫ (Без изменений) --- +// --- ОСНОВНЫЕ УЗЛЫ --- + +// Определение функции +struct FuncDefNode : Node { + std::string returnType; + std::string name; + std::vector params; + std::shared_ptr body; + + FuncDefNode(std::string rt, std::string n, std::vector p, std::shared_ptr b) + : returnType(rt), name(n), params(p), body(b) {} + + // Eval ничего не делает, функция регистрируется Парсером + std::string eval(Context& ctx) override { return ""; } +}; + +// RETURN - выбрасывает исключение с значением +struct ReturnNode : Node { + std::unique_ptr expr; + ReturnNode(std::unique_ptr e) : expr(std::move(e)) {} + std::string eval(Context& ctx) override { + std::string result = expr ? expr->eval(ctx) : "0"; + throw ReturnValue{result}; + } +}; + +// ВЫЗОВ ФУНКЦИИ - самое важное для тебя! +struct FuncCallNode : Node { + std::string name; + std::vector> args; + + FuncCallNode(std::string n, std::vector> a) + : name(n), args(std::move(a)) {} + + std::string eval(Context& ctx) override { + auto funcNodeBase = ctx.getFunc(name); + if (!funcNodeBase) { + std::cerr << "Runtime Error: Function '" << name << "' not found." << std::endl; exit(1); + } + + FuncDefNode* funcDef = static_cast(funcNodeBase.get()); + + if (args.size() != funcDef->params.size()) { + std::cerr << "Args count mismatch for '" << name << "'" << std::endl; exit(1); + } + + std::vector argValues; + for (auto& arg : args) argValues.push_back(arg->eval(ctx)); + + // Находим глобальный контекст + Context* root = &ctx; + while (root->parent != nullptr) root = root->parent; + + // Создаем локальную область видимости + Context funcScope; + funcScope.parent = root; + + for (size_t i = 0; i < funcDef->params.size(); i++) { + funcScope.defineVar(funcDef->params[i].name, argValues[i]); + } + + try { + funcDef->body->eval(funcScope); + } catch (const ReturnValue& ret) { + return ret.value; // ВОЗВРАЩАЕМ ЗНАЧЕНИЕ В ПЕРЕМЕННУЮ + } + + return "0"; + } +}; + struct NumberNode : Node { std::string val; NumberNode(std::string v) : val(v) {} @@ -50,185 +172,35 @@ struct StringNode : Node { struct VarAccessNode : Node { std::string name; VarAccessNode(std::string n) : name(n) {} - std::string eval(Context& ctx) override { - if (ctx.variables.find(name) == ctx.variables.end()) { - // Проверка, может это массив? - if(ctx.arrays.count(name)) return "ARRAY:" + name; - std::cerr << "Runtime Error: Var '" << name << "' not found." << std::endl; exit(1); - } - return ctx.variables[name]; - } + std::string eval(Context& ctx) override { return ctx.getVar(name); } }; - -// --- ЛОГИКА И УПРАВЛЕНИЕ --- - -// Сравнение: ==, !=, <, > -// Сравнение: ==, !=, <, > -struct CompareNode : Node { - std::string op; - std::unique_ptr left, right; - CompareNode(std::string o, std::unique_ptr l, std::unique_ptr r) - : op(o), left(std::move(l)), right(std::move(r)) {} - +struct GlobalVarDeclNode : Node { + std::string type, name; std::unique_ptr expr; + GlobalVarDeclNode(std::string t, std::string n, std::unique_ptr e) : type(t), name(n), expr(std::move(e)) {} std::string eval(Context& ctx) override { - std::string lStr = left->eval(ctx); - std::string rStr = right->eval(ctx); - - // Проверяем, являются ли значения числами - bool lIsNum = (lStr.find_first_not_of("0123456789.-") == std::string::npos); - bool rIsNum = (rStr.find_first_not_of("0123456789.-") == std::string::npos); - - // Если хотя бы одно значение - текст, сравниваем как СТРОКИ - if (!lIsNum || !rIsNum) { - if (op == "==") return (lStr == rStr) ? "1" : "0"; - if (op == "!=") return (lStr != rStr) ? "1" : "0"; - // Для строк операции > и < работают лексикографически (по алфавиту) - if (op == "<") return (lStr < rStr) ? "1" : "0"; - if (op == ">") return (lStr > rStr) ? "1" : "0"; - return "0"; - } - - // Если оба числа - переводим в double и сравниваем математически - double l = std::stod(lStr); - double r = std::stod(rStr); - - if (op == "==") return (std::abs(l - r) < 0.00001) ? "1" : "0"; - if (op == "!=") return (std::abs(l - r) > 0.00001) ? "1" : "0"; - if (op == "<") return (l < r) ? "1" : "0"; - if (op == ">") return (l > r) ? "1" : "0"; - return "0"; - } -}; - -// IF / ELSE -struct IfNode : Node { - std::unique_ptr condition; - std::unique_ptr thenBlock; - std::unique_ptr elseBlock; // Может быть nullptr - - IfNode(std::unique_ptr c, std::unique_ptr t, std::unique_ptr e) - : condition(std::move(c)), thenBlock(std::move(t)), elseBlock(std::move(e)) {} - - std::string eval(Context& ctx) override { - std::string res = condition->eval(ctx); - if (res == "1") { // Истина - thenBlock->eval(ctx); - } else if (elseBlock) { - elseBlock->eval(ctx); - } + Context* root = &ctx; + while (root->parent != nullptr) root = root->parent; + root->defineVar(name, expr->eval(ctx)); return ""; } }; - -// WHILE -struct WhileNode : Node { - std::unique_ptr condition; - std::unique_ptr body; - WhileNode(std::unique_ptr c, std::unique_ptr b) - : condition(std::move(c)), body(std::move(b)) {} - - std::string eval(Context& ctx) override { - while (true) { - std::string res = condition->eval(ctx); - if (res != "1") break; // Если не 1, выходим - body->eval(ctx); - } - return ""; - } -}; - -// --- МАССИВЫ --- - -// array myArr 10; -struct ArrayDeclNode : Node { - std::string name; - std::unique_ptr sizeExpr; - ArrayDeclNode(std::string n, std::unique_ptr s) : name(n), sizeExpr(std::move(s)) {} - std::string eval(Context& ctx) override { - int size = std::stoi(sizeExpr->eval(ctx)); - ctx.arrays[name] = std::vector(size, "0"); - return ""; - } -}; - -// set(arr, index, value); -struct ArraySetNode : Node { - std::string name; - std::unique_ptr idx; - std::unique_ptr val; - ArraySetNode(std::string n, std::unique_ptr i, std::unique_ptr v) - : name(n), idx(std::move(i)), val(std::move(v)) {} - std::string eval(Context& ctx) override { - if (!ctx.arrays.count(name)) { std::cerr << "Array not found: " << name << std::endl; exit(1); } - int i = std::stoi(idx->eval(ctx)); - if (i < 0 || i >= ctx.arrays[name].size()) { std::cerr << "Index out of bounds" << std::endl; exit(1); } - ctx.arrays[name][i] = val->eval(ctx); - return ""; - } -}; - -// get(arr, index) -struct ArrayGetNode : Node { - std::string name; - std::unique_ptr idx; - ArrayGetNode(std::string n, std::unique_ptr i) : name(n), idx(std::move(i)) {} - std::string eval(Context& ctx) override { - if (!ctx.arrays.count(name)) { std::cerr << "Array not found: " << name << std::endl; exit(1); } - int i = std::stoi(idx->eval(ctx)); - if (i < 0 || i >= ctx.arrays[name].size()) { std::cerr << "Index out of bounds" << std::endl; exit(1); } - return ctx.arrays[name][i]; - } -}; - -struct ArraySizeNode : Node { - std::string name; - ArraySizeNode(std::string n) : name(n) {} - std::string eval(Context& ctx) override { - if (!ctx.arrays.count(name)) return "0"; - return std::to_string(ctx.arrays[name].size()); - } -}; - -// --- INCLUDE --- -// Чтобы не было круговых зависимостей, мы парсим файл прямо тут (костыль, но рабочий для .h файла) -// В реальном проекте парсер нужно передавать или делать Include отдельным механизмом -struct IncludeNode : Node { - std::string filename; - // Нам нужен указатель на функцию парсинга... но мы схитрим - // Мы просто прочитаем файл и добавим его как текст, но AST строится статически. - // Поэтому Include в интерпретаторах часто работает на этапе парсинга, а не выполнения. - // Но ты просил "библиотеки". Сделаем Runtime Include. - - // ВНИМАНИЕ: Для полноценного include здесь нужен доступ к Parser. - // Я оставлю заглушку, а реализацию сделаю в Parser.cpp через рекурсию. - // Это просто маркер. - std::string eval(Context& ctx) override { return ""; } -}; - - -// Остальные ноды (VarDecl, Assign, BinOp, Functions...) остаются такими же, -// скопируй их из предыдущего ответа, я добавлю только ИСПРАВЛЕННЫЙ FOX. - struct VarDeclNode : Node { std::string type, 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 << "Redeclaration: " << name << std::endl; exit(1); } - ctx.variables[name] = expr->eval(ctx); - return ctx.variables[name]; + // Здесь expr->eval(ctx) может быть FuncCallNode, который вернет результат! + ctx.defineVar(name, expr->eval(ctx)); + return ""; } }; - 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 << "Unknown var: " << name << std::endl; exit(1); } - ctx.variables[name] = expr->eval(ctx); - return ctx.variables[name]; + ctx.setVar(name, expr->eval(ctx)); + return ""; } }; - 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)) {} @@ -245,53 +217,69 @@ struct BinOpNode : Node { return "0"; } }; - +struct CompareNode : Node { + std::string op; std::unique_ptr left, right; + CompareNode(std::string 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 { + double l = std::stod(left->eval(ctx)); double r = std::stod(right->eval(ctx)); + if (op == "==") return (std::abs(l - r) < 0.001) ? "1" : "0"; + if (op == "!=") return (std::abs(l - r) > 0.001) ? "1" : "0"; + if (op == "<") return (l < r) ? "1" : "0"; + if (op == ">") return (l > r) ? "1" : "0"; + return "0"; + } +}; +struct IfNode : Node { + std::unique_ptr cond, thenB, elseB; + IfNode(std::unique_ptr c, std::unique_ptr t, std::unique_ptr e) : cond(std::move(c)), thenB(std::move(t)), elseB(std::move(e)) {} + std::string eval(Context& ctx) override { + if (cond->eval(ctx) == "1") thenB->eval(ctx); + else if (elseB) elseB->eval(ctx); + return ""; + } +}; +struct WhileNode : Node { + std::unique_ptr cond, body; + WhileNode(std::unique_ptr c, std::unique_ptr b) : cond(std::move(c)), body(std::move(b)) {} + std::string eval(Context& ctx) override { + while (cond->eval(ctx) == "1") body->eval(ctx); + return ""; + } +}; struct BlockNode : Node { - std::vector> statements; + std::vector> stmts; std::string eval(Context& ctx) override { - std::string last = ""; - for(auto& s : statements) last = s->eval(ctx); - return last; + for(auto& s : stmts) s->eval(ctx); + return ""; } }; - -struct FuncDefNode : Node { - std::string name; std::shared_ptr body; - FuncDefNode(std::string n, std::shared_ptr b) : name(n), body(b) {} - std::string eval(Context& ctx) override { ctx.functions[name] = body; return ""; } -}; - -struct FuncCallNode : Node { - std::string name; - FuncCallNode(std::string n) : name(n) {} - std::string eval(Context& ctx) override { - if(!ctx.functions.count(name)) { std::cerr << "Unknown func: " << name << 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(Context& ctx) override { std::cout << expr->eval(ctx) << std::endl; return ""; } }; - struct InputNode : Node { 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); } -}; - -// ИСПРАВЛЕННЫЙ FOX NODE: Теперь он выводит текст сам! -struct FoxNode : Node { +struct ArrayDeclNode : Node { + std::string name; std::unique_ptr size; + ArrayDeclNode(std::string n, std::unique_ptr s) : name(n), size(std::move(s)) {} std::string eval(Context& ctx) override { - std::cout << " (\\_/)\n (o.o) FoxLang v4.0\n (> <)" << std::endl; - return "fox"; + ctx.arrays[name] = std::vector(std::stoi(size->eval(ctx)), "0"); + return ""; } -}; \ No newline at end of file +}; +struct ArraySetNode : Node { + std::string name; std::unique_ptr idx, val; + ArraySetNode(std::string n, std::unique_ptr i, std::unique_ptr v) : name(n), idx(std::move(i)), val(std::move(v)) {} + std::string eval(Context& ctx) override { + auto& arr = ctx.getArray(name); arr[std::stoi(idx->eval(ctx))] = val->eval(ctx); return ""; + } +}; +struct ArrayGetNode : Node { + std::string name; std::unique_ptr idx; + ArrayGetNode(std::string n, std::unique_ptr i) : name(n), idx(std::move(i)) {} + std::string eval(Context& ctx) override { return ctx.getArray(name)[std::stoi(idx->eval(ctx))]; } +}; +struct IncludeNode : Node { std::string eval(Context& ctx) override { return ""; } }; +struct FoxNode : Node { std::string eval(Context& ctx) override { std::cout << "FoxLang" << std::endl; return ""; } }; \ No newline at end of file diff --git a/src/Lexer.cpp b/src/Lexer.cpp index 1881dcc..0abe40e 100644 --- a/src/Lexer.cpp +++ b/src/Lexer.cpp @@ -9,22 +9,17 @@ std::vector Lexer::tokenize() { while (pos < source.length()) { char current = source[pos]; - // 1. Пропуск пробелов if (isspace(current)) { if (current == '\n') line++; pos++; continue; } - // 2. ОБРАБОТКА КОММЕНТАРИЕВ if (current == '/' && pos + 1 < source.length() && source[pos + 1] == '/') { - while (pos < source.length() && source[pos] != '\n') { - pos++; - } + 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++]; @@ -34,18 +29,14 @@ std::vector Lexer::tokenize() { } 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_LITERAL, str, line}); } - // 5. Идентификаторы и Ключевые слова - // ИСПРАВЛЕНИЕ ТУТ: Добавлена проверка || current == '_' else if (isalpha(current) || current == '_') { std::string id; - // И ТУТ: Добавлена проверка || source[pos] == '_' while (pos < source.length() && (isalnum(source[pos]) || source[pos] == '_')) { id += source[pos++]; } @@ -58,7 +49,6 @@ std::vector Lexer::tokenize() { 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}); - // Ключевые слова v4.0 else if (id == "while") tokens.push_back({TokenType::WHILE, id, line}); else if (id == "if") tokens.push_back({TokenType::IF, id, line}); else if (id == "else") tokens.push_back({TokenType::ELSE, id, line}); @@ -67,9 +57,10 @@ std::vector Lexer::tokenize() { else if (id == "get") tokens.push_back({TokenType::GET, id, line}); else if (id == "size") tokens.push_back({TokenType::SIZE, id, line}); else if (id == "include") tokens.push_back({TokenType::INCLUDE, id, line}); + else if (id == "return") tokens.push_back({TokenType::RETURN, id, line}); + else if (id == "global") tokens.push_back({TokenType::GLOBAL, id, line}); else tokens.push_back({TokenType::IDENTIFIER, id, line}); } - // 6. Операторы else { if (current == '=' && pos+1 < source.length() && source[pos+1] == '=') { tokens.push_back({TokenType::EQ, "==", line}); pos+=2; continue; diff --git a/src/Parser.cpp b/src/Parser.cpp index a2622a6..cc4147c 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -3,11 +3,7 @@ #include #include #include -#include // Нужно для C++17, чтобы работать с путями красиво -// Хелпер для получения папки из пути к файлу -// Пример: "test/main.fox" -> "test/" -// Пример: "script.fox" -> "./" std::string getDirectory(const std::string& filepath) { size_t found = filepath.find_last_of("/\\"); if (found == std::string::npos) return "./"; @@ -18,42 +14,76 @@ 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 " << (int)type + << " got '" << tokens[pos].value << "' 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_LITERAL) return std::make_unique(consume(TokenType::STRING_LITERAL).value); + // 1. УНАРНЫЙ МИНУС (Обработка отрицательных чисел: -5, -var) + if (tokens[pos].type == TokenType::MINUS) { + consume(TokenType::MINUS); + // Превращаем -5 в (0 - 5) + return std::make_unique('-', + std::make_unique("0"), + primary() // Рекурсивно вызываем primary, чтобы считать само число или скобку + ); + } + + // 2. Числа + if (tokens[pos].type == TokenType::NUMBER) { + return std::make_unique(consume(TokenType::NUMBER).value); + } + + // 3. Строки + if (tokens[pos].type == TokenType::STRING_LITERAL) { + return std::make_unique(consume(TokenType::STRING_LITERAL).value); + } + // 4. Переменные и Вызовы функций + if (tokens[pos].type == TokenType::IDENTIFIER) { + std::string name = consume(TokenType::IDENTIFIER).value; + + // Если дальше скобка '(', значит это ВЫЗОВ ФУНКЦИИ + if (tokens[pos].type == TokenType::LPAREN) { + consume(TokenType::LPAREN); + std::vector> args; + if (tokens[pos].type != TokenType::RPAREN) { + args.push_back(expression()); + while (tokens[pos].type == TokenType::COMMA) { + consume(TokenType::COMMA); + args.push_back(expression()); + } + } + consume(TokenType::RPAREN); + return std::make_unique(name, std::move(args)); + } + // Иначе это просто доступ к переменной + return std::make_unique(name); + } + + // Остальные проверки (массивы, input, скобки) if (tokens[pos].type == TokenType::GET) { consume(TokenType::GET); consume(TokenType::LPAREN); std::string name = consume(TokenType::IDENTIFIER).value; consume(TokenType::COMMA); auto idx = expression(); consume(TokenType::RPAREN); return std::make_unique(name, std::move(idx)); } - if (tokens[pos].type == TokenType::SIZE) { - consume(TokenType::SIZE); consume(TokenType::LPAREN); - std::string name = consume(TokenType::IDENTIFIER).value; - consume(TokenType::RPAREN); return std::make_unique(name); - } - 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); - } - 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 n = expression(); consume(TokenType::RPAREN); return std::make_unique(std::move(n)); } - 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 n = expression(); consume(TokenType::RPAREN); return n; } - std::cerr << "Error: Unexpected token '" << tokens[pos].value << "' line " << tokens[pos].line << std::endl; + if (tokens[pos].type == TokenType::INPUT) { + consume(TokenType::INPUT); consume(TokenType::LPAREN); consume(TokenType::RPAREN); + return std::make_unique(); + } + + if (tokens[pos].type == TokenType::LPAREN) { + consume(TokenType::LPAREN); + auto n = expression(); + consume(TokenType::RPAREN); + return n; + } + + std::cerr << "Parser Error: Unexpected token '" << tokens[pos].value + << "' at line " << tokens[pos].line << std::endl; exit(1); } @@ -89,79 +119,119 @@ std::unique_ptr Parser::parseBlock() { consume(TokenType::LBRACE); auto block = std::make_unique(); while (tokens[pos].type != TokenType::RBRACE && tokens[pos].type != TokenType::END) { - block->statements.push_back(statement()); + block->stmts.push_back(statement()); } consume(TokenType::RBRACE); return block; } -// --- ЛОГИКА ИМПОРТА --- void processInclude(std::string filename, Context& ctx, std::string currentFile) { - // 1. Вычисляем путь: Папка текущего файла + Имя подключаемого std::string dir = getDirectory(currentFile); std::string fullPath = dir + filename; - - // Открываем файл std::ifstream file(fullPath); - if (!file.is_open()) { - // Если не нашли, пробуем искать просто по имени (в текущей рабочей папке) - file.open(filename); - if (!file.is_open()) { - std::cerr << "Include Error: File '" << fullPath << "' (or '" << filename << "') not found!" << std::endl; - exit(1); - } - fullPath = filename; // Если нашли локально, обновляем путь - } - + if (!file.is_open()) { file.open(filename); if(!file.is_open()) exit(1); } std::stringstream buffer; buffer << file.rdbuf(); Lexer lexer(buffer.str()); - auto tokens = lexer.tokenize(); - - Parser parser(tokens); - parser.globalContext = ctx; // Передаем память - parser.currentFile = fullPath; // Указываем новый файл как текущий - - // ВАЖНО: Включаем режим импорта! - // Это заставит парсер игнорировать вызовы функций в mainLogic - parser.importMode = true; - - parser.run(); - ctx = parser.globalContext; // Забираем обновленную память + Parser parser(lexer.tokenize()); + parser.globalContext = ctx; parser.currentFile = fullPath; parser.importMode = true; + parser.run(); ctx = parser.globalContext; } std::unique_ptr Parser::statement() { - // 1. INCLUDE if (tokens[pos].type == TokenType::INCLUDE) { consume(TokenType::INCLUDE); consume(TokenType::LPAREN); std::string file = consume(TokenType::STRING_LITERAL).value; consume(TokenType::RPAREN); consume(TokenType::SEMICOLON); - - // ВАЖНО: Передаем текущий файл, чтобы вычислить относительный путь processInclude(file, globalContext, currentFile); - return std::make_unique(); } - // 2. ОБЪЯВЛЕНИЕ ПЕРЕМЕННЫХ (Всегда выполняем, даже при импорте!) - if (tokens[pos].type == TokenType::INT_KW || tokens[pos].type == TokenType::STRING_KW) { + if (tokens[pos].type == TokenType::GLOBAL) { + consume(TokenType::GLOBAL); + std::string type; + if (tokens[pos].type == TokenType::INT_KW) type = "int"; + else if (tokens[pos].type == TokenType::STRING_KW) type = "string"; + pos++; + std::string name = consume(TokenType::IDENTIFIER).value; + consume(TokenType::ASSIGN); + auto expr = expression(); + consume(TokenType::SEMICOLON); + return std::make_unique(type, name, std::move(expr)); + } + + if (tokens[pos].type == TokenType::INT_KW || tokens[pos].type == TokenType::STRING_KW || tokens[pos].type == TokenType::VOID_KW) { std::string type = tokens[pos].value; pos++; std::string name = consume(TokenType::IDENTIFIER).value; + + // Определение функции + if (tokens[pos].type == TokenType::LPAREN) { + consume(TokenType::LPAREN); + std::vector params; + if (tokens[pos].type != TokenType::RPAREN) { + while(true) { + std::string pType = tokens[pos].value; pos++; + std::string pName = consume(TokenType::IDENTIFIER).value; + params.push_back({pType, pName}); + if (tokens[pos].type == TokenType::COMMA) consume(TokenType::COMMA); else break; + } + } + consume(TokenType::RPAREN); + auto body = parseBlock(); + globalContext.defineFunc(name, std::make_shared(type, name, params, std::move(body))); + return std::make_unique(); + } + + // Объявление переменной consume(TokenType::ASSIGN); auto expr = expression(); consume(TokenType::SEMICOLON); return std::make_unique(type, name, std::move(expr)); } - // 3. ФУНКЦИИ (Всегда выполняем, то есть запоминаем их) - if (tokens[pos].type == TokenType::VOID_KW) { - consume(TokenType::VOID_KW); - std::string name = consume(TokenType::IDENTIFIER).value; - consume(TokenType::LPAREN); consume(TokenType::RPAREN); - auto body = parseBlock(); - return std::make_unique(name, std::move(body)); + if (tokens[pos].type == TokenType::RETURN) { + consume(TokenType::RETURN); + std::unique_ptr expr = nullptr; + if (tokens[pos].type != TokenType::SEMICOLON) expr = expression(); + consume(TokenType::SEMICOLON); + if (importMode) return std::make_unique(); + return std::make_unique(std::move(expr)); + } + + if (tokens[pos].type == TokenType::WHILE) { + consume(TokenType::WHILE); consume(TokenType::LPAREN); + auto cond = comparison(); consume(TokenType::RPAREN); + auto body = parseBlock(); + if (importMode) return std::make_unique(); + return std::make_unique(std::move(cond), std::move(body)); + } + + if (tokens[pos].type == TokenType::IF) { + consume(TokenType::IF); consume(TokenType::LPAREN); + auto cond = comparison(); consume(TokenType::RPAREN); + auto thenB = parseBlock(); + std::unique_ptr elseB = nullptr; + if (tokens[pos].type == TokenType::ELSE) { + consume(TokenType::ELSE); + if (tokens[pos].type == TokenType::IF) elseB = statement(); else elseB = parseBlock(); + } + if (importMode) return std::make_unique(); + return std::make_unique(std::move(cond), std::move(thenB), std::move(elseB)); + } + + if (tokens[pos].type == TokenType::PRINT) { + consume(TokenType::PRINT); consume(TokenType::LPAREN); + auto expr = expression(); + consume(TokenType::RPAREN); consume(TokenType::SEMICOLON); + if (importMode) return std::make_unique(); + return std::make_unique(std::move(expr)); + } + + if (tokens[pos].type == TokenType::FOX) { + consume(TokenType::FOX); consume(TokenType::LPAREN); consume(TokenType::RPAREN); consume(TokenType::SEMICOLON); + if (importMode) return std::make_unique(); + return std::make_unique(); } - // 4. МАССИВЫ (Выполняем, глобальные массивы нужны) if (tokens[pos].type == TokenType::ARRAY) { consume(TokenType::ARRAY); std::string name = consume(TokenType::IDENTIFIER).value; @@ -169,98 +239,48 @@ std::unique_ptr Parser::statement() { consume(TokenType::SEMICOLON); return std::make_unique(name, std::move(size)); } - - // ========================================================= - // БЛОК ИГНОРИРОВАНИЯ (Код ниже НЕ выполняется при include) - // ========================================================= - - // 5. WHILE - if (tokens[pos].type == TokenType::WHILE) { - consume(TokenType::WHILE); consume(TokenType::LPAREN); - auto cond = comparison(); - consume(TokenType::RPAREN); - auto body = parseBlock(); - - if (importMode) return std::make_unique(); // <-- Игнор - return std::make_unique(std::move(cond), std::move(body)); - } - - // 6. IF - if (tokens[pos].type == TokenType::IF) { - consume(TokenType::IF); consume(TokenType::LPAREN); - auto cond = comparison(); - consume(TokenType::RPAREN); - auto thenB = parseBlock(); - std::unique_ptr elseB = nullptr; - if (tokens[pos].type == TokenType::ELSE) { - consume(TokenType::ELSE); - if (tokens[pos].type == TokenType::IF) elseB = statement(); - else elseB = parseBlock(); - } - - if (importMode) return std::make_unique(); // <-- Игнор - return std::make_unique(std::move(cond), std::move(thenB), std::move(elseB)); - } - - // 7. PRINT - if (tokens[pos].type == TokenType::PRINT) { - consume(TokenType::PRINT); consume(TokenType::LPAREN); - auto expr = expression(); - consume(TokenType::RPAREN); consume(TokenType::SEMICOLON); - - if (importMode) return std::make_unique(); // <-- Игнор - return std::make_unique(std::move(expr)); - } - - // 8. FOX - if (tokens[pos].type == TokenType::FOX) { - consume(TokenType::FOX); consume(TokenType::LPAREN); consume(TokenType::RPAREN); consume(TokenType::SEMICOLON); - if (importMode) return std::make_unique(); // <-- Игнор - return std::make_unique(); - } - - // 9. SET (Arrays) + if (tokens[pos].type == TokenType::SET) { consume(TokenType::SET); consume(TokenType::LPAREN); std::string name = consume(TokenType::IDENTIFIER).value; consume(TokenType::COMMA); auto idx = expression(); consume(TokenType::COMMA); auto val = expression(); consume(TokenType::RPAREN); consume(TokenType::SEMICOLON); - if (importMode) return std::make_unique(); // <-- Игнор + if (importMode) return std::make_unique(); return std::make_unique(name, std::move(idx), std::move(val)); } - // 10. IDENTIFIER (Присваивание или Вызов функции) 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); - - if (importMode) return std::make_unique(); // <-- Игнор присваивания + if (importMode) return std::make_unique(); 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); - - if (importMode) return std::make_unique(); // <-- Игнор вызова (mainLogic не сработает) - return std::make_unique(name); + consume(TokenType::LPAREN); + std::vector> args; + if (tokens[pos].type != TokenType::RPAREN) { + args.push_back(expression()); + while (tokens[pos].type == TokenType::COMMA) { + consume(TokenType::COMMA); + args.push_back(expression()); + } + } + consume(TokenType::RPAREN); consume(TokenType::SEMICOLON); + if (importMode) return std::make_unique(); + return std::make_unique(name, std::move(args)); } } - 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); + std::cerr << "Unknown statement " << tokens[pos].value << std::endl; exit(1); } void Parser::run() { while (tokens[pos].type != TokenType::END) { - auto stmt = statement(); - // stmt->eval вызывается, но если мы вернули BlockNode (пустышку) при импорте, - // то ничего не произойдет. - stmt->eval(globalContext); + statement()->eval(globalContext); } } \ No newline at end of file diff --git a/src/Token.h b/src/Token.h index 077d97f..d5d32bf 100644 --- a/src/Token.h +++ b/src/Token.h @@ -3,17 +3,20 @@ enum class TokenType { NUMBER, STRING_LITERAL, - PLUS, MINUS, STAR, SLASH, MOD, // Добавил MOD (%) - LPAREN, RPAREN, LBRACE, RBRACE, LBRACKET, RBRACKET, // [] + PLUS, MINUS, STAR, SLASH, MOD, + LPAREN, RPAREN, LBRACE, RBRACE, LBRACKET, RBRACKET, SEMICOLON, COMMA, ASSIGN, - EQ, NEQ, LT, GT, // ==, !=, <, > + EQ, NEQ, LT, GT, // Ключевые слова PRINT, INPUT, ROUND, RANDOM, FOX, - INT_KW, STRING_KW, VOID_KW, + INT_KW, STRING_KW, VOID_KW, // Типы данных WHILE, IF, ELSE, - ARRAY, SET, GET, SIZE, // Массивы - INCLUDE, // Библиотеки + ARRAY, SET, GET, SIZE, + INCLUDE, + + // НОВЫЕ: возврат и глобальные + RETURN, GLOBAL, IDENTIFIER, END, ERROR diff --git a/test/return.fox b/test/return.fox new file mode 100644 index 0000000..a3f204c --- /dev/null +++ b/test/return.fox @@ -0,0 +1,24 @@ +// Функция, которая проверяет число и возвращает код ошибки +int checkNumber(int n) { + if (n < 0) { + return 1; // Код ошибки 1: число отрицательное + } + if (n > 100) { + return 2; // Код ошибки 2: число слишком большое + } + return 0; // 0 - всё хорошо +} + +// Переменная для теста +int val = -5; + +// присваиваем результат функции в переменную! +int errorCode = checkNumber(val); + +// Проверяем, что записалось +if (errorCode == 0) { + print("Success!"); +} else { + print("Error happened. Code:"); + print(errorCode); // Выведет 1 +} \ No newline at end of file