Условные конструкции, циклы, массивы и библтотеки
This commit is contained in:
+3
-1
@@ -24,4 +24,6 @@ void mainLogic() {
|
|||||||
showStats();
|
showStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
mainLogic();
|
void init() {
|
||||||
|
print("Initializing libs...");
|
||||||
|
}
|
||||||
@@ -6,21 +6,21 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
// Предварительное объявление
|
// Forward declaration
|
||||||
struct Node;
|
struct Node;
|
||||||
|
|
||||||
// --- ПАМЯТЬ ЯЗЫКА ---
|
// --- ПАМЯТЬ ---
|
||||||
struct Context {
|
struct Context {
|
||||||
// Переменные: Имя -> Значение
|
|
||||||
std::map<std::string, std::string> variables;
|
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;
|
std::map<std::string, std::shared_ptr<Node>> functions;
|
||||||
|
// МАССИВЫ: Имя -> Вектор значений
|
||||||
|
std::map<std::string, std::vector<std::string>> arrays;
|
||||||
|
|
||||||
bool exists(const std::string& name) {
|
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;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- УЗЛЫ ---
|
|
||||||
struct Node {
|
struct Node {
|
||||||
virtual ~Node() = default;
|
virtual ~Node() = default;
|
||||||
virtual std::string eval(Context& ctx) = 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(Context& ctx) override { return val; }
|
std::string eval(Context& ctx) override { return val; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StringNode : Node {
|
struct StringNode : Node {
|
||||||
std::string val;
|
std::string val;
|
||||||
StringNode(std::string v) : val(v) {}
|
StringNode(std::string v) : val(v) {}
|
||||||
std::string eval(Context& ctx) override { return val; }
|
std::string eval(Context& ctx) override { return val; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// 2. Доступ к переменной (x)
|
|
||||||
struct VarAccessNode : Node {
|
struct VarAccessNode : Node {
|
||||||
std::string name;
|
std::string name;
|
||||||
VarAccessNode(std::string n) : name(n) {}
|
VarAccessNode(std::string n) : name(n) {}
|
||||||
std::string eval(Context& ctx) override {
|
std::string eval(Context& ctx) override {
|
||||||
if (!ctx.exists(name)) {
|
if (ctx.variables.find(name) == ctx.variables.end()) {
|
||||||
std::cerr << "Runtime Error: Variable '" << name << "' is not defined." << std::endl;
|
// Проверка, может это массив?
|
||||||
exit(1);
|
if(ctx.arrays.count(name)) return "ARRAY:" + name;
|
||||||
|
std::cerr << "Runtime Error: Var '" << name << "' not found." << std::endl; exit(1);
|
||||||
}
|
}
|
||||||
return ctx.variables[name];
|
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;
|
struct CompareNode : Node {
|
||||||
std::unique_ptr<Node> expr;
|
std::string op;
|
||||||
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 {
|
|
||||||
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)
|
CompareNode(std::string 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 eval(Context& ctx) override {
|
||||||
std::string lStr = left->eval(ctx);
|
std::string lStr = left->eval(ctx);
|
||||||
std::string rStr = right->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 l = std::stod(lStr);
|
||||||
double r = std::stod(rStr);
|
double r = std::stod(rStr);
|
||||||
if (op == '+') return formatNumber(l + r);
|
|
||||||
if (op == '-') return formatNumber(l - r);
|
if (op == "==") return (std::abs(l - r) < 0.00001) ? "1" : "0";
|
||||||
if (op == '*') return formatNumber(l * r);
|
if (op == "!=") return (std::abs(l - r) > 0.00001) ? "1" : "0";
|
||||||
if (op == '/') return formatNumber((r!=0)? l/r : 0);
|
if (op == "<") return (l < r) ? "1" : "0";
|
||||||
|
if (op == ">") return (l > r) ? "1" : "0";
|
||||||
return "0";
|
return "0";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 6. Блок кода { ... }
|
// IF / ELSE
|
||||||
struct BlockNode : Node {
|
struct IfNode : Node {
|
||||||
std::vector<std::unique_ptr<Node>> statements;
|
std::unique_ptr<Node> condition;
|
||||||
std::string eval(Context& ctx) override {
|
std::unique_ptr<Node> thenBlock;
|
||||||
std::string lastVal = "";
|
std::unique_ptr<Node> elseBlock; // Может быть nullptr
|
||||||
for (auto& stmt : statements) {
|
|
||||||
lastVal = stmt->eval(ctx);
|
|
||||||
}
|
|
||||||
return lastVal;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 7. Объявление функции (void myFunc() { ... })
|
IfNode(std::unique_ptr<Node> c, std::unique_ptr<Node> t, std::unique_ptr<Node> e)
|
||||||
// Мы не выполняем тело, а СОХРАНЯЕМ его в память.
|
: condition(std::move(c)), thenBlock(std::move(t)), elseBlock(std::move(e)) {}
|
||||||
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 {
|
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 "";
|
return "";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 8. Вызов функции (myFunc())
|
// WHILE
|
||||||
|
struct WhileNode : Node {
|
||||||
|
std::unique_ptr<Node> condition;
|
||||||
|
std::unique_ptr<Node> body;
|
||||||
|
WhileNode(std::unique_ptr<Node> c, std::unique_ptr<Node> 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<Node> sizeExpr;
|
||||||
|
ArrayDeclNode(std::string n, std::unique_ptr<Node> 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<std::string>(size, "0");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// set(arr, index, value);
|
||||||
|
struct ArraySetNode : Node {
|
||||||
|
std::string name;
|
||||||
|
std::unique_ptr<Node> idx;
|
||||||
|
std::unique_ptr<Node> val;
|
||||||
|
ArraySetNode(std::string n, std::unique_ptr<Node> i, std::unique_ptr<Node> 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<Node> idx;
|
||||||
|
ArrayGetNode(std::string n, std::unique_ptr<Node> 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<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 << "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<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 << "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<Node> left, right;
|
||||||
|
BinOpNode(char o, std::unique_ptr<Node> l, std::unique_ptr<Node> 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<std::unique_ptr<Node>> 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<Node> body;
|
||||||
|
FuncDefNode(std::string n, std::shared_ptr<Node> b) : name(n), body(b) {}
|
||||||
|
std::string eval(Context& ctx) override { ctx.functions[name] = body; return ""; }
|
||||||
|
};
|
||||||
|
|
||||||
struct FuncCallNode : Node {
|
struct FuncCallNode : Node {
|
||||||
std::string name;
|
std::string name;
|
||||||
FuncCallNode(std::string n) : name(n) {}
|
FuncCallNode(std::string n) : name(n) {}
|
||||||
|
|
||||||
std::string eval(Context& ctx) override {
|
std::string eval(Context& ctx) override {
|
||||||
if (ctx.functions.find(name) == ctx.functions.end()) {
|
if(!ctx.functions.count(name)) { std::cerr << "Unknown func: " << name << std::endl; exit(1); }
|
||||||
std::cerr << "Error: Function '" << name << "' is not defined." << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
// Выполняем сохраненное тело
|
|
||||||
return ctx.functions[name]->eval(ctx);
|
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(Context& ctx) override {
|
std::string eval(Context& ctx) override { std::cout << expr->eval(ctx) << std::endl; return ""; }
|
||||||
std::cout << expr->eval(ctx) << std::endl;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InputNode : Node {
|
struct InputNode : Node {
|
||||||
std::string eval(Context& ctx) override {
|
std::string eval(Context& ctx) override { std::string b; std::getline(std::cin, b); return b; }
|
||||||
std::string b; std::getline(std::cin, b); return b;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
struct RoundNode : Node {
|
struct RoundNode : Node {
|
||||||
std::unique_ptr<Node> expr;
|
std::unique_ptr<Node> expr;
|
||||||
@@ -195,4 +286,12 @@ struct RoundNode : Node {
|
|||||||
};
|
};
|
||||||
struct RandomNode : Node {
|
struct RandomNode : Node {
|
||||||
std::string eval(Context& ctx) override { return std::to_string(rand() % 100); }
|
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";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
+31
-7
@@ -16,13 +16,12 @@ std::vector<Token> Lexer::tokenize() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. ОБРАБОТКА КОММЕНТАРИЕВ (НОВОЕ)
|
// 2. ОБРАБОТКА КОММЕНТАРИЕВ
|
||||||
// Если видим '/', и следующий символ тоже '/', то это комментарий
|
|
||||||
if (current == '/' && pos + 1 < source.length() && source[pos + 1] == '/') {
|
if (current == '/' && pos + 1 < source.length() && source[pos + 1] == '/') {
|
||||||
while (pos < source.length() && source[pos] != '\n') {
|
while (pos < source.length() && source[pos] != '\n') {
|
||||||
pos++; // Пропускаем всё до конца строки
|
pos++;
|
||||||
}
|
}
|
||||||
continue; // Переходим к следующей итерации цикла
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Числа
|
// 3. Числа
|
||||||
@@ -43,9 +42,13 @@ std::vector<Token> Lexer::tokenize() {
|
|||||||
tokens.push_back({TokenType::STRING_LITERAL, str, line});
|
tokens.push_back({TokenType::STRING_LITERAL, str, line});
|
||||||
}
|
}
|
||||||
// 5. Идентификаторы и Ключевые слова
|
// 5. Идентификаторы и Ключевые слова
|
||||||
else if (isalpha(current)) {
|
// ИСПРАВЛЕНИЕ ТУТ: Добавлена проверка || current == '_'
|
||||||
|
else if (isalpha(current) || current == '_') {
|
||||||
std::string id;
|
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});
|
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});
|
||||||
@@ -55,22 +58,43 @@ std::vector<Token> Lexer::tokenize() {
|
|||||||
else if (id == "int") tokens.push_back({TokenType::INT_KW, 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 == "string") tokens.push_back({TokenType::STRING_KW, id, line});
|
||||||
else if (id == "void") tokens.push_back({TokenType::VOID_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});
|
else tokens.push_back({TokenType::IDENTIFIER, id, line});
|
||||||
}
|
}
|
||||||
// 6. Операторы
|
// 6. Операторы
|
||||||
else {
|
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) {
|
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::MOD, "%", 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::LBRACKET, "[", line}); break;
|
||||||
|
case ']': tokens.push_back({TokenType::RBRACKET, "]", line}); break;
|
||||||
case ';': tokens.push_back({TokenType::SEMICOLON, ";", line}); break;
|
case ';': tokens.push_back({TokenType::SEMICOLON, ";", line}); break;
|
||||||
case ',': tokens.push_back({TokenType::COMMA, ",", line}); break;
|
case ',': tokens.push_back({TokenType::COMMA, ",", line}); break;
|
||||||
case '=': tokens.push_back({TokenType::ASSIGN, "=", 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:
|
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);
|
||||||
|
|||||||
+161
-41
@@ -1,5 +1,18 @@
|
|||||||
#include "Parser.h"
|
#include "Parser.h"
|
||||||
|
#include "Lexer.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <filesystem> // Нужно для 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<Token> t) : tokens(t) {}
|
Parser::Parser(std::vector<Token> t) : tokens(t) {}
|
||||||
|
|
||||||
@@ -11,46 +24,42 @@ Token Parser::consume(TokenType type) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- ВЫРАЖЕНИЯ (Остаются без изменений) ---
|
||||||
std::unique_ptr<Node> Parser::primary() {
|
std::unique_ptr<Node> Parser::primary() {
|
||||||
if (tokens[pos].type == TokenType::NUMBER)
|
if (tokens[pos].type == TokenType::NUMBER) return std::make_unique<NumberNode>(consume(TokenType::NUMBER).value);
|
||||||
return std::make_unique<NumberNode>(consume(TokenType::NUMBER).value);
|
if (tokens[pos].type == TokenType::STRING_LITERAL) return std::make_unique<StringNode>(consume(TokenType::STRING_LITERAL).value);
|
||||||
|
|
||||||
if (tokens[pos].type == TokenType::STRING_LITERAL)
|
|
||||||
return std::make_unique<StringNode>(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<ArrayGetNode>(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<ArraySizeNode>(name);
|
||||||
|
}
|
||||||
if (tokens[pos].type == TokenType::IDENTIFIER) {
|
if (tokens[pos].type == TokenType::IDENTIFIER) {
|
||||||
std::string name = consume(TokenType::IDENTIFIER).value;
|
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||||
if (tokens[pos].type == TokenType::LPAREN) {
|
if (tokens[pos].type == TokenType::LPAREN) {
|
||||||
consume(TokenType::LPAREN);
|
consume(TokenType::LPAREN); consume(TokenType::RPAREN);
|
||||||
consume(TokenType::RPAREN);
|
|
||||||
return std::make_unique<FuncCallNode>(name);
|
return std::make_unique<FuncCallNode>(name);
|
||||||
}
|
}
|
||||||
return std::make_unique<VarAccessNode>(name);
|
return std::make_unique<VarAccessNode>(name);
|
||||||
}
|
}
|
||||||
|
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::INPUT) {
|
if (tokens[pos].type == TokenType::ROUND) { consume(TokenType::ROUND); consume(TokenType::LPAREN); auto n = expression(); consume(TokenType::RPAREN); return std::make_unique<RoundNode>(std::move(n)); }
|
||||||
consume(TokenType::INPUT); consume(TokenType::LPAREN); consume(TokenType::RPAREN);
|
if (tokens[pos].type == TokenType::RANDOM) { consume(TokenType::RANDOM); consume(TokenType::LPAREN); consume(TokenType::RPAREN); return std::make_unique<RandomNode>(); }
|
||||||
return std::make_unique<InputNode>();
|
if (tokens[pos].type == TokenType::LPAREN) { consume(TokenType::LPAREN); auto n = expression(); consume(TokenType::RPAREN); return n; }
|
||||||
}
|
|
||||||
if (tokens[pos].type == TokenType::ROUND) {
|
|
||||||
consume(TokenType::ROUND); consume(TokenType::LPAREN); auto n = expression(); consume(TokenType::RPAREN);
|
|
||||||
return std::make_unique<RoundNode>(std::move(n));
|
|
||||||
}
|
|
||||||
if (tokens[pos].type == TokenType::RANDOM) {
|
|
||||||
consume(TokenType::RANDOM); consume(TokenType::LPAREN); consume(TokenType::RPAREN);
|
|
||||||
return std::make_unique<RandomNode>();
|
|
||||||
}
|
|
||||||
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;
|
std::cerr << "Error: Unexpected token '" << tokens[pos].value << "' line " << tokens[pos].line << std::endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Node> Parser::multiplication() {
|
std::unique_ptr<Node> Parser::multiplication() {
|
||||||
auto node = primary();
|
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++;
|
char op = tokens[pos].value[0]; pos++;
|
||||||
node = std::make_unique<BinOpNode>(op, std::move(node), primary());
|
node = std::make_unique<BinOpNode>(op, std::move(node), primary());
|
||||||
}
|
}
|
||||||
@@ -66,10 +75,19 @@ std::unique_ptr<Node> Parser::expression() {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ИЗМЕНЕНИЕ: Возвращаем unique_ptr и используем make_unique
|
std::unique_ptr<Node> 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<CompareNode>(op, std::move(node), expression());
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<BlockNode> Parser::parseBlock() {
|
std::unique_ptr<BlockNode> Parser::parseBlock() {
|
||||||
consume(TokenType::LBRACE);
|
consume(TokenType::LBRACE);
|
||||||
auto block = std::make_unique<BlockNode>(); // Здесь было make_shared
|
auto block = std::make_unique<BlockNode>();
|
||||||
while (tokens[pos].type != TokenType::RBRACE && tokens[pos].type != TokenType::END) {
|
while (tokens[pos].type != TokenType::RBRACE && tokens[pos].type != TokenType::END) {
|
||||||
block->statements.push_back(statement());
|
block->statements.push_back(statement());
|
||||||
}
|
}
|
||||||
@@ -77,11 +95,56 @@ std::unique_ptr<BlockNode> Parser::parseBlock() {
|
|||||||
return block;
|
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<Node> Parser::statement() {
|
std::unique_ptr<Node> 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<BlockNode>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. ОБЪЯВЛЕНИЕ ПЕРЕМЕННЫХ (Всегда выполняем, даже при импорте!)
|
||||||
if (tokens[pos].type == TokenType::INT_KW || tokens[pos].type == TokenType::STRING_KW) {
|
if (tokens[pos].type == TokenType::INT_KW || tokens[pos].type == TokenType::STRING_KW) {
|
||||||
std::string type = tokens[pos].value;
|
std::string type = tokens[pos].value; pos++;
|
||||||
pos++;
|
|
||||||
std::string name = consume(TokenType::IDENTIFIER).value;
|
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||||
consume(TokenType::ASSIGN);
|
consume(TokenType::ASSIGN);
|
||||||
auto expr = expression();
|
auto expr = expression();
|
||||||
@@ -89,50 +152,105 @@ std::unique_ptr<Node> Parser::statement() {
|
|||||||
return std::make_unique<VarDeclNode>(type, name, std::move(expr));
|
return std::make_unique<VarDeclNode>(type, name, std::move(expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Объявление функций
|
// 3. ФУНКЦИИ (Всегда выполняем, то есть запоминаем их)
|
||||||
if (tokens[pos].type == TokenType::VOID_KW) {
|
if (tokens[pos].type == TokenType::VOID_KW) {
|
||||||
consume(TokenType::VOID_KW);
|
consume(TokenType::VOID_KW);
|
||||||
std::string name = consume(TokenType::IDENTIFIER).value;
|
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||||
consume(TokenType::LPAREN);
|
consume(TokenType::LPAREN); consume(TokenType::RPAREN);
|
||||||
consume(TokenType::RPAREN);
|
|
||||||
|
|
||||||
// Тут магия: unique_ptr автоматически превратится в shared_ptr при создании FuncDefNode
|
|
||||||
auto body = parseBlock();
|
auto body = parseBlock();
|
||||||
return std::make_unique<FuncDefNode>(name, std::move(body));
|
return std::make_unique<FuncDefNode>(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<ArrayDeclNode>(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<BlockNode>(); // <-- Игнор
|
||||||
|
return std::make_unique<WhileNode>(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<Node> 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<BlockNode>(); // <-- Игнор
|
||||||
|
return std::make_unique<IfNode>(std::move(cond), std::move(thenB), std::move(elseB));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. PRINT
|
||||||
if (tokens[pos].type == TokenType::PRINT) {
|
if (tokens[pos].type == TokenType::PRINT) {
|
||||||
consume(TokenType::PRINT); consume(TokenType::LPAREN);
|
consume(TokenType::PRINT); consume(TokenType::LPAREN);
|
||||||
auto expr = expression();
|
auto expr = expression();
|
||||||
consume(TokenType::RPAREN); consume(TokenType::SEMICOLON);
|
consume(TokenType::RPAREN); consume(TokenType::SEMICOLON);
|
||||||
|
|
||||||
|
if (importMode) return std::make_unique<BlockNode>(); // <-- Игнор
|
||||||
return std::make_unique<PrintNode>(std::move(expr));
|
return std::make_unique<PrintNode>(std::move(expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 8. FOX
|
||||||
if (tokens[pos].type == TokenType::FOX) {
|
if (tokens[pos].type == TokenType::FOX) {
|
||||||
consume(TokenType::FOX); consume(TokenType::LPAREN); consume(TokenType::RPAREN); consume(TokenType::SEMICOLON);
|
consume(TokenType::FOX); consume(TokenType::LPAREN); consume(TokenType::RPAREN); consume(TokenType::SEMICOLON);
|
||||||
return std::make_unique<StringNode>("Fox says: Hello!");
|
if (importMode) return std::make_unique<BlockNode>(); // <-- Игнор
|
||||||
|
return std::make_unique<FoxNode>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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<BlockNode>(); // <-- Игнор
|
||||||
|
return std::make_unique<ArraySetNode>(name, std::move(idx), std::move(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10. IDENTIFIER (Присваивание или Вызов функции)
|
||||||
if (tokens[pos].type == TokenType::IDENTIFIER) {
|
if (tokens[pos].type == TokenType::IDENTIFIER) {
|
||||||
if (tokens[pos+1].type == TokenType::ASSIGN) {
|
if (tokens[pos+1].type == TokenType::ASSIGN) {
|
||||||
std::string name = consume(TokenType::IDENTIFIER).value;
|
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||||
consume(TokenType::ASSIGN);
|
consume(TokenType::ASSIGN);
|
||||||
auto expr = expression();
|
auto expr = expression();
|
||||||
consume(TokenType::SEMICOLON);
|
consume(TokenType::SEMICOLON);
|
||||||
|
|
||||||
|
if (importMode) return std::make_unique<BlockNode>(); // <-- Игнор присваивания
|
||||||
return std::make_unique<AssignNode>(name, std::move(expr));
|
return std::make_unique<AssignNode>(name, std::move(expr));
|
||||||
}
|
}
|
||||||
if (tokens[pos+1].type == TokenType::LPAREN) {
|
if (tokens[pos+1].type == TokenType::LPAREN) {
|
||||||
std::string name = consume(TokenType::IDENTIFIER).value;
|
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||||
consume(TokenType::LPAREN); consume(TokenType::RPAREN); consume(TokenType::SEMICOLON);
|
consume(TokenType::LPAREN); consume(TokenType::RPAREN); consume(TokenType::SEMICOLON);
|
||||||
|
|
||||||
|
if (importMode) return std::make_unique<BlockNode>(); // <-- Игнор вызова (mainLogic не сработает)
|
||||||
return std::make_unique<FuncCallNode>(name);
|
return std::make_unique<FuncCallNode>(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;
|
std::cerr << "Syntax Error: Unknown statement '" << tokens[pos].value << "' at line " << tokens[pos].line << std::endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -141,6 +259,8 @@ std::unique_ptr<Node> Parser::statement() {
|
|||||||
void Parser::run() {
|
void Parser::run() {
|
||||||
while (tokens[pos].type != TokenType::END) {
|
while (tokens[pos].type != TokenType::END) {
|
||||||
auto stmt = statement();
|
auto stmt = statement();
|
||||||
|
// stmt->eval вызывается, но если мы вернули BlockNode (пустышку) при импорте,
|
||||||
|
// то ничего не произойдет.
|
||||||
stmt->eval(globalContext);
|
stmt->eval(globalContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+14
-7
@@ -3,22 +3,29 @@
|
|||||||
#include "AST.h"
|
#include "AST.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <string> // Не забудь
|
||||||
|
|
||||||
class Parser {
|
class Parser {
|
||||||
std::vector<Token> tokens;
|
std::vector<Token> tokens;
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
Context globalContext;
|
|
||||||
|
public:
|
||||||
|
Context globalContext;
|
||||||
|
|
||||||
|
// НОВЫЕ ПОЛЯ ДЛЯ ПУТЕЙ И ИМПОРТА
|
||||||
|
std::string currentFile; // Путь к файлу, который сейчас парсится
|
||||||
|
bool importMode = false; // Если true, то выполняем только объявления (vars/funcs)
|
||||||
|
|
||||||
|
Parser(std::vector<Token> t);
|
||||||
|
|
||||||
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();
|
||||||
|
std::unique_ptr<Node> comparison();
|
||||||
std::unique_ptr<Node> statement();
|
|
||||||
// ИЗМЕНЕНИЕ: теперь возвращает unique_ptr
|
|
||||||
std::unique_ptr<BlockNode> parseBlock();
|
|
||||||
|
|
||||||
public:
|
std::unique_ptr<Node> statement();
|
||||||
Parser(std::vector<Token> t);
|
std::unique_ptr<BlockNode> parseBlock();
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
};
|
};
|
||||||
+9
-5
@@ -3,15 +3,19 @@
|
|||||||
|
|
||||||
enum class TokenType {
|
enum class TokenType {
|
||||||
NUMBER, STRING_LITERAL,
|
NUMBER, STRING_LITERAL,
|
||||||
PLUS, MINUS, STAR, SLASH,
|
PLUS, MINUS, STAR, SLASH, MOD, // Добавил MOD (%)
|
||||||
LPAREN, RPAREN, LBRACE, RBRACE,
|
LPAREN, RPAREN, LBRACE, RBRACE, LBRACKET, RBRACKET, // []
|
||||||
SEMICOLON, COMMA, ASSIGN, // =, , ;
|
SEMICOLON, COMMA, ASSIGN,
|
||||||
|
EQ, NEQ, LT, GT, // ==, !=, <, >
|
||||||
|
|
||||||
// Ключевые слова
|
// Ключевые слова
|
||||||
PRINT, INPUT, ROUND, RANDOM, FOX,
|
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
|
END, ERROR
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
BIN
Binary file not shown.
+11
-4
@@ -10,20 +10,27 @@ int main(int argc, char* argv[]) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1. Читаем файл
|
||||||
std::ifstream file(argv[1]);
|
std::ifstream file(argv[1]);
|
||||||
if (!file.is_open()) {
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::stringstream buffer;
|
std::stringstream buffer;
|
||||||
buffer << file.rdbuf();
|
buffer << file.rdbuf();
|
||||||
|
std::string code = buffer.str();
|
||||||
|
|
||||||
Lexer lexer(buffer.str());
|
// 2. Запускаем конвейер
|
||||||
auto tokens = lexer.tokenize();
|
Lexer lexer(code);
|
||||||
|
std::vector<Token> tokens = lexer.tokenize();
|
||||||
|
|
||||||
Parser parser(tokens);
|
Parser parser(tokens);
|
||||||
|
|
||||||
|
// ВАЖНО: Передаем имя файла, чтобы парсер знал, где он находится
|
||||||
|
parser.currentFile = argv[1];
|
||||||
|
|
||||||
parser.run();
|
parser.run();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,30 @@
|
|||||||
{
|
{
|
||||||
|
include("script.fox");
|
||||||
|
// Глоб. Переменные
|
||||||
|
string Player = "Artem";
|
||||||
|
int hpplayer = 100;
|
||||||
|
int level = 1;
|
||||||
|
int usr_input = 0;
|
||||||
|
|
||||||
print("Добро пожаловать в FoxLang");
|
void Hello() {
|
||||||
print(input() + input());
|
print("Привет, " + Player + "!");
|
||||||
|
print("Тебе предстоит пройти это подземелье");
|
||||||
|
}
|
||||||
|
Hello();
|
||||||
|
|
||||||
}
|
void ShowStats() {
|
||||||
|
print("-----------Статистика-----------");
|
||||||
|
print("Имя игрока: " + Player);
|
||||||
|
print("Здоровье: " + hpplayer);
|
||||||
|
print("Уровень: " + level);
|
||||||
|
}
|
||||||
|
ShowStats();
|
||||||
|
|
||||||
|
usr_input = input();
|
||||||
|
|
||||||
|
if (usr_input == 1) {
|
||||||
|
init();
|
||||||
|
} else {
|
||||||
|
print("Не найдено действие для этого ввода.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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).");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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!");
|
||||||
Reference in New Issue
Block a user