From 58eff827c1d48148e3d22d4c84b37d4115105e90 Mon Sep 17 00:00:00 2001 From: JohnnyT Date: Tue, 9 Sep 2025 05:26:42 -0600 Subject: [PATCH 1/2] Releases v3.4.0 --- AGENTS.md | 16 ++++++++++++++++ CHANGELOG.md | 25 +++++++++++++++++++++++++ README.md | 20 ++++++++++++++++++-- mix.exs | 2 +- 4 files changed, 60 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7fb516e..1022106 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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: `3 days ago`, `2 weeks from now`, `next month`, `last year` +- Date/DateTime arithmetic: `#2024-01-10# + 5 days`, `#2024-01-15T10:30:00Z# - 2 hours` +- Grammar updates: `duration` and `relative_date` productions +- Full pipeline support (lexer, parser, compiler, evaluator, string visitor) with tests +- Examples: + + ```elixir + Predicator.evaluate("created_at > 3 days ago", %{"created_at" => ~U[2024-01-20 00:00:00Z]}) + Predicator.evaluate("due_at < 2 weeks from now", %{"due_at" => Date.add(Date.utc_today(), 10)}) + Predicator.evaluate("#2024-01-10# + 5 days = #2024-01-15#", %{}) + Predicator.evaluate("#2024-01-15T10:30:00Z# - 2 hours < #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"}}`) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11d3742..2b173bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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., `3 days ago`, `2 weeks from now`, `next month`, `last year`) +- Date and DateTime arithmetic using durations (e.g., `#2024-01-10# + 5 days`, `#2024-01-15T10:30:00Z# - 2 hours`) +- Grammar additions: `duration` and `relative_date` productions +- Full pipeline support (lexer, parser, compiler, evaluator, string visitor) with tests + +#### Examples: + +```elixir +Predicator.evaluate("created_at > 3 days ago", %{"created_at" => ~U[2024-01-20 00:00:00Z]}) +Predicator.evaluate("due_at < 2 weeks from now", %{"due_at" => Date.add(Date.utc_today(), 10)}) +Predicator.evaluate("#2024-01-10# + 5 days = #2024-01-15#", %{}) +Predicator.evaluate("#2024-01-15T10:30:00Z# - 2 hours < #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 diff --git a/README.md b/README.md index 570a8a7..508f7cc 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 ``` @@ -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 > 3 days ago", %{"created_at" => ~U[2024-01-20 00:00:00Z]}) +true + +iex> Predicator.evaluate!("due_at < 2 weeks from now", %{"due_at" => Date.add(Date.utc_today(), 10)}) +true + +iex> Predicator.evaluate!("#2024-01-10# + 5 days = #2024-01-15#", %{}) +true + +iex> Predicator.evaluate!("#2024-01-15T10:30:00Z# - 2 hours < #2024-01-15T10:30:00Z#", %{}) +true + # List literals and membership iex> Predicator.evaluate!("role in ['admin', 'manager']", %{"role" => "admin"}) true @@ -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., `3 days`, `2 hours`, `15 minutes`) + - In relative expressions: `3 days ago`, `2 weeks from now`, `next month`, `last year` + - In arithmetic: `#2024-01-10# + 5 days`, `#2024-01-15T10:30:00Z# - 2 hours` - **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) @@ -554,4 +571,3 @@ mix dialyzer # Type checking ## Documentation Full documentation is available at [HexDocs](https://hexdocs.pm/predicator). - diff --git a/mix.exs b/mix.exs index d80b69b..0c14589 100644 --- a/mix.exs +++ b/mix.exs @@ -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 [ From 6903002d5937a646113ba40830dba2affea80786 Mon Sep 17 00:00:00 2001 From: JohnnyT Date: Tue, 9 Sep 2025 05:34:12 -0600 Subject: [PATCH 2/2] Updates docs to use actual implementation --- AGENTS.md | 12 ++++++------ CHANGELOG.md | 12 ++++++------ README.md | 14 +++++++------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1022106..b086b77 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -152,17 +152,17 @@ test/predicator/ ### Durations and Relative Dates (v3.4.0) - Natural-language durations and relative time expressions -- Relative dates: `3 days ago`, `2 weeks from now`, `next month`, `last year` -- Date/DateTime arithmetic: `#2024-01-10# + 5 days`, `#2024-01-15T10:30:00Z# - 2 hours` +- 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 > 3 days ago", %{"created_at" => ~U[2024-01-20 00:00:00Z]}) - Predicator.evaluate("due_at < 2 weeks from now", %{"due_at" => Date.add(Date.utc_today(), 10)}) - Predicator.evaluate("#2024-01-10# + 5 days = #2024-01-15#", %{}) - Predicator.evaluate("#2024-01-15T10:30:00Z# - 2 hours < #2024-01-15T10:30:00Z#", %{}) + 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) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b173bf..fa5cf10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,18 +13,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Durations and relative date/time arithmetic -- New duration literals and relative date expressions (e.g., `3 days ago`, `2 weeks from now`, `next month`, `last year`) -- Date and DateTime arithmetic using durations (e.g., `#2024-01-10# + 5 days`, `#2024-01-15T10:30:00Z# - 2 hours`) +- 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 > 3 days ago", %{"created_at" => ~U[2024-01-20 00:00:00Z]}) -Predicator.evaluate("due_at < 2 weeks from now", %{"due_at" => Date.add(Date.utc_today(), 10)}) -Predicator.evaluate("#2024-01-10# + 5 days = #2024-01-15#", %{}) -Predicator.evaluate("#2024-01-15T10:30:00Z# - 2 hours < #2024-01-15T10:30:00Z#", %{}) +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 diff --git a/README.md b/README.md index 508f7cc..5396233 100644 --- a/README.md +++ b/README.md @@ -60,16 +60,16 @@ iex> Predicator.evaluate!("created_at < #2024-01-15T10:30:00Z#", %{"created_at" true # Durations, relative dates, and date arithmetic -iex> Predicator.evaluate!("created_at > 3 days ago", %{"created_at" => ~U[2024-01-20 00:00:00Z]}) +iex> Predicator.evaluate!("created_at > 3d ago", %{"created_at" => ~U[2024-01-20 00:00:00Z]}) true -iex> Predicator.evaluate!("due_at < 2 weeks from now", %{"due_at" => Date.add(Date.utc_today(), 10)}) +iex> Predicator.evaluate!("due_at < 2w from now", %{"due_at" => Date.add(Date.utc_today(), 10)}) true -iex> Predicator.evaluate!("#2024-01-10# + 5 days = #2024-01-15#", %{}) +iex> Predicator.evaluate!("#2024-01-10# + 5d = #2024-01-15#", %{}) true -iex> Predicator.evaluate!("#2024-01-15T10:30:00Z# - 2 hours < #2024-01-15T10:30:00Z#", %{}) +iex> Predicator.evaluate!("#2024-01-15T10:30:00Z# - 2h < #2024-01-15T10:30:00Z#", %{}) true # List literals and membership @@ -348,9 +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., `3 days`, `2 hours`, `15 minutes`) - - In relative expressions: `3 days ago`, `2 weeks from now`, `next month`, `last year` - - In arithmetic: `#2024-01-10# + 5 days`, `#2024-01-15T10:30:00Z# - 2 hours` +- **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)