addding return
This commit is contained in:
@@ -12,16 +12,68 @@
|
||||
// Forward declaration
|
||||
struct Node;
|
||||
|
||||
// --- ПАМЯТЬ ---
|
||||
struct FuncParam {
|
||||
std::string type;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
// Контекст памяти (переменные и функции)
|
||||
struct Context {
|
||||
Context* parent = nullptr; // Для глобальных переменных
|
||||
std::map<std::string, std::string> variables;
|
||||
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) {
|
||||
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<std::string>& 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<Node> 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<Node> 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<FuncParam> params;
|
||||
std::shared_ptr<Node> body;
|
||||
|
||||
FuncDefNode(std::string rt, std::string n, std::vector<FuncParam> p, std::shared_ptr<Node> b)
|
||||
: returnType(rt), name(n), params(p), body(b) {}
|
||||
|
||||
// Eval ничего не делает, функция регистрируется Парсером
|
||||
std::string eval(Context& ctx) override { return ""; }
|
||||
};
|
||||
|
||||
// RETURN - выбрасывает исключение с значением
|
||||
struct ReturnNode : Node {
|
||||
std::unique_ptr<Node> expr;
|
||||
ReturnNode(std::unique_ptr<Node> 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<std::unique_ptr<Node>> args;
|
||||
|
||||
FuncCallNode(std::string n, std::vector<std::unique_ptr<Node>> 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<FuncDefNode*>(funcNodeBase.get());
|
||||
|
||||
if (args.size() != funcDef->params.size()) {
|
||||
std::cerr << "Args count mismatch for '" << name << "'" << std::endl; exit(1);
|
||||
}
|
||||
|
||||
std::vector<std::string> 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<Node> left, right;
|
||||
CompareNode(std::string o, std::unique_ptr<Node> l, std::unique_ptr<Node> r)
|
||||
: op(o), left(std::move(l)), right(std::move(r)) {}
|
||||
|
||||
struct GlobalVarDeclNode : Node {
|
||||
std::string type, name; std::unique_ptr<Node> expr;
|
||||
GlobalVarDeclNode(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 {
|
||||
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<Node> condition;
|
||||
std::unique_ptr<Node> thenBlock;
|
||||
std::unique_ptr<Node> elseBlock; // Может быть nullptr
|
||||
|
||||
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)) {}
|
||||
|
||||
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<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];
|
||||
// Здесь expr->eval(ctx) может быть FuncCallNode, который вернет результат!
|
||||
ctx.defineVar(name, expr->eval(ctx));
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
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];
|
||||
ctx.setVar(name, expr->eval(ctx));
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
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)) {}
|
||||
@@ -245,53 +217,69 @@ struct BinOpNode : Node {
|
||||
return "0";
|
||||
}
|
||||
};
|
||||
|
||||
struct CompareNode : Node {
|
||||
std::string op; std::unique_ptr<Node> left, right;
|
||||
CompareNode(std::string 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 {
|
||||
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<Node> cond, thenB, elseB;
|
||||
IfNode(std::unique_ptr<Node> c, std::unique_ptr<Node> t, std::unique_ptr<Node> 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<Node> cond, body;
|
||||
WhileNode(std::unique_ptr<Node> c, std::unique_ptr<Node> 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<std::unique_ptr<Node>> statements;
|
||||
std::vector<std::unique_ptr<Node>> 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<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 {
|
||||
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<Node> expr;
|
||||
PrintNode(std::unique_ptr<Node> 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<Node> expr;
|
||||
RoundNode(std::unique_ptr<Node> e) : expr(std::move(e)) {}
|
||||
std::string eval(Context& ctx) override { return formatNumber(std::round(std::stod(expr->eval(ctx)))); }
|
||||
};
|
||||
struct RandomNode : Node {
|
||||
std::string eval(Context& ctx) override { return std::to_string(rand() % 100); }
|
||||
};
|
||||
|
||||
// ИСПРАВЛЕННЫЙ FOX NODE: Теперь он выводит текст сам!
|
||||
struct FoxNode : Node {
|
||||
struct ArrayDeclNode : Node {
|
||||
std::string name; std::unique_ptr<Node> size;
|
||||
ArrayDeclNode(std::string n, std::unique_ptr<Node> 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::string>(std::stoi(size->eval(ctx)), "0");
|
||||
return "";
|
||||
}
|
||||
};
|
||||
};
|
||||
struct ArraySetNode : Node {
|
||||
std::string name; std::unique_ptr<Node> idx, 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 {
|
||||
auto& arr = ctx.getArray(name); arr[std::stoi(idx->eval(ctx))] = val->eval(ctx); return "";
|
||||
}
|
||||
};
|
||||
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 { 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 ""; } };
|
||||
+3
-12
@@ -9,22 +9,17 @@ std::vector<Token> 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<Token> 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<Token> 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<Token> 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;
|
||||
|
||||
+156
-136
@@ -3,11 +3,7 @@
|
||||
#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 "./";
|
||||
@@ -18,42 +14,76 @@ Parser::Parser(std::vector<Token> 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<Node> Parser::primary() {
|
||||
if (tokens[pos].type == TokenType::NUMBER) 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);
|
||||
// 1. УНАРНЫЙ МИНУС (Обработка отрицательных чисел: -5, -var)
|
||||
if (tokens[pos].type == TokenType::MINUS) {
|
||||
consume(TokenType::MINUS);
|
||||
// Превращаем -5 в (0 - 5)
|
||||
return std::make_unique<BinOpNode>('-',
|
||||
std::make_unique<NumberNode>("0"),
|
||||
primary() // Рекурсивно вызываем primary, чтобы считать само число или скобку
|
||||
);
|
||||
}
|
||||
|
||||
// 2. Числа
|
||||
if (tokens[pos].type == TokenType::NUMBER) {
|
||||
return std::make_unique<NumberNode>(consume(TokenType::NUMBER).value);
|
||||
}
|
||||
|
||||
// 3. Строки
|
||||
if (tokens[pos].type == TokenType::STRING_LITERAL) {
|
||||
return std::make_unique<StringNode>(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<std::unique_ptr<Node>> 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<FuncCallNode>(name, std::move(args));
|
||||
}
|
||||
// Иначе это просто доступ к переменной
|
||||
return std::make_unique<VarAccessNode>(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<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) {
|
||||
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||
if (tokens[pos].type == TokenType::LPAREN) {
|
||||
consume(TokenType::LPAREN); consume(TokenType::RPAREN);
|
||||
return std::make_unique<FuncCallNode>(name);
|
||||
}
|
||||
return std::make_unique<VarAccessNode>(name);
|
||||
}
|
||||
if (tokens[pos].type == TokenType::INPUT) { consume(TokenType::INPUT); consume(TokenType::LPAREN); consume(TokenType::RPAREN); return std::make_unique<InputNode>(); }
|
||||
if (tokens[pos].type == TokenType::ROUND) { 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;
|
||||
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::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<BlockNode> Parser::parseBlock() {
|
||||
consume(TokenType::LBRACE);
|
||||
auto block = std::make_unique<BlockNode>();
|
||||
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<Node> 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<BlockNode>();
|
||||
}
|
||||
|
||||
// 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<GlobalVarDeclNode>(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<FuncParam> 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<FuncDefNode>(type, name, params, std::move(body)));
|
||||
return std::make_unique<BlockNode>();
|
||||
}
|
||||
|
||||
// Объявление переменной
|
||||
consume(TokenType::ASSIGN);
|
||||
auto expr = expression();
|
||||
consume(TokenType::SEMICOLON);
|
||||
return std::make_unique<VarDeclNode>(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<FuncDefNode>(name, std::move(body));
|
||||
if (tokens[pos].type == TokenType::RETURN) {
|
||||
consume(TokenType::RETURN);
|
||||
std::unique_ptr<Node> expr = nullptr;
|
||||
if (tokens[pos].type != TokenType::SEMICOLON) expr = expression();
|
||||
consume(TokenType::SEMICOLON);
|
||||
if (importMode) return std::make_unique<BlockNode>();
|
||||
return std::make_unique<ReturnNode>(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<BlockNode>();
|
||||
return std::make_unique<WhileNode>(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<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));
|
||||
}
|
||||
|
||||
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<BlockNode>();
|
||||
return std::make_unique<PrintNode>(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<BlockNode>();
|
||||
return std::make_unique<FoxNode>();
|
||||
}
|
||||
|
||||
// 4. МАССИВЫ (Выполняем, глобальные массивы нужны)
|
||||
if (tokens[pos].type == TokenType::ARRAY) {
|
||||
consume(TokenType::ARRAY);
|
||||
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||
@@ -169,98 +239,48 @@ std::unique_ptr<Node> Parser::statement() {
|
||||
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) {
|
||||
consume(TokenType::PRINT); consume(TokenType::LPAREN);
|
||||
auto expr = expression();
|
||||
consume(TokenType::RPAREN); consume(TokenType::SEMICOLON);
|
||||
|
||||
if (importMode) return std::make_unique<BlockNode>(); // <-- Игнор
|
||||
return std::make_unique<PrintNode>(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<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>(); // <-- Игнор
|
||||
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+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<BlockNode>(); // <-- Игнор присваивания
|
||||
if (importMode) return std::make_unique<BlockNode>();
|
||||
return std::make_unique<AssignNode>(name, std::move(expr));
|
||||
}
|
||||
if (tokens[pos+1].type == TokenType::LPAREN) {
|
||||
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||
consume(TokenType::LPAREN); consume(TokenType::RPAREN); consume(TokenType::SEMICOLON);
|
||||
|
||||
if (importMode) return std::make_unique<BlockNode>(); // <-- Игнор вызова (mainLogic не сработает)
|
||||
return std::make_unique<FuncCallNode>(name);
|
||||
consume(TokenType::LPAREN);
|
||||
std::vector<std::unique_ptr<Node>> 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<BlockNode>();
|
||||
return std::make_unique<FuncCallNode>(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);
|
||||
}
|
||||
}
|
||||
+9
-6
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user