dsl

package module
v0.0.0-...-17b8eba Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 15, 2026 License: GPL-3.0 Imports: 4 Imported by: 0

README

DSLparser

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

Содержание

Установка

go get github.com/MxAer/DSLparser

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

import "github.com/MxAer/DSLparser"

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

package main

import (
    "fmt"
    "github.com/MxAer/DSLparser"
)

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.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Evaluate

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

func IntPtr

func IntPtr(i int) *int

func StringPtr

func StringPtr(s string) *string

Types

type ComparisonNode

type ComparisonNode struct {
	Field    string
	Operator string
	Value    interface{}
	Position int
}

func (*ComparisonNode) Evaluate

func (n *ComparisonNode) Evaluate(ctx *EvaluationContext) (bool, error)

func (*ComparisonNode) NodeCount

func (n *ComparisonNode) NodeCount() int

func (*ComparisonNode) String

func (n *ComparisonNode) String() string

func (*ComparisonNode) Validate

func (n *ComparisonNode) Validate(tier Tier) []ValidationError

type EvaluationContext

type EvaluationContext struct {
	Transaction Transaction
	User        User
}

type FieldType

type FieldType int
const (
	TypeNumber FieldType = iota
	TypeString
)

type Lexer

type Lexer struct {
	// contains filtered or unexported fields
}

func NewLexer

func NewLexer(input string) *Lexer

func (*Lexer) Tokenize

func (l *Lexer) Tokenize() ([]Token, error)

type LogicalNode

type LogicalNode struct {
	Operator string
	Left     Node
	Right    Node
}

func (*LogicalNode) Evaluate

func (n *LogicalNode) Evaluate(ctx *EvaluationContext) (bool, error)

func (*LogicalNode) NodeCount

func (n *LogicalNode) NodeCount() int

func (*LogicalNode) String

func (n *LogicalNode) String() string

func (*LogicalNode) Validate

func (n *LogicalNode) Validate(tier Tier) []ValidationError

type Node

type Node interface {
	Evaluate(*EvaluationContext) (bool, error)
	String() string
	NodeCount() int
	Validate(Tier) []ValidationError
}

func Parse

func Parse(expression string) (Node, error)

type Parser

type Parser struct {
	// contains filtered or unexported fields
}

type Tier

type Tier int
const (
	Tier0 Tier = iota
	Tier1
	Tier2
	Tier3
	Tier4
	Tier5
)

type Token

type Token struct {
	Type     TokenType
	Value    string
	Position int
}

type TokenType

type TokenType int
const (
	TokenField TokenType = iota
	TokenOperator
	TokenLogicalOp
	TokenNumber
	TokenString
	TokenLParen
	TokenRParen
	TokenEOF
)

type Transaction

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

type User

type User struct {
	Age    *int
	Region *string
}

type ValidationError

type ValidationError struct {
	Code     string
	Message  string
	Position int
	Near     string
}

func Validate

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

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL