diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7bfdaac --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.kiro/settings/lsp.json diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1f1fe0c --- /dev/null +++ b/CHANGELOG.md @@ -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, превращая его в полноценный язык программирования с поддержкой пользовательских функций и современной модульной системы. diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 40b0ee1..2d20dd0 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -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-арт лисы. | \ No newline at end of file diff --git a/LIBRARIES.md b/LIBRARIES.md new file mode 100644 index 0000000..36261a5 --- /dev/null +++ b/LIBRARIES.md @@ -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. Используйте функции в основном коде + +Система автоматически найдет файл относительно вашего скрипта! diff --git a/README.md b/README.md index 213a683..02ac6a9 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,158 @@ # 🦊 FoxLang -![Version](https://img.shields.io/badge/version-4.0.0-orange) ![Language](https://img.shields.io/badge/language-C++17-blue) ![License](https://img.shields.io/badge/license-MIT-green) +![Version](https://img.shields.io/badge/version-5.0.1-orange) ![Language](https://img.shields.io/badge/language-C++17-blue) ![License](https://img.shields.io/badge/license-MIT-green) -**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 diff --git a/demo.fox b/demo.fox new file mode 100644 index 0000000..75a7014 --- /dev/null +++ b/demo.fox @@ -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(); diff --git a/foxlang b/foxlang index 0fc32b9..66f685f 100755 Binary files a/foxlang and b/foxlang differ diff --git a/simple_test.fox b/simple_test.fox new file mode 100644 index 0000000..d0f05f1 --- /dev/null +++ b/simple_test.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(); diff --git a/src/AST.h b/src/AST.h index 85e79b7..79a2905 100644 --- a/src/AST.h +++ b/src/AST.h @@ -8,8 +8,8 @@ #include #include #include +#include -// 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 variables; - std::map> functions; // Храним функции - std::map> arrays; + Context* parent = nullptr; + std::map variables; + std::map> functions; + std::map> 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& getArray(const std::string& name) { + std::vector& 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 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 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 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 p, std::shared_ptr 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 expr; ReturnNode(std::unique_ptr 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> args; @@ -122,7 +114,152 @@ struct FuncCallNode : Node { FuncCallNode(std::string n, std::vector> 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 argValues; + std::vector 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 expr; + VarDeclNode(std::string t, std::string n, std::unique_ptr 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 expr; - GlobalVarDeclNode(std::string t, std::string n, std::unique_ptr e) : type(t), name(n), expr(std::move(e)) {} - std::string eval(Context& ctx) override { + std::string type, name; + std::unique_ptr expr; + GlobalVarDeclNode(std::string t, std::string n, std::unique_ptr 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 expr; - VarDeclNode(std::string t, std::string n, std::unique_ptr e) : type(t), name(n), expr(std::move(e)) {} - std::string eval(Context& ctx) override { - // Здесь expr->eval(ctx) может быть FuncCallNode, который вернет результат! - ctx.defineVar(name, expr->eval(ctx)); - return ""; - } -}; -struct AssignNode : Node { - std::string name; std::unique_ptr expr; - AssignNode(std::string n, std::unique_ptr e) : name(n), expr(std::move(e)) {} - std::string eval(Context& ctx) override { + +struct VarAssignNode : Node { + std::string name; + std::unique_ptr expr; + VarAssignNode(std::string n, std::unique_ptr 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 left, right; - BinOpNode(char o, std::unique_ptr l, std::unique_ptr r) : op(o), left(std::move(l)), right(std::move(r)) {} - std::string eval(Context& ctx) override { - std::string lStr = left->eval(ctx); std::string rStr = right->eval(ctx); - bool isStr = (lStr.find_first_not_of("0123456789.-") != std::string::npos) || (rStr.find_first_not_of("0123456789.-") != std::string::npos); - if (op == '+' && isStr) return lStr + rStr; - double l = std::stod(lStr); double r = std::stod(rStr); - if (op == '+') return formatNumber(l+r); - if (op == '-') return formatNumber(l-r); - if (op == '*') return formatNumber(l*r); - if (op == '/') return formatNumber(r!=0 ? l/r : 0); - if (op == '%') return formatNumber((int)l % (int)r); - return "0"; + char op; + std::unique_ptr left, right; + BinOpNode(char o, std::unique_ptr l, std::unique_ptr r) + : op(o), left(std::move(l)), right(std::move(r)) {} + + 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 left, right; - CompareNode(std::string o, std::unique_ptr l, std::unique_ptr r) : op(o), left(std::move(l)), right(std::move(r)) {} - std::string eval(Context& ctx) override { - 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 operand; + UnaryOpNode(std::string o, std::unique_ptr 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 cond, thenB, elseB; - IfNode(std::unique_ptr c, std::unique_ptr t, std::unique_ptr 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 cond, body; - WhileNode(std::unique_ptr c, std::unique_ptr 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(size, {"int", "0"}); + return {"void", ""}; } }; + +struct ArraySetNode : Node { + std::string name; + std::unique_ptr index, value; + ArraySetNode(std::string n, std::unique_ptr i, std::unique_ptr 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 index; + ArrayGetNode(std::string n, std::unique_ptr 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> 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 expr; - PrintNode(std::unique_ptr 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 condition, thenB, elseB; + IfNode(std::unique_ptr c, std::unique_ptr t, std::unique_ptr 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 condition, body; + WhileNode(std::unique_ptr c, std::unique_ptr 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 init, condition, step, body; + ForNode(std::unique_ptr i, std::unique_ptr c, std::unique_ptr s, std::unique_ptr 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 size; - ArrayDeclNode(std::string n, std::unique_ptr s) : name(n), size(std::move(s)) {} - std::string eval(Context& ctx) override { - ctx.arrays[name] = std::vector(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 idx, val; - ArraySetNode(std::string n, std::unique_ptr i, std::unique_ptr v) : name(n), idx(std::move(i)), val(std::move(v)) {} - std::string eval(Context& ctx) override { - auto& arr = ctx.getArray(name); arr[std::stoi(idx->eval(ctx))] = val->eval(ctx); return ""; + +struct ReadFileNode : Node { + std::unique_ptr filename; + ReadFileNode(std::unique_ptr 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 idx; - ArrayGetNode(std::string n, std::unique_ptr 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 ""; } }; \ No newline at end of file diff --git a/src/AST_old.h b/src/AST_old.h new file mode 100644 index 0000000..57d869d --- /dev/null +++ b/src/AST_old.h @@ -0,0 +1,434 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// 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 variables; // Stores typed values + std::map> functions; + std::map> 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& 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 func) { + functions[name] = func; + } + + std::shared_ptr 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 params; + std::shared_ptr body; + + FuncDefNode(std::string rt, std::string n, std::vector p, std::shared_ptr b) + : returnType(rt), name(n), params(p), body(b) {} + + Value eval(Context& ctx) override { return {"void", ""}; } +}; + +struct ReturnNode : Node { + std::unique_ptr expr; + ReturnNode(std::unique_ptr 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> args; + + FuncCallNode(std::string n, std::vector> 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(funcNodeBase.get()); + + if (args.size() != funcDef->params.size()) { + throw std::runtime_error("Args count mismatch for '" + name + "'"); + } + + std::vector 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 expr; + VarDeclNode(std::string t, std::string n, std::unique_ptr 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 expr; + GlobalVarDeclNode(std::string t, std::string n, std::unique_ptr 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 expr; + VarAssignNode(std::string n, std::unique_ptr 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 left, right; + BinOpNode(char o, std::unique_ptr l, std::unique_ptr 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 operand; + UnaryOpNode(std::string o, std::unique_ptr 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(size, {"int", "0"}); + return {"void", ""}; + } +}; + +struct ArraySetNode : Node { + std::string name; + std::unique_ptr index, value; + ArraySetNode(std::string n, std::unique_ptr i, std::unique_ptr 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 index; + ArrayGetNode(std::string n, std::unique_ptr 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> stmts; + Value eval(Context& ctx) override { + for (auto& stmt : stmts) stmt->eval(ctx); + return {"void", ""}; + } +}; + +struct IfNode : Node { + std::unique_ptr condition, thenB, elseB; + IfNode(std::unique_ptr c, std::unique_ptr t, std::unique_ptr 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 condition, body; + WhileNode(std::unique_ptr c, std::unique_ptr 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", ""}; } +}; diff --git a/src/Lexer.cpp b/src/Lexer.cpp index 3056df0..c68c0c4 100644 --- a/src/Lexer.cpp +++ b/src/Lexer.cpp @@ -23,7 +23,9 @@ std::vector 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 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 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 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 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 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)); } diff --git a/src/Parser.cpp b/src/Parser.cpp index 916515a..9060f0c 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -29,6 +29,12 @@ std::unique_ptr Parser::primary() { ); } + // 1.5. УНАРНЫЙ НЕ (Обработка !condition) + if (tokens[pos].type == TokenType::NOT) { + consume(TokenType::NOT); + return std::make_unique("!", primary()); + } + // 2. Числа if (tokens[pos].type == TokenType::NUMBER) { return std::make_unique(consume(TokenType::NUMBER).value); @@ -39,7 +45,17 @@ std::unique_ptr Parser::primary() { return std::make_unique(consume(TokenType::STRING_LITERAL).value); } - // 4. Переменные и Вызовы функций + // 4. Boolean литералы + if (tokens[pos].type == TokenType::TRUE_KW) { + consume(TokenType::TRUE_KW); + return std::make_unique(true); + } + if (tokens[pos].type == TokenType::FALSE_KW) { + consume(TokenType::FALSE_KW); + return std::make_unique(false); + } + + // 5. Переменные и Вызовы функций if (tokens[pos].type == TokenType::IDENTIFIER) { std::string name = consume(TokenType::IDENTIFIER).value; @@ -57,6 +73,13 @@ std::unique_ptr Parser::primary() { consume(TokenType::RPAREN); return std::make_unique(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(name); + } + // Иначе это просто доступ к переменной return std::make_unique(name); } @@ -74,6 +97,13 @@ std::unique_ptr Parser::primary() { return std::make_unique(); } + if (tokens[pos].type == TokenType::READ_FILE) { + consume(TokenType::READ_FILE); consume(TokenType::LPAREN); + auto filename = expression(); + consume(TokenType::RPAREN); + return std::make_unique(std::move(filename)); + } + if (tokens[pos].type == TokenType::LPAREN) { consume(TokenType::LPAREN); auto n = expression(); @@ -108,7 +138,25 @@ std::unique_ptr 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(op, std::move(node), expression()); + return std::make_unique(op[0], std::move(node), expression()); + } + return node; +} + +std::unique_ptr Parser::logicalAnd() { + auto node = comparison(); + while (tokens[pos].type == TokenType::AND) { + std::string op = tokens[pos].value; pos++; + node = std::make_unique(op[0], std::move(node), comparison()); + } + return node; +} + +std::unique_ptr Parser::logicalOr() { + auto node = logicalAnd(); + while (tokens[pos].type == TokenType::OR) { + std::string op = tokens[pos].value; pos++; + node = std::make_unique(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 Parser::statement() { if (tokens[pos].type == TokenType::INCLUDE) { consume(TokenType::INCLUDE); consume(TokenType::LPAREN); @@ -167,11 +220,27 @@ std::unique_ptr Parser::statement() { return std::make_unique(); } + 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(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 Parser::statement() { return std::make_unique(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 Parser::statement() { std::unique_ptr expr = nullptr; if (tokens[pos].type != TokenType::SEMICOLON) expr = expression(); consume(TokenType::SEMICOLON); - if (importMode) return std::make_unique(); return std::make_unique(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(); return std::make_unique(std::move(cond), std::move(body)); } + + if (tokens[pos].type == TokenType::FOR) { + consume(TokenType::FOR); consume(TokenType::LPAREN); + + std::unique_ptr init = nullptr; + if (tokens[pos].type != TokenType::SEMICOLON) { + init = statement(); + } else { + consume(TokenType::SEMICOLON); + } + + std::unique_ptr cond = nullptr; + if (tokens[pos].type != TokenType::SEMICOLON) { + cond = comparison(); + } else { + cond = std::make_unique("1"); + } + consume(TokenType::SEMICOLON); + + std::unique_ptr 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(name, std::move(expr)); + } else { + step = expression(); + } + } + consume(TokenType::RPAREN); + + auto body = parseBlock(); + if (importMode) return std::make_unique(); + return std::make_unique(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 elseB = nullptr; if (tokens[pos].type == TokenType::ELSE) { @@ -244,21 +348,24 @@ std::unique_ptr Parser::statement() { auto expr = expression(); consume(TokenType::RPAREN); consume(TokenType::SEMICOLON); if (importMode) return std::make_unique(); - return std::make_unique(std::move(expr)); + std::vector> args; + args.push_back(std::move(expr)); + return std::make_unique("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(); - return std::make_unique(); + std::vector> args; + return std::make_unique("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(name, std::move(size)); + return std::make_unique(name, size); } if (tokens[pos].type == TokenType::SET) { @@ -278,7 +385,7 @@ std::unique_ptr Parser::statement() { auto expr = expression(); consume(TokenType::SEMICOLON); if (importMode) return std::make_unique(); - return std::make_unique(name, std::move(expr)); + return std::make_unique(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 Parser::statement() { if (importMode) return std::make_unique(); return std::make_unique(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(); + return std::make_unique(name); + } + } + + if (tokens[pos].type == TokenType::LBRACE) { + return parseBlock(); } throw std::runtime_error("Unknown statement " + tokens[pos].value); diff --git a/src/Parser.h b/src/Parser.h index 1db670b..d492e3f 100644 --- a/src/Parser.h +++ b/src/Parser.h @@ -3,29 +3,32 @@ #include "AST.h" #include #include -#include // Не забудь +#include class Parser { std::vector tokens; size_t pos = 0; - + public: Context globalContext; - - // НОВЫЕ ПОЛЯ ДЛЯ ПУТЕЙ И ИМПОРТА + + std::string currentFile; // Путь к файлу, который сейчас парсится bool importMode = false; // Если true, то выполняем только объявления (vars/funcs) Parser(std::vector t); - + Token consume(TokenType type); std::unique_ptr primary(); std::unique_ptr multiplication(); std::unique_ptr expression(); std::unique_ptr comparison(); + std::unique_ptr logicalAnd(); + std::unique_ptr logicalOr(); std::unique_ptr statement(); std::unique_ptr parseBlock(); - - void run(); -}; \ No newline at end of file + + void processUsing(const std::string& libName, Context& ctx); + void run(); +}; diff --git a/src/Token.h b/src/Token.h index d5d32bf..5a878b8 100644 --- a/src/Token.h +++ b/src/Token.h @@ -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, diff --git a/src/foxlang b/src/foxlang new file mode 100755 index 0000000..66f685f Binary files /dev/null and b/src/foxlang differ diff --git a/src/net.fox b/src/net.fox new file mode 100644 index 0000000..08b6bbf --- /dev/null +++ b/src/net.fox @@ -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(); +} diff --git a/src/net_simple.fox b/src/net_simple.fox new file mode 100644 index 0000000..47f85ca --- /dev/null +++ b/src/net_simple.fox @@ -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; +} diff --git a/telegram_bot/README.md b/telegram_bot/README.md new file mode 100644 index 0000000..e1e4c7c --- /dev/null +++ b/telegram_bot/README.md @@ -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 - используйте свободно! diff --git a/telegram_bot/bot_api.fox b/telegram_bot/bot_api.fox new file mode 100644 index 0000000..fda8be4 --- /dev/null +++ b/telegram_bot/bot_api.fox @@ -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..."); +} diff --git a/telegram_bot/code_executor.fox b/telegram_bot/code_executor.fox new file mode 100644 index 0000000..b8a3f2f --- /dev/null +++ b/telegram_bot/code_executor.fox @@ -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 + "..."; +} diff --git a/telegram_bot/main.fox b/telegram_bot/main.fox new file mode 100644 index 0000000..7c50a7c --- /dev/null +++ b/telegram_bot/main.fox @@ -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(); diff --git a/telegram_bot/run_bot.sh b/telegram_bot/run_bot.sh new file mode 100755 index 0000000..bf0fd27 --- /dev/null +++ b/telegram_bot/run_bot.sh @@ -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 diff --git a/telegram_bot/token.txt b/telegram_bot/token.txt new file mode 100644 index 0000000..696715a --- /dev/null +++ b/telegram_bot/token.txt @@ -0,0 +1,12 @@ +# FoxLang Telegram Bot Configuration + +# Поместите сюда токен вашего бота от @BotFather +# Пример: 123456789:ABCdefGHIjklMNOpqrsTUVwxyz + +# Получить токен: +# 1. Найдите @BotFather в Telegram +# 2. Отправьте /newbot +# 3. Следуйте инструкциям +# 4. Скопируйте токен сюда + +8070909449:AAG8IaMC2HIswrVKD1fSrlnW417dSk8X55s diff --git a/test/arrays.fox b/test/arrays.fox new file mode 100644 index 0000000..191a36a --- /dev/null +++ b/test/arrays.fox @@ -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(); diff --git a/test/builtin_functions.fox b/test/builtin_functions.fox new file mode 100644 index 0000000..2db64e2 --- /dev/null +++ b/test/builtin_functions.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!"); diff --git a/test/control_flow.fox b/test/control_flow.fox new file mode 100644 index 0000000..03ac95b --- /dev/null +++ b/test/control_flow.fox @@ -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(); diff --git a/test/functions.fox b/test/functions.fox new file mode 100644 index 0000000..d4e595b --- /dev/null +++ b/test/functions.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(); diff --git a/test/lib_utils.fox b/test/lib_utils.fox deleted file mode 100644 index c369a24..0000000 --- a/test/lib_utils.fox +++ /dev/null @@ -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)."); - } -} \ No newline at end of file diff --git a/test/main_app.fox b/test/main_app.fox deleted file mode 100644 index 9448712..0000000 --- a/test/main_app.fox +++ /dev/null @@ -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!"); \ No newline at end of file diff --git a/test/math_operations.fox b/test/math_operations.fox new file mode 100644 index 0000000..29698e8 --- /dev/null +++ b/test/math_operations.fox @@ -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(); diff --git a/test/math_utils.fox b/test/math_utils.fox new file mode 100644 index 0000000..2a819a6 --- /dev/null +++ b/test/math_utils.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; +} diff --git a/test/modules.fox b/test/modules.fox new file mode 100644 index 0000000..697d780 --- /dev/null +++ b/test/modules.fox @@ -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(); diff --git a/test/read_file.fox b/test/read_file.fox new file mode 100644 index 0000000..812593e --- /dev/null +++ b/test/read_file.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("=== Тест завершен ==="); diff --git a/test/return.fox b/test/return.fox deleted file mode 100644 index a3f204c..0000000 --- a/test/return.fox +++ /dev/null @@ -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 -} \ No newline at end of file diff --git a/test/script.fox b/test/script.fox deleted file mode 100644 index 89a3b64..0000000 --- a/test/script.fox +++ /dev/null @@ -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..."); -} \ No newline at end of file diff --git a/test/string_utils.fox b/test/string_utils.fox new file mode 100644 index 0000000..3986eca --- /dev/null +++ b/test/string_utils.fox @@ -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; +} diff --git a/test/test.fox b/test/test.fox deleted file mode 100644 index ae70a33..0000000 --- a/test/test.fox +++ /dev/null @@ -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("Не найдено действие для этого ввода."); - } -} diff --git a/test/variables.fox b/test/variables.fox new file mode 100644 index 0000000..c078d7b --- /dev/null +++ b/test/variables.fox @@ -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(); diff --git a/test_all.sh b/test_all.sh new file mode 100755 index 0000000..fe25a38 --- /dev/null +++ b/test_all.sh @@ -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 diff --git a/test_complete.fox b/test_complete.fox new file mode 100644 index 0000000..eb50f82 --- /dev/null +++ b/test_complete.fox @@ -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(); diff --git a/test_logical.fox b/test_logical.fox new file mode 100644 index 0000000..f848d98 --- /dev/null +++ b/test_logical.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(); diff --git a/test_new_features.fox b/test_new_features.fox new file mode 100644 index 0000000..8de5302 --- /dev/null +++ b/test_new_features.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(); diff --git a/test_simple_net.fox b/test_simple_net.fox new file mode 100644 index 0000000..8868149 --- /dev/null +++ b/test_simple_net.fox @@ -0,0 +1,4 @@ +// Простой тест без using +string response = http_get("test"); +print(response); +fox(); diff --git a/test_using.fox b/test_using.fox new file mode 100644 index 0000000..757f5e9 --- /dev/null +++ b/test_using.fox @@ -0,0 +1,7 @@ +// Тест using библиотеки +using net.fox; + +string response = http_get("https://example.com"); +print("Response: " + response); + +fox(); diff --git a/test_using_simple.fox b/test_using_simple.fox new file mode 100644 index 0000000..980254f --- /dev/null +++ b/test_using_simple.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();