diff --git a/CMakeLists.txt b/CMakeLists.txt index 5df1f6d..303be16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.20 FATAL_ERROR) message( "Configuring: ${CMAKE_CURRENT_SOURCE_DIR}") -set(CMAKE_PROJECT_VERSION 0.0.3 ) +set(CMAKE_PROJECT_VERSION 0.0.4 ) # Project name project( avm diff --git a/README.md b/README.md index 4eaba25..38a28a6 100644 --- a/README.md +++ b/README.md @@ -43,21 +43,23 @@ The Associative Relations Model (ARM) is a mathematical model for storing and pr - Support for structured data (arrays and associative arrays) - JSON serialization/deserialization (null, boolean, array, number, string, object) - Logical operations (NOT, AND, OR) defined as truth tables in entity maps +- Conditional construct `If` with lazy evaluation of then/else branches - Relative addressing operator `[]` for evaluating functions via `eval()` - Multi-dimensional relative addressing for passing arguments -- Expression interpreter `interpret()` for evaluating logical expressions from JSON +- Expression interpreter `interpret()` for evaluating logical and conditional expressions from JSON ### Current Status -**Version: 0.0.3** (Alpha) +**Version: 0.0.4** (Alpha) Implemented: - JSON null, boolean, array, number (unsigned, integer, float), string, object serialization/deserialization -- Base vocabulary initialization (R, E, True, False, Unsigned, Integer, Float, String, Object, Not, And, Or) +- Base vocabulary initialization (R, E, True, False, Unsigned, Integer, Float, String, Object, Not, And, Or, If) - Logical operations NOT, AND, OR with truth tables via entity map +- Conditional construct If with lazy evaluation (e.g. `{"If": [true, "yes", "no"]}` → `"yes"`) - Relative addressing operator `[]` via `eval()` function for computing logical functions -- Expression interpreter for evaluating logical expressions from JSON (e.g. `{"Not": [true]}` → `false`) -- 125 unit tests + 16 JSON roundtrip tests +- Expression interpreter for evaluating logical and conditional expressions from JSON +- 147 unit tests + 16 JSON roundtrip tests - CI/CD pipeline (GitHub Actions) for Linux, macOS, Windows In Progress: @@ -108,6 +110,13 @@ echo '{"Not": [{"And": [true, false]}]}' > expr.json cat res.json # true ``` +Conditional expressions with lazy evaluation: +```bash +echo '{"If": [true, true, false]}' > cond.json +./avm cond.json +cat res.json # true +``` + ### Dependencies - C++20 compatible compiler @@ -154,21 +163,23 @@ AVM (Associative Virtual Machine) — проект, реализующий ви - Поддержка структурированных данных (массивы и ассоциативные массивы) - Сериализация/десериализация JSON (null, boolean, array, number, string, object) - Логические операции (NOT, AND, OR), определённые как таблицы истинности в entity map +- Условная конструкция `If` с ленивым вычислением веток then/else - Оператор относительной адресации `[]` для вычисления функций через `eval()` - Многомерная относительная адресация для передачи аргументов -- Интерпретатор выражений `interpret()` для вычисления логических выражений из JSON +- Интерпретатор выражений `interpret()` для вычисления логических и условных выражений из JSON ### Текущее состояние -**Версия: 0.0.3** (Альфа) +**Версия: 0.0.4** (Альфа) Реализовано: - Сериализация/десериализация JSON null, boolean, array, number (unsigned, integer, float), string, object -- Инициализация базового словаря (R, E, True, False, Unsigned, Integer, Float, String, Object, Not, And, Or) +- Инициализация базового словаря (R, E, True, False, Unsigned, Integer, Float, String, Object, Not, And, Or, If) - Логические операции NOT, AND, OR с таблицами истинности через entity map +- Условная конструкция If с ленивым вычислением (например `{"If": [true, "да", "нет"]}` → `"да"`) - Оператор относительной адресации `[]` через функцию `eval()` для вычисления логических функций -- Интерпретатор выражений для вычисления логических выражений из JSON (например `{"Not": [true]}` → `false`) -- 125 модульных тестов + 16 JSON roundtrip тестов +- Интерпретатор выражений для вычисления логических и условных выражений из JSON +- 147 модульных тестов + 16 JSON roundtrip тестов - CI/CD пайплайн (GitHub Actions) для Linux, macOS, Windows В разработке: @@ -219,6 +230,13 @@ echo '{"Not": [{"And": [true, false]}]}' > expr.json cat res.json # true ``` +Условные выражения с ленивым вычислением: +```bash +echo '{"If": [true, true, false]}' > cond.json +./avm cond.json +cat res.json # true +``` + ### Зависимости - Компилятор с поддержкой C++20 diff --git a/analysis.md b/analysis.md index 7e6ff7f..b8e3396 100644 --- a/analysis.md +++ b/analysis.md @@ -99,7 +99,7 @@ AVM (Associative Virtual Machine) — это проект, реализующи - Жёстко закодированные пути файлов (`"res.json"`) #### 2.3. Тестирование -- ~~Нет автоматических unit-тестов~~ ✅ 125 модульных тестов +- ~~Нет автоматических unit-тестов~~ ✅ 147 модульных тестов - ~~Нет интеграционных тестов~~ ✅ 16 JSON roundtrip тестов через CTest - ~~CMake настроен с `enable_testing()`, но тесты не определены~~ ✅ 17 тестов в CTest - ~~Нет CI/CD конфигурации (GitHub Actions)~~ ✅ GitHub Actions для 3 платформ @@ -128,12 +128,12 @@ AVM (Associative Virtual Machine) — это проект, реализующи |---------|----------| | Язык программирования | C++ (стандарт C++20) | | Система сборки | CMake 3.20+ | -| Основной файл кода | ~562 строки (main.cpp) | -| Заголовочный файл | ~463 строки (avm.h) | +| Основной файл кода | ~584 строки (main.cpp) | +| Заголовочный файл | ~473 строки (avm.h) | | Внешние зависимости | nlohmann/json, LinksPlatform, str_switch | | Лицензия | MIT | -| Unit-тесты | 125 модульных тестов | -| Тестовые файлы | 21 JSON файл | +| Unit-тесты | 147 модульных тестов | +| Тестовые файлы | 24 JSON файла | --- @@ -142,9 +142,9 @@ AVM (Associative Virtual Machine) — это проект, реализующи Проект находится на стадии альфа-версии: - **Концепция**: Хорошо проработана теоретически -- **Реализация**: JSON roundtrip полностью работает, логические операции реализованы, интерпретатор выражений работает +- **Реализация**: JSON roundtrip полностью работает, логические операции реализованы, интерпретатор выражений работает, условная конструкция If с ленивым вычислением - **Документация**: Двуязычная документация, описание алгоритма сериализации -- **Тестирование**: 125 модульных тестов + 16 интеграционных, CI/CD на 3 платформах +- **Тестирование**: 147 модульных тестов + 16 интеграционных, CI/CD на 3 платформах - **Готовность к использованию**: Прототип с работающей базовой функциональностью --- @@ -158,10 +158,10 @@ AVM представляет собой интересный исследова 2. Создание публичного API для CRUD операций 3. Примеры использования и руководства 4. Пакеты для менеджеров зависимостей (vcpkg, Conan) -5. Условные конструкции и рекурсия в интерпретаторе +5. ~~Условные конструкции~~ ✅ If с ленивым вычислением (PR #28) и рекурсия в интерпретаторе --- *Дата анализа: январь 2026* *Последнее обновление: февраль 2026* -*Версия проекта: 0.0.3* +*Версия проекта: 0.0.4* diff --git a/include/avm.h b/include/avm.h index 63fe46e..4ae78d1 100644 --- a/include/avm.h +++ b/include/avm.h @@ -277,6 +277,7 @@ struct rel_t : obj_aspect, static inline rel_t *Not; static inline rel_t *And; static inline rel_t *Or; + static inline rel_t *If; protected: rel_t() @@ -363,10 +364,12 @@ struct rel_t : obj_aspect, add_rel(Not); add_rel(And); add_rel(Or); + add_rel(If); Not->update(Not, E); // (NOT, Ent) — NOT есть сущность And->update(And, E); // (AND, Ent) — AND есть сущность Or->update(Or, E); // (OR, Ent) — OR есть сущность + If->update(If, E); // (IF, Ent) — IF есть сущность // NOT: таблица истинности через entity map // NOT[True] = False, NOT[False] = True @@ -405,6 +408,13 @@ struct rel_t : obj_aspect, // OR[False][True] = True, OR[False][False] = False (*or_false)[True] = True; (*or_false)[False] = False; + + // IF: условная конструкция через entity map + // IF[condition] = condition (identity для boolean) + // Используется интерпретатором для выбора ветки then/else + // IF[True] = True, IF[False] = False + (*If)[True] = True; + (*If)[False] = False; } ~base_voc() { diff --git a/plan.md b/plan.md index 4781914..518fbb2 100644 --- a/plan.md +++ b/plan.md @@ -53,7 +53,7 @@ 3. ~~**Создать интерпретатор выражений**~~ ✅ — интерпретация логических выражений из JSON в МАО (PR #20) #### Средний приоритет -4. **Реализовать условные конструкции** — if-then-else через ассоциативные структуры +4. ~~**Реализовать условные конструкции**~~ ✅ — If с ленивым вычислением через entity map и `interpret()` (PR #28) 5. **Добавить поддержку рекурсии** — рекурсивные определения функций 6. **Создать стандартную библиотеку** — базовые функции и операции @@ -135,7 +135,7 @@ ### Задачи по приоритетам #### Критический приоритет (сделать в первую очередь) -1. ~~**Добавить unit-тесты**~~ ✅ — 125 модульных тестов (PR #9, #19, #20) +1. ~~**Добавить unit-тесты**~~ ✅ — 147 модульных тестов (PR #9, #19, #20, #28) 2. ~~**Настроить CI/CD**~~ ✅ — GitHub Actions для Ubuntu, Windows, macOS (PR #6) 3. ~~**Обеспечить кроссплатформенность**~~ ✅ — сборка для Linux, macOS, Windows (PR #6, #14) 4. ~~**Улучшить README**~~ ✅ — двуязычная документация (PR #2) @@ -180,6 +180,7 @@ ### Фаза 2: Функциональность (текущая) - ~~Логические операции и вычисления~~ ✅ NOT, AND, OR + оператор `eval()` (PR #19) - ~~Интерпретатор выражений~~ ✅ вычисление логических выражений из JSON через `interpret()` (PR #20) +- ~~Условные конструкции~~ ✅ If с ленивым вычислением через entity map (PR #28) - Персистентное хранение - API для программного использования - Примеры и руководства @@ -208,4 +209,4 @@ *Дата создания: январь 2026* *Последнее обновление: февраль 2026* -*Версия плана: 1.2* +*Версия плана: 1.3* diff --git a/src/main.cpp b/src/main.cpp index e42023f..072de74 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -199,6 +199,8 @@ rel_t *resolve_operator(const string &name) return rel_t::And; if (name == "Or") return rel_t::Or; + if (name == "If") + return rel_t::If; return nullptr; } @@ -218,6 +220,7 @@ rel_t *interpret(const json &expr) { // Объект с одним ключом — оператор с аргументами // {"Not": [true]} или {"And": [true, false]} + // {"If": [condition, then_expr, else_expr]} — условная конструкция if (expr.size() != 1) return rel_t::E; // некорректное выражение @@ -232,6 +235,22 @@ rel_t *interpret(const json &expr) if (!args.is_array() || args.empty()) return rel_t::E; // аргументы должны быть непустым массивом + // If: ленивое вычисление — вычисляется только нужная ветка + // {"If": [condition, then_expr, else_expr]} + if (op == rel_t::If) + { + if (args.size() != 3) + return rel_t::E; // If требует ровно 3 аргумента + + rel_t *condition = interpret(args[0]); + rel_t *checked = eval(rel_t::If, condition); // If[condition] + if (checked == rel_t::True) + return interpret(args[1]); // вычисляем then-ветку + if (checked == rel_t::False) + return interpret(args[2]); // вычисляем else-ветку + return rel_t::E; // условие не boolean + } + // Вычисляем аргументы рекурсивно и применяем оператор // Унарный оператор: func[arg] if (args.size() == 1) @@ -484,7 +503,7 @@ int main(int argc, char *argv[]) break; default: cout << R"(https://github.com/netkeep80/avm - Associative Virtual Machine [Version 0.0.3] + Associative Virtual Machine [Version 0.0.4] _____________ / \ / \ diff --git a/test/expr_if_false.json b/test/expr_if_false.json new file mode 100644 index 0000000..28be3a6 --- /dev/null +++ b/test/expr_if_false.json @@ -0,0 +1 @@ +{"If": [false, true, false]} \ No newline at end of file diff --git a/test/expr_if_nested.json b/test/expr_if_nested.json new file mode 100644 index 0000000..766ff47 --- /dev/null +++ b/test/expr_if_nested.json @@ -0,0 +1 @@ +{"If": [{"And": [true, true]}, {"Not": [false]}, {"Or": [false, false]}]} \ No newline at end of file diff --git a/test/expr_if_true.json b/test/expr_if_true.json new file mode 100644 index 0000000..d14f171 --- /dev/null +++ b/test/expr_if_true.json @@ -0,0 +1 @@ +{"If": [true, true, false]} \ No newline at end of file diff --git a/test/unit_test.cpp b/test/unit_test.cpp index c265fed..cc123e3 100644 --- a/test/unit_test.cpp +++ b/test/unit_test.cpp @@ -468,6 +468,104 @@ void test_interpret_error_cases() check(interpret(non_array_args) == rel_t::E, "interpret({Not: true}) = E"); } +// === Тесты условной конструкции If === + +void test_if_vocabulary() +{ + check(rel_t::If != nullptr, "If is not null"); + + // If должен отличаться от других операций + check(rel_t::If != rel_t::Not, "If != Not"); + check(rel_t::If != rel_t::And, "If != And"); + check(rel_t::If != rel_t::Or, "If != Or"); + + // If является сущностью (sub == E) + check(rel_t::If->sub == rel_t::E, "If->sub == E"); +} + +void test_if_eval() +{ + // IF[True] = True (условие истинно) + check(eval(rel_t::If, rel_t::True) == rel_t::True, "IF[True] = True"); + // IF[False] = False (условие ложно) + check(eval(rel_t::If, rel_t::False) == rel_t::False, "IF[False] = False"); + // IF[R] = E (не определено для R) + check(eval(rel_t::If, rel_t::R) == rel_t::E, "IF[R] = E (undefined for R)"); +} + +void test_interpret_if_basic() +{ + // {"If": [true, true, false]} = true (условие истинно → then-ветка) + json if_true = {{"If", json::array({true, true, false})}}; + check(interpret(if_true) == rel_t::True, "interpret({If: [true, true, false]}) = True"); + + // {"If": [false, true, false]} = false (условие ложно → else-ветка) + json if_false = {{"If", json::array({false, true, false})}}; + check(interpret(if_false) == rel_t::False, "interpret({If: [false, true, false]}) = False"); + + // {"If": [true, false, true]} = false (условие истинно → then-ветка = false) + json if_true_rev = {{"If", json::array({true, false, true})}}; + check(interpret(if_true_rev) == rel_t::False, "interpret({If: [true, false, true]}) = False"); + + // {"If": [false, false, true]} = true (условие ложно → else-ветка = true) + json if_false_rev = {{"If", json::array({false, false, true})}}; + check(interpret(if_false_rev) == rel_t::True, "interpret({If: [false, false, true]}) = True"); +} + +void test_interpret_if_with_expressions() +{ + // {"If": [{"Not": [false]}, true, false]} = true + // условие: NOT[False] = True → then-ветка = true + json if_not = {{"If", json::array({{{"Not", json::array({false})}}, true, false})}}; + check(interpret(if_not) == rel_t::True, "interpret({If: [{Not: [false]}, true, false]}) = True"); + + // {"If": [{"And": [true, false]}, true, false]} = false + // условие: AND[True][False] = False → else-ветка = false + json if_and = {{"If", json::array({{{"And", json::array({true, false})}}, true, false})}}; + check(interpret(if_and) == rel_t::False, "interpret({If: [{And: [true, false]}, true, false]}) = False"); + + // {"If": [{"Or": [false, true]}, true, false]} = true + // условие: OR[False][True] = True → then-ветка = true + json if_or = {{"If", json::array({{{"Or", json::array({false, true})}}, true, false})}}; + check(interpret(if_or) == rel_t::True, "interpret({If: [{Or: [false, true]}, true, false]}) = True"); +} + +void test_interpret_if_nested() +{ + // Вложенные If: {"If": [true, {"If": [false, true, false]}, true]} = false + // условие: true → then = {"If": [false, true, false]} = false + json nested_if = {{"If", json::array({true, {{"If", json::array({false, true, false})}}, true})}}; + check(interpret(nested_if) == rel_t::False, "interpret(nested If: then-branch evaluated) = False"); + + // {"If": [false, true, {"If": [true, false, true]}]} = false + // условие: false → else = {"If": [true, false, true]} = false + json nested_if2 = {{"If", json::array({false, true, {{"If", json::array({true, false, true})}}})}}; + check(interpret(nested_if2) == rel_t::False, "interpret(nested If: else-branch evaluated) = False"); + + // Комбинация If и логических операций + // {"If": [{"And": [true, true]}, {"Not": [false]}, {"Or": [false, false]}]} + // условие: AND[True][True] = True → then = NOT[False] = True + json complex_if = {{"If", json::array({{{"And", json::array({true, true})}}, {{"Not", json::array({false})}}, {{"Or", json::array({false, false})}}})}}; + check(interpret(complex_if) == rel_t::True, "interpret(complex If with logical ops) = True"); +} + +void test_interpret_if_error_cases() +{ + // If с неправильным количеством аргументов + json if_no_args = {{"If", json::array()}}; + check(interpret(if_no_args) == rel_t::E, "interpret({If: []}) = E"); + + json if_one_arg = {{"If", json::array({true})}}; + check(interpret(if_one_arg) == rel_t::E, "interpret({If: [true]}) = E"); + + json if_two_args = {{"If", json::array({true, false})}}; + check(interpret(if_two_args) == rel_t::E, "interpret({If: [true, false]}) = E"); + + // If с условием null (не boolean) + json if_null_cond = {{"If", json::array({nullptr, true, false})}}; + check(interpret(if_null_cond) == rel_t::E, "interpret({If: [null, true, false]}) = E"); +} + // === Тесты счётчиков памяти === void test_memory_counters() @@ -518,6 +616,12 @@ int main() test_interpret_nested(); test_interpret_deeply_nested(); test_interpret_error_cases(); + test_if_vocabulary(); + test_if_eval(); + test_interpret_if_basic(); + test_interpret_if_with_expressions(); + test_interpret_if_nested(); + test_interpret_if_error_cases(); test_memory_counters(); cout << endl;