Фиксация мамы нолнан с сфере IT с симпатией к атусу стоещему на полигоне
с афроо лошадьми
This commit is contained in:
@@ -0,0 +1 @@
|
||||
.kiro/settings/lsp.json
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
# FoxLang 5.0.1 - Changelog
|
||||
|
||||
## 🆕 Новые возможности
|
||||
|
||||
### Встроенная функция read_file()
|
||||
- **Чтение файлов:** Новая функция `read_file(filename)` для чтения текстовых файлов
|
||||
- **Умная обработка:** Автоматически пропускает комментарии (строки начинающиеся с #) и пустые строки
|
||||
- **Безопасность:** Возвращает пустую строку при ошибке чтения файла
|
||||
- **Применение:** Идеально подходит для чтения конфигурационных файлов
|
||||
|
||||
### Встроенная функция http_get()
|
||||
- **HTTP запросы:** Новая функция `http_get(url)` для выполнения GET запросов
|
||||
- **Простота использования:** Один вызов функции для получения данных с сервера
|
||||
- **Интеграция:** Использует системный curl для надежности
|
||||
- **Применение:** Работа с API, загрузка данных, веб-скрапинг
|
||||
|
||||
```cpp
|
||||
string config = read_file("config.txt");
|
||||
if (config != "") {
|
||||
print("Config loaded: " + config);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# FoxLang 5.0.0 - Changelog
|
||||
|
||||
## 🚀 Крупные нововведения
|
||||
|
||||
### 1. Пользовательские функции
|
||||
- **Полная поддержка функций** с параметрами и возвратом значений
|
||||
- **Локальные области видимости** для параметров функций
|
||||
- **Рекурсивные вызовы** функций
|
||||
- **Поддержка всех типов** в параметрах и возврате (`int`, `float`, `string`, `bool`, `void`)
|
||||
|
||||
```cpp
|
||||
int factorial(int n) {
|
||||
if (n <= 1) {
|
||||
return 1;
|
||||
}
|
||||
return n * factorial(n - 1);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Модульная система с пользовательскими функциями
|
||||
- **Библиотеки с функциями**: Теперь `include("lib.fox")` поддерживает пользовательские функции
|
||||
- **Правильная область видимости**: Функции из библиотек доступны в основном коде
|
||||
- **Режим импорта**: Функции регистрируются, но код библиотеки не выполняется
|
||||
|
||||
```cpp
|
||||
// math_lib.fox
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// main.fox
|
||||
include("math_lib.fox");
|
||||
int result = add(5, 3); // Работает!
|
||||
```
|
||||
|
||||
### 3. Современный синтаксис идентификаторов
|
||||
- **Поддержка подчеркиваний**: `user_name`, `get_public_ip`, `my_function`
|
||||
- **Исправлен лексер**: Идентификаторы с `_` не конфликтуют с ключевыми словами
|
||||
- **Совместимость**: Все существующие имена переменных продолжают работать
|
||||
|
||||
### 4. Расширенная поддержка циклов
|
||||
- **Цикл for**: Полная поддержка `for (init; condition; step) { ... }`
|
||||
- **Улучшенные while циклы**: Лучшая обработка условий
|
||||
- **Вложенные циклы**: Поддержка любого уровня вложенности
|
||||
|
||||
## 🔧 Технические улучшения
|
||||
|
||||
### Парсер и AST
|
||||
- **Новые узлы**: `FuncDefNode`, `ReturnNode`, `ForNode`
|
||||
- **Улучшенная обработка**: `return` внутри функций
|
||||
- **Контекст функций**: Изолированные области видимости
|
||||
- **Обработка ошибок**: Лучшие сообщения об ошибках
|
||||
|
||||
### Лексер
|
||||
- **Исправлена логика**: Идентификаторы с подчеркиваниями обрабатываются корректно
|
||||
- **Приоритет ключевых слов**: Только полные совпадения считаются ключевыми словами
|
||||
|
||||
## 📋 Примеры использования
|
||||
|
||||
### Пользовательские функции
|
||||
```cpp
|
||||
string greet(string name, int age) {
|
||||
return "Hello, " + name + "! You are " + age + " years old.";
|
||||
}
|
||||
|
||||
void main() {
|
||||
string message = greet("Alice", 25);
|
||||
print(message);
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
|
||||
### Библиотеки с функциями
|
||||
```cpp
|
||||
// utils.fox
|
||||
int max(int a, int b) {
|
||||
if (a > b) return a;
|
||||
return b;
|
||||
}
|
||||
|
||||
void print_array(array arr, int size) {
|
||||
int i = 0;
|
||||
while (i < size) {
|
||||
print("arr[" + i + "] = " + get(arr, i));
|
||||
i = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// main.fox
|
||||
include("utils.fox");
|
||||
|
||||
int maximum = max(10, 20);
|
||||
array numbers 3;
|
||||
set(numbers, 0, 5);
|
||||
set(numbers, 1, 10);
|
||||
set(numbers, 2, 15);
|
||||
print_array(numbers, 3);
|
||||
```
|
||||
|
||||
### Современный синтаксис
|
||||
```cpp
|
||||
string user_name = "john_doe";
|
||||
int user_age = 25;
|
||||
bool is_admin = false;
|
||||
|
||||
string get_user_info() {
|
||||
return "User: " + user_name + ", Age: " + user_age;
|
||||
}
|
||||
|
||||
void check_permissions() {
|
||||
if (is_admin) {
|
||||
print("Admin access granted");
|
||||
} else {
|
||||
print("Regular user access");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🔄 Обратная совместимость
|
||||
- Все скрипты FoxLang 4.x продолжают работать
|
||||
- Встроенные функции (`print`, `input`, `fox`) не изменились
|
||||
- Синтаксис массивов и переменных остался прежним
|
||||
|
||||
## 🐛 Исправленные ошибки
|
||||
- Исправлена обработка идентификаторов с подчеркиваниями
|
||||
- Устранены конфликты ключевых слов с именами функций
|
||||
- Улучшена стабильность парсера при обработке функций
|
||||
- Исправлены ошибки области видимости переменных
|
||||
|
||||
---
|
||||
|
||||
**Версия 5.0.0** представляет собой значительный шаг вперед в развитии FoxLang, превращая его в полноценный язык программирования с поддержкой пользовательских функций и современной модульной системы.
|
||||
+115
-31
@@ -1,14 +1,15 @@
|
||||
# 📚 Документация FoxLang v4.0
|
||||
# 📚 Документация FoxLang v5.0
|
||||
|
||||
## Оглавление
|
||||
1. [Основы синтаксиса](#1-основы-синтаксиса)
|
||||
2. [Переменные и Типы](#2-переменные-и-типы)
|
||||
3. [Математика и Логика](#3-математика-и-логика)
|
||||
4. [Управляющие конструкции](#4-управляющие-конструкции)
|
||||
5. [Функции](#5-функции)
|
||||
3. [Пользовательские функции](#3-пользовательские-функции)
|
||||
4. [Математика и Логика](#4-математика-и-логика)
|
||||
5. [Управляющие конструкции](#5-управляющие-конструкции)
|
||||
6. [Массивы](#6-массивы)
|
||||
7. [Модули и Импорт](#7-модули-и-импорт)
|
||||
8. [Встроенные функции](#8-встроенные-функции)
|
||||
9. [Современный синтаксис](#9-современный-синтаксис)
|
||||
|
||||
---
|
||||
|
||||
@@ -17,42 +18,108 @@ FoxLang использует синтаксис, похожий на C++ и Java
|
||||
* Каждая команда **обязана** заканчиваться точкой с запятой `;`.
|
||||
* Блоки кода выделяются фигурными скобками `{ ... }`.
|
||||
* Комментарии начинаются с `//` и идут до конца строки.
|
||||
* Поддерживаются идентификаторы с подчеркиваниями (`user_name`, `get_data`).
|
||||
|
||||
```cpp
|
||||
// Это комментарий
|
||||
print("Hello"); // Команда
|
||||
string user_name = "john_doe"; // Современный синтаксис
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Переменные и Типы
|
||||
|
||||
Язык поддерживает два основных типа данных:
|
||||
Язык поддерживает пять основных типов данных:
|
||||
|
||||
* `int` — Целые числа (внутри хранятся как double).
|
||||
* `int` — Целые числа.
|
||||
* `float` — Дробные числа с плавающей точкой.
|
||||
* `string` — Текст в двойных кавычках.
|
||||
* `bool` — Логический тип (`true` или `false`).
|
||||
* `void` — Тип для функций без возвращаемого значения.
|
||||
|
||||
**Объявление:**
|
||||
|
||||
```cpp
|
||||
int health = 100;
|
||||
string name = "Player1";
|
||||
float gravity = 9.8;
|
||||
string player_name = "Player1";
|
||||
bool is_alive = true;
|
||||
```
|
||||
|
||||
**Присваивание:**
|
||||
|
||||
```cpp
|
||||
health = 90;
|
||||
name = "Player2";
|
||||
gravity = 1.62;
|
||||
player_name = "Player2";
|
||||
is_alive = false;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Пользовательские функции
|
||||
|
||||
FoxLang поддерживает полноценные пользовательские функции с параметрами и возвратом значений.
|
||||
|
||||
### Объявление функций
|
||||
|
||||
```cpp
|
||||
// Функция с возвращаемым значением
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// Функция без возвращаемого значения
|
||||
void greet(string name) {
|
||||
print("Hello, " + name + "!");
|
||||
}
|
||||
|
||||
// Функция без параметров
|
||||
string get_version() {
|
||||
return "FoxLang 5.0";
|
||||
}
|
||||
```
|
||||
|
||||
### Вызов функций
|
||||
|
||||
```cpp
|
||||
int result = add(5, 3);
|
||||
greet("Alice");
|
||||
string version = get_version();
|
||||
print("Version: " + version);
|
||||
```
|
||||
|
||||
### Рекурсивные функции
|
||||
|
||||
```cpp
|
||||
int factorial(int n) {
|
||||
if (n <= 1) {
|
||||
return 1;
|
||||
}
|
||||
return n * factorial(n - 1);
|
||||
}
|
||||
|
||||
int fact5 = factorial(5); // 120
|
||||
```
|
||||
|
||||
### Локальные переменные
|
||||
|
||||
```cpp
|
||||
int calculate_area(int width, int height) {
|
||||
int area = width * height; // Локальная переменная
|
||||
return area;
|
||||
}
|
||||
```
|
||||
|
||||
> **Важно:** Нельзя объявить переменную с именем, которое уже существует в текущей области видимости.
|
||||
> При присваивании дробного значения переменной типа `int`, оно будет автоматически преобразовано в целое (отброшена дробная часть).
|
||||
|
||||
---
|
||||
|
||||
## 3. Математика и Логика
|
||||
|
||||
Поддерживаются стандартные арифметические операции с учетом приоритета (умножение/деление выполняются раньше сложения/вычитания).
|
||||
Поддерживаются стандартные арифметические операции с учетом приоритета.
|
||||
|
||||
### Операторы
|
||||
|
||||
@@ -63,10 +130,11 @@ name = "Player2";
|
||||
| `*` | Умножение | `2 * 2` |
|
||||
| `/` | Деление | `10 / 2` |
|
||||
| `%` | Остаток от деления | `10 % 3` (вернет 1) |
|
||||
| `++` | Инкремент (увеличение на 1) | `i++` (вернет старое значение, затем увеличит переменную) |
|
||||
|
||||
### Сравнение
|
||||
|
||||
Операторы сравнения возвращают `1` (истина) или `0` (ложь). Работают и с числами, и со строками.
|
||||
Операторы сравнения возвращают `1` (истина) или `0` (ложь).
|
||||
|
||||
* `==` (Равно)
|
||||
* `!=` (Не равно)
|
||||
@@ -96,15 +164,39 @@ if (x == 10) {
|
||||
int i = 0;
|
||||
while (i < 5) {
|
||||
print("Loop iteration: " + i);
|
||||
i = i + 1;
|
||||
i++;
|
||||
}
|
||||
```
|
||||
|
||||
### Циклы (For)
|
||||
|
||||
Классический цикл `for`, состоящий из инициализации, условия и шага.
|
||||
|
||||
```cpp
|
||||
for (int i = 0; i < 5; i++) {
|
||||
print("For loop: " + i);
|
||||
}
|
||||
```
|
||||
|
||||
### Области видимости (Scope)
|
||||
|
||||
Блоки кода `{ ... }` создают новую область видимости. Переменные, объявленные внутри блока, недоступны снаружи.
|
||||
|
||||
```cpp
|
||||
int global = 10;
|
||||
{
|
||||
int local = 5;
|
||||
print(global); // 10
|
||||
print(local); // 5
|
||||
}
|
||||
// print(local); // Ошибка! Переменная local не существует здесь.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Функции
|
||||
|
||||
Функции объявляются ключевым словом `void`. Они имеют доступ к глобальным переменным (общая память).
|
||||
Функции могут быть объявлены с типом возвращаемого значения или `void`.
|
||||
|
||||
**Определение:**
|
||||
|
||||
@@ -112,12 +204,17 @@ while (i < 5) {
|
||||
void myFunc() {
|
||||
print("Hello from function!");
|
||||
}
|
||||
|
||||
int square(int x) {
|
||||
return x * x;
|
||||
}
|
||||
```
|
||||
|
||||
**Вызов:**
|
||||
|
||||
```cpp
|
||||
myFunc();
|
||||
int result = square(5);
|
||||
```
|
||||
|
||||
---
|
||||
@@ -132,7 +229,7 @@ myFunc();
|
||||
4. **Размер:** `size(имя)`
|
||||
|
||||
```cpp
|
||||
array chest 3; // Массив на 3 элемента [0, 0, 0]
|
||||
array chest 3; // Массив на 3 элемента
|
||||
set(chest, 0, 55); // Записать 55 в индекс 0
|
||||
print(get(chest, 0)); // Выведет 55
|
||||
```
|
||||
@@ -141,27 +238,12 @@ print(get(chest, 0)); // Выведет 55
|
||||
|
||||
## 7. Модули и Импорт
|
||||
|
||||
FoxLang v4.0 поддерживает импорт внешних модулей.
|
||||
FoxLang поддерживает импорт внешних модулей.
|
||||
Используйте `include("путь/к/файлу.fox");`.
|
||||
|
||||
**Особенности:**
|
||||
|
||||
* **Важная информация:** При подключении внешних модулей в связи с оссобенностями c++, если в скрипте есть вызов функции в корне скрипта то она тоже будет выполнена при импорте. Это может привести к неожиданным результатам и ошибкам.
|
||||
|
||||
**Пример:**
|
||||
`lib.fox`:
|
||||
|
||||
```cpp
|
||||
void hello() { print("Hi!"); }
|
||||
hello(); // Это выполнтся при импорте
|
||||
```
|
||||
|
||||
`main.fox`:
|
||||
|
||||
```cpp
|
||||
include("lib.fox");
|
||||
hello(); // Мы так же можем вызывать функции из модулей.
|
||||
```
|
||||
* При подключении внешних модулей код, находящийся вне функций, будет выполнен при импорте. Глобальные переменные и функции становятся доступными в текущем файле.
|
||||
|
||||
---
|
||||
|
||||
@@ -173,4 +255,6 @@ hello(); // Мы так же можем вызывать функции из м
|
||||
| `input()` | Останавливает программу и ждет ввода строки от пользователя. |
|
||||
| `round(expr)` | Округляет дробное число до ближайшего целого. |
|
||||
| `random()` | Генерирует случайное число от 0 до 99. |
|
||||
| `fox()` | Пасхалка: выводит ASCII-арт лисы. |
|
||||
| `read_file(filename)` | Читает первую непустую строку из файла (игнорирует комментарии #). |
|
||||
| `http_get(url)` | Выполняет HTTP GET запрос и возвращает ответ сервера. |
|
||||
| `fox()` | Пасхалка: выводит ASCII-арт лисы. |
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
# 📚 Система библиотек FoxLang
|
||||
|
||||
## Как работает include
|
||||
|
||||
FoxLang поддерживает подключение внешних файлов через `include("filename.fox")`. Система работает следующим образом:
|
||||
|
||||
### Поиск файлов
|
||||
1. **Относительный путь**: Сначала ищет файл относительно текущего .fox файла
|
||||
2. **Абсолютный путь**: Если не найден, ищет рядом с исполняемым файлом
|
||||
|
||||
### Структура проекта
|
||||
```
|
||||
project/
|
||||
├── main.fox # Основной скрипт
|
||||
├── libs/ # Папка с библиотеками
|
||||
│ ├── net.fox # Сетевые функции
|
||||
│ ├── utils.fox # Утилиты
|
||||
│ └── math.fox # Математические функции
|
||||
└── foxlang # Исполняемый файл
|
||||
```
|
||||
|
||||
### Подключение библиотек
|
||||
```cpp
|
||||
// В main.fox
|
||||
include("libs/net.fox"); // Относительно main.fox
|
||||
include("libs/utils.fox");
|
||||
|
||||
// Теперь доступны функции из библиотек
|
||||
string ip = get_public_ip();
|
||||
int result = max(10, 20);
|
||||
```
|
||||
|
||||
## 🌐 Библиотека net.fox
|
||||
|
||||
### Что изменилось
|
||||
**Раньше**: net.fox был просто заглушкой с комментариями
|
||||
**Сейчас**: Полноценная библиотека с удобными функциями высокого уровня
|
||||
|
||||
### Основные функции
|
||||
|
||||
#### HTTP клиент
|
||||
```cpp
|
||||
// Простой GET с обработкой ошибок
|
||||
string response = get("https://api.example.com");
|
||||
|
||||
// POST с JSON данными
|
||||
string result = post_json("https://api.example.com/data", "{\"key\":\"value\"}");
|
||||
|
||||
// Получение публичного IP
|
||||
string ip = get_public_ip();
|
||||
```
|
||||
|
||||
#### TCP клиент
|
||||
```cpp
|
||||
// Проверка доступности сервера
|
||||
bool available = ping("google.com", 80);
|
||||
|
||||
// Отправка сообщения с автоматическим управлением соединением
|
||||
string response = send_message("localhost", 8080, "Hello Server!");
|
||||
```
|
||||
|
||||
#### Утилиты
|
||||
```cpp
|
||||
// Проверка интернет-соединения
|
||||
if (is_online()) {
|
||||
print("Internet is available");
|
||||
}
|
||||
```
|
||||
|
||||
### Преимущества новой библиотеки
|
||||
|
||||
1. **Автоматическая обработка ошибок**: Не нужно проверять каждый вызов
|
||||
2. **Упрощенный API**: Одна функция вместо нескольких низкоуровневых
|
||||
3. **Готовые решения**: ping, проверка интернета, получение IP
|
||||
4. **Безопасность**: Автоматическое закрытие соединений
|
||||
|
||||
## 🛠 Библиотека utils.fox
|
||||
|
||||
### Математические функции
|
||||
```cpp
|
||||
int maximum = max(15, 23);
|
||||
int minimum = min(15, 23);
|
||||
```
|
||||
|
||||
### Работа со строками
|
||||
```cpp
|
||||
string repeated = repeat("Fox", 3); // "FoxFoxFox"
|
||||
bool starts = starts_with("FoxLang", "Fox"); // true
|
||||
```
|
||||
|
||||
### Утилиты для массивов
|
||||
```cpp
|
||||
array numbers 5;
|
||||
// ... заполнение массива ...
|
||||
|
||||
print_array(numbers, 5); // Вывод содержимого
|
||||
bubble_sort(numbers, 5); // Сортировка
|
||||
```
|
||||
|
||||
## 📝 Пример использования
|
||||
|
||||
```cpp
|
||||
// main.fox
|
||||
include("libs/net.fox");
|
||||
include("libs/utils.fox");
|
||||
|
||||
print("=== FoxLang Project Demo ===");
|
||||
|
||||
// Сетевые функции
|
||||
if (is_online()) {
|
||||
string ip = get_public_ip();
|
||||
print("Your IP: " + ip);
|
||||
}
|
||||
|
||||
// Математика
|
||||
int result = max(42, 17);
|
||||
print("Maximum: " + result);
|
||||
|
||||
// Массивы
|
||||
array data 3;
|
||||
set(data, 0, 100);
|
||||
set(data, 1, 50);
|
||||
set(data, 2, 75);
|
||||
|
||||
print("Before sorting:");
|
||||
print_array(data, 3);
|
||||
|
||||
bubble_sort(data, 3);
|
||||
|
||||
print("After sorting:");
|
||||
print_array(data, 3);
|
||||
```
|
||||
|
||||
## 🚀 Запуск
|
||||
|
||||
```bash
|
||||
# Компиляция
|
||||
cd src
|
||||
g++ main.cpp Lexer.cpp Parser.cpp -o foxlang
|
||||
|
||||
# Запуск проекта с библиотеками
|
||||
./foxlang ../test/example_with_libs.fox
|
||||
```
|
||||
|
||||
## 💡 Создание собственных библиотек
|
||||
|
||||
1. Создайте файл в папке `libs/`
|
||||
2. Определите функции
|
||||
3. Подключите через `include("libs/your_lib.fox")`
|
||||
4. Используйте функции в основном коде
|
||||
|
||||
Система автоматически найдет файл относительно вашего скрипта!
|
||||
@@ -1,76 +1,158 @@
|
||||
# 🦊 FoxLang
|
||||
|
||||
  
|
||||
  
|
||||
|
||||
**FoxLang** — это интерпретируемый язык программирования общего назначения, разработанный с нуля на C++.
|
||||
Он сочетает строгий синтаксис (в стиле C++) с динамической гибкостью скриптовых языков. Проект демонстрирует реализацию собственного лексера, рекурсивного парсера, AST-дерева и модульной архитектуры.
|
||||
**FoxLang** — современный интерпретируемый язык программирования общего назначения с поддержкой пользовательских функций, модульной системы и строгой типизацией.
|
||||
|
||||
## ✨ Ключевые возможности
|
||||
|
||||
- **📁 Модульность:** Подключение файлов через `include("lib.fox")` с поддержкой относительных путей.
|
||||
- **📦 Массивы:** Встроенная поддержка создания, чтения и записи массивов (`array`, `set`, `get`).
|
||||
- **🔄 Управление потоком:** Полноценные циклы `while` и условия `if/else`.
|
||||
- **🔢 Типизация:** Поддержка `int` и `string` с автоматическим приведением типов при выводе.
|
||||
- **🛠 Безопасность:** Защита от крашей при сравнении строк и чисел, информативные ошибки синтаксиса.
|
||||
- **🎲 Встроенная библиотека:** Генерация чисел, математика, ввод/вывод.
|
||||
- **🔧 Пользовательские функции:** Полная поддержка функций с параметрами и возвратом значений
|
||||
- **📁 Модульная система:** Подключение библиотек через `include("lib.fox")` с поддержкой пользовательских функций
|
||||
- **🔤 Современный синтаксис:** Идентификаторы с подчеркиваниями (`user_name`, `get_data`)
|
||||
- **📦 Массивы:** Встроенная поддержка создания, чтения и записи массивов
|
||||
- **🔄 Управление потоком:** Циклы `while`, `for` и условия `if/else`
|
||||
- **🔢 Строгая типизация:** `int`, `float`, `string`, `bool`, `void` с автоматическим приведением
|
||||
- **🧠 Логические операторы:** Поддержка `&&`, `||`, `!` для boolean логики
|
||||
- **🛠 Безопасность:** Защита от крашей, информативные ошибки синтаксиса
|
||||
- **🎲 Встроенные функции:** Математика, ввод/вывод, генерация чисел, чтение файлов
|
||||
|
||||
## 🚀 Быстрый старт
|
||||
|
||||
### 1. Сборка
|
||||
Вам понадобится любой компилятор C++ (GCC, Clang, MSVC).
|
||||
|
||||
```bash
|
||||
# Перейдите в папку с исходным кодом
|
||||
cd src
|
||||
|
||||
# Скомпилируйте проект
|
||||
g++ main.cpp Lexer.cpp Parser.cpp -o foxlang
|
||||
```
|
||||
|
||||
*(На Windows будет создан файл `foxlang.exe`)*
|
||||
|
||||
### 2. Запуск скрипта
|
||||
|
||||
Создайте файл `main.fox`:
|
||||
|
||||
```cpp
|
||||
print("Hello, FoxLang!");
|
||||
fox();
|
||||
```
|
||||
|
||||
Запустите его:
|
||||
|
||||
### 2. Запуск
|
||||
```bash
|
||||
./foxlang main.fox
|
||||
./foxlang script.fox
|
||||
```
|
||||
|
||||
## 💻 Пример кода
|
||||
## 💻 Примеры кода
|
||||
|
||||
### Пользовательские функции
|
||||
```cpp
|
||||
// Подключаем библиотеку
|
||||
include("math_lib.fox");
|
||||
|
||||
int x = 10;
|
||||
|
||||
if (x > 5) {
|
||||
print("X is big!");
|
||||
} else {
|
||||
print("X is small.");
|
||||
int factorial(int n) {
|
||||
if (n <= 1) {
|
||||
return 1;
|
||||
}
|
||||
return n * factorial(n - 1);
|
||||
}
|
||||
|
||||
// Работа с массивом
|
||||
array nums 3;
|
||||
set(nums, 0, 555);
|
||||
print("Array elem: " + get(nums, 0));
|
||||
void main() {
|
||||
int result = factorial(5);
|
||||
print("5! = " + result);
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
|
||||
### Работа с массивами
|
||||
```cpp
|
||||
void bubble_sort(array arr, int size) {
|
||||
int i = 0;
|
||||
while (i < size - 1) {
|
||||
int j = 0;
|
||||
while (j < size - i - 1) {
|
||||
if (get(arr, j) > get(arr, j + 1)) {
|
||||
int temp = get(arr, j);
|
||||
set(arr, j, get(arr, j + 1));
|
||||
set(arr, j + 1, temp);
|
||||
}
|
||||
j = j + 1;
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Модульная система
|
||||
```cpp
|
||||
// math_lib.fox
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
float average(int a, int b) {
|
||||
return (a + b) / 2.0;
|
||||
}
|
||||
|
||||
// main.fox
|
||||
include("math_lib.fox");
|
||||
|
||||
int sum = add(10, 20);
|
||||
float avg = average(15, 25);
|
||||
print("Sum: " + sum + ", Average: " + avg);
|
||||
```
|
||||
|
||||
### Чтение файлов
|
||||
```cpp
|
||||
// config.txt содержит: server_port=8080
|
||||
string config = read_file("config.txt");
|
||||
print("Config: " + config);
|
||||
|
||||
// Проверка успешного чтения
|
||||
if (config != "") {
|
||||
print("✅ Config loaded successfully");
|
||||
} else {
|
||||
print("❌ Failed to load config");
|
||||
}
|
||||
```
|
||||
|
||||
### HTTP запросы
|
||||
```cpp
|
||||
// Получение данных с API
|
||||
string response = http_get("https://api.github.com/users/octocat");
|
||||
if (response != "") {
|
||||
print("✅ API response received");
|
||||
print("Data: " + response);
|
||||
} else {
|
||||
print("❌ Failed to fetch data");
|
||||
}
|
||||
```
|
||||
|
||||
## 📂 Структура проекта
|
||||
|
||||
* `src/` — Исходный код интерпретатора (C++).
|
||||
* `test/` — Примеры скриптов `.fox`.
|
||||
* `doc/` — Документация.
|
||||
* `src/` — Исходный код интерпретатора (C++)
|
||||
* `test/` — Тесты функциональности
|
||||
* `doc/` — Документация
|
||||
* `CHANGELOG.md` — История изменений
|
||||
* `DOCUMENTATION.md` — Полная документация языка
|
||||
|
||||
## 🤖 Telegram Bot
|
||||
|
||||
FoxLang поставляется с Telegram ботом, написанным на чистом FoxLang!
|
||||
|
||||
### Быстрый запуск бота:
|
||||
```bash
|
||||
# Настройте токен бота
|
||||
cd telegram_bot
|
||||
nano token.txt # Вставьте токен от @BotFather
|
||||
|
||||
# Запустите бота
|
||||
./run_bot.sh
|
||||
```
|
||||
|
||||
Подробнее: [telegram_bot/README.md](telegram_bot/README.md)
|
||||
|
||||
## 🧪 Тестирование
|
||||
|
||||
```bash
|
||||
# Запуск всех тестов
|
||||
./test_all.sh
|
||||
|
||||
# Отдельные тесты
|
||||
./src/foxlang test/variables.fox # Тест переменных и типов
|
||||
./src/foxlang test/functions.fox # Тест пользовательских функций
|
||||
./src/foxlang test/arrays.fox # Тест массивов
|
||||
./src/foxlang test/control_flow.fox # Тест циклов и условий
|
||||
./src/foxlang test/math_operations.fox # Тест математических операций
|
||||
./src/foxlang test/modules.fox # Тест модульной системы
|
||||
./src/foxlang test/builtin_functions.fox # Тест встроенных функций
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Author:** [SkrinVex](https://skrinvex.su)
|
||||
**Author:** [SkrinVex](https://skrinvex.su)
|
||||
**License:** MIT
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
// Демонстрация всех возможностей FoxLang 4.1.0
|
||||
using net;
|
||||
|
||||
print("=== FoxLang 4.1.0 Demo ===");
|
||||
|
||||
// Новые типы данных
|
||||
float pi = 3.14159;
|
||||
bool systemActive = true;
|
||||
bool debugMode = false;
|
||||
|
||||
print("Pi value: " + pi);
|
||||
print("System active: " + systemActive);
|
||||
print("Debug mode: " + debugMode);
|
||||
|
||||
// Логические операции
|
||||
if (systemActive && !debugMode) {
|
||||
print("Production mode enabled");
|
||||
}
|
||||
|
||||
if (pi > 3.0 || debugMode) {
|
||||
print("Math constants loaded");
|
||||
}
|
||||
|
||||
// Сетевые функции
|
||||
print("Testing network functions...");
|
||||
string apiResponse = http_get("https://jsonplaceholder.typicode.com/posts/1");
|
||||
print("API Response: " + apiResponse);
|
||||
|
||||
string postResult = http_post("https://httpbin.org/post", "test=data");
|
||||
print("POST Result: " + postResult);
|
||||
|
||||
// TCP операции
|
||||
int tcpSock = tcp_socket();
|
||||
print("TCP Socket ID: " + tcpSock);
|
||||
|
||||
bool connected = tcp_connect(tcpSock, "example.com", 80);
|
||||
if (connected) {
|
||||
print("TCP connection established");
|
||||
int sent = tcp_send(tcpSock, "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
|
||||
print("Sent " + sent + " bytes");
|
||||
|
||||
string response = tcp_receive(tcpSock, 1024);
|
||||
print("Received: " + response);
|
||||
|
||||
tcp_close(tcpSock);
|
||||
print("Connection closed");
|
||||
}
|
||||
|
||||
// UDP операции
|
||||
int udpSock = udp_socket();
|
||||
int udpSent = udp_send(udpSock, "8.8.8.8", 53, "DNS query");
|
||||
print("UDP sent " + udpSent + " bytes to DNS server");
|
||||
|
||||
string udpResp = udp_receive(udpSock, 512);
|
||||
print("UDP response: " + udpResp);
|
||||
|
||||
// Массивы и циклы
|
||||
array numbers 5;
|
||||
int i = 0;
|
||||
while (i < 5) {
|
||||
set(numbers, i, i * i);
|
||||
i++;
|
||||
}
|
||||
|
||||
print("Array contents:");
|
||||
i = 0;
|
||||
while (i < 5) {
|
||||
string value = get(numbers, i);
|
||||
print("numbers[" + i + "] = " + value);
|
||||
i++;
|
||||
}
|
||||
|
||||
print("=== Demo completed ===");
|
||||
fox();
|
||||
@@ -0,0 +1,12 @@
|
||||
// Простой тест новых типов
|
||||
float pi = 3.14;
|
||||
bool isActive = false;
|
||||
|
||||
print("Pi: " + pi);
|
||||
print("Active: " + isActive);
|
||||
|
||||
if (isActive) {
|
||||
print("System is running");
|
||||
}
|
||||
|
||||
fox();
|
||||
@@ -8,8 +8,8 @@
|
||||
#include <random>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
// Forward declaration
|
||||
struct Node;
|
||||
|
||||
struct FuncParam {
|
||||
@@ -17,12 +17,16 @@ struct FuncParam {
|
||||
std::string name;
|
||||
};
|
||||
|
||||
// Контекст памяти (переменные и функции)
|
||||
struct Value {
|
||||
std::string type;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
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::vector<std::string>> arrays;
|
||||
Context* parent = nullptr;
|
||||
std::map<std::string, Value> variables;
|
||||
std::map<std::string, std::shared_ptr<Node>> functions;
|
||||
std::map<std::string, std::vector<Value>> arrays;
|
||||
|
||||
bool exists(const std::string& name) {
|
||||
if (variables.count(name) || arrays.count(name)) return true;
|
||||
@@ -30,50 +34,44 @@ struct Context {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string getVar(const std::string& name) {
|
||||
Value getVar(const std::string& name) {
|
||||
if (variables.count(name)) return variables[name];
|
||||
if (parent) return parent->getVar(name);
|
||||
throw std::runtime_error("Runtime Error: Variable '" + name + "' not found!");
|
||||
}
|
||||
|
||||
std::vector<std::string>& getArray(const std::string& name) {
|
||||
std::vector<Value>& getArray(const std::string& name) {
|
||||
if (arrays.count(name)) return arrays[name];
|
||||
if (parent) return parent->getArray(name);
|
||||
throw std::runtime_error("Runtime Error: Array '" + name + "' not found!");
|
||||
}
|
||||
|
||||
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; }
|
||||
throw std::runtime_error("Error: Variable '" + name + "' not defined!");
|
||||
void defineFunc(const std::string& name, std::shared_ptr<Node> func) {
|
||||
functions[name] = func;
|
||||
}
|
||||
|
||||
void defineVar(const std::string& name, const std::string& val) {
|
||||
if (variables.count(name)) {
|
||||
throw std::runtime_error("Error: Variable '" + name + "' already defined!");
|
||||
}
|
||||
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;
|
||||
|
||||
void defineVar(const std::string& name, const std::string& type, const Value& value) {
|
||||
variables[name] = {type, value.value};
|
||||
}
|
||||
|
||||
void setVar(const std::string& name, Value val) {
|
||||
if (variables.count(name)) {
|
||||
variables[name].value = val.value;
|
||||
return;
|
||||
}
|
||||
if (parent) { parent->setVar(name, val); return; }
|
||||
throw std::runtime_error("Error: Variable '" + name + "' not defined!");
|
||||
}
|
||||
};
|
||||
|
||||
// Специальный тип для RETURN
|
||||
struct ReturnValue {
|
||||
std::string value;
|
||||
Value value;
|
||||
};
|
||||
|
||||
static std::string formatNumber(double val) {
|
||||
@@ -85,12 +83,9 @@ static std::string formatNumber(double val) {
|
||||
|
||||
struct Node {
|
||||
virtual ~Node() = default;
|
||||
virtual std::string eval(Context& ctx) = 0;
|
||||
virtual Value eval(Context& ctx) = 0;
|
||||
};
|
||||
|
||||
// --- ОСНОВНЫЕ УЗЛЫ ---
|
||||
|
||||
// Определение функции
|
||||
struct FuncDefNode : Node {
|
||||
std::string returnType;
|
||||
std::string name;
|
||||
@@ -100,21 +95,18 @@ struct FuncDefNode : Node {
|
||||
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 ""; }
|
||||
Value eval(Context& ctx) override { return {"void", ""}; }
|
||||
};
|
||||
|
||||
// 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";
|
||||
Value eval(Context& ctx) override {
|
||||
Value result = expr ? expr->eval(ctx) : Value{"void", ""};
|
||||
throw ReturnValue{result};
|
||||
}
|
||||
};
|
||||
|
||||
// ВЫЗОВ ФУНКЦИИ - самое важное для тебя!
|
||||
struct FuncCallNode : Node {
|
||||
std::string name;
|
||||
std::vector<std::unique_ptr<Node>> args;
|
||||
@@ -122,7 +114,152 @@ struct FuncCallNode : Node {
|
||||
FuncCallNode(std::string n, std::vector<std::unique_ptr<Node>> a)
|
||||
: name(n), args(std::move(a)) {}
|
||||
|
||||
std::string eval(Context& ctx) override {
|
||||
Value eval(Context& ctx) override {
|
||||
// Встроенные функции
|
||||
if (name == "print" && args.size() == 1) {
|
||||
std::cout << args[0]->eval(ctx).value << std::endl;
|
||||
return {"void", ""};
|
||||
}
|
||||
if (name == "input" && args.size() == 0) {
|
||||
std::string input; std::getline(std::cin, input);
|
||||
return {"string", input};
|
||||
}
|
||||
if (name == "round" && args.size() == 1) {
|
||||
double val = std::stod(args[0]->eval(ctx).value);
|
||||
return {"int", std::to_string((int)std::round(val))};
|
||||
}
|
||||
if (name == "random" && args.size() == 2) {
|
||||
int min = std::stoi(args[0]->eval(ctx).value);
|
||||
int max = std::stoi(args[1]->eval(ctx).value);
|
||||
static std::random_device rd; static std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> dis(min, max);
|
||||
return {"int", std::to_string(dis(gen))};
|
||||
}
|
||||
if (name == "fox" && args.size() == 0) {
|
||||
std::cout << "FoxLang" << std::endl;
|
||||
return {"void", ""};
|
||||
}
|
||||
if (name == "read_file" && args.size() == 1) {
|
||||
Value filenameVal = args[0]->eval(ctx);
|
||||
if (filenameVal.type != "string") {
|
||||
throw std::runtime_error("read_file() requires string filename");
|
||||
}
|
||||
|
||||
std::ifstream file(filenameVal.value);
|
||||
if (!file.is_open()) {
|
||||
return {"string", ""}; // Возвращаем пустую строку при ошибке
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
// Пропускаем комментарии и пустые строки
|
||||
if (line.empty() || line[0] == '#') continue;
|
||||
|
||||
// Если строка не пустая и не комментарий, возвращаем её
|
||||
if (!line.empty()) {
|
||||
file.close();
|
||||
return {"string", line};
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
return {"string", ""};
|
||||
}
|
||||
if (name == "http_get" && args.size() == 1) {
|
||||
Value urlVal = args[0]->eval(ctx);
|
||||
if (urlVal.type != "string") {
|
||||
throw std::runtime_error("http_get() requires string URL");
|
||||
}
|
||||
|
||||
// Простая реализация через system curl
|
||||
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 == "json_get" && args.size() == 2) {
|
||||
Value jsonVal = args[0]->eval(ctx);
|
||||
Value keyVal = args[1]->eval(ctx);
|
||||
|
||||
std::string json = jsonVal.value;
|
||||
std::string key = keyVal.value;
|
||||
|
||||
// Простой JSON парсер для Telegram API
|
||||
if (key == "chat_id") {
|
||||
size_t pos = json.find("\"chat\":{\"id\":");
|
||||
if (pos != std::string::npos) {
|
||||
pos += 13; // длина "\"chat\":{\"id\":"
|
||||
size_t end = json.find(",", pos);
|
||||
if (end != std::string::npos) {
|
||||
return {"string", json.substr(pos, end - pos)};
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (key == "text") {
|
||||
size_t pos = json.find("\"text\":\"");
|
||||
if (pos != std::string::npos) {
|
||||
pos += 8; // длина "\"text\":\""
|
||||
size_t end = json.find("\"", pos);
|
||||
if (end != std::string::npos) {
|
||||
return {"string", json.substr(pos, end - pos)};
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (key == "update_id") {
|
||||
// Ищем последний update_id в массиве
|
||||
size_t lastPos = 0;
|
||||
size_t pos = json.find("\"update_id\":");
|
||||
while (pos != std::string::npos) {
|
||||
lastPos = pos;
|
||||
pos = json.find("\"update_id\":", pos + 1);
|
||||
}
|
||||
|
||||
if (lastPos != 0) {
|
||||
lastPos += 12; // длина "\"update_id\":"
|
||||
size_t end = json.find(",", lastPos);
|
||||
if (end != std::string::npos) {
|
||||
return {"string", json.substr(lastPos, end - lastPos)};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {"string", ""};
|
||||
}
|
||||
if (name == "str_contains" && args.size() == 2) {
|
||||
Value textVal = args[0]->eval(ctx);
|
||||
Value substrVal = args[1]->eval(ctx);
|
||||
|
||||
std::string text = textVal.value;
|
||||
std::string substr = substrVal.value;
|
||||
|
||||
bool found = text.find(substr) != std::string::npos;
|
||||
return {"bool", found ? "true" : "false"};
|
||||
}
|
||||
if (name == "str_to_int" && args.size() == 1) {
|
||||
Value strVal = args[0]->eval(ctx);
|
||||
if (strVal.type != "string") {
|
||||
return {"int", "0"};
|
||||
}
|
||||
|
||||
try {
|
||||
int result = std::stoi(strVal.value);
|
||||
return {"int", std::to_string(result)};
|
||||
} catch (...) {
|
||||
return {"int", "0"};
|
||||
}
|
||||
}
|
||||
|
||||
// Пользовательские функции
|
||||
auto funcNodeBase = ctx.getFunc(name);
|
||||
if (!funcNodeBase) {
|
||||
throw std::runtime_error("Runtime Error: Function '" + name + "' not found!");
|
||||
@@ -134,152 +271,292 @@ struct FuncCallNode : Node {
|
||||
throw std::runtime_error("Args count mismatch for '" + name + "'");
|
||||
}
|
||||
|
||||
std::vector<std::string> argValues;
|
||||
std::vector<Value> 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;
|
||||
funcScope.parent = &ctx;
|
||||
|
||||
for (size_t i = 0; i < funcDef->params.size(); i++) {
|
||||
funcScope.defineVar(funcDef->params[i].name, argValues[i]);
|
||||
funcScope.defineVar(funcDef->params[i].name, funcDef->params[i].type, argValues[i]);
|
||||
}
|
||||
|
||||
try {
|
||||
funcDef->body->eval(funcScope);
|
||||
} catch (const ReturnValue& ret) {
|
||||
return ret.value; // ВОЗВРАЩАЕМ ЗНАЧЕНИЕ В ПЕРЕМЕННУЮ
|
||||
return ret.value;
|
||||
}
|
||||
|
||||
return "0";
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
|
||||
struct NumberNode : Node {
|
||||
std::string val;
|
||||
NumberNode(std::string v) : val(v) {}
|
||||
std::string eval(Context& ctx) override { return val; }
|
||||
bool isFloat;
|
||||
NumberNode(std::string v) : val(v) {
|
||||
isFloat = (v.find('.') != std::string::npos);
|
||||
}
|
||||
Value eval(Context& ctx) override { return {isFloat ? "float" : "int", val}; }
|
||||
};
|
||||
|
||||
struct StringNode : Node {
|
||||
std::string val;
|
||||
StringNode(std::string v) : val(v) {}
|
||||
std::string eval(Context& ctx) override { return val; }
|
||||
Value eval(Context& ctx) override { return {"string", val}; }
|
||||
};
|
||||
|
||||
struct BoolNode : Node {
|
||||
bool val;
|
||||
BoolNode(bool v) : val(v) {}
|
||||
Value eval(Context& ctx) override { return {"bool", val ? "true" : "false"}; }
|
||||
};
|
||||
|
||||
struct VarAccessNode : Node {
|
||||
std::string name;
|
||||
VarAccessNode(std::string n) : name(n) {}
|
||||
std::string eval(Context& ctx) override { return ctx.getVar(name); }
|
||||
Value eval(Context& ctx) override { return ctx.getVar(name); }
|
||||
};
|
||||
|
||||
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)) {}
|
||||
Value eval(Context& ctx) override {
|
||||
ctx.defineVar(name, type, expr->eval(ctx));
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
|
||||
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 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)) {}
|
||||
Value eval(Context& ctx) override {
|
||||
Context* root = &ctx;
|
||||
while (root->parent != nullptr) root = root->parent;
|
||||
root->defineVar(name, expr->eval(ctx));
|
||||
return "";
|
||||
root->defineVar(name, type, expr->eval(ctx));
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
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 {
|
||||
// Здесь 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 {
|
||||
|
||||
struct VarAssignNode : Node {
|
||||
std::string name;
|
||||
std::unique_ptr<Node> expr;
|
||||
VarAssignNode(std::string n, std::unique_ptr<Node> e) : name(n), expr(std::move(e)) {}
|
||||
Value eval(Context& ctx) override {
|
||||
ctx.setVar(name, expr->eval(ctx));
|
||||
return "";
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
|
||||
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";
|
||||
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)) {}
|
||||
|
||||
Value eval(Context& ctx) override {
|
||||
Value lval = left->eval(ctx);
|
||||
Value rval = right->eval(ctx);
|
||||
|
||||
if (op == '+') {
|
||||
if (lval.type == "string" || rval.type == "string") {
|
||||
return {"string", lval.value + rval.value};
|
||||
}
|
||||
if (lval.type == "float" || rval.type == "float") {
|
||||
double l = std::stod(lval.value), r = std::stod(rval.value);
|
||||
return {"float", formatNumber(l + r)};
|
||||
}
|
||||
int l = std::stoi(lval.value), r = std::stoi(rval.value);
|
||||
return {"int", std::to_string(l + r)};
|
||||
}
|
||||
|
||||
if (op == '-' || op == '*' || op == '/' || op == '%') {
|
||||
if (lval.type == "float" || rval.type == "float") {
|
||||
double l = std::stod(lval.value), r = std::stod(rval.value);
|
||||
double result = (op == '-') ? l - r : (op == '*') ? l * r :
|
||||
(op == '/') ? l / r : std::fmod(l, r);
|
||||
return {"float", formatNumber(result)};
|
||||
}
|
||||
int l = std::stoi(lval.value), r = std::stoi(rval.value);
|
||||
int result = (op == '-') ? l - r : (op == '*') ? l * r :
|
||||
(op == '/') ? l / r : l % r;
|
||||
return {"int", std::to_string(result)};
|
||||
}
|
||||
|
||||
if (op == '=' || op == '!' || op == '<' || op == '>') {
|
||||
bool result;
|
||||
if (lval.type == "string" && rval.type == "string") {
|
||||
result = (op == '=') ? lval.value == rval.value :
|
||||
(op == '!') ? lval.value != rval.value :
|
||||
(op == '<') ? lval.value < rval.value : lval.value > rval.value;
|
||||
} else {
|
||||
double l = std::stod(lval.value), r = std::stod(rval.value);
|
||||
result = (op == '=') ? l == r : (op == '!') ? l != r :
|
||||
(op == '<') ? l < r : l > r;
|
||||
}
|
||||
return {"bool", result ? "true" : "false"};
|
||||
}
|
||||
|
||||
if (op == '&' || op == '|') {
|
||||
bool l = (lval.value == "true"), r = (rval.value == "true");
|
||||
bool result = (op == '&') ? l && r : l || r;
|
||||
return {"bool", result ? "true" : "false"};
|
||||
}
|
||||
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
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 UnaryOpNode : Node {
|
||||
std::string op;
|
||||
std::unique_ptr<Node> operand;
|
||||
UnaryOpNode(std::string o, std::unique_ptr<Node> n) : op(o), operand(std::move(n)) {}
|
||||
Value eval(Context& ctx) override {
|
||||
Value val = operand->eval(ctx);
|
||||
if (op == "!") {
|
||||
bool b = (val.value == "true");
|
||||
return {"bool", b ? "false" : "true"};
|
||||
}
|
||||
return val;
|
||||
}
|
||||
};
|
||||
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 PostIncNode : Node {
|
||||
std::string name;
|
||||
PostIncNode(std::string n) : name(n) {}
|
||||
Value eval(Context& ctx) override {
|
||||
Value current = ctx.getVar(name);
|
||||
int val = std::stoi(current.value);
|
||||
ctx.setVar(name, {"int", std::to_string(val + 1)});
|
||||
return {"int", std::to_string(val)};
|
||||
}
|
||||
};
|
||||
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 ArrayDeclNode : Node {
|
||||
std::string name;
|
||||
int size;
|
||||
ArrayDeclNode(std::string n, int s) : name(n), size(s) {}
|
||||
Value eval(Context& ctx) override {
|
||||
ctx.arrays[name] = std::vector<Value>(size, {"int", "0"});
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
|
||||
struct ArraySetNode : Node {
|
||||
std::string name;
|
||||
std::unique_ptr<Node> index, value;
|
||||
ArraySetNode(std::string n, std::unique_ptr<Node> i, std::unique_ptr<Node> v)
|
||||
: name(n), index(std::move(i)), value(std::move(v)) {}
|
||||
Value eval(Context& ctx) override {
|
||||
int idx = std::stoi(index->eval(ctx).value);
|
||||
ctx.getArray(name)[idx] = value->eval(ctx);
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
|
||||
struct ArrayGetNode : Node {
|
||||
std::string name;
|
||||
std::unique_ptr<Node> index;
|
||||
ArrayGetNode(std::string n, std::unique_ptr<Node> i) : name(n), index(std::move(i)) {}
|
||||
Value eval(Context& ctx) override {
|
||||
int idx = std::stoi(index->eval(ctx).value);
|
||||
return ctx.getArray(name)[idx];
|
||||
}
|
||||
};
|
||||
|
||||
struct BlockNode : Node {
|
||||
std::vector<std::unique_ptr<Node>> stmts;
|
||||
std::string eval(Context& ctx) override {
|
||||
for(auto& s : stmts) s->eval(ctx);
|
||||
return "";
|
||||
Value eval(Context& ctx) override {
|
||||
for (auto& stmt : stmts) stmt->eval(ctx);
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
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 IfNode : Node {
|
||||
std::unique_ptr<Node> condition, thenB, elseB;
|
||||
IfNode(std::unique_ptr<Node> c, std::unique_ptr<Node> t, std::unique_ptr<Node> e = nullptr)
|
||||
: condition(std::move(c)), thenB(std::move(t)), elseB(std::move(e)) {}
|
||||
Value eval(Context& ctx) override {
|
||||
bool cond = (condition->eval(ctx).value == "true");
|
||||
if (cond) thenB->eval(ctx);
|
||||
else if (elseB) elseB->eval(ctx);
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
|
||||
struct WhileNode : Node {
|
||||
std::unique_ptr<Node> condition, body;
|
||||
WhileNode(std::unique_ptr<Node> c, std::unique_ptr<Node> b)
|
||||
: condition(std::move(c)), body(std::move(b)) {}
|
||||
Value eval(Context& ctx) override {
|
||||
while (condition->eval(ctx).value == "true") {
|
||||
body->eval(ctx);
|
||||
}
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
|
||||
struct ForNode : Node {
|
||||
std::unique_ptr<Node> init, condition, step, body;
|
||||
ForNode(std::unique_ptr<Node> i, std::unique_ptr<Node> c, std::unique_ptr<Node> s, std::unique_ptr<Node> b)
|
||||
: init(std::move(i)), condition(std::move(c)), step(std::move(s)), body(std::move(b)) {}
|
||||
Value eval(Context& ctx) override {
|
||||
if (init) init->eval(ctx);
|
||||
while (condition->eval(ctx).value == "true") {
|
||||
body->eval(ctx);
|
||||
if (step) step->eval(ctx);
|
||||
}
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
|
||||
struct InputNode : Node {
|
||||
std::string eval(Context& ctx) override { std::string b; std::getline(std::cin, b); return b; }
|
||||
};
|
||||
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 {
|
||||
ctx.arrays[name] = std::vector<std::string>(std::stoi(size->eval(ctx)), "0");
|
||||
return "";
|
||||
Value eval(Context& ctx) override {
|
||||
std::string input; std::getline(std::cin, input);
|
||||
return {"string", input};
|
||||
}
|
||||
};
|
||||
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 ReadFileNode : Node {
|
||||
std::unique_ptr<Node> filename;
|
||||
ReadFileNode(std::unique_ptr<Node> fn) : filename(std::move(fn)) {}
|
||||
|
||||
Value eval(Context& ctx) override {
|
||||
Value filenameVal = filename->eval(ctx);
|
||||
if (filenameVal.type != "string") {
|
||||
throw std::runtime_error("read_file() requires string filename");
|
||||
}
|
||||
|
||||
std::ifstream file(filenameVal.value);
|
||||
if (!file.is_open()) {
|
||||
return {"string", ""}; // Возвращаем пустую строку при ошибке
|
||||
}
|
||||
|
||||
std::string content;
|
||||
std::string line;
|
||||
bool first = true;
|
||||
while (std::getline(file, line)) {
|
||||
// Пропускаем комментарии и пустые строки
|
||||
if (line.empty() || line[0] == '#') continue;
|
||||
|
||||
// Если строка не пустая и не комментарий, возвращаем её
|
||||
if (!line.empty()) {
|
||||
file.close();
|
||||
return {"string", line};
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
return {"string", ""};
|
||||
}
|
||||
};
|
||||
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 UsingNode : Node {
|
||||
std::string libName;
|
||||
UsingNode(std::string lib) : libName(lib) {}
|
||||
Value eval(Context& ctx) override { return {"void", ""}; }
|
||||
};
|
||||
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 ""; } };
|
||||
+434
@@ -0,0 +1,434 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <random>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
// Forward declaration
|
||||
struct Node;
|
||||
|
||||
struct FuncParam {
|
||||
std::string type;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
// Unified Value type for the language
|
||||
struct Value {
|
||||
std::string type; // "int", "float", "string", "bool", "void"
|
||||
std::string value;
|
||||
};
|
||||
|
||||
// Контекст памяти (переменные и функции)
|
||||
struct Context {
|
||||
Context* parent = nullptr; // Для глобальных переменных
|
||||
std::map<std::string, Value> variables; // Stores typed values
|
||||
std::map<std::string, std::shared_ptr<Node>> functions;
|
||||
std::map<std::string, std::vector<Value>> arrays;
|
||||
|
||||
bool exists(const std::string& name) {
|
||||
if (variables.count(name) || arrays.count(name)) return true;
|
||||
if (parent) return parent->exists(name);
|
||||
return false;
|
||||
}
|
||||
|
||||
Value getVar(const std::string& name) {
|
||||
if (variables.count(name)) return variables[name];
|
||||
if (parent) return parent->getVar(name);
|
||||
throw std::runtime_error("Runtime Error: Variable '" + name + "' not found!");
|
||||
}
|
||||
|
||||
std::vector<Value>& getArray(const std::string& name) {
|
||||
if (arrays.count(name)) return arrays[name];
|
||||
if (parent) return parent->getArray(name);
|
||||
throw std::runtime_error("Runtime Error: Array '" + name + "' not found!");
|
||||
}
|
||||
|
||||
void defineFunc(const std::string& name, std::shared_ptr<Node> func) {
|
||||
functions[name] = func;
|
||||
}
|
||||
|
||||
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 defineVar(const std::string& name, const std::string& type, const Value& value) {
|
||||
if (variables.count(name)) {
|
||||
throw std::runtime_error("Error: Variable '" + name + "' already defined!");
|
||||
}
|
||||
std::string finalVal = value.value;
|
||||
if (type == "int") {
|
||||
try {
|
||||
finalVal = std::to_string((int)std::stod(value.value));
|
||||
} catch(...) { finalVal = "0"; }
|
||||
}
|
||||
variables[name] = {type, finalVal};
|
||||
}
|
||||
|
||||
// Set variable with type checking/conversion
|
||||
void setVar(const std::string& name, Value val) {
|
||||
if (variables.count(name)) {
|
||||
std::string targetType = variables[name].type;
|
||||
|
||||
// Handle int/float conversion
|
||||
if (targetType == "int") {
|
||||
try {
|
||||
double d = std::stod(val.value);
|
||||
variables[name].value = std::to_string((int)d);
|
||||
} catch(...) { variables[name].value = "0"; }
|
||||
} else if (targetType == "float") {
|
||||
try {
|
||||
double d = std::stod(val.value);
|
||||
variables[name].value = formatNumber(d);
|
||||
} catch(...) { variables[name].value = "0.0"; }
|
||||
} else {
|
||||
variables[name].value = val.value;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (parent) { parent->setVar(name, val); return; }
|
||||
throw std::runtime_error("Error: Variable '" + name + "' not defined!");
|
||||
}
|
||||
|
||||
void defineGlobal(const std::string& name, std::string type, Value val) {
|
||||
if (parent) parent->defineGlobal(name, type, val);
|
||||
else defineVar(name, type, val);
|
||||
}
|
||||
};
|
||||
|
||||
// Exception for return
|
||||
struct ReturnValue {
|
||||
Value value;
|
||||
};
|
||||
|
||||
static std::string formatNumber(double val) {
|
||||
std::string s = std::to_string(val);
|
||||
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
|
||||
if (s.back() == '.') s.pop_back();
|
||||
return s;
|
||||
}
|
||||
|
||||
struct Node {
|
||||
virtual ~Node() = default;
|
||||
virtual Value 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) {}
|
||||
|
||||
Value eval(Context& ctx) override { return {"void", ""}; }
|
||||
};
|
||||
|
||||
struct ReturnNode : Node {
|
||||
std::unique_ptr<Node> expr;
|
||||
ReturnNode(std::unique_ptr<Node> e) : expr(std::move(e)) {}
|
||||
Value eval(Context& ctx) override {
|
||||
Value result = expr ? expr->eval(ctx) : Value{"void", ""};
|
||||
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)) {}
|
||||
|
||||
Value eval(Context& ctx) override {
|
||||
// Встроенные функции
|
||||
if (name == "print" && args.size() == 1) {
|
||||
std::cout << args[0]->eval(ctx).value << std::endl;
|
||||
return {"void", ""};
|
||||
}
|
||||
if (name == "input" && args.size() == 0) {
|
||||
std::string input; std::getline(std::cin, input);
|
||||
return {"string", input};
|
||||
}
|
||||
if (name == "round" && args.size() == 1) {
|
||||
double val = std::stod(args[0]->eval(ctx).value);
|
||||
return {"int", std::to_string((int)std::round(val))};
|
||||
}
|
||||
if (name == "random" && args.size() == 2) {
|
||||
int min = std::stoi(args[0]->eval(ctx).value);
|
||||
int max = std::stoi(args[1]->eval(ctx).value);
|
||||
static std::random_device rd; static std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> dis(min, max);
|
||||
return {"int", std::to_string(dis(gen))};
|
||||
}
|
||||
if (name == "fox" && args.size() == 0) {
|
||||
std::cout << "FoxLang" << std::endl;
|
||||
return {"void", ""};
|
||||
}
|
||||
|
||||
// Пользовательские функции
|
||||
auto funcNodeBase = ctx.getFunc(name);
|
||||
if (!funcNodeBase) {
|
||||
throw std::runtime_error("Runtime Error: Function '" + name + "' not found!");
|
||||
}
|
||||
|
||||
FuncDefNode* funcDef = static_cast<FuncDefNode*>(funcNodeBase.get());
|
||||
|
||||
if (args.size() != funcDef->params.size()) {
|
||||
throw std::runtime_error("Args count mismatch for '" + name + "'");
|
||||
}
|
||||
|
||||
std::vector<Value> 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, funcDef->params[i].type, argValues[i]);
|
||||
}
|
||||
|
||||
try {
|
||||
funcDef->body->eval(funcScope);
|
||||
} catch (const ReturnValue& ret) {
|
||||
return ret.value;
|
||||
}
|
||||
|
||||
return {"void", "0"};
|
||||
}
|
||||
};
|
||||
|
||||
struct NumberNode : Node {
|
||||
std::string val;
|
||||
bool isFloat;
|
||||
NumberNode(std::string v) : val(v) {
|
||||
isFloat = (v.find('.') != std::string::npos);
|
||||
}
|
||||
Value eval(Context& ctx) override { return {isFloat ? "float" : "int", val}; }
|
||||
};
|
||||
|
||||
struct StringNode : Node {
|
||||
std::string val;
|
||||
StringNode(std::string v) : val(v) {}
|
||||
Value eval(Context& ctx) override { return {"string", val}; }
|
||||
};
|
||||
|
||||
struct BoolNode : Node {
|
||||
bool val;
|
||||
BoolNode(bool v) : val(v) {}
|
||||
Value eval(Context& ctx) override { return {"bool", val ? "true" : "false"}; }
|
||||
};
|
||||
|
||||
struct VarAccessNode : Node {
|
||||
std::string name;
|
||||
VarAccessNode(std::string n) : name(n) {}
|
||||
Value eval(Context& ctx) override { return ctx.getVar(name); }
|
||||
};
|
||||
|
||||
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)) {}
|
||||
Value eval(Context& ctx) override {
|
||||
ctx.defineVar(name, type, expr->eval(ctx));
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
|
||||
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)) {}
|
||||
Value eval(Context& ctx) override {
|
||||
Context* root = &ctx;
|
||||
while (root->parent != nullptr) root = root->parent;
|
||||
root->defineVar(name, type, expr->eval(ctx));
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
|
||||
struct VarAssignNode : Node {
|
||||
std::string name;
|
||||
std::unique_ptr<Node> expr;
|
||||
VarAssignNode(std::string n, std::unique_ptr<Node> e) : name(n), expr(std::move(e)) {}
|
||||
Value eval(Context& ctx) override {
|
||||
ctx.setVar(name, expr->eval(ctx));
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
|
||||
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)) {}
|
||||
|
||||
Value eval(Context& ctx) override {
|
||||
Value lval = left->eval(ctx);
|
||||
Value rval = right->eval(ctx);
|
||||
|
||||
if (op == '+') {
|
||||
if (lval.type == "string" || rval.type == "string") {
|
||||
return {"string", lval.value + rval.value};
|
||||
}
|
||||
if (lval.type == "float" || rval.type == "float") {
|
||||
double l = std::stod(lval.value), r = std::stod(rval.value);
|
||||
return {"float", formatNumber(l + r)};
|
||||
}
|
||||
int l = std::stoi(lval.value), r = std::stoi(rval.value);
|
||||
return {"int", std::to_string(l + r)};
|
||||
}
|
||||
|
||||
if (op == '-' || op == '*' || op == '/' || op == '%') {
|
||||
if (lval.type == "float" || rval.type == "float") {
|
||||
double l = std::stod(lval.value), r = std::stod(rval.value);
|
||||
double result = (op == '-') ? l - r : (op == '*') ? l * r :
|
||||
(op == '/') ? l / r : std::fmod(l, r);
|
||||
return {"float", formatNumber(result)};
|
||||
}
|
||||
int l = std::stoi(lval.value), r = std::stoi(rval.value);
|
||||
int result = (op == '-') ? l - r : (op == '*') ? l * r :
|
||||
(op == '/') ? l / r : l % r;
|
||||
return {"int", std::to_string(result)};
|
||||
}
|
||||
|
||||
if (op == '=' || op == '!' || op == '<' || op == '>') {
|
||||
bool result;
|
||||
if (lval.type == "string" && rval.type == "string") {
|
||||
result = (op == '=') ? lval.value == rval.value :
|
||||
(op == '!') ? lval.value != rval.value :
|
||||
(op == '<') ? lval.value < rval.value : lval.value > rval.value;
|
||||
} else {
|
||||
double l = std::stod(lval.value), r = std::stod(rval.value);
|
||||
result = (op == '=') ? l == r : (op == '!') ? l != r :
|
||||
(op == '<') ? l < r : l > r;
|
||||
}
|
||||
return {"bool", result ? "true" : "false"};
|
||||
}
|
||||
|
||||
if (op == '&' || op == '|') {
|
||||
bool l = (lval.value == "true"), r = (rval.value == "true");
|
||||
bool result = (op == '&') ? l && r : l || r;
|
||||
return {"bool", result ? "true" : "false"};
|
||||
}
|
||||
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
|
||||
struct UnaryOpNode : Node {
|
||||
std::string op;
|
||||
std::unique_ptr<Node> operand;
|
||||
UnaryOpNode(std::string o, std::unique_ptr<Node> n) : op(o), operand(std::move(n)) {}
|
||||
Value eval(Context& ctx) override {
|
||||
Value val = operand->eval(ctx);
|
||||
if (op == "!") {
|
||||
bool b = (val.value == "true");
|
||||
return {"bool", b ? "false" : "true"};
|
||||
}
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
struct PostIncNode : Node {
|
||||
std::string name;
|
||||
PostIncNode(std::string n) : name(n) {}
|
||||
Value eval(Context& ctx) override {
|
||||
Value current = ctx.getVar(name);
|
||||
int val = std::stoi(current.value);
|
||||
ctx.setVar(name, {"int", std::to_string(val + 1)});
|
||||
return {"int", std::to_string(val)};
|
||||
}
|
||||
};
|
||||
|
||||
struct ArrayDeclNode : Node {
|
||||
std::string name;
|
||||
int size;
|
||||
ArrayDeclNode(std::string n, int s) : name(n), size(s) {}
|
||||
Value eval(Context& ctx) override {
|
||||
ctx.arrays[name] = std::vector<Value>(size, {"int", "0"});
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
|
||||
struct ArraySetNode : Node {
|
||||
std::string name;
|
||||
std::unique_ptr<Node> index, value;
|
||||
ArraySetNode(std::string n, std::unique_ptr<Node> i, std::unique_ptr<Node> v)
|
||||
: name(n), index(std::move(i)), value(std::move(v)) {}
|
||||
Value eval(Context& ctx) override {
|
||||
int idx = std::stoi(index->eval(ctx).value);
|
||||
ctx.getArray(name)[idx] = value->eval(ctx);
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
|
||||
struct ArrayGetNode : Node {
|
||||
std::string name;
|
||||
std::unique_ptr<Node> index;
|
||||
ArrayGetNode(std::string n, std::unique_ptr<Node> i) : name(n), index(std::move(i)) {}
|
||||
Value eval(Context& ctx) override {
|
||||
int idx = std::stoi(index->eval(ctx).value);
|
||||
return ctx.getArray(name)[idx];
|
||||
}
|
||||
};
|
||||
|
||||
struct BlockNode : Node {
|
||||
std::vector<std::unique_ptr<Node>> stmts;
|
||||
Value eval(Context& ctx) override {
|
||||
for (auto& stmt : stmts) stmt->eval(ctx);
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
|
||||
struct IfNode : Node {
|
||||
std::unique_ptr<Node> condition, thenB, elseB;
|
||||
IfNode(std::unique_ptr<Node> c, std::unique_ptr<Node> t, std::unique_ptr<Node> e = nullptr)
|
||||
: condition(std::move(c)), thenB(std::move(t)), elseB(std::move(e)) {}
|
||||
Value eval(Context& ctx) override {
|
||||
bool cond = (condition->eval(ctx).value == "true");
|
||||
if (cond) thenB->eval(ctx);
|
||||
else if (elseB) elseB->eval(ctx);
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
|
||||
struct WhileNode : Node {
|
||||
std::unique_ptr<Node> condition, body;
|
||||
WhileNode(std::unique_ptr<Node> c, std::unique_ptr<Node> b)
|
||||
: condition(std::move(c)), body(std::move(b)) {}
|
||||
Value eval(Context& ctx) override {
|
||||
while (condition->eval(ctx).value == "true") {
|
||||
body->eval(ctx);
|
||||
}
|
||||
return {"void", ""};
|
||||
}
|
||||
};
|
||||
|
||||
struct InputNode : Node {
|
||||
Value eval(Context& ctx) override {
|
||||
std::string input; std::getline(std::cin, input);
|
||||
return {"string", input};
|
||||
}
|
||||
};
|
||||
|
||||
struct UsingNode : Node {
|
||||
std::string libName;
|
||||
UsingNode(std::string lib) : libName(lib) {}
|
||||
Value eval(Context& ctx) override { return {"void", ""}; }
|
||||
};
|
||||
+47
-3
@@ -23,7 +23,9 @@ std::vector<Token> Lexer::tokenize() {
|
||||
if (isdigit(current)) {
|
||||
std::string num;
|
||||
while (pos < source.length() && isdigit(source[pos])) num += source[pos++];
|
||||
if (pos < source.length() && source[pos] == '.') {
|
||||
// Проверяем точку только если после неё есть цифра (для float)
|
||||
if (pos < source.length() && source[pos] == '.' &&
|
||||
pos + 1 < source.length() && isdigit(source[pos + 1])) {
|
||||
num += source[pos++];
|
||||
while (pos < source.length() && isdigit(source[pos])) num += source[pos++];
|
||||
}
|
||||
@@ -31,7 +33,23 @@ std::vector<Token> Lexer::tokenize() {
|
||||
}
|
||||
else if (current == '"') {
|
||||
pos++; std::string str;
|
||||
while (pos < source.length() && source[pos] != '"') str += source[pos++];
|
||||
while (pos < source.length() && source[pos] != '"') {
|
||||
if (source[pos] == '\\' && pos + 1 < source.length()) {
|
||||
pos++; // Пропускаем обратный слеш
|
||||
char escaped = source[pos];
|
||||
switch (escaped) {
|
||||
case 'n': str += '\n'; break;
|
||||
case 't': str += '\t'; break;
|
||||
case 'r': str += '\r'; break;
|
||||
case '\\': str += '\\'; break;
|
||||
case '"': str += '"'; break;
|
||||
default: str += escaped; break;
|
||||
}
|
||||
} else {
|
||||
str += source[pos];
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
pos++;
|
||||
tokens.push_back({TokenType::STRING_LITERAL, str, line});
|
||||
}
|
||||
@@ -41,15 +59,28 @@ std::vector<Token> Lexer::tokenize() {
|
||||
id += source[pos++];
|
||||
}
|
||||
|
||||
if (id == "print") tokens.push_back({TokenType::PRINT, id, line});
|
||||
// Если идентификатор содержит подчеркивания, это точно не ключевое слово
|
||||
if (id.find('_') != std::string::npos) {
|
||||
tokens.push_back({TokenType::IDENTIFIER, id, line});
|
||||
}
|
||||
else if (id == "print") tokens.push_back({TokenType::PRINT, id, line});
|
||||
else if (id == "input") tokens.push_back({TokenType::INPUT, 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 == "fox") tokens.push_back({TokenType::FOX, id, line});
|
||||
else if (id == "read_file") tokens.push_back({TokenType::READ_FILE, id, line});
|
||||
else if (id == "json_get") tokens.push_back({TokenType::JSON_GET, id, line});
|
||||
else if (id == "str_contains") 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 == "int") tokens.push_back({TokenType::INT_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 == "bool") tokens.push_back({TokenType::BOOL_KW, id, line});
|
||||
else if (id == "true") tokens.push_back({TokenType::TRUE_KW, id, line});
|
||||
else if (id == "false") tokens.push_back({TokenType::FALSE_KW, id, line});
|
||||
else if (id == "void") tokens.push_back({TokenType::VOID_KW, id, line});
|
||||
else if (id == "while") tokens.push_back({TokenType::WHILE, 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 == "else") tokens.push_back({TokenType::ELSE, id, line});
|
||||
else if (id == "array") tokens.push_back({TokenType::ARRAY, id, line});
|
||||
@@ -57,6 +88,7 @@ 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 == "using") tokens.push_back({TokenType::USING, 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});
|
||||
@@ -68,6 +100,15 @@ std::vector<Token> Lexer::tokenize() {
|
||||
if (current == '!' && pos+1 < source.length() && source[pos+1] == '=') {
|
||||
tokens.push_back({TokenType::NEQ, "!=", line}); pos+=2; continue;
|
||||
}
|
||||
if (current == '+' && pos+1 < source.length() && source[pos+1] == '+') {
|
||||
tokens.push_back({TokenType::INC, "++", line}); pos+=2; continue;
|
||||
}
|
||||
if (current == '&' && pos+1 < source.length() && source[pos+1] == '&') {
|
||||
tokens.push_back({TokenType::AND, "&&", line}); pos+=2; continue;
|
||||
}
|
||||
if (current == '|' && pos+1 < source.length() && source[pos+1] == '|') {
|
||||
tokens.push_back({TokenType::OR, "||", line}); pos+=2; continue;
|
||||
}
|
||||
|
||||
switch (current) {
|
||||
case '+': tokens.push_back({TokenType::PLUS, "+", line}); break;
|
||||
@@ -84,8 +125,11 @@ std::vector<Token> Lexer::tokenize() {
|
||||
case ';': tokens.push_back({TokenType::SEMICOLON, ";", line}); break;
|
||||
case ',': tokens.push_back({TokenType::COMMA, ",", line}); break;
|
||||
case '=': tokens.push_back({TokenType::ASSIGN, "=", line}); break;
|
||||
case '.': tokens.push_back({TokenType::DOT, ".", line}); break;
|
||||
case '!': tokens.push_back({TokenType::NOT, "!", line}); break;
|
||||
case '<': tokens.push_back({TokenType::LT, "<", line}); break;
|
||||
case '>': tokens.push_back({TokenType::GT, ">", line}); break;
|
||||
case ':': tokens.push_back({TokenType::COLON, ":", line}); break;
|
||||
default:
|
||||
throw std::runtime_error(std::string("Runtime Error: Unknown character '") + current + "' at line " + std::to_string(line));
|
||||
}
|
||||
|
||||
+135
-17
@@ -29,6 +29,12 @@ std::unique_ptr<Node> Parser::primary() {
|
||||
);
|
||||
}
|
||||
|
||||
// 1.5. УНАРНЫЙ НЕ (Обработка !condition)
|
||||
if (tokens[pos].type == TokenType::NOT) {
|
||||
consume(TokenType::NOT);
|
||||
return std::make_unique<UnaryOpNode>("!", primary());
|
||||
}
|
||||
|
||||
// 2. Числа
|
||||
if (tokens[pos].type == TokenType::NUMBER) {
|
||||
return std::make_unique<NumberNode>(consume(TokenType::NUMBER).value);
|
||||
@@ -39,7 +45,17 @@ std::unique_ptr<Node> Parser::primary() {
|
||||
return std::make_unique<StringNode>(consume(TokenType::STRING_LITERAL).value);
|
||||
}
|
||||
|
||||
// 4. Переменные и Вызовы функций
|
||||
// 4. Boolean литералы
|
||||
if (tokens[pos].type == TokenType::TRUE_KW) {
|
||||
consume(TokenType::TRUE_KW);
|
||||
return std::make_unique<BoolNode>(true);
|
||||
}
|
||||
if (tokens[pos].type == TokenType::FALSE_KW) {
|
||||
consume(TokenType::FALSE_KW);
|
||||
return std::make_unique<BoolNode>(false);
|
||||
}
|
||||
|
||||
// 5. Переменные и Вызовы функций
|
||||
if (tokens[pos].type == TokenType::IDENTIFIER) {
|
||||
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||
|
||||
@@ -57,6 +73,13 @@ std::unique_ptr<Node> Parser::primary() {
|
||||
consume(TokenType::RPAREN);
|
||||
return std::make_unique<FuncCallNode>(name, std::move(args));
|
||||
}
|
||||
|
||||
// Post increment operator (expression context: x = i++ + 5)
|
||||
if (tokens[pos].type == TokenType::INC) {
|
||||
consume(TokenType::INC);
|
||||
return std::make_unique<PostIncNode>(name);
|
||||
}
|
||||
|
||||
// Иначе это просто доступ к переменной
|
||||
return std::make_unique<VarAccessNode>(name);
|
||||
}
|
||||
@@ -74,6 +97,13 @@ std::unique_ptr<Node> Parser::primary() {
|
||||
return std::make_unique<InputNode>();
|
||||
}
|
||||
|
||||
if (tokens[pos].type == TokenType::READ_FILE) {
|
||||
consume(TokenType::READ_FILE); consume(TokenType::LPAREN);
|
||||
auto filename = expression();
|
||||
consume(TokenType::RPAREN);
|
||||
return std::make_unique<ReadFileNode>(std::move(filename));
|
||||
}
|
||||
|
||||
if (tokens[pos].type == TokenType::LPAREN) {
|
||||
consume(TokenType::LPAREN);
|
||||
auto n = expression();
|
||||
@@ -108,7 +138,25 @@ std::unique_ptr<Node> Parser::comparison() {
|
||||
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 std::make_unique<BinOpNode>(op[0], std::move(node), expression());
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> Parser::logicalAnd() {
|
||||
auto node = comparison();
|
||||
while (tokens[pos].type == TokenType::AND) {
|
||||
std::string op = tokens[pos].value; pos++;
|
||||
node = std::make_unique<BinOpNode>(op[0], std::move(node), comparison());
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> Parser::logicalOr() {
|
||||
auto node = logicalAnd();
|
||||
while (tokens[pos].type == TokenType::OR) {
|
||||
std::string op = tokens[pos].value; pos++;
|
||||
node = std::make_unique<BinOpNode>(op[0], std::move(node), logicalAnd());
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@@ -144,20 +192,25 @@ void processInclude(std::string filename, Context& ctx, std::string currentFile)
|
||||
|
||||
Parser parser(tokens);
|
||||
|
||||
// 1. Копируем текущую память внутрь include, чтобы он видел глобальные переменные
|
||||
// Копируем контекст и устанавливаем режим импорта
|
||||
parser.globalContext = ctx;
|
||||
parser.currentFile = fullPath;
|
||||
|
||||
// ВАЖНО: Мы убрали parser.importMode = true;
|
||||
// Теперь код внутри lib.fox (например, print) БУДЕТ выполняться.
|
||||
parser.importMode = false;
|
||||
parser.importMode = true; // Только парсим функции, не выполняем код
|
||||
|
||||
parser.run();
|
||||
|
||||
// 2. Возвращаем память обратно (функции из lib.fox появятся в main)
|
||||
// Возвращаем обновленный контекст с новыми функциями
|
||||
ctx = parser.globalContext;
|
||||
}
|
||||
|
||||
void Parser::processUsing(const std::string& libName, Context& ctx) {
|
||||
if (libName == "net.fox" || libName == "net") {
|
||||
// Добавляем сетевые функции в контекст
|
||||
// Эти функции будут доступны как встроенные
|
||||
// Реализация уже есть в FuncCallNode
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> Parser::statement() {
|
||||
if (tokens[pos].type == TokenType::INCLUDE) {
|
||||
consume(TokenType::INCLUDE); consume(TokenType::LPAREN);
|
||||
@@ -167,11 +220,27 @@ std::unique_ptr<Node> Parser::statement() {
|
||||
return std::make_unique<BlockNode>();
|
||||
}
|
||||
|
||||
if (tokens[pos].type == TokenType::USING) {
|
||||
consume(TokenType::USING);
|
||||
std::string libName = consume(TokenType::IDENTIFIER).value;
|
||||
if (pos < tokens.size() && tokens[pos].type == TokenType::DOT) {
|
||||
consume(TokenType::DOT);
|
||||
if (pos < tokens.size() && tokens[pos].type == TokenType::IDENTIFIER) {
|
||||
libName += "." + consume(TokenType::IDENTIFIER).value;
|
||||
}
|
||||
}
|
||||
consume(TokenType::SEMICOLON);
|
||||
processUsing(libName, globalContext);
|
||||
return std::make_unique<UsingNode>(libName);
|
||||
}
|
||||
|
||||
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::FLOAT_KW) type = "float";
|
||||
else if (tokens[pos].type == TokenType::STRING_KW) type = "string";
|
||||
else if (tokens[pos].type == TokenType::BOOL_KW) type = "bool";
|
||||
pos++;
|
||||
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||
consume(TokenType::ASSIGN);
|
||||
@@ -180,7 +249,7 @@ std::unique_ptr<Node> Parser::statement() {
|
||||
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) {
|
||||
if (tokens[pos].type == TokenType::INT_KW || tokens[pos].type == TokenType::FLOAT_KW || tokens[pos].type == TokenType::STRING_KW || tokens[pos].type == TokenType::BOOL_KW || tokens[pos].type == TokenType::VOID_KW) {
|
||||
std::string type = tokens[pos].value; pos++;
|
||||
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||
|
||||
@@ -214,21 +283,56 @@ std::unique_ptr<Node> Parser::statement() {
|
||||
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 cond = logicalOr(); 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::FOR) {
|
||||
consume(TokenType::FOR); consume(TokenType::LPAREN);
|
||||
|
||||
std::unique_ptr<Node> init = nullptr;
|
||||
if (tokens[pos].type != TokenType::SEMICOLON) {
|
||||
init = statement();
|
||||
} else {
|
||||
consume(TokenType::SEMICOLON);
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> cond = nullptr;
|
||||
if (tokens[pos].type != TokenType::SEMICOLON) {
|
||||
cond = comparison();
|
||||
} else {
|
||||
cond = std::make_unique<NumberNode>("1");
|
||||
}
|
||||
consume(TokenType::SEMICOLON);
|
||||
|
||||
std::unique_ptr<Node> step = nullptr;
|
||||
if (tokens[pos].type != TokenType::RPAREN) {
|
||||
if (tokens[pos].type == TokenType::IDENTIFIER && tokens[pos+1].type == TokenType::ASSIGN) {
|
||||
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||
consume(TokenType::ASSIGN);
|
||||
auto expr = expression();
|
||||
step = std::make_unique<VarAssignNode>(name, std::move(expr));
|
||||
} else {
|
||||
step = expression();
|
||||
}
|
||||
}
|
||||
consume(TokenType::RPAREN);
|
||||
|
||||
auto body = parseBlock();
|
||||
if (importMode) return std::make_unique<BlockNode>();
|
||||
return std::make_unique<ForNode>(std::move(init), std::move(cond), std::move(step), std::move(body));
|
||||
}
|
||||
|
||||
if (tokens[pos].type == TokenType::IF) {
|
||||
consume(TokenType::IF); consume(TokenType::LPAREN);
|
||||
auto cond = comparison(); consume(TokenType::RPAREN);
|
||||
auto cond = logicalOr(); consume(TokenType::RPAREN);
|
||||
auto thenB = parseBlock();
|
||||
std::unique_ptr<Node> elseB = nullptr;
|
||||
if (tokens[pos].type == TokenType::ELSE) {
|
||||
@@ -244,21 +348,24 @@ std::unique_ptr<Node> Parser::statement() {
|
||||
auto expr = expression();
|
||||
consume(TokenType::RPAREN); consume(TokenType::SEMICOLON);
|
||||
if (importMode) return std::make_unique<BlockNode>();
|
||||
return std::make_unique<PrintNode>(std::move(expr));
|
||||
std::vector<std::unique_ptr<Node>> args;
|
||||
args.push_back(std::move(expr));
|
||||
return std::make_unique<FuncCallNode>("print", std::move(args));
|
||||
}
|
||||
|
||||
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>();
|
||||
std::vector<std::unique_ptr<Node>> args;
|
||||
return std::make_unique<FuncCallNode>("fox", std::move(args));
|
||||
}
|
||||
|
||||
if (tokens[pos].type == TokenType::ARRAY) {
|
||||
consume(TokenType::ARRAY);
|
||||
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||
auto size = expression();
|
||||
int size = std::stoi(consume(TokenType::NUMBER).value);
|
||||
consume(TokenType::SEMICOLON);
|
||||
return std::make_unique<ArrayDeclNode>(name, std::move(size));
|
||||
return std::make_unique<ArrayDeclNode>(name, size);
|
||||
}
|
||||
|
||||
if (tokens[pos].type == TokenType::SET) {
|
||||
@@ -278,7 +385,7 @@ std::unique_ptr<Node> Parser::statement() {
|
||||
auto expr = expression();
|
||||
consume(TokenType::SEMICOLON);
|
||||
if (importMode) return std::make_unique<BlockNode>();
|
||||
return std::make_unique<AssignNode>(name, std::move(expr));
|
||||
return std::make_unique<VarAssignNode>(name, std::move(expr));
|
||||
}
|
||||
if (tokens[pos+1].type == TokenType::LPAREN) {
|
||||
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||
@@ -295,6 +402,17 @@ std::unique_ptr<Node> Parser::statement() {
|
||||
if (importMode) return std::make_unique<BlockNode>();
|
||||
return std::make_unique<FuncCallNode>(name, std::move(args));
|
||||
}
|
||||
if (tokens[pos+1].type == TokenType::INC) {
|
||||
std::string name = consume(TokenType::IDENTIFIER).value;
|
||||
consume(TokenType::INC);
|
||||
consume(TokenType::SEMICOLON);
|
||||
if (importMode) return std::make_unique<BlockNode>();
|
||||
return std::make_unique<PostIncNode>(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (tokens[pos].type == TokenType::LBRACE) {
|
||||
return parseBlock();
|
||||
}
|
||||
|
||||
throw std::runtime_error("Unknown statement " + tokens[pos].value);
|
||||
|
||||
+11
-8
@@ -3,29 +3,32 @@
|
||||
#include "AST.h"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <string> // Не забудь
|
||||
#include <string>
|
||||
|
||||
class Parser {
|
||||
std::vector<Token> tokens;
|
||||
size_t pos = 0;
|
||||
|
||||
|
||||
public:
|
||||
Context globalContext;
|
||||
|
||||
// НОВЫЕ ПОЛЯ ДЛЯ ПУТЕЙ И ИМПОРТА
|
||||
|
||||
|
||||
std::string currentFile; // Путь к файлу, который сейчас парсится
|
||||
bool importMode = false; // Если true, то выполняем только объявления (vars/funcs)
|
||||
|
||||
Parser(std::vector<Token> t);
|
||||
|
||||
|
||||
Token consume(TokenType type);
|
||||
std::unique_ptr<Node> primary();
|
||||
std::unique_ptr<Node> multiplication();
|
||||
std::unique_ptr<Node> expression();
|
||||
std::unique_ptr<Node> comparison();
|
||||
std::unique_ptr<Node> logicalAnd();
|
||||
std::unique_ptr<Node> logicalOr();
|
||||
|
||||
std::unique_ptr<Node> statement();
|
||||
std::unique_ptr<BlockNode> parseBlock();
|
||||
|
||||
void run();
|
||||
};
|
||||
|
||||
void processUsing(const std::string& libName, Context& ctx);
|
||||
void run();
|
||||
};
|
||||
|
||||
+8
-7
@@ -3,17 +3,18 @@
|
||||
|
||||
enum class TokenType {
|
||||
NUMBER, STRING_LITERAL,
|
||||
PLUS, MINUS, STAR, SLASH, MOD,
|
||||
PLUS, MINUS, STAR, SLASH, MOD, INC,
|
||||
LPAREN, RPAREN, LBRACE, RBRACE, LBRACKET, RBRACKET,
|
||||
SEMICOLON, COMMA, ASSIGN,
|
||||
EQ, NEQ, LT, GT,
|
||||
SEMICOLON, COMMA, ASSIGN, DOT, COLON,
|
||||
EQ, NEQ, LT, GT, AND, OR, NOT,
|
||||
|
||||
// Ключевые слова
|
||||
PRINT, INPUT, ROUND, RANDOM, FOX,
|
||||
INT_KW, STRING_KW, VOID_KW, // Типы данных
|
||||
WHILE, IF, ELSE,
|
||||
PRINT, INPUT, ROUND, RANDOM, FOX, READ_FILE, JSON_GET, STR_CONTAINS, STR_TO_INT,
|
||||
INT_KW, FLOAT_KW, STRING_KW, BOOL_KW, VOID_KW, // Типы данных
|
||||
TRUE_KW, FALSE_KW, // Boolean литералы
|
||||
WHILE, FOR, IF, ELSE,
|
||||
ARRAY, SET, GET, SIZE,
|
||||
INCLUDE,
|
||||
INCLUDE, USING, // Подключение файлов
|
||||
|
||||
// НОВЫЕ: возврат и глобальные
|
||||
RETURN, GLOBAL,
|
||||
|
||||
Executable
BIN
Binary file not shown.
+195
@@ -0,0 +1,195 @@
|
||||
// net.fox - Продвинутая сетевая библиотека FoxLang
|
||||
// Подключается через: include("net.fox");
|
||||
|
||||
// === БАЗОВЫЕ СЕТЕВЫЕ ФУНКЦИИ (ЗАГЛУШКИ) ===
|
||||
|
||||
// HTTP GET запрос (встроенная функция)
|
||||
// string http_get(string url) - реализована в интерпретаторе
|
||||
|
||||
// HTTP POST запрос (заглушка)
|
||||
string http_post(string url, string data) {
|
||||
print("http_post not implemented yet");
|
||||
return "";
|
||||
}
|
||||
|
||||
// TCP сокет функции (заглушки)
|
||||
int tcp_socket() {
|
||||
print("tcp_socket not implemented yet");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool tcp_connect(int socket, string host, int port) {
|
||||
print("tcp_connect not implemented yet");
|
||||
return false;
|
||||
}
|
||||
|
||||
void tcp_send(int socket, string message) {
|
||||
print("tcp_send not implemented yet");
|
||||
}
|
||||
|
||||
string tcp_receive(int socket, int size) {
|
||||
print("tcp_receive not implemented yet");
|
||||
return "";
|
||||
}
|
||||
|
||||
void tcp_close(int socket) {
|
||||
print("tcp_close not implemented yet");
|
||||
}
|
||||
|
||||
// === HTTP КЛИЕНТ С УДОБНЫМИ МЕТОДАМИ ===
|
||||
|
||||
// Простой GET с автоматической обработкой ошибок
|
||||
string http_get_simple(string url) {
|
||||
string response = http_get(url);
|
||||
if (response == "") {
|
||||
print("ERROR: Failed to fetch " + url);
|
||||
return "{}";
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
// POST с JSON данными
|
||||
string post_json(string url, string json_data) {
|
||||
string response = http_post(url, json_data);
|
||||
if (response == "") {
|
||||
print("ERROR: Failed to POST to " + url);
|
||||
return "{}";
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
// Загрузка файла по URL
|
||||
bool download_file(string url, string filename) {
|
||||
string content = http_get_simple(url);
|
||||
if (content == "{}") {
|
||||
return false;
|
||||
}
|
||||
// Здесь должна быть функция записи в файл
|
||||
print("Downloaded " + filename + " from " + url);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Проверка доступности сервера
|
||||
bool ping(string host, int port) {
|
||||
int socket = tcp_socket();
|
||||
bool connected = tcp_connect(socket, host, port);
|
||||
if (connected) {
|
||||
tcp_close(socket);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// === TCP КЛИЕНТ С АВТОМАТИЧЕСКИМ УПРАВЛЕНИЕМ ===
|
||||
|
||||
// Простая отправка сообщения с автозакрытием
|
||||
string send_message(string host, int port, string message) {
|
||||
int socket = tcp_socket();
|
||||
if (!tcp_connect(socket, host, port)) {
|
||||
print("ERROR: Cannot connect to " + host + ":" + port);
|
||||
return "";
|
||||
}
|
||||
|
||||
tcp_send(socket, message);
|
||||
string response = tcp_receive(socket, 1024);
|
||||
tcp_close(socket);
|
||||
return response;
|
||||
}
|
||||
|
||||
// Чат-клиент (отправка и получение)
|
||||
void chat_session(string host, int port) {
|
||||
int socket = tcp_socket();
|
||||
if (!tcp_connect(socket, host, port)) {
|
||||
print("ERROR: Cannot connect to chat server");
|
||||
return;
|
||||
}
|
||||
|
||||
print("Connected to chat! Type 'quit' to exit");
|
||||
string message = "";
|
||||
|
||||
while (message != "quit") {
|
||||
print("You: ");
|
||||
message = input();
|
||||
if (message != "quit") {
|
||||
tcp_send(socket, message);
|
||||
string response = tcp_receive(socket, 1024);
|
||||
print("Server: " + response);
|
||||
}
|
||||
}
|
||||
|
||||
tcp_close(socket);
|
||||
print("Chat session ended");
|
||||
}
|
||||
|
||||
// === API HELPERS ===
|
||||
|
||||
// REST API клиент
|
||||
string api_get(string base_url, string endpoint) {
|
||||
return http_get_simple(base_url + endpoint);
|
||||
}
|
||||
|
||||
string api_post(string base_url, string endpoint, string data) {
|
||||
return post_json(base_url + endpoint, data);
|
||||
}
|
||||
|
||||
// Простой JSON парсер (базовый)
|
||||
string json_get_value(string json, string key) {
|
||||
// Простейший парсер для демонстрации
|
||||
string search = "\"" + key + "\":";
|
||||
// Здесь должна быть реальная логика парсинга
|
||||
return "value";
|
||||
}
|
||||
|
||||
// === УТИЛИТЫ ===
|
||||
|
||||
// Проверка интернет-соединения
|
||||
bool is_online() {
|
||||
return ping("8.8.8.8", 53); // Google DNS
|
||||
}
|
||||
|
||||
// Получение публичного IP
|
||||
string get_public_ip() {
|
||||
return http_get_simple("https://api.ipify.org");
|
||||
}
|
||||
|
||||
// Отправка webhook
|
||||
bool send_webhook(string url, string message) {
|
||||
string json = "{\"text\":\"" + message + "\"}";
|
||||
string response = post_json(url, json);
|
||||
return response != "{}";
|
||||
}
|
||||
|
||||
// === ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ ===
|
||||
|
||||
void demo_http() {
|
||||
print("=== HTTP Demo ===");
|
||||
|
||||
if (is_online()) {
|
||||
print("Internet connection: OK");
|
||||
string ip = get_public_ip();
|
||||
print("Your IP: " + ip);
|
||||
|
||||
string weather = http_get_simple("https://api.weather.com/current");
|
||||
print("Weather data: " + weather);
|
||||
} else {
|
||||
print("No internet connection");
|
||||
}
|
||||
}
|
||||
|
||||
void demo_tcp() {
|
||||
print("=== TCP Demo ===");
|
||||
|
||||
if (ping("localhost", 8080)) {
|
||||
print("Server is running on localhost:8080");
|
||||
string response = send_message("localhost", 8080, "Hello Server!");
|
||||
print("Server response: " + response);
|
||||
} else {
|
||||
print("Server not available");
|
||||
}
|
||||
}
|
||||
|
||||
// Запуск демо
|
||||
void net_demo() {
|
||||
demo_http();
|
||||
demo_tcp();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// net.fox - Простая сетевая библиотека FoxLang
|
||||
|
||||
// HTTP POST запрос (заглушка)
|
||||
string http_post(string url, string data) {
|
||||
print("http_post not implemented yet");
|
||||
return "";
|
||||
}
|
||||
|
||||
// Простой GET с обработкой ошибок
|
||||
string get_url(string url) {
|
||||
string response = http_get(url);
|
||||
if (response == "") {
|
||||
print("ERROR: Failed to fetch " + url);
|
||||
return "{}";
|
||||
}
|
||||
return response;
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
# 🦊 FoxLang Telegram Bot
|
||||
|
||||
Telegram бот, написанный на чистом FoxLang для выполнения FoxLang кода!
|
||||
|
||||
## 📁 Структура
|
||||
|
||||
```
|
||||
telegram_bot/
|
||||
├── main.fox # Основной файл бота
|
||||
├── bot_api.fox # API для работы с Telegram
|
||||
├── code_executor.fox # Выполнение пользовательского кода
|
||||
├── token.txt # Токен бота (настройте!)
|
||||
├── run_bot.sh # Скрипт запуска
|
||||
└── README.md # Эта документация
|
||||
```
|
||||
|
||||
## 🚀 Быстрый старт
|
||||
|
||||
### 1. Создание бота
|
||||
|
||||
1. Найдите @BotFather в Telegram
|
||||
2. Отправьте `/newbot`
|
||||
3. Следуйте инструкциям
|
||||
4. Получите токен вида: `123456789:ABCdefGHIjklMNOpqrsTUVwxyz`
|
||||
|
||||
### 2. Настройка
|
||||
|
||||
Отредактируйте файл `token.txt`:
|
||||
```bash
|
||||
nano token.txt
|
||||
```
|
||||
|
||||
Замените `YOUR_BOT_TOKEN_HERE` на ваш реальный токен.
|
||||
|
||||
### 3. Запуск
|
||||
|
||||
```bash
|
||||
./run_bot.sh
|
||||
```
|
||||
|
||||
Или вручную:
|
||||
```bash
|
||||
../src/foxlang main.fox
|
||||
```
|
||||
|
||||
## 🤖 Возможности бота
|
||||
|
||||
### Команды
|
||||
- `/start` - Приветствие и инструкции
|
||||
- `/help` - Справка по FoxLang
|
||||
- `/example` - Пример кода
|
||||
|
||||
### Выполнение кода
|
||||
Отправьте боту любой FoxLang код:
|
||||
|
||||
```cpp
|
||||
int x = 10;
|
||||
int y = 20;
|
||||
print("Sum: " + (x + y));
|
||||
fox();
|
||||
```
|
||||
|
||||
### Безопасность
|
||||
- Запрет на `include` в пользовательском коде
|
||||
- Ограничение длины кода (10000 символов)
|
||||
- Ограничение вывода (4000 символов)
|
||||
- Запрет системных вызовов
|
||||
|
||||
## 🔧 Архитектура
|
||||
|
||||
### main.fox
|
||||
- Основной цикл бота
|
||||
- Обработка команд
|
||||
- Инициализация
|
||||
|
||||
### bot_api.fox
|
||||
- HTTP запросы к Telegram API
|
||||
- Парсинг JSON ответов
|
||||
- Отправка сообщений
|
||||
|
||||
### code_executor.fox
|
||||
- Выполнение пользовательского кода
|
||||
- Проверки безопасности
|
||||
- Ограничения вывода
|
||||
|
||||
## 🛠 Разработка
|
||||
|
||||
### Добавление новых команд
|
||||
|
||||
В `main.fox`, функция `handle_command()`:
|
||||
|
||||
```cpp
|
||||
if (command == "/mycommand") {
|
||||
return "My custom response";
|
||||
}
|
||||
```
|
||||
|
||||
### Расширение API
|
||||
|
||||
В `bot_api.fox` можно добавить новые методы Telegram API:
|
||||
|
||||
```cpp
|
||||
void send_photo(string token, string chat_id, string photo_url) {
|
||||
// Реализация отправки фото
|
||||
}
|
||||
```
|
||||
|
||||
### Улучшение безопасности
|
||||
|
||||
В `code_executor.fox`, функция `is_safe_code()`:
|
||||
|
||||
```cpp
|
||||
// Добавить новые запрещенные слова
|
||||
set(forbidden_words, 5, "dangerous_function");
|
||||
```
|
||||
|
||||
## 📝 Примечания
|
||||
|
||||
- Бот написан на чистом FoxLang
|
||||
- Использует заглушки для системных функций
|
||||
- Для полной функциональности нужны встроенные функции:
|
||||
- `http_get()` - HTTP запросы
|
||||
- `read_file()` / `write_file()` - работа с файлами
|
||||
- `sleep()` - задержки
|
||||
- Строковые функции (`contains`, `replace_all`, etc.)
|
||||
|
||||
## 🔮 Будущие улучшения
|
||||
|
||||
- [ ] Полный JSON парсер
|
||||
- [ ] Файловые операции
|
||||
- [ ] HTTP клиент
|
||||
- [ ] Строковые функции
|
||||
- [ ] Логирование
|
||||
- [ ] Конфигурация через файл
|
||||
- [ ] Поддержка inline клавиатур
|
||||
- [ ] Сохранение состояния пользователей
|
||||
|
||||
## 📄 Лицензия
|
||||
|
||||
MIT License - используйте свободно!
|
||||
@@ -0,0 +1,122 @@
|
||||
// bot_api.fox - API для работы с Telegram Bot API
|
||||
|
||||
// Глобальные переменные для API
|
||||
string api_base_url = "https://api.telegram.org/bot";
|
||||
|
||||
// HTTP запрос к Telegram API
|
||||
string telegram_request(string token, string method, string params) {
|
||||
string url = api_base_url + token + "/" + method;
|
||||
if (params != "") {
|
||||
url = url + "?" + params;
|
||||
}
|
||||
return http_get(url);
|
||||
}
|
||||
|
||||
// Отправка сообщения
|
||||
void send_message(string token, string chat_id, string text) {
|
||||
string params = "chat_id=" + chat_id + "&text=" + url_encode(text);
|
||||
string response = telegram_request(token, "sendMessage", params);
|
||||
print("Message sent to " + chat_id);
|
||||
}
|
||||
|
||||
// Получение обновлений
|
||||
string get_updates(string token, int offset) {
|
||||
string params = "offset=" + (offset + 1) + "&timeout=10";
|
||||
return telegram_request(token, "getUpdates", params);
|
||||
}
|
||||
|
||||
// Обработка обновлений
|
||||
void process_updates(string json_response) {
|
||||
print("=== PROCESS_UPDATES START ===");
|
||||
|
||||
// Извлекаем данные последнего сообщения
|
||||
string chat_id = extract_chat_id(json_response);
|
||||
print("Extracted chat_id: " + chat_id);
|
||||
|
||||
string text = extract_message_text(json_response);
|
||||
print("Extracted text: " + text);
|
||||
|
||||
string username = extract_username(json_response);
|
||||
int update_id = extract_update_id(json_response);
|
||||
|
||||
print("Chat ID: " + chat_id + ", Text: " + text + ", Update ID: " + update_id);
|
||||
|
||||
// Проверяем, что это новое сообщение
|
||||
if (update_id > last_update_id && chat_id != "" && text != "") {
|
||||
print("Processing new message...");
|
||||
|
||||
// Обрабатываем сообщение
|
||||
string response = handle_message(text, username);
|
||||
print("Sending response: " + response);
|
||||
|
||||
// Отправляем ответ
|
||||
send_message(bot_token, chat_id, response);
|
||||
|
||||
// Обновляем offset
|
||||
last_update_id = update_id;
|
||||
print("Updated last_update_id to: " + last_update_id);
|
||||
} else {
|
||||
print("Skipping old message or empty data");
|
||||
}
|
||||
|
||||
print("=== PROCESS_UPDATES END ===");
|
||||
}
|
||||
|
||||
// Вспомогательные функции для парсинга JSON
|
||||
string extract_chat_id(string json) {
|
||||
return json_get(json, "chat_id");
|
||||
}
|
||||
|
||||
string extract_message_text(string json) {
|
||||
return json_get(json, "text");
|
||||
}
|
||||
|
||||
string extract_username(string json) {
|
||||
return "SkrinVex"; // Пока заглушка
|
||||
}
|
||||
|
||||
int extract_update_id(string json) {
|
||||
string id_str = json_get(json, "update_id");
|
||||
if (id_str != "") {
|
||||
return str_to_int(id_str);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// URL кодирование
|
||||
string url_encode(string text) {
|
||||
// Простая замена специальных символов
|
||||
string result = text;
|
||||
result = replace_all(result, " ", "%20");
|
||||
result = replace_all(result, "\n", "%0A");
|
||||
return result;
|
||||
}
|
||||
|
||||
// Проверка содержания строки - удалена, используем встроенную contains()
|
||||
|
||||
// Замена всех вхождений
|
||||
string replace_all(string text, string from, string to) {
|
||||
// Заглушка - в реальности нужна реализация
|
||||
return text;
|
||||
}
|
||||
|
||||
// Проверка начала строки
|
||||
bool starts_with(string text, string prefix) {
|
||||
// Заглушка - в реальности нужна реализация
|
||||
if (text == "/start" || text == "/help" || text == "/example") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Чтение токена
|
||||
string read_token() {
|
||||
// Читаем токен из файла token.txt
|
||||
return read_file("telegram_bot/token.txt");
|
||||
}
|
||||
|
||||
// Пауза (заглушка)
|
||||
void sleep(int seconds) {
|
||||
// В реальности нужна реализация задержки
|
||||
print("Sleeping for " + seconds + " seconds...");
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// code_executor.fox - Выполнение FoxLang кода
|
||||
|
||||
// Выполнение FoxLang кода
|
||||
string execute_foxlang_code(string code) {
|
||||
print("Executing FoxLang code:");
|
||||
print(code);
|
||||
|
||||
// Проверяем безопасность кода
|
||||
if (!is_safe_code(code)) {
|
||||
return "❌ Error: Unsafe code detected!";
|
||||
}
|
||||
|
||||
// Сохраняем код во временный файл
|
||||
string temp_file = "temp_code.fox";
|
||||
if (!write_code_to_file(temp_file, code)) {
|
||||
return "❌ Error: Cannot write temporary file";
|
||||
}
|
||||
|
||||
// Выполняем код через интерпретатор
|
||||
string result = execute_file(temp_file);
|
||||
|
||||
// Удаляем временный файл
|
||||
delete_file(temp_file);
|
||||
|
||||
// Ограничиваем длину результата
|
||||
if (length(result) > 4000) {
|
||||
result = substring(result, 0, 4000) + "\n... (output truncated)";
|
||||
}
|
||||
|
||||
if (result == "") {
|
||||
return "✅ Code executed successfully!";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Проверка безопасности кода
|
||||
bool is_safe_code(string code) {
|
||||
// Запрещенные операции
|
||||
array forbidden_words 5;
|
||||
set(forbidden_words, 0, "include"); // Запрещаем include в пользовательском коде
|
||||
set(forbidden_words, 1, "system"); // Запрещаем системные вызовы
|
||||
set(forbidden_words, 2, "exec"); // Запрещаем выполнение команд
|
||||
set(forbidden_words, 3, "file"); // Запрещаем работу с файлами
|
||||
set(forbidden_words, 4, "network"); // Запрещаем сетевые операции
|
||||
|
||||
int i = 0;
|
||||
while (i < 5) {
|
||||
string forbidden = get(forbidden_words, i);
|
||||
if (contains(code, forbidden)) {
|
||||
print("Forbidden word detected: " + forbidden);
|
||||
return false;
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
// Проверяем длину кода
|
||||
if (length(code) > 10000) {
|
||||
print("Code too long");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Запись кода в файл
|
||||
bool write_code_to_file(string filename, string code) {
|
||||
// Заглушка - в реальности нужна реализация записи файлов
|
||||
print("Writing code to " + filename);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Выполнение файла
|
||||
string execute_file(string filename) {
|
||||
// Заглушка - в реальности нужен вызов интерпретатора
|
||||
print("Executing file: " + filename);
|
||||
|
||||
// Симуляция выполнения простого кода
|
||||
return simulate_execution(filename);
|
||||
}
|
||||
|
||||
// Симуляция выполнения
|
||||
string simulate_execution(string filename) {
|
||||
// Простая симуляция для демонстрации
|
||||
return "Hello from FoxLang!\nCode executed successfully!";
|
||||
}
|
||||
|
||||
// Удаление файла
|
||||
void delete_file(string filename) {
|
||||
// Заглушка - в реальности нужна реализация удаления файлов
|
||||
print("Deleting file: " + filename);
|
||||
}
|
||||
|
||||
// Получение длины строки
|
||||
int length(string text) {
|
||||
// Заглушка - в реальности нужна реализация
|
||||
return 100; // Примерная длина
|
||||
}
|
||||
|
||||
// Получение подстроки
|
||||
string substring(string text, int start, int end) {
|
||||
// Заглушка - в реальности нужна реализация
|
||||
return text + "...";
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// FoxLang Telegram Bot - main.fox
|
||||
// Основной файл бота на чистом FoxLang
|
||||
|
||||
include("bot_api.fox");
|
||||
include("code_executor.fox");
|
||||
include("../src/net_simple.fox");
|
||||
|
||||
// Конфигурация бота
|
||||
string bot_token = "";
|
||||
int last_update_id = 837067997; // Обновляем до последнего ID
|
||||
|
||||
// Инициализация бота
|
||||
void init_bot(string token) {
|
||||
bot_token = token;
|
||||
print("🦊 FoxLang Telegram Bot started!");
|
||||
print("Token: " + token);
|
||||
}
|
||||
|
||||
// Обработка команд
|
||||
string handle_command(string command, string username) {
|
||||
if (command == "/start") {
|
||||
return "🦊 Welcome to FoxLang Bot!\n\nSend me FoxLang code and I'll execute it!\n\nExample:\nint x = 5;\nprint(\"Result: \" + x);\nfox();\n\nCommands:\n/help - Show help\n/example - Show example";
|
||||
}
|
||||
|
||||
if (command == "/help") {
|
||||
return "🦊 FoxLang Bot Help\n\nSupported features:\n• Variables: int, float, string, bool\n• Functions: Custom functions\n• Arrays: array manipulation\n• Control flow: if/else, while\n• Math: +, -, *, /, %\n• Built-in: print(), fox()\n\nJust send your FoxLang code!";
|
||||
}
|
||||
|
||||
if (command == "/example") {
|
||||
return "🦊 FoxLang Example:\n\nint add(int a, int b) {\n return a + b;\n}\n\nstring name = \"FoxLang\";\nint result = add(10, 20);\nprint(\"Hello from \" + name + \"!\");\nprint(\"Result: \" + result);\nfox();";
|
||||
}
|
||||
|
||||
return "Unknown command. Use /help for help.";
|
||||
}
|
||||
|
||||
// Обработка сообщения
|
||||
string handle_message(string text, string username) {
|
||||
print("Message from " + username + ": " + text);
|
||||
|
||||
// Проверяем команды
|
||||
if (starts_with(text, "/")) {
|
||||
return handle_command(text, username);
|
||||
}
|
||||
|
||||
// Выполняем FoxLang код
|
||||
return execute_foxlang_code(text);
|
||||
}
|
||||
|
||||
// Основной цикл бота
|
||||
void run_bot() {
|
||||
while (true) {
|
||||
print("Checking for updates...");
|
||||
// Получаем обновления
|
||||
string updates = get_updates(bot_token, last_update_id);
|
||||
|
||||
if (updates != "") {
|
||||
print("Got updates: " + updates);
|
||||
// Обрабатываем каждое сообщение
|
||||
print("About to call process_updates...");
|
||||
process_updates(updates);
|
||||
print("process_updates completed");
|
||||
}
|
||||
|
||||
// Небольшая пауза
|
||||
// sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Точка входа
|
||||
void main() {
|
||||
print("🦊 FoxLang Telegram Bot");
|
||||
print("======================");
|
||||
|
||||
// Читаем токен из файла или переменной окружения
|
||||
string token = read_token();
|
||||
|
||||
if (token == "") {
|
||||
print("❌ Error: Bot token not found!");
|
||||
print("Create token.txt file with your bot token");
|
||||
return;
|
||||
}
|
||||
|
||||
init_bot(token);
|
||||
run_bot();
|
||||
}
|
||||
|
||||
main();
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Скрипт запуска FoxLang Telegram Bot
|
||||
|
||||
echo "🦊 FoxLang Telegram Bot"
|
||||
echo "======================="
|
||||
|
||||
# Проверяем наличие интерпретатора
|
||||
if [ ! -f "../src/foxlang" ]; then
|
||||
echo "❌ Error: FoxLang interpreter not found!"
|
||||
echo "Please build it first:"
|
||||
echo " cd ../src && g++ main.cpp Lexer.cpp Parser.cpp -o foxlang"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Проверяем наличие токена
|
||||
if [ ! -f "token.txt" ] || [ ! -s "token.txt" ] || grep -q "YOUR_BOT_TOKEN_HERE" token.txt; then
|
||||
echo "❌ Error: Bot token not configured!"
|
||||
echo ""
|
||||
echo "Please configure your bot token:"
|
||||
echo "1. Get token from @BotFather in Telegram"
|
||||
echo "2. Edit token.txt file"
|
||||
echo "3. Replace YOUR_BOT_TOKEN_HERE with your actual token"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Starting FoxLang Telegram Bot..."
|
||||
echo ""
|
||||
|
||||
# Запускаем бота
|
||||
../src/foxlang main.fox
|
||||
@@ -0,0 +1,12 @@
|
||||
# FoxLang Telegram Bot Configuration
|
||||
|
||||
# Поместите сюда токен вашего бота от @BotFather
|
||||
# Пример: 123456789:ABCdefGHIjklMNOpqrsTUVwxyz
|
||||
|
||||
# Получить токен:
|
||||
# 1. Найдите @BotFather в Telegram
|
||||
# 2. Отправьте /newbot
|
||||
# 3. Следуйте инструкциям
|
||||
# 4. Скопируйте токен сюда
|
||||
|
||||
8070909449:AAG8IaMC2HIswrVKD1fSrlnW417dSk8X55s
|
||||
@@ -0,0 +1,47 @@
|
||||
// Тест массивов
|
||||
print("=== Arrays Test ===");
|
||||
|
||||
// Создание массива
|
||||
array numbers 5;
|
||||
|
||||
// Заполнение массива
|
||||
set(numbers, 0, 10);
|
||||
set(numbers, 1, 20);
|
||||
set(numbers, 2, 30);
|
||||
set(numbers, 3, 40);
|
||||
set(numbers, 4, 50);
|
||||
|
||||
// Чтение из массива
|
||||
print("Array elements:");
|
||||
int i = 0;
|
||||
while (i < 5) {
|
||||
int value = get(numbers, i);
|
||||
print("numbers[" + i + "] = " + value);
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
// Массив с подчеркиванием в имени
|
||||
array test_array 3;
|
||||
set(test_array, 0, 100);
|
||||
set(test_array, 1, 200);
|
||||
set(test_array, 2, 300);
|
||||
|
||||
print("Test array:");
|
||||
int j = 0;
|
||||
while (j < 3) {
|
||||
int val = get(test_array, j);
|
||||
print("test_array[" + j + "] = " + val);
|
||||
j = j + 1;
|
||||
}
|
||||
|
||||
// Сумма элементов массива
|
||||
int sum = 0;
|
||||
int k = 0;
|
||||
while (k < 5) {
|
||||
sum = sum + get(numbers, k);
|
||||
k = k + 1;
|
||||
}
|
||||
print("Sum of numbers: " + sum);
|
||||
|
||||
print("Arrays test completed!");
|
||||
fox();
|
||||
@@ -0,0 +1,15 @@
|
||||
// Тест встроенных функций
|
||||
print("=== Built-in Functions Test ===");
|
||||
|
||||
// Тест print
|
||||
print("Testing print function");
|
||||
|
||||
// Тест математических функций (заглушки)
|
||||
print("Testing math functions (built-in):");
|
||||
print("Math functions work!");
|
||||
|
||||
// Тест fox функции
|
||||
print("Testing fox function:");
|
||||
fox();
|
||||
|
||||
print("Built-in functions test completed!");
|
||||
@@ -0,0 +1,60 @@
|
||||
// Тест циклов и условий
|
||||
print("=== Control Flow Test ===");
|
||||
|
||||
// Тест if/else
|
||||
int x = 10;
|
||||
if (x > 5) {
|
||||
print("x is greater than 5");
|
||||
} else {
|
||||
print("x is not greater than 5");
|
||||
}
|
||||
|
||||
// Тест логических операторов
|
||||
bool a = true;
|
||||
bool b = false;
|
||||
|
||||
if (a && !b) {
|
||||
print("a is true and b is false");
|
||||
}
|
||||
|
||||
if (a || b) {
|
||||
print("a or b is true");
|
||||
}
|
||||
|
||||
// Тест while цикла
|
||||
print("While loop test:");
|
||||
int counter = 0;
|
||||
while (counter < 5) {
|
||||
print("Counter: " + counter);
|
||||
counter = counter + 1;
|
||||
}
|
||||
|
||||
// Тест for цикла
|
||||
print("For loop test:");
|
||||
int i = 0;
|
||||
while (i < 3) {
|
||||
print("For loop i: " + i);
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
// Вложенные циклы
|
||||
print("Nested loops test:");
|
||||
int outer = 0;
|
||||
while (outer < 3) {
|
||||
int inner = 0;
|
||||
while (inner < 2) {
|
||||
print("outer: " + outer + ", inner: " + inner);
|
||||
inner = inner + 1;
|
||||
}
|
||||
outer = outer + 1;
|
||||
}
|
||||
|
||||
// Переменные с подчеркиваниями в циклах
|
||||
int loop_counter = 0;
|
||||
while (loop_counter < 3) {
|
||||
print("loop_counter: " + loop_counter);
|
||||
loop_counter = loop_counter + 1;
|
||||
}
|
||||
|
||||
print("Control flow test completed!");
|
||||
fox();
|
||||
@@ -0,0 +1,30 @@
|
||||
// Простой тест пользовательских функций
|
||||
print("=== Functions Test ===");
|
||||
|
||||
// Простая функция
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// Функция со строкой
|
||||
string greet(string name) {
|
||||
return "Hello!";
|
||||
}
|
||||
|
||||
// Void функция
|
||||
void say_hello() {
|
||||
print("Hello from function!");
|
||||
}
|
||||
|
||||
// Тестирование
|
||||
print("Testing add function:");
|
||||
print("Result: " + add(5, 3));
|
||||
|
||||
print("Testing greet function:");
|
||||
print(greet("Alice"));
|
||||
|
||||
print("Testing void function:");
|
||||
say_hello();
|
||||
|
||||
print("Functions test completed!");
|
||||
fox();
|
||||
@@ -1,38 +0,0 @@
|
||||
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).");
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
// 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!");
|
||||
@@ -0,0 +1,45 @@
|
||||
// Тест математических операций
|
||||
print("=== Math Operations Test ===");
|
||||
|
||||
// Базовые операции
|
||||
int a = 10;
|
||||
int b = 3;
|
||||
|
||||
print("a = " + a + ", b = " + b);
|
||||
print("a + b = " + (a + b));
|
||||
print("a - b = " + (a - b));
|
||||
print("a * b = " + (a * b));
|
||||
print("a / b = " + (a / b));
|
||||
print("a % b = " + (a % b));
|
||||
|
||||
// Операции с float
|
||||
float x = 10.5;
|
||||
float y = 3.2;
|
||||
|
||||
print("x = " + x + ", y = " + y);
|
||||
print("x + y = " + (x + y));
|
||||
print("x - y = " + (x - y));
|
||||
print("x * y = " + (x * y));
|
||||
print("x / y = " + (x / y));
|
||||
|
||||
// Сравнения
|
||||
print("Comparison tests:");
|
||||
print("10 == 10: true");
|
||||
print("10 != 5: true");
|
||||
print("10 > 5: true");
|
||||
print("5 < 10: true");
|
||||
|
||||
// Строковые операции
|
||||
string str1 = "Hello";
|
||||
string str2 = "World";
|
||||
string result = str1 + " " + str2;
|
||||
print("String concatenation: " + result);
|
||||
|
||||
// Переменные с подчеркиваниями
|
||||
int first_number = 15;
|
||||
int second_number = 25;
|
||||
int math_result = first_number + second_number;
|
||||
print("first_number + second_number = " + math_result);
|
||||
|
||||
print("Math operations test completed!");
|
||||
fox();
|
||||
@@ -0,0 +1,32 @@
|
||||
// math_utils.fox - Математические утилиты
|
||||
|
||||
int add_numbers(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
int multiply(int x, int y) {
|
||||
return x * y;
|
||||
}
|
||||
|
||||
int max_value(int a, int b) {
|
||||
if (a > b) {
|
||||
return a;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
int min_value(int a, int b) {
|
||||
if (a < b) {
|
||||
return a;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
float calculate_average(int a, int b) {
|
||||
return (a + b) / 2.0;
|
||||
}
|
||||
|
||||
// Функция с подчеркиванием
|
||||
int power_of_two(int n) {
|
||||
return n * n;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Тест модульной системы
|
||||
print("=== Modules Test ===");
|
||||
|
||||
// Подключаем математические утилиты
|
||||
include("math_utils.fox");
|
||||
|
||||
// Подключаем строковые утилиты
|
||||
include("string_utils.fox");
|
||||
|
||||
// Тестируем математические функции
|
||||
int sum = add_numbers(10, 20);
|
||||
print("add_numbers(10, 20) = " + sum);
|
||||
|
||||
int product = multiply(5, 6);
|
||||
print("multiply(5, 6) = " + product);
|
||||
|
||||
int maximum = max_value(15, 25);
|
||||
print("max_value(15, 25) = " + maximum);
|
||||
|
||||
int minimum = min_value(15, 25);
|
||||
print("min_value(15, 25) = " + minimum);
|
||||
|
||||
float avg = calculate_average(10, 20);
|
||||
print("calculate_average(10, 20) = " + avg);
|
||||
|
||||
int squared = power_of_two(7);
|
||||
print("power_of_two(7) = " + squared);
|
||||
|
||||
// Тестируем строковые функции
|
||||
string greeting = create_greeting("Alice");
|
||||
print(greeting);
|
||||
|
||||
string repeated = repeat_string("Fox", 3);
|
||||
print("repeat_string('Fox', 3) = " + repeated);
|
||||
|
||||
string user_info = format_user_info("john_doe", 25);
|
||||
print(user_info);
|
||||
|
||||
string full_name = get_full_name("John", "Doe");
|
||||
print("get_full_name('John', 'Doe') = " + full_name);
|
||||
|
||||
print("Modules test completed!");
|
||||
fox();
|
||||
@@ -0,0 +1,24 @@
|
||||
// Тест функции read_file
|
||||
print("=== Тест функции read_file ===");
|
||||
|
||||
// Создаем тестовый файл
|
||||
string test_content = "Hello from FoxLang!";
|
||||
|
||||
// Тестируем чтение существующего файла
|
||||
string content = read_file("telegram_bot/token.txt");
|
||||
if (content != "") {
|
||||
print("✅ Чтение файла: УСПЕШНО");
|
||||
print("Содержимое: " + content);
|
||||
} else {
|
||||
print("❌ Чтение файла: ОШИБКА");
|
||||
}
|
||||
|
||||
// Тестируем чтение несуществующего файла
|
||||
string empty = read_file("nonexistent.txt");
|
||||
if (empty == "") {
|
||||
print("✅ Обработка ошибки: УСПЕШНО");
|
||||
} else {
|
||||
print("❌ Обработка ошибки: ОШИБКА");
|
||||
}
|
||||
|
||||
print("=== Тест завершен ===");
|
||||
@@ -1,24 +0,0 @@
|
||||
// Функция, которая проверяет число и возвращает код ошибки
|
||||
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
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
int coins = 100;
|
||||
string playerName = "Alex";
|
||||
|
||||
// Коментприй
|
||||
void showStats() {
|
||||
print("--- Stats ---");
|
||||
print("Player: " + playerName);
|
||||
print("Coins: " + coins);
|
||||
}
|
||||
|
||||
void mainLogic() {
|
||||
print("Welcome to Fox Game!");
|
||||
showStats();
|
||||
|
||||
print("You found a chest! Adding 50 coins.");
|
||||
coins = coins + 50;
|
||||
|
||||
showStats();
|
||||
|
||||
print("Enter new name:");
|
||||
playerName = input();
|
||||
|
||||
print("Updated profile:");
|
||||
showStats();
|
||||
}
|
||||
|
||||
void init() {
|
||||
print("Initializing libs...");
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// string_utils.fox - Строковые утилиты
|
||||
|
||||
string create_greeting(string name) {
|
||||
return "Hello, " + name + "!";
|
||||
}
|
||||
|
||||
string repeat_string(string text, int count) {
|
||||
string result = "";
|
||||
int i = 0;
|
||||
while (i < count) {
|
||||
result = result + text;
|
||||
i = i + 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
string format_user_info(string user_name, int age) {
|
||||
return "User: " + user_name + ", Age: " + age;
|
||||
}
|
||||
|
||||
// Функция с подчеркиванием
|
||||
string get_full_name(string first_name, string last_name) {
|
||||
return first_name + " " + last_name;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
include("script.fox");
|
||||
// Глоб. Переменные
|
||||
string Player = "Artem";
|
||||
int hpplayer = 100;
|
||||
int level = 1;
|
||||
int usr_input = 0;
|
||||
|
||||
void Hello() {
|
||||
print("Привет, " + Player + "!");
|
||||
print("Тебе предстоит пройти это подземелье");
|
||||
}
|
||||
Hello();
|
||||
|
||||
void ShowStats() {
|
||||
print("-----------Статистика-----------");
|
||||
print("Имя игрока: " + Player);
|
||||
print("Здоровье: " + hpplayer);
|
||||
print("Уровень: " + level);
|
||||
}
|
||||
ShowStats();
|
||||
|
||||
usr_input = input();
|
||||
|
||||
if (usr_input == 1) {
|
||||
init();
|
||||
} else {
|
||||
print("Не найдено действие для этого ввода.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Тест базовых типов данных и переменных
|
||||
print("=== Variables Test ===");
|
||||
|
||||
// Целые числа
|
||||
int age = 25;
|
||||
print("Age: " + age);
|
||||
|
||||
// Дробные числа
|
||||
float pi = 3.14159;
|
||||
print("Pi: " + pi);
|
||||
|
||||
// Строки
|
||||
string name = "FoxLang";
|
||||
print("Name: " + name);
|
||||
|
||||
// Логические значения
|
||||
bool is_active = true;
|
||||
bool is_disabled = false;
|
||||
print("Active: " + is_active);
|
||||
print("Disabled: " + is_disabled);
|
||||
|
||||
// Переменные с подчеркиваниями
|
||||
string user_name = "john_doe";
|
||||
int user_age = 30;
|
||||
bool is_admin = false;
|
||||
|
||||
print("User: " + user_name);
|
||||
print("User age: " + user_age);
|
||||
print("Is admin: " + is_admin);
|
||||
|
||||
print("Variables test completed!");
|
||||
fox();
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Скрипт для запуска всех тестов FoxLang
|
||||
|
||||
echo "🦊 Running FoxLang Test Suite"
|
||||
echo "=============================="
|
||||
|
||||
# Проверяем наличие интерпретатора
|
||||
if [ ! -f "src/foxlang" ]; then
|
||||
echo "❌ Error: foxlang interpreter not found. Please build it first:"
|
||||
echo " cd src && g++ main.cpp Lexer.cpp Parser.cpp -o foxlang"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Список тестов
|
||||
tests=(
|
||||
"variables.fox"
|
||||
"functions.fox"
|
||||
"arrays.fox"
|
||||
"control_flow.fox"
|
||||
"math_operations.fox"
|
||||
"modules.fox"
|
||||
"builtin_functions.fox"
|
||||
)
|
||||
|
||||
passed=0
|
||||
failed=0
|
||||
|
||||
# Запускаем каждый тест
|
||||
for test in "${tests[@]}"; do
|
||||
echo ""
|
||||
echo "🧪 Running test: $test"
|
||||
echo "----------------------------"
|
||||
|
||||
if ./src/foxlang "test/$test"; then
|
||||
echo "✅ $test - PASSED"
|
||||
((passed++))
|
||||
else
|
||||
echo "❌ $test - FAILED"
|
||||
((failed++))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=============================="
|
||||
echo "📊 Test Results:"
|
||||
echo " Passed: $passed"
|
||||
echo " Failed: $failed"
|
||||
echo " Total: $((passed + failed))"
|
||||
|
||||
if [ $failed -eq 0 ]; then
|
||||
echo "🎉 All tests passed!"
|
||||
exit 0
|
||||
else
|
||||
echo "💥 Some tests failed!"
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,59 @@
|
||||
// Полный тест новых возможностей FoxLang 4.0
|
||||
using net;
|
||||
|
||||
// Новые типы данных
|
||||
float temperature = 36.6;
|
||||
bool isOnline = true;
|
||||
bool hasError = false;
|
||||
|
||||
print("=== Тест новых типов ===");
|
||||
print("Temperature: " + temperature);
|
||||
print("Online status: " + isOnline);
|
||||
print("Error status: " + hasError);
|
||||
|
||||
// Тест boolean логики
|
||||
if (isOnline && !hasError) {
|
||||
print("System is healthy");
|
||||
}
|
||||
|
||||
if (temperature > 36.0) {
|
||||
print("Temperature is normal");
|
||||
}
|
||||
|
||||
// Тест сетевых функций
|
||||
print("\n=== Тест сетевых функций ===");
|
||||
string response = http_get("https://api.example.com/data");
|
||||
print("HTTP GET: " + response);
|
||||
|
||||
string postResult = http_post("https://api.example.com/submit", "data=test");
|
||||
print("HTTP POST: " + postResult);
|
||||
|
||||
// TCP тест
|
||||
int tcpSocket = tcp_socket();
|
||||
print("TCP Socket created: " + tcpSocket);
|
||||
|
||||
bool connected = tcp_connect(tcpSocket, "localhost", 8080);
|
||||
if (connected) {
|
||||
print("TCP connection established");
|
||||
int bytesSent = tcp_send(tcpSocket, "Hello TCP Server!");
|
||||
print("Bytes sent: " + bytesSent);
|
||||
|
||||
string received = tcp_receive(tcpSocket, 1024);
|
||||
print("Received: " + received);
|
||||
|
||||
tcp_close(tcpSocket);
|
||||
print("TCP connection closed");
|
||||
}
|
||||
|
||||
// UDP тест
|
||||
int udpSocket = udp_socket();
|
||||
print("UDP Socket created: " + udpSocket);
|
||||
|
||||
int udpSent = udp_send(udpSocket, "localhost", 9090, "Hello UDP!");
|
||||
print("UDP bytes sent: " + udpSent);
|
||||
|
||||
string udpData = udp_receive(udpSocket, 512);
|
||||
print("UDP received: " + udpData);
|
||||
|
||||
print("\n=== Тест завершен ===");
|
||||
fox();
|
||||
@@ -0,0 +1,17 @@
|
||||
// Тест логических операторов
|
||||
using net;
|
||||
|
||||
bool isOnline = true;
|
||||
bool hasError = false;
|
||||
|
||||
print("Online: " + isOnline);
|
||||
print("Error: " + hasError);
|
||||
|
||||
if (isOnline && !hasError) {
|
||||
print("System OK");
|
||||
}
|
||||
|
||||
string response = http_get("test.com");
|
||||
print("Response: " + response);
|
||||
|
||||
fox();
|
||||
@@ -0,0 +1,40 @@
|
||||
// Тест новых типов float и bool
|
||||
using net.fox;
|
||||
|
||||
float pi = 3.14159;
|
||||
bool isActive = true;
|
||||
bool isComplete = false;
|
||||
|
||||
print("Pi value: " + pi);
|
||||
print("Is active: " + isActive);
|
||||
print("Is complete: " + isComplete);
|
||||
|
||||
// Тест сравнений
|
||||
if (pi > 3.0) {
|
||||
print("Pi is greater than 3");
|
||||
}
|
||||
|
||||
if (isActive == true) {
|
||||
print("System is active");
|
||||
}
|
||||
|
||||
if (isComplete != true) {
|
||||
print("Task is not complete");
|
||||
}
|
||||
|
||||
// Тест сетевых функций
|
||||
string response = http_get("https://example.com");
|
||||
print("HTTP Response: " + response);
|
||||
|
||||
int socket = tcp_socket();
|
||||
print("Socket created: " + socket);
|
||||
|
||||
bool connected = tcp_connect(socket, "localhost", 8080);
|
||||
if (connected) {
|
||||
print("Connected to server");
|
||||
int sent = tcp_send(socket, "Hello Server");
|
||||
print("Bytes sent: " + sent);
|
||||
tcp_close(socket);
|
||||
}
|
||||
|
||||
fox();
|
||||
@@ -0,0 +1,4 @@
|
||||
// Простой тест без using
|
||||
string response = http_get("test");
|
||||
print(response);
|
||||
fox();
|
||||
@@ -0,0 +1,7 @@
|
||||
// Тест using библиотеки
|
||||
using net.fox;
|
||||
|
||||
string response = http_get("https://example.com");
|
||||
print("Response: " + response);
|
||||
|
||||
fox();
|
||||
@@ -0,0 +1,10 @@
|
||||
// Тест using без точки
|
||||
using net;
|
||||
|
||||
string response = http_get("https://example.com");
|
||||
print("Response: " + response);
|
||||
|
||||
int socket = tcp_socket();
|
||||
print("Socket: " + socket);
|
||||
|
||||
fox();
|
||||
Reference in New Issue
Block a user