diff --git a/foxlang b/foxlang new file mode 100755 index 0000000..fb53c70 Binary files /dev/null and b/foxlang differ diff --git a/script.fox b/script.fox index 880e854..89a3b64 100644 --- a/script.fox +++ b/script.fox @@ -24,4 +24,6 @@ void mainLogic() { showStats(); } -mainLogic(); \ No newline at end of file +void init() { + print("Initializing libs..."); +} \ No newline at end of file diff --git a/src/AST.h b/src/AST.h index 921cc02..8004e19 100644 --- a/src/AST.h +++ b/src/AST.h @@ -6,21 +6,21 @@ #include #include #include +#include +#include -// Предварительное объявление +// Forward declaration struct Node; -// --- ПАМЯТЬ ЯЗЫКА --- +// --- ПАМЯТЬ --- struct Context { - // Переменные: Имя -> Значение std::map variables; - // Типы переменных: Имя -> Тип ("int", "string") - std::map varTypes; - // Функции: Имя -> Узел тела функции (BlockNode) std::map> functions; + // МАССИВЫ: Имя -> Вектор значений + std::map> arrays; bool exists(const std::string& name) { - return variables.find(name) != variables.end(); + return variables.count(name) || arrays.count(name); } }; @@ -31,162 +31,253 @@ static std::string formatNumber(double val) { return s; } -// --- УЗЛЫ --- struct Node { virtual ~Node() = default; virtual std::string eval(Context& ctx) = 0; }; -// 1. Базовые значения +// --- БАЗОВЫЕ НОДЫ (Без изменений) --- struct NumberNode : Node { std::string val; NumberNode(std::string v) : val(v) {} 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); + 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]; } }; -// 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; +// Сравнение: ==, !=, <, > +// Сравнение: ==, !=, <, > +struct CompareNode : Node { + std::string op; std::unique_ptr left, right; - BinOpNode(char o, std::unique_ptr l, std::unique_ptr r) + 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 { 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; + // Проверяем, являются ли значения числами + 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 formatNumber(l + r); - if (op == '-') return formatNumber(l - r); - if (op == '*') return formatNumber(l * r); - if (op == '/') return formatNumber((r!=0)? l/r : 0); + + 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"; } }; -// 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; - } -}; +// IF / ELSE +struct IfNode : Node { + std::unique_ptr condition; + std::unique_ptr thenBlock; + std::unique_ptr elseBlock; // Может быть nullptr -// 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) {} + 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 { - ctx.functions[name] = body; + std::string res = condition->eval(ctx); + if (res == "1") { // Истина + thenBlock->eval(ctx); + } else if (elseBlock) { + elseBlock->eval(ctx); + } return ""; } }; -// 8. Вызов функции (myFunc()) +// 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]; + } +}; + +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]; + } +}; + +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)) {} + 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; + 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); + if (op == '%') return formatNumber((int)l % (int)r); + return "0"; + } +}; + +struct BlockNode : Node { + std::vector> statements; + std::string eval(Context& ctx) override { + std::string last = ""; + for(auto& s : statements) last = s->eval(ctx); + return last; + } +}; + +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.find(name) == ctx.functions.end()) { - std::cerr << "Error: Function '" << name << "' is not defined." << std::endl; - exit(1); - } - // Выполняем сохраненное тело + 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 ""; - } + 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; - } + std::string eval(Context& ctx) override { std::string b; std::getline(std::cin, b); return b; } }; struct RoundNode : Node { std::unique_ptr expr; @@ -195,4 +286,12 @@ struct RoundNode : Node { }; struct RandomNode : Node { std::string eval(Context& ctx) override { return std::to_string(rand() % 100); } +}; + +// ИСПРАВЛЕННЫЙ FOX NODE: Теперь он выводит текст сам! +struct FoxNode : Node { + std::string eval(Context& ctx) override { + std::cout << " (\\_/)\n (o.o) FoxLang v4.0\n (> <)" << std::endl; + return "fox"; + } }; \ No newline at end of file diff --git a/src/Lexer.cpp b/src/Lexer.cpp index bd7cf4f..1881dcc 100644 --- a/src/Lexer.cpp +++ b/src/Lexer.cpp @@ -16,13 +16,12 @@ std::vector Lexer::tokenize() { continue; } - // 2. ОБРАБОТКА КОММЕНТАРИЕВ (НОВОЕ) - // Если видим '/', и следующий символ тоже '/', то это комментарий + // 2. ОБРАБОТКА КОММЕНТАРИЕВ if (current == '/' && pos + 1 < source.length() && source[pos + 1] == '/') { while (pos < source.length() && source[pos] != '\n') { - pos++; // Пропускаем всё до конца строки + pos++; } - continue; // Переходим к следующей итерации цикла + continue; } // 3. Числа @@ -43,9 +42,13 @@ std::vector Lexer::tokenize() { tokens.push_back({TokenType::STRING_LITERAL, str, line}); } // 5. Идентификаторы и Ключевые слова - else if (isalpha(current)) { + // ИСПРАВЛЕНИЕ ТУТ: Добавлена проверка || current == '_' + else if (isalpha(current) || current == '_') { std::string id; - while (pos < source.length() && isalnum(source[pos])) id += source[pos++]; + // И ТУТ: Добавлена проверка || source[pos] == '_' + while (pos < source.length() && (isalnum(source[pos]) || 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}); @@ -55,22 +58,43 @@ 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}); + else if (id == "array") tokens.push_back({TokenType::ARRAY, id, line}); + else if (id == "set") tokens.push_back({TokenType::SET, id, line}); + 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 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; + } + if (current == '!' && pos+1 < source.length() && source[pos+1] == '=') { + tokens.push_back({TokenType::NEQ, "!=", line}); pos+=2; continue; + } + 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::MOD, "%", 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::LBRACKET, "[", line}); break; + case ']': tokens.push_back({TokenType::RBRACKET, "]", line}); break; case ';': tokens.push_back({TokenType::SEMICOLON, ";", line}); break; case ',': tokens.push_back({TokenType::COMMA, ",", line}); break; case '=': tokens.push_back({TokenType::ASSIGN, "=", line}); break; + case '<': tokens.push_back({TokenType::LT, "<", line}); break; + case '>': tokens.push_back({TokenType::GT, ">", line}); break; default: std::cerr << "Lexer Error: Unknown char '" << current << "' at line " << line << std::endl; exit(1); diff --git a/src/Parser.cpp b/src/Parser.cpp index 6613b95..a2622a6 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -1,5 +1,18 @@ #include "Parser.h" +#include "Lexer.h" #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 "./"; + return filepath.substr(0, found + 1); +} Parser::Parser(std::vector t) : tokens(t) {} @@ -11,46 +24,42 @@ Token Parser::consume(TokenType type) { 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); + 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::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); + 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; - } - + 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; exit(1); } std::unique_ptr Parser::multiplication() { auto node = primary(); - while (tokens[pos].type == TokenType::STAR || tokens[pos].type == TokenType::SLASH) { + while (tokens[pos].type == TokenType::STAR || tokens[pos].type == TokenType::SLASH || tokens[pos].type == TokenType::MOD) { char op = tokens[pos].value[0]; pos++; node = std::make_unique(op, std::move(node), primary()); } @@ -66,10 +75,19 @@ std::unique_ptr Parser::expression() { return node; } -// ИЗМЕНЕНИЕ: Возвращаем unique_ptr и используем make_unique +std::unique_ptr Parser::comparison() { + auto node = expression(); + if (tokens[pos].type == TokenType::EQ || tokens[pos].type == TokenType::NEQ || + tokens[pos].type == TokenType::LT || tokens[pos].type == TokenType::GT) { + std::string op = tokens[pos].value; pos++; + return std::make_unique(op, std::move(node), expression()); + } + return node; +} + std::unique_ptr Parser::parseBlock() { consume(TokenType::LBRACE); - auto block = std::make_unique(); // Здесь было make_shared + auto block = std::make_unique(); while (tokens[pos].type != TokenType::RBRACE && tokens[pos].type != TokenType::END) { block->statements.push_back(statement()); } @@ -77,11 +95,56 @@ std::unique_ptr Parser::parseBlock() { 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; // Если нашли локально, обновляем путь + } + + 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; // Забираем обновленную память +} + std::unique_ptr Parser::statement() { - // 1. Объявление переменных + // 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) { - std::string type = tokens[pos].value; - pos++; + std::string type = tokens[pos].value; pos++; std::string name = consume(TokenType::IDENTIFIER).value; consume(TokenType::ASSIGN); auto expr = expression(); @@ -89,50 +152,105 @@ std::unique_ptr Parser::statement() { return std::make_unique(type, name, std::move(expr)); } - // 2. Объявление функций + // 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); - - // Тут магия: unique_ptr автоматически превратится в shared_ptr при создании FuncDefNode + consume(TokenType::LPAREN); consume(TokenType::RPAREN); auto body = parseBlock(); return std::make_unique(name, std::move(body)); } - // 3. Команда PRINT + // 4. МАССИВЫ (Выполняем, глобальные массивы нужны) + if (tokens[pos].type == TokenType::ARRAY) { + consume(TokenType::ARRAY); + std::string name = consume(TokenType::IDENTIFIER).value; + auto size = expression(); + 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); - return std::make_unique("Fox says: Hello!"); + 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(); // <-- Игнор + 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(); // <-- Игнор присваивания 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); } } - // ИЗМЕНЕНИЕ: Просто возвращаем результат parseBlock, никаких преобразований не нужно - if (tokens[pos].type == TokenType::LBRACE) { - return 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); @@ -141,6 +259,8 @@ std::unique_ptr Parser::statement() { void Parser::run() { while (tokens[pos].type != TokenType::END) { auto stmt = statement(); + // stmt->eval вызывается, но если мы вернули BlockNode (пустышку) при импорте, + // то ничего не произойдет. stmt->eval(globalContext); } } \ No newline at end of file diff --git a/src/Parser.h b/src/Parser.h index 5d21fd2..1db670b 100644 --- a/src/Parser.h +++ b/src/Parser.h @@ -3,22 +3,29 @@ #include "AST.h" #include #include +#include // Не забудь class Parser { std::vector tokens; size_t pos = 0; - Context globalContext; + +public: + Context globalContext; + + // НОВЫЕ ПОЛЯ ДЛЯ ПУТЕЙ И ИМПОРТА + std::string currentFile; // Путь к файлу, который сейчас парсится + bool importMode = false; // Если true, то выполняем только объявления (vars/funcs) + Parser(std::vector t); + Token consume(TokenType type); std::unique_ptr primary(); std::unique_ptr multiplication(); std::unique_ptr expression(); - - std::unique_ptr statement(); - // ИЗМЕНЕНИЕ: теперь возвращает unique_ptr - std::unique_ptr parseBlock(); + std::unique_ptr comparison(); -public: - Parser(std::vector t); + std::unique_ptr statement(); + std::unique_ptr parseBlock(); + void run(); }; \ No newline at end of file diff --git a/src/Token.h b/src/Token.h index 2283c9b..077d97f 100644 --- a/src/Token.h +++ b/src/Token.h @@ -3,15 +3,19 @@ enum class TokenType { NUMBER, STRING_LITERAL, - PLUS, MINUS, STAR, SLASH, - LPAREN, RPAREN, LBRACE, RBRACE, - SEMICOLON, COMMA, ASSIGN, // =, , ; + PLUS, MINUS, STAR, SLASH, MOD, // Добавил MOD (%) + LPAREN, RPAREN, LBRACE, RBRACE, LBRACKET, RBRACKET, // [] + SEMICOLON, COMMA, ASSIGN, + EQ, NEQ, LT, GT, // ==, !=, <, > // Ключевые слова PRINT, INPUT, ROUND, RANDOM, FOX, - INT_KW, STRING_KW, VOID_KW, // int, string, void + INT_KW, STRING_KW, VOID_KW, + WHILE, IF, ELSE, + ARRAY, SET, GET, SIZE, // Массивы + INCLUDE, // Библиотеки - IDENTIFIER, // Имена переменных и функций + IDENTIFIER, END, ERROR }; diff --git a/src/foxlang b/src/foxlang deleted file mode 100755 index 2145ed2..0000000 Binary files a/src/foxlang and /dev/null differ diff --git a/src/main.cpp b/src/main.cpp index 27ea438..35a0b11 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,20 +10,27 @@ int main(int argc, char* argv[]) { return 1; } + // 1. Читаем файл std::ifstream file(argv[1]); if (!file.is_open()) { - std::cerr << "Error: File not found!" << std::endl; + std::cerr << "Error: Could not open file " << argv[1] << std::endl; return 1; } std::stringstream buffer; buffer << file.rdbuf(); + std::string code = buffer.str(); - Lexer lexer(buffer.str()); - auto tokens = lexer.tokenize(); + // 2. Запускаем конвейер + Lexer lexer(code); + std::vector tokens = lexer.tokenize(); Parser parser(tokens); + + // ВАЖНО: Передаем имя файла, чтобы парсер знал, где он находится + parser.currentFile = argv[1]; + parser.run(); return 0; -} +} \ No newline at end of file diff --git a/test.fox b/test.fox index 0e9e967..ef7aa44 100644 --- a/test.fox +++ b/test.fox @@ -1,6 +1,30 @@ { + include("script.fox"); + // Глоб. Переменные + string Player = "Artem"; + int hpplayer = 100; + int level = 1; + int usr_input = 0; - print("Добро пожаловать в FoxLang"); - print(input() + input()); + void Hello() { + print("Привет, " + Player + "!"); + print("Тебе предстоит пройти это подземелье"); + } + Hello(); -} + void ShowStats() { + print("-----------Статистика-----------"); + print("Имя игрока: " + Player); + print("Здоровье: " + hpplayer); + print("Уровень: " + level); + } + ShowStats(); + + usr_input = input(); + + if (usr_input == 1) { + init(); + } else { + print("Не найдено действие для этого ввода."); + } +} \ No newline at end of file diff --git a/test/lib_utils.fox b/test/lib_utils.fox new file mode 100644 index 0000000..c369a24 --- /dev/null +++ b/test/lib_utils.fox @@ -0,0 +1,38 @@ +print(">>> Library 'lib_utils.fox' loaded successfully!"); + +// Глобальные переменные для передачи "аргументов" в функции +// (так как в v4.0 аргументы передаются через глобальную память) +int arg_val = 0; +int return_val = 0; + +// --- Функция 1: Вычисление факториала --- +void factorial() { + int counter = 1; + int result = 1; + + // Цикл while + while (counter < arg_val + 1) { + result = result * counter; + counter = counter + 1; + } + return_val = result; +} + +// --- Функция 2: Красивый заголовок --- +void printHeader() { + print("==================================="); + print(" FOX LANG v4.0 DEMO SYSTEM "); + print("==================================="); + fox(); // Вызов встроенной лисы +} + +// --- Функция 3: Проверка четности --- +void checkParity() { + // Используем оператор остатка % + int rem = arg_val % 2; + if (rem == 0) { + print("Number " + arg_val + " is EVEN (Chetnoe)."); + } else { + print("Number " + arg_val + " is ODD (Nechetnoe)."); + } +} \ No newline at end of file diff --git a/test/main_app.fox b/test/main_app.fox new file mode 100644 index 0000000..9448712 --- /dev/null +++ b/test/main_app.fox @@ -0,0 +1,102 @@ +// 1. Подключаем нашу библиотеку +include("lib_utils.fox"); + +// Используем функцию из библиотеки +printHeader(); + +// ========================================== +// ЧАСТЬ 1: Работа с вводом и условиями +// ========================================== +print(""); +print("--- Step 1: User Interaction ---"); +print("Enter your name:"); +string name = input(); + +if (name == "Fox") { + print("Hello, Creator!"); +} else { + print("Welcome, user " + name); +} + +// ========================================== +// ЧАСТЬ 2: Математика и Библиотечные функции +// ========================================== +print(""); +print("--- Step 2: Math & Logic ---"); +print("Enter a small number to calculate Factorial:"); +string inputNum = input(); +int num = inputNum; // Автоматическое преобразование типов (строка -> число) + +// Передаем аргумент через глобальную переменную библиотеки +arg_val = num; +factorial(); // Вызываем функцию из lib_utils.fox + +print("Factorial of " + num + " is: " + return_val); + +// Проверяем на четность +checkParity(); + +// ========================================== +// ЧАСТЬ 3: Массивы и Сортировка (Bubble Sort) +// ========================================== +print(""); +print("--- Step 3: Array Sorting (Bubble Sort) ---"); + +// Объявляем массив на 5 элементов +int arrSize = 5; +array myNumbers arrSize; + +// Заполняем массив случайными числами +int i = 0; +while (i < arrSize) { + int rnd = random(); + set(myNumbers, i, rnd); + i = i + 1; +} + +// Выводим исходный массив +print("Original Array:"); +int k = 0; +while (k < arrSize) { + print("Index " + k + ": " + get(myNumbers, k)); + k = k + 1; +} + +print("Sorting now... please wait..."); + +// Реализация сортировки пузырьком +int n = arrSize; +int pass = 0; + +// Внешний цикл +while (pass < n) { + int j = 0; + // Внутренний цикл (n - 1) + while (j < n - 1) { + // Получаем два соседних элемента + int val1 = get(myNumbers, j); + int val2 = get(myNumbers, j + 1); + + // Если левый больше правого - меняем местами + if (val1 > val2) { + set(myNumbers, j, val2); + set(myNumbers, j + 1, val1); + } + j = j + 1; + } + pass = pass + 1; +} + +// Выводим отсортированный массив +print("Sorted Array (Result):"); +k = 0; +while (k < arrSize) { + print("Index " + k + ": " + get(myNumbers, k)); + k = k + 1; +} + +// ========================================== +// ФИНАЛ +// ========================================== +print(""); +print("Test Complete. FoxLang is working perfectly!"); \ No newline at end of file