Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,22 @@ test/predicator/

## Recent Additions (2025)

### Durations and Relative Dates (v3.4.0)

- Natural-language durations and relative time expressions
- Relative dates: `3d ago`, `2w from now`, `next 1mo`, `last 1y`
- Date/DateTime arithmetic: `#2024-01-10# + 5d`, `#2024-01-15T10:30:00Z# - 2h`
- Grammar updates: `duration` and `relative_date` productions
- Full pipeline support (lexer, parser, compiler, evaluator, string visitor) with tests
- Examples:

```elixir
Predicator.evaluate("created_at > 3d ago", %{"created_at" => ~U[2024-01-20 00:00:00Z]})
Predicator.evaluate("due_at < 2w from now", %{"due_at" => Date.add(Date.utc_today(), 10)})
Predicator.evaluate("#2024-01-10# + 5d = #2024-01-15#", %{})
Predicator.evaluate("#2024-01-15T10:30:00Z# - 2h < #2024-01-15T10:30:00Z#", %{})
```

### Object Literals (v3.1.0 - JavaScript-Style Objects)

- **Syntax Support**: Complete JavaScript-style object literal syntax (`{}`, `{name: "John"}`, `{user: {role: "admin"}}`)
Expand Down
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [3.4.0] - 2025-09-09

### Added

#### Durations and relative date/time arithmetic

- New duration literals and relative date expressions (e.g., `3d ago`, `2w from now`, `next 1mo`, `last 1y`)
- Date and DateTime arithmetic using durations (e.g., `#2024-01-10# + 5d`, `#2024-01-15T10:30:00Z# - 2h`)
- Grammar additions: `duration` and `relative_date` productions
- Full pipeline support (lexer, parser, compiler, evaluator, string visitor) with tests

#### Examples:

```elixir
Predicator.evaluate("created_at > 3d ago", %{"created_at" => ~U[2024-01-20 00:00:00Z]})
Predicator.evaluate("due_at < 2w from now", %{"due_at" => Date.add(Date.utc_today(), 10)})
Predicator.evaluate("#2024-01-10# + 5d = #2024-01-15#", %{})
Predicator.evaluate("#2024-01-15T10:30:00Z# - 2h < #2024-01-15T10:30:00Z#", %{})
```

### Documentation

- Updated EBNF grammar in docs
- Added AGENTS.md with model-agnostic agent guidance; `CLAUDE.md` now references the same content

## [3.3.0] - 2025-08-31

### Added
Expand Down
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Predicator allows you to safely evaluate user-defined expressions without the se
- 🔄 **Reversible**: Convert AST back to string expressions with formatting options
- 🧮 **Arithmetic**: Full arithmetic operations (`+`, `-`, `*`, `/`, `%`) with proper precedence
- 📅 **Date Support**: Native date and datetime literals with ISO 8601 format
- ⏳ **Durations & Relative Dates**: Natural-language durations and relative time expressions with date arithmetic
- 📋 **Lists**: List literals with membership operations (`in`, `contains`)
- 🧠 **Smart Logic**: Logical operators with proper precedence (`AND`, `OR`, `NOT`)
- 🔧 **Functions**: Built-in functions for string, numeric, and date operations
Expand All @@ -32,7 +33,7 @@ Add `predicator` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:predicator, "~> 3.3"}
{:predicator, "~> 3.4"}
]
end
```
Expand All @@ -58,6 +59,19 @@ true
iex> Predicator.evaluate!("created_at < #2024-01-15T10:30:00Z#", %{"created_at" => ~U[2024-01-10 09:00:00Z]})
true

# Durations, relative dates, and date arithmetic
iex> Predicator.evaluate!("created_at > 3d ago", %{"created_at" => ~U[2024-01-20 00:00:00Z]})
true

iex> Predicator.evaluate!("due_at < 2w from now", %{"due_at" => Date.add(Date.utc_today(), 10)})
true

iex> Predicator.evaluate!("#2024-01-10# + 5d = #2024-01-15#", %{})
true

iex> Predicator.evaluate!("#2024-01-15T10:30:00Z# - 2h < #2024-01-15T10:30:00Z#", %{})
true

# List literals and membership
iex> Predicator.evaluate!("role in ['admin', 'manager']", %{"role" => "admin"})
true
Expand Down Expand Up @@ -334,6 +348,9 @@ iex> Predicator.evaluate("'coding' in user.hobbies", list_context)
- **Booleans**: `true`, `false` (or plain identifiers like `active`, `expired`)
- **Dates**: `#2024-01-15#` (ISO 8601 date format)
- **DateTimes**: `#2024-01-15T10:30:00Z#` (ISO 8601 datetime format with timezone)
- **Durations**: Natural units for time spans (e.g., `3d`, `2h`, `15m`)
- In relative expressions: `3d ago`, `2w from now`, `next 1mo`, `last 1y`
- In arithmetic: `#2024-01-10# + 5d`, `#2024-01-15T10:30:00Z# - 2h`
- **Lists**: `[1, 2, 3]`, `['admin', 'manager']` (homogeneous collections)
- **Objects**: `{}`, `{name: "John", age: 30}`, `{user: {role: "admin"}}` (JavaScript-style object literals)
- **Identifiers**: `score`, `user_name`, `is_active`, `user.profile.name`, `user['key']`, `items[0]` (variable references with dot notation and bracket notation for nested data)
Expand Down Expand Up @@ -554,4 +571,3 @@ mix dialyzer # Type checking
## Documentation

Full documentation is available at [HexDocs](https://hexdocs.pm/predicator).

2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defmodule Predicator.MixProject do
use Mix.Project

@app :predicator
@version "3.3.0"
@version "3.4.0"
@description "A secure, non-evaling condition (boolean predicate) engine for end users"
@source_url "https://github.com/riddler/predicator-ex"
@deps [
Expand Down