No description
Find a file
2026-01-16 10:24:58 +00:00
example Some DSL parsers 2017-09-11 11:38:43 +03:00
dsl.go Some DSL parsers 2017-09-11 11:38:43 +03:00
errors.go Some DSL parsers 2017-09-11 11:38:43 +03:00
evaluator.go Some DSL parsers 2017-09-11 11:38:43 +03:00
go.mod Fixed mod 2017-09-11 11:40:43 +03:00
LICENSE Some DSL parsers 2017-09-11 11:38:43 +03:00
parser.go Some DSL parsers 2017-09-11 11:38:43 +03:00
README.md Обновить README.md 2026-01-16 10:24:58 +00:00
validator.go Some DSL parsers 2017-09-11 11:38:43 +03:00

DSLparser

Go-пакет для парсинга, валидации и выполнения DSL (Domain Specific Language) выражений для систем фрод-мониторинга. Пакет реализует специфичный язык запросов для проверки транзакций на мошенничество.

Содержание

Установка

go get balyukhub.ru/igor/dsl-parser

Импортируйте в ваш проект:

import "balyukhub.ru/igor/dsl-parser"

Быстрый старт

package main

import (
    "fmt"
    "balyukhub.ru/igor/dsl-parser"
)

func main() {
    // Создаем контекст транзакции
    ctx := &dsl.EvaluationContext{
        Transaction: dsl.Transaction{
            Amount:   15000.50,
            Currency: "RUB",
        },
        User: dsl.User{
            Age:    dsl.IntPtr(25),
            Region: dsl.StringPtr("RU"),
        },
    }

    // Валидируем выражение
    expr := "amount > 10000 AND currency = 'RUB'"
    valid, errors := dsl.Validate(expr, dsl.Tier3)
    
    if valid {
        // Вычисляем результат
        result, description, err := dsl.Evaluate(expr, ctx)
        fmt.Printf("Result: %v\nExpression: %s\n", result, description)
    } else {
        for _, err := range errors {
            fmt.Printf("Error: %s\n", err.Message)
        }
    }
}

Синтаксис DSL

Базовые элементы

1. Поля (Fields)

amount          - сумма транзакции (число)
currency        - валюта транзакции (строка)
merchantId      - ID мерчанта (строка)
ipAddress       - IP-адрес (строка)
deviceId        - ID устройства (строка)
user.age        - возраст пользователя (число, может быть null)
user.region     - регион пользователя (строка, может быть null)

2. Литералы (Literals)

  • Числа: 1000, 50.5, 0.01
  • Строки: 'RUB', 'USD', '127.0.0.1' (в одинарных кавычках)

3. Операторы сравнения (Comparison Operators)

>   - больше
>=  - больше или равно
<   - меньше
<=  - меньше или равно
=   - равно
!=  - не равно

4. Логические операторы (Logical Operators)

AND - логическое И (регистронезависимый)
OR  - логическое ИЛИ (регистронезависимый)
NOT - логическое НЕ (регистронезависимый)

5. Группировка (Grouping)

( ... ) - скобки для изменения приоритета

Грамматика (EBNF)

expression = term { "OR" term }
term       = factor { "AND" factor }
factor     = "NOT" factor | comparison | "(" expression ")"
comparison = field operator value
field      = "amount" | "currency" | "merchantId" | "ipAddress" | "deviceId"
           | "user.age" | "user.region"
operator   = ">" | ">=" | "<" | "<=" | "=" | "!="
value      = number | string
string     = "'" { character } "'"
number     = digit { digit } [ "." digit { digit } ]

Приоритет операторов

  1. NOT - наивысший приоритет
  2. AND - средний приоритет
  3. OR - низший приоритет
  4. Скобки ( ) - изменяют приоритет

Правила типизации

  1. Числовые поля (amount, user.age):

    • Поддерживают все операторы сравнения: >, >=, <, <=, =, !=
    • Сравниваются только с числовыми литералами
  2. Строковые поля (currency, merchantId, ipAddress, deviceId, user.region):

    • Поддерживают только операторы: =, !=
    • Сравниваются только со строковыми литералами
    • Регистрозависимое сравнение
  3. Null-безопасность (только для полей пользователя):

    • Если user.age или user.region равно null
    • Любое сравнение с null возвращает false
    • Пример: user.age > 18false (если user.age = null)

Форматирование выражений

Пакет автоматически нормализует выражения:

  • AND/OR/NOT приводятся к верхнему регистру
  • Добавляются пробелы вокруг операторов
  • Сохраняется логическая эквивалентность

Пример нормализации:

amount>100 and currency='RUB'  →  amount > 100 AND currency = 'RUB'

Уровни поддержки

Tier 0 - Базовый режим

  • Все выражения считаются невалидными
  • Возвращается ошибка DSL_UNSUPPORTED_TIER
  • Используется для начальной интеграции без поддержки DSL

Tier 1 - Минимальный набор

  • Поля: amount
  • Операторы: >, >=, <, <=, =, !=
  • Литералы: числа
  • Пример: amount > 1000

Tier 2 - Строковые поля

  • Добавляет поля: currency, merchantId, ipAddress, deviceId
  • Добавляет литералы: строки в одинарных кавычках
  • Ограничение: для строк только = и !=
  • Пример: currency = 'USD'

Tier 3 - Базовая логика

  • Добавляет операторы: AND, OR
  • Приоритет: AND выше OR
  • Пример: amount > 100 AND currency = 'RUB'

Tier 4 - Расширенная логика

  • Добавляет оператор: NOT
  • Добавляет группировку: ( ... )
  • Приоритет: NOT > AND > OR
  • Пример: NOT (amount > 10000 AND merchantId = '123')

Tier 5 - Поля пользователя

  • Добавляет поля: user.age, user.region
  • Null-безопасность: сравнение с nullfalse
  • Пример: user.age > 18 AND user.region = 'EU'

API

Основные функции

Validate(expression string, tier Tier) (bool, []ValidationError)

Валидирует DSL выражение для указанного уровня.

Параметры:

  • expression - строка с DSL выражением
  • tier - уровень поддержки (0-5)

Возвращает:

  • bool - true если выражение валидно
  • []ValidationError - список ошибок (пустой при успехе)

Коды ошибок:

  • DSL_PARSE_ERROR - синтаксическая ошибка
  • DSL_INVALID_FIELD - неизвестное поле
  • DSL_INVALID_OPERATOR - недопустимый оператор для типа
  • DSL_UNSUPPORTED_TIER - функция недоступна на текущем уровне
  • DSL_TOO_COMPLEX - выражение превышает 100 узлов AST

Evaluate(expression string, ctx *EvaluationContext) (bool, string, error)

Вычисляет DSL выражение для заданного контекста.

Параметры:

  • expression - валидное DSL выражение
  • ctx - контекст с данными транзакции и пользователя

Возвращает:

  • bool - результат вычисления (true/false)
  • string - нормализованная форма выражения
  • error - ошибка вычисления (nil при успехе)

Типы данных

EvaluationContext

type EvaluationContext struct {
    Transaction Transaction
    User        User
}

type Transaction struct {
    Amount     float64
    Currency   string
    MerchantID string
    IPAddress  string
    DeviceID   string
}

type User struct {
    Age    *int    // может быть nil
    Region *string // может быть nil
}

Вспомогательные функции

func IntPtr(i int) *int
func StringPtr(s string) *string

Примеры

Пример 1: Простая проверка суммы

expr := "amount > 10000"
ctx := &dsl.EvaluationContext{
    Transaction: dsl.Transaction{Amount: 15000},
}
result, normalized, _ := dsl.Evaluate(expr, ctx)
// result = true
// normalized = "amount > 10000"

Пример 2: Проверка валюты и суммы

expr := "amount > 100 AND currency = 'USD'"
ctx := &dsl.EvaluationContext{
    Transaction: dsl.Transaction{
        Amount:   500,
        Currency: "USD",
    },
}
// result = true

Пример 3: Сложная логика с пользователем

expr := "NOT (amount > 10000 AND merchantId = 'blocked') OR user.region = 'TRUSTED'"
ctx := &dsl.EvaluationContext{
    Transaction: dsl.Transaction{
        Amount:     15000,
        MerchantID: "blocked",
    },
    User: dsl.User{
        Region: dsl.StringPtr("TRUSTED"),
    },
}
// result = true (благодаря второй части OR)

Пример 4: Null-безопасность

expr := "user.age > 18"
ctx := &dsl.EvaluationContext{
    User: dsl.User{
        Age: nil, // возраст не указан
    },
}
// result = false (null → false)

Пример 5: Комбинированные условия

expr := "(amount > 1000 AND currency = 'EUR') OR (amount > 5000 AND currency = 'USD')"
ctx := &dsl.EvaluationContext{
    Transaction: dsl.Transaction{
        Amount:   6000,
        Currency: "USD",
    },
}
// result = true

Ограничения

1. Ограничение сложности

  • Максимальное количество узлов AST: 100
  • При превышении: ошибка DSL_TOO_COMPLEX
  • Узел = поле, оператор, литерал или логическая операция

2. Ограничения типов

  • Строковые поля: только = и !=
  • Числовые поля: все операторы сравнения
  • Смешивание типов запрещено: amount > 'text' → ошибка

3. Null-обработка

  • Только для полей user.age и user.region
  • Любая операция с nullfalse
  • Не вызывает ошибок выполнения

4. Семантика выполнения

  • Short-circuit evaluation для AND/OR
  • Не упрощает логически противоречивые выражения
  • Пример: amount > 100 AND amount < 50 → вычисляется как false

5. Форматирование

  • Автоматическая нормализация пробелов
  • Регистронезависимые AND/OR/NOT
  • Сохранение логической эквивалентности

Разработка

Добавление новых полей

  1. Добавить поле в fieldTypes (тип)
  2. Добавить обработку в ComparisonNode.Evaluate()
  3. Обновить документацию и грамматику

Добавление новых операторов

  1. Добавить оператор в isComparisonOperator()
  2. Добавить обработку в compare* функции
  3. Обновить валидатор типов

Тестирование

# Запуск тестов
go test ./...

# Тестирование конкретного уровня
go test -v -run "TestTier[1-5]"

# Проверка покрытия
go test -coverprofile=coverage.out
go tool cover -html=coverage.out

Производительность

  • Парсинг: O(n) по длине выражения
  • Вычисление: O(n) по количеству узлов
  • Валидация: O(n) + проверка уровня
  • Подходит для обработки тысяч транзакций в секунду

Лицензия

MIT License.