adeded new features
This commit is contained in:
@@ -1,2 +1,3 @@
|
|||||||
.kiro/settings/lsp.json
|
.kiro/settings/lsp.json
|
||||||
web-ide
|
web-ide
|
||||||
|
desktop-app
|
||||||
|
|||||||
+185
-42
@@ -117,11 +117,11 @@ int calculate_area(int width, int height) {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Математика и Логика
|
## 4. Математика и Логика
|
||||||
|
|
||||||
Поддерживаются стандартные арифметические операции с учетом приоритета.
|
Поддерживаются стандартные арифметические операции с учетом приоритета.
|
||||||
|
|
||||||
### Операторы
|
### Арифметические операторы
|
||||||
|
|
||||||
| Оператор | Описание | Пример |
|
| Оператор | Описание | Пример |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
@@ -132,18 +132,36 @@ int calculate_area(int width, int height) {
|
|||||||
| `%` | Остаток от деления | `10 % 3` (вернет 1) |
|
| `%` | Остаток от деления | `10 % 3` (вернет 1) |
|
||||||
| `++` | Инкремент (увеличение на 1) | `i++` (вернет старое значение, затем увеличит переменную) |
|
| `++` | Инкремент (увеличение на 1) | `i++` (вернет старое значение, затем увеличит переменную) |
|
||||||
|
|
||||||
### Сравнение
|
### Операторы сравнения
|
||||||
|
|
||||||
Операторы сравнения возвращают `1` (истина) или `0` (ложь).
|
Операторы сравнения возвращают `1` (истина) или `0` (ложь).
|
||||||
|
|
||||||
* `==` (Равно)
|
| Оператор | Описание | Пример |
|
||||||
* `!=` (Не равно)
|
| --- | --- | --- |
|
||||||
* `<` (Меньше)
|
| `==` | Равно | `x == 5` |
|
||||||
* `>` (Больше)
|
| `!=` | Не равно | `x != 5` |
|
||||||
|
| `<` | Меньше | `x < 10` |
|
||||||
|
| `>` | Больше | `x > 0` |
|
||||||
|
|
||||||
|
### Логические операторы
|
||||||
|
|
||||||
|
FoxLang поддерживает логические операторы для работы с boolean значениями:
|
||||||
|
|
||||||
|
| Оператор | Описание | Пример |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `&&` | Логическое И (AND) | `(x > 0) && (x < 10)` |
|
||||||
|
| `\|\|` | Логическое ИЛИ (OR) | `(x == 0) \|\| (x == 1)` |
|
||||||
|
| `!` | Логическое НЕ (NOT) | `!(x == 0)` |
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
bool is_valid = (age >= 18) && (age <= 65);
|
||||||
|
bool is_weekend = (day == "Saturday") || (day == "Sunday");
|
||||||
|
bool is_not_empty = !name.empty();
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Управляющие конструкции
|
## 5. Управляющие конструкции
|
||||||
|
|
||||||
### Условия (If / Else)
|
### Условия (If / Else)
|
||||||
|
|
||||||
@@ -178,6 +196,45 @@ for (int i = 0; i < 5; i++) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Switch/Case конструкции
|
||||||
|
|
||||||
|
FoxLang поддерживает конструкции `switch/case` с поддержкой `break` и `default`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int day = 3;
|
||||||
|
switch (day) {
|
||||||
|
case 1:
|
||||||
|
print("Понедельник");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
print("Вторник");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
print("Среда");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
print("Другой день");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Управление циклами
|
||||||
|
|
||||||
|
- `break` — Прерывает выполнение цикла или switch
|
||||||
|
- `continue` — Переходит к следующей итерации цикла
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (i == 5) {
|
||||||
|
continue; // Пропустить 5
|
||||||
|
}
|
||||||
|
if (i == 8) {
|
||||||
|
break; // Выйти из цикла
|
||||||
|
}
|
||||||
|
print(i);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Области видимости (Scope)
|
### Области видимости (Scope)
|
||||||
|
|
||||||
Блоки кода `{ ... }` создают новую область видимости. Переменные, объявленные внутри блока, недоступны снаружи.
|
Блоки кода `{ ... }` создают новую область видимости. Переменные, объявленные внутри блока, недоступны снаружи.
|
||||||
@@ -194,31 +251,6 @@ int global = 10;
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. Функции
|
|
||||||
|
|
||||||
Функции могут быть объявлены с типом возвращаемого значения или `void`.
|
|
||||||
|
|
||||||
**Определение:**
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
void myFunc() {
|
|
||||||
print("Hello from function!");
|
|
||||||
}
|
|
||||||
|
|
||||||
int square(int x) {
|
|
||||||
return x * x;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Вызов:**
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
myFunc();
|
|
||||||
int result = square(5);
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Массивы
|
## 6. Массивы
|
||||||
|
|
||||||
Массивы в FoxLang имеют фиксированный размер при создании.
|
Массивы в FoxLang имеют фиксированный размер при создании.
|
||||||
@@ -249,12 +281,123 @@ FoxLang поддерживает импорт внешних модулей.
|
|||||||
|
|
||||||
## 8. Встроенные функции
|
## 8. Встроенные функции
|
||||||
|
|
||||||
| Функция | Описание |
|
### Ввод/Вывод
|
||||||
| --- | --- |
|
| Функция | Описание | Пример |
|
||||||
| `print(expr)` | Выводит текст или результат выражения в консоль. |
|
| --- | --- | --- |
|
||||||
| `input()` | Останавливает программу и ждет ввода строки от пользователя. |
|
| `print(expr...)` | Выводит текст или результат выражения в консоль. Может принимать несколько аргументов. | `print("Hello", name);` |
|
||||||
| `round(expr)` | Округляет дробное число до ближайшего целого. |
|
| `input()` | Ждет ввода строки от пользователя и возвращает её. | `string name = input();` |
|
||||||
| `random()` | Генерирует случайное число от 0 до 99. |
|
| `input(prompt)` | Выводит приглашение и ждет ввода строки. | `string name = input("Имя: ");` |
|
||||||
| `read_file(filename)` | Читает первую непустую строку из файла (игнорирует комментарии #). |
|
|
||||||
| `http_get(url)` | Выполняет HTTP GET запрос и возвращает ответ сервера. |
|
### Математические функции
|
||||||
| `fox()` | Пасхалка: выводит название языка. |
|
| Функция | Описание | Пример |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `round(number)` | Округляет дробное число до ближайшего целого. | `int x = round(3.7); // 4` |
|
||||||
|
| `random(min, max)` | Генерирует случайное число в диапазоне от min до max включительно. | `int dice = random(1, 6);` |
|
||||||
|
|
||||||
|
### Работа с файлами
|
||||||
|
| Функция | Описание | Пример |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `read_file(filename)` | Читает первую непустую строку из файла (игнорирует комментарии #). | `string config = read_file("config.txt");` |
|
||||||
|
|
||||||
|
### HTTP запросы
|
||||||
|
| Функция | Описание | Пример |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `httpget(url)` | Выполняет HTTP GET запрос и возвращает ответ сервера. | `string data = httpget("https://api.example.com");` |
|
||||||
|
| `httppost(url, data)` | Выполняет HTTP POST запрос с данными. | `string response = httppost(url, "{\"key\":\"value\"}");` |
|
||||||
|
| `httppost(url, data, content_type)` | HTTP POST с указанием типа контента. | `httppost(url, data, "application/json");` |
|
||||||
|
| `httpput(url, data)` | Выполняет HTTP PUT запрос с данными. | `string response = httpput(url, data);` |
|
||||||
|
| `httpput(url, data, content_type)` | HTTP PUT с указанием типа контента. | `httpput(url, data, "text/plain");` |
|
||||||
|
| `httpdelete(url)` | Выполняет HTTP DELETE запрос. | `string response = httpdelete(url);` |
|
||||||
|
|
||||||
|
### Работа со строками и JSON
|
||||||
|
| Функция | Описание | Пример |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `json_get(json_string, key)` | Извлекает значение по ключу из JSON строки. Поддерживает ключи: "chat_id", "text", "update_id". | `string chat_id = json_get(response, "chat_id");` |
|
||||||
|
| `str_contains(text, substring)` | Проверяет, содержит ли строка подстроку. Возвращает `true` или `false`. | `bool found = str_contains("Hello World", "World");` |
|
||||||
|
| `str_to_int(string)` | Преобразует строку в целое число. При ошибке возвращает 0. | `int num = str_to_int("123");` |
|
||||||
|
|
||||||
|
### Ввод с клавиатуры (низкоуровневый)
|
||||||
|
| Функция | Описание | Пример |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `getch()` | Читает один символ с клавиатуры без нажатия Enter. | `string key = getch();` |
|
||||||
|
| `kbhit()` | Проверяет, была ли нажата клавиша. Возвращает `true` или `false`. | `bool pressed = kbhit();` |
|
||||||
|
|
||||||
|
### Системные функции
|
||||||
|
| Функция | Описание | Пример |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `wait(milliseconds)` | Приостанавливает выполнение программы на указанное количество миллисекунд. | `wait(1000); // Пауза 1 секунда` |
|
||||||
|
| `fox()` | Пасхалка: выводит название языка "FoxLang". | `fox();` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Современный синтаксис
|
||||||
|
|
||||||
|
FoxLang поддерживает современные соглашения по именованию и синтаксису:
|
||||||
|
|
||||||
|
### Идентификаторы с подчеркиваниями
|
||||||
|
|
||||||
|
В отличие от старых версий, FoxLang теперь полностью поддерживает идентификаторы с подчеркиваниями:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Переменные
|
||||||
|
string user_name = "john_doe";
|
||||||
|
int max_health = 100;
|
||||||
|
bool is_game_over = false;
|
||||||
|
|
||||||
|
// Функции
|
||||||
|
void calculate_damage(int base_damage, float multiplier) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_player_score() {
|
||||||
|
return player_score;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Глобальные переменные
|
||||||
|
|
||||||
|
FoxLang поддерживает объявление глобальных переменных с ключевым словом `global`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
global int game_score = 0;
|
||||||
|
global string player_name = "Unknown";
|
||||||
|
|
||||||
|
void update_score(int points) {
|
||||||
|
game_score = game_score + points;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
print("Score: " + game_score);
|
||||||
|
update_score(100);
|
||||||
|
print("New score: " + game_score);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Примеры современного кода
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Современная функция с подчеркиваниями
|
||||||
|
bool check_user_permissions(string user_role, int required_level) {
|
||||||
|
if (user_role == "admin") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int user_level = get_user_level(user_role);
|
||||||
|
return user_level >= required_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Работа с массивами
|
||||||
|
void sort_user_scores(array scores, int count) {
|
||||||
|
for (int i = 0; i < count - 1; i++) {
|
||||||
|
for (int j = 0; j < count - i - 1; j++) {
|
||||||
|
if (get(scores, j) > get(scores, j + 1)) {
|
||||||
|
int temp = get(scores, j);
|
||||||
|
set(scores, j, get(scores, j + 1));
|
||||||
|
set(scores, j + 1, temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|||||||
@@ -9,6 +9,15 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <conio.h>
|
||||||
|
#else
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
struct Node;
|
struct Node;
|
||||||
|
|
||||||
@@ -74,6 +83,9 @@ struct ReturnValue {
|
|||||||
Value value;
|
Value value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BreakException {};
|
||||||
|
struct ContinueException {};
|
||||||
|
|
||||||
static std::string formatNumber(double val) {
|
static std::string formatNumber(double val) {
|
||||||
std::string s = std::to_string(val);
|
std::string s = std::to_string(val);
|
||||||
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
|
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
|
||||||
@@ -116,14 +128,58 @@ struct FuncCallNode : Node {
|
|||||||
|
|
||||||
Value eval(Context& ctx) override {
|
Value eval(Context& ctx) override {
|
||||||
// Встроенные функции
|
// Встроенные функции
|
||||||
if (name == "print" && args.size() == 1) {
|
if (name == "print") {
|
||||||
std::cout << args[0]->eval(ctx).value << std::endl;
|
if (args.empty()) {
|
||||||
|
std::cout << std::endl;
|
||||||
|
} else {
|
||||||
|
for (size_t i = 0; i < args.size(); i++) {
|
||||||
|
if (i > 0) std::cout << " ";
|
||||||
|
std::cout << args[i]->eval(ctx).value;
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
return {"void", ""};
|
return {"void", ""};
|
||||||
}
|
}
|
||||||
if (name == "input" && args.size() == 0) {
|
if (name == "input") {
|
||||||
std::string input; std::getline(std::cin, input);
|
if (args.size() == 1) {
|
||||||
|
// Вывести приглашение
|
||||||
|
std::cout << args[0]->eval(ctx).value;
|
||||||
|
}
|
||||||
|
std::string input;
|
||||||
|
std::getline(std::cin, input);
|
||||||
return {"string", input};
|
return {"string", input};
|
||||||
}
|
}
|
||||||
|
if (name == "getch" && args.size() == 0) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return {"string", std::string(1, _getch())};
|
||||||
|
#else
|
||||||
|
struct termios oldt, newt;
|
||||||
|
tcgetattr(STDIN_FILENO, &oldt);
|
||||||
|
newt = oldt;
|
||||||
|
newt.c_lflag &= ~(ICANON | ECHO);
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
||||||
|
char ch = getchar();
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
||||||
|
return {"string", std::string(1, ch)};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (name == "kbhit" && args.size() == 0) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return {"bool", _kbhit() ? "true" : "false"};
|
||||||
|
#else
|
||||||
|
int ch = getchar();
|
||||||
|
if (ch != EOF) {
|
||||||
|
ungetc(ch, stdin);
|
||||||
|
return {"bool", "true"};
|
||||||
|
}
|
||||||
|
return {"bool", "false"};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (name == "wait" && args.size() == 1) {
|
||||||
|
int milliseconds = std::stoi(args[0]->eval(ctx).value);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
||||||
|
return {"void", ""};
|
||||||
|
}
|
||||||
if (name == "round" && args.size() == 1) {
|
if (name == "round" && args.size() == 1) {
|
||||||
double val = std::stod(args[0]->eval(ctx).value);
|
double val = std::stod(args[0]->eval(ctx).value);
|
||||||
return {"int", std::to_string((int)std::round(val))};
|
return {"int", std::to_string((int)std::round(val))};
|
||||||
@@ -258,6 +314,68 @@ struct FuncCallNode : Node {
|
|||||||
return {"int", "0"};
|
return {"int", "0"};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (name == "httpget" && args.size() == 1) {
|
||||||
|
Value urlVal = args[0]->eval(ctx);
|
||||||
|
std::string cmd = "curl -s \"" + urlVal.value + "\"";
|
||||||
|
FILE* pipe = popen(cmd.c_str(), "r");
|
||||||
|
if (!pipe) return {"string", ""};
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
char buffer[128];
|
||||||
|
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
|
||||||
|
result += buffer;
|
||||||
|
}
|
||||||
|
pclose(pipe);
|
||||||
|
return {"string", result};
|
||||||
|
}
|
||||||
|
if (name == "httppost" && args.size() >= 2) {
|
||||||
|
Value urlVal = args[0]->eval(ctx);
|
||||||
|
Value dataVal = args[1]->eval(ctx);
|
||||||
|
std::string contentType = args.size() > 2 ? args[2]->eval(ctx).value : "application/json";
|
||||||
|
|
||||||
|
std::string cmd = "curl -s -X POST -H \"Content-Type: " + contentType + "\" -d \"" + dataVal.value + "\" \"" + urlVal.value + "\"";
|
||||||
|
FILE* pipe = popen(cmd.c_str(), "r");
|
||||||
|
if (!pipe) return {"string", ""};
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
char buffer[128];
|
||||||
|
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
|
||||||
|
result += buffer;
|
||||||
|
}
|
||||||
|
pclose(pipe);
|
||||||
|
return {"string", result};
|
||||||
|
}
|
||||||
|
if (name == "httpput" && args.size() >= 2) {
|
||||||
|
Value urlVal = args[0]->eval(ctx);
|
||||||
|
Value dataVal = args[1]->eval(ctx);
|
||||||
|
std::string contentType = args.size() > 2 ? args[2]->eval(ctx).value : "application/json";
|
||||||
|
|
||||||
|
std::string cmd = "curl -s -X PUT -H \"Content-Type: " + contentType + "\" -d \"" + dataVal.value + "\" \"" + urlVal.value + "\"";
|
||||||
|
FILE* pipe = popen(cmd.c_str(), "r");
|
||||||
|
if (!pipe) return {"string", ""};
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
char buffer[128];
|
||||||
|
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
|
||||||
|
result += buffer;
|
||||||
|
}
|
||||||
|
pclose(pipe);
|
||||||
|
return {"string", result};
|
||||||
|
}
|
||||||
|
if (name == "httpdelete" && args.size() == 1) {
|
||||||
|
Value urlVal = args[0]->eval(ctx);
|
||||||
|
std::string cmd = "curl -s -X DELETE \"" + urlVal.value + "\"";
|
||||||
|
FILE* pipe = popen(cmd.c_str(), "r");
|
||||||
|
if (!pipe) return {"string", ""};
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
char buffer[128];
|
||||||
|
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
|
||||||
|
result += buffer;
|
||||||
|
}
|
||||||
|
pclose(pipe);
|
||||||
|
return {"string", result};
|
||||||
|
}
|
||||||
|
|
||||||
// Пользовательские функции
|
// Пользовательские функции
|
||||||
auto funcNodeBase = ctx.getFunc(name);
|
auto funcNodeBase = ctx.getFunc(name);
|
||||||
@@ -494,7 +612,13 @@ struct WhileNode : Node {
|
|||||||
: condition(std::move(c)), body(std::move(b)) {}
|
: condition(std::move(c)), body(std::move(b)) {}
|
||||||
Value eval(Context& ctx) override {
|
Value eval(Context& ctx) override {
|
||||||
while (condition->eval(ctx).value == "true") {
|
while (condition->eval(ctx).value == "true") {
|
||||||
body->eval(ctx);
|
try {
|
||||||
|
body->eval(ctx);
|
||||||
|
} catch (const BreakException&) {
|
||||||
|
break;
|
||||||
|
} catch (const ContinueException&) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return {"void", ""};
|
return {"void", ""};
|
||||||
}
|
}
|
||||||
@@ -507,13 +631,80 @@ struct ForNode : Node {
|
|||||||
Value eval(Context& ctx) override {
|
Value eval(Context& ctx) override {
|
||||||
if (init) init->eval(ctx);
|
if (init) init->eval(ctx);
|
||||||
while (condition->eval(ctx).value == "true") {
|
while (condition->eval(ctx).value == "true") {
|
||||||
body->eval(ctx);
|
try {
|
||||||
|
body->eval(ctx);
|
||||||
|
} catch (const BreakException&) {
|
||||||
|
break;
|
||||||
|
} catch (const ContinueException&) {
|
||||||
|
// continue - выполняем step и продолжаем цикл
|
||||||
|
}
|
||||||
if (step) step->eval(ctx);
|
if (step) step->eval(ctx);
|
||||||
}
|
}
|
||||||
return {"void", ""};
|
return {"void", ""};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BreakNode : Node {
|
||||||
|
Value eval(Context& ctx) override {
|
||||||
|
throw BreakException{};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ContinueNode : Node {
|
||||||
|
Value eval(Context& ctx) override {
|
||||||
|
throw ContinueException{};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WaitNode : Node {
|
||||||
|
std::unique_ptr<Node> timeExpr;
|
||||||
|
WaitNode(std::unique_ptr<Node> t) : timeExpr(std::move(t)) {}
|
||||||
|
Value eval(Context& ctx) override {
|
||||||
|
int milliseconds = std::stoi(timeExpr->eval(ctx).value);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
||||||
|
return {"void", ""};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SwitchNode : Node {
|
||||||
|
std::unique_ptr<Node> expr;
|
||||||
|
std::vector<std::pair<std::unique_ptr<Node>, std::unique_ptr<Node>>> cases; // value, body
|
||||||
|
std::unique_ptr<Node> defaultCase;
|
||||||
|
|
||||||
|
SwitchNode(std::unique_ptr<Node> e) : expr(std::move(e)) {}
|
||||||
|
|
||||||
|
Value eval(Context& ctx) override {
|
||||||
|
Value switchValue = expr->eval(ctx);
|
||||||
|
bool executed = false;
|
||||||
|
bool fallthrough = false;
|
||||||
|
|
||||||
|
for (auto& caseItem : cases) {
|
||||||
|
if (!executed && !fallthrough) {
|
||||||
|
Value caseValue = caseItem.first->eval(ctx);
|
||||||
|
if (switchValue.value == caseValue.value) {
|
||||||
|
executed = true;
|
||||||
|
fallthrough = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fallthrough) {
|
||||||
|
try {
|
||||||
|
caseItem.second->eval(ctx);
|
||||||
|
} catch (const BreakException&) {
|
||||||
|
fallthrough = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!executed && defaultCase) {
|
||||||
|
defaultCase->eval(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {"void", ""};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct InputNode : Node {
|
struct InputNode : Node {
|
||||||
Value eval(Context& ctx) override {
|
Value eval(Context& ctx) override {
|
||||||
std::string input; std::getline(std::cin, input);
|
std::string input; std::getline(std::cin, input);
|
||||||
|
|||||||
+16
-4
@@ -68,10 +68,10 @@ std::vector<Token> Lexer::tokenize() {
|
|||||||
else if (id == "round") tokens.push_back({TokenType::ROUND, id, line});
|
else if (id == "round") tokens.push_back({TokenType::ROUND, id, line});
|
||||||
else if (id == "random") tokens.push_back({TokenType::RANDOM, id, line});
|
else if (id == "random") tokens.push_back({TokenType::RANDOM, id, line});
|
||||||
else if (id == "fox") tokens.push_back({TokenType::FOX, id, line});
|
else if (id == "fox") tokens.push_back({TokenType::FOX, id, line});
|
||||||
else if (id == "read_file") tokens.push_back({TokenType::READ_FILE, id, line});
|
else if (id == "readfile") tokens.push_back({TokenType::READ_FILE, id, line});
|
||||||
else if (id == "json_get") tokens.push_back({TokenType::JSON_GET, id, line});
|
else if (id == "jsonget") tokens.push_back({TokenType::JSON_GET, id, line});
|
||||||
else if (id == "str_contains") tokens.push_back({TokenType::STR_CONTAINS, id, line});
|
else if (id == "strcontains") tokens.push_back({TokenType::STR_CONTAINS, id, line});
|
||||||
else if (id == "str_to_int") tokens.push_back({TokenType::STR_TO_INT, id, line});
|
else if (id == "strtoint") tokens.push_back({TokenType::STR_TO_INT, id, line});
|
||||||
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 == "float") tokens.push_back({TokenType::FLOAT_KW, id, line});
|
else if (id == "float") tokens.push_back({TokenType::FLOAT_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});
|
||||||
@@ -83,6 +83,12 @@ std::vector<Token> Lexer::tokenize() {
|
|||||||
else if (id == "for") tokens.push_back({TokenType::FOR, id, line});
|
else if (id == "for") tokens.push_back({TokenType::FOR, id, line});
|
||||||
else if (id == "if") tokens.push_back({TokenType::IF, 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 == "else") tokens.push_back({TokenType::ELSE, id, line});
|
||||||
|
else if (id == "switch") tokens.push_back({TokenType::SWITCH, id, line});
|
||||||
|
else if (id == "case") tokens.push_back({TokenType::CASE, id, line});
|
||||||
|
else if (id == "default") tokens.push_back({TokenType::DEFAULT, id, line});
|
||||||
|
else if (id == "break") tokens.push_back({TokenType::BREAK, id, line});
|
||||||
|
else if (id == "continue") tokens.push_back({TokenType::CONTINUE, id, line});
|
||||||
|
else if (id == "wait") tokens.push_back({TokenType::WAIT, id, line});
|
||||||
else if (id == "array") tokens.push_back({TokenType::ARRAY, 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 == "set") tokens.push_back({TokenType::SET, id, line});
|
||||||
else if (id == "get") tokens.push_back({TokenType::GET, id, line});
|
else if (id == "get") tokens.push_back({TokenType::GET, id, line});
|
||||||
@@ -91,6 +97,12 @@ std::vector<Token> Lexer::tokenize() {
|
|||||||
else if (id == "using") tokens.push_back({TokenType::USING, id, line});
|
else if (id == "using") tokens.push_back({TokenType::USING, id, line});
|
||||||
else if (id == "return") tokens.push_back({TokenType::RETURN, 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 if (id == "global") tokens.push_back({TokenType::GLOBAL, id, line});
|
||||||
|
else if (id == "httpget") tokens.push_back({TokenType::HTTP_GET, id, line});
|
||||||
|
else if (id == "httppost") tokens.push_back({TokenType::HTTP_POST, id, line});
|
||||||
|
else if (id == "httpput") tokens.push_back({TokenType::HTTP_PUT, id, line});
|
||||||
|
else if (id == "httpdelete") tokens.push_back({TokenType::HTTP_DELETE, id, line});
|
||||||
|
else if (id == "getch") tokens.push_back({TokenType::GETCH, id, line});
|
||||||
|
else if (id == "kbhit") tokens.push_back({TokenType::KBHIT, id, line});
|
||||||
else tokens.push_back({TokenType::IDENTIFIER, id, line});
|
else tokens.push_back({TokenType::IDENTIFIER, id, line});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
+135
-2
@@ -93,8 +93,13 @@ std::unique_ptr<Node> Parser::primary() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tokens[pos].type == TokenType::INPUT) {
|
if (tokens[pos].type == TokenType::INPUT) {
|
||||||
consume(TokenType::INPUT); consume(TokenType::LPAREN); consume(TokenType::RPAREN);
|
consume(TokenType::INPUT); consume(TokenType::LPAREN);
|
||||||
return std::make_unique<InputNode>();
|
std::vector<std::unique_ptr<Node>> args;
|
||||||
|
if (tokens[pos].type != TokenType::RPAREN) {
|
||||||
|
args.push_back(expression());
|
||||||
|
}
|
||||||
|
consume(TokenType::RPAREN);
|
||||||
|
return std::make_unique<FuncCallNode>("input", std::move(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokens[pos].type == TokenType::READ_FILE) {
|
if (tokens[pos].type == TokenType::READ_FILE) {
|
||||||
@@ -104,6 +109,70 @@ std::unique_ptr<Node> Parser::primary() {
|
|||||||
return std::make_unique<ReadFileNode>(std::move(filename));
|
return std::make_unique<ReadFileNode>(std::move(filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTP функции в выражениях
|
||||||
|
if (tokens[pos].type == TokenType::HTTP_GET) {
|
||||||
|
consume(TokenType::HTTP_GET); consume(TokenType::LPAREN);
|
||||||
|
auto url = expression();
|
||||||
|
consume(TokenType::RPAREN);
|
||||||
|
std::vector<std::unique_ptr<Node>> args;
|
||||||
|
args.push_back(std::move(url));
|
||||||
|
return std::make_unique<FuncCallNode>("httpget", std::move(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens[pos].type == TokenType::HTTP_POST) {
|
||||||
|
consume(TokenType::HTTP_POST); consume(TokenType::LPAREN);
|
||||||
|
auto url = expression();
|
||||||
|
consume(TokenType::COMMA);
|
||||||
|
auto data = expression();
|
||||||
|
std::vector<std::unique_ptr<Node>> args;
|
||||||
|
args.push_back(std::move(url));
|
||||||
|
args.push_back(std::move(data));
|
||||||
|
if (tokens[pos].type == TokenType::COMMA) {
|
||||||
|
consume(TokenType::COMMA);
|
||||||
|
args.push_back(expression());
|
||||||
|
}
|
||||||
|
consume(TokenType::RPAREN);
|
||||||
|
return std::make_unique<FuncCallNode>("httppost", std::move(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens[pos].type == TokenType::HTTP_PUT) {
|
||||||
|
consume(TokenType::HTTP_PUT); consume(TokenType::LPAREN);
|
||||||
|
auto url = expression();
|
||||||
|
consume(TokenType::COMMA);
|
||||||
|
auto data = expression();
|
||||||
|
std::vector<std::unique_ptr<Node>> args;
|
||||||
|
args.push_back(std::move(url));
|
||||||
|
args.push_back(std::move(data));
|
||||||
|
if (tokens[pos].type == TokenType::COMMA) {
|
||||||
|
consume(TokenType::COMMA);
|
||||||
|
args.push_back(expression());
|
||||||
|
}
|
||||||
|
consume(TokenType::RPAREN);
|
||||||
|
return std::make_unique<FuncCallNode>("httpput", std::move(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens[pos].type == TokenType::HTTP_DELETE) {
|
||||||
|
consume(TokenType::HTTP_DELETE); consume(TokenType::LPAREN);
|
||||||
|
auto url = expression();
|
||||||
|
consume(TokenType::RPAREN);
|
||||||
|
std::vector<std::unique_ptr<Node>> args;
|
||||||
|
args.push_back(std::move(url));
|
||||||
|
return std::make_unique<FuncCallNode>("httpdelete", std::move(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функции ввода
|
||||||
|
if (tokens[pos].type == TokenType::GETCH) {
|
||||||
|
consume(TokenType::GETCH); consume(TokenType::LPAREN); consume(TokenType::RPAREN);
|
||||||
|
std::vector<std::unique_ptr<Node>> args;
|
||||||
|
return std::make_unique<FuncCallNode>("getch", std::move(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens[pos].type == TokenType::KBHIT) {
|
||||||
|
consume(TokenType::KBHIT); consume(TokenType::LPAREN); consume(TokenType::RPAREN);
|
||||||
|
std::vector<std::unique_ptr<Node>> args;
|
||||||
|
return std::make_unique<FuncCallNode>("kbhit", std::move(args));
|
||||||
|
}
|
||||||
|
|
||||||
if (tokens[pos].type == TokenType::LPAREN) {
|
if (tokens[pos].type == TokenType::LPAREN) {
|
||||||
consume(TokenType::LPAREN);
|
consume(TokenType::LPAREN);
|
||||||
auto n = expression();
|
auto n = expression();
|
||||||
@@ -343,6 +412,70 @@ std::unique_ptr<Node> Parser::statement() {
|
|||||||
return std::make_unique<IfNode>(std::move(cond), std::move(thenB), std::move(elseB));
|
return std::make_unique<IfNode>(std::move(cond), std::move(thenB), std::move(elseB));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tokens[pos].type == TokenType::BREAK) {
|
||||||
|
consume(TokenType::BREAK); consume(TokenType::SEMICOLON);
|
||||||
|
return std::make_unique<BreakNode>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens[pos].type == TokenType::CONTINUE) {
|
||||||
|
consume(TokenType::CONTINUE); consume(TokenType::SEMICOLON);
|
||||||
|
return std::make_unique<ContinueNode>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens[pos].type == TokenType::WAIT) {
|
||||||
|
consume(TokenType::WAIT); consume(TokenType::LPAREN);
|
||||||
|
auto timeExpr = expression();
|
||||||
|
consume(TokenType::RPAREN); consume(TokenType::SEMICOLON);
|
||||||
|
return std::make_unique<WaitNode>(std::move(timeExpr));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens[pos].type == TokenType::SWITCH) {
|
||||||
|
consume(TokenType::SWITCH); consume(TokenType::LPAREN);
|
||||||
|
auto expr = expression();
|
||||||
|
consume(TokenType::RPAREN); consume(TokenType::LBRACE);
|
||||||
|
|
||||||
|
auto switchNode = std::make_unique<SwitchNode>(std::move(expr));
|
||||||
|
|
||||||
|
while (tokens[pos].type == TokenType::CASE || tokens[pos].type == TokenType::DEFAULT) {
|
||||||
|
if (tokens[pos].type == TokenType::CASE) {
|
||||||
|
consume(TokenType::CASE);
|
||||||
|
auto caseValue = expression();
|
||||||
|
consume(TokenType::COLON);
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<Node>> caseStatements;
|
||||||
|
while (tokens[pos].type != TokenType::CASE &&
|
||||||
|
tokens[pos].type != TokenType::DEFAULT &&
|
||||||
|
tokens[pos].type != TokenType::RBRACE) {
|
||||||
|
caseStatements.push_back(statement());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto caseBody = std::make_unique<BlockNode>();
|
||||||
|
for (auto& stmt : caseStatements) {
|
||||||
|
static_cast<BlockNode*>(caseBody.get())->stmts.push_back(std::move(stmt));
|
||||||
|
}
|
||||||
|
|
||||||
|
switchNode->cases.push_back(std::make_pair(std::move(caseValue), std::move(caseBody)));
|
||||||
|
} else if (tokens[pos].type == TokenType::DEFAULT) {
|
||||||
|
consume(TokenType::DEFAULT); consume(TokenType::COLON);
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<Node>> defaultStatements;
|
||||||
|
while (tokens[pos].type != TokenType::RBRACE) {
|
||||||
|
defaultStatements.push_back(statement());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto defaultBody = std::make_unique<BlockNode>();
|
||||||
|
for (auto& stmt : defaultStatements) {
|
||||||
|
static_cast<BlockNode*>(defaultBody.get())->stmts.push_back(std::move(stmt));
|
||||||
|
}
|
||||||
|
|
||||||
|
switchNode->defaultCase = std::move(defaultBody);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(TokenType::RBRACE);
|
||||||
|
return std::move(switchNode);
|
||||||
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
|||||||
+10
-1
@@ -12,13 +12,22 @@ enum class TokenType {
|
|||||||
PRINT, INPUT, ROUND, RANDOM, FOX, READ_FILE, JSON_GET, STR_CONTAINS, STR_TO_INT,
|
PRINT, INPUT, ROUND, RANDOM, FOX, READ_FILE, JSON_GET, STR_CONTAINS, STR_TO_INT,
|
||||||
INT_KW, FLOAT_KW, STRING_KW, BOOL_KW, VOID_KW, // Типы данных
|
INT_KW, FLOAT_KW, STRING_KW, BOOL_KW, VOID_KW, // Типы данных
|
||||||
TRUE_KW, FALSE_KW, // Boolean литералы
|
TRUE_KW, FALSE_KW, // Boolean литералы
|
||||||
WHILE, FOR, IF, ELSE,
|
WHILE, FOR, IF, ELSE, SWITCH, CASE, DEFAULT,
|
||||||
ARRAY, SET, GET, SIZE,
|
ARRAY, SET, GET, SIZE,
|
||||||
INCLUDE, USING, // Подключение файлов
|
INCLUDE, USING, // Подключение файлов
|
||||||
|
|
||||||
// НОВЫЕ: возврат и глобальные
|
// НОВЫЕ: возврат и глобальные
|
||||||
RETURN, GLOBAL,
|
RETURN, GLOBAL,
|
||||||
|
|
||||||
|
// Управление потоком
|
||||||
|
BREAK, CONTINUE, WAIT,
|
||||||
|
|
||||||
|
// Сетевые функции
|
||||||
|
HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_DELETE,
|
||||||
|
|
||||||
|
// Ввод с клавиатуры
|
||||||
|
GETCH, KBHIT,
|
||||||
|
|
||||||
IDENTIFIER,
|
IDENTIFIER,
|
||||||
END, ERROR
|
END, ERROR
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user