Proposal 0001: QuerySpec Host DSL

Status

draft

Summary

Introduce a host-agnostic QuerySpec model and syntax so Hyperphp can support richer domain DSLs (database, CMS, search, and similar domains) without requiring hosts to parse raw free-form text statements.

Motivation

Current host extensibility is command-oriented. This is effective for simple verbs, but awkward for query-like language.

We want script expressions such as:

tell application "database"
    set user_id to id of user of request
    set user_name to name of first user whose id is user_id
end tell

and:

tell application "database"
    set latest_posts to every post sorted by created reverse limit 10
    set author_posts to every post whose author is "patrik" and status is "published" sorted by created normal limit 5
end tell

to compile to one shared query protocol that any host can execute.

Goals

  • Add a shared QuerySpec execution protocol for hosts.
  • Keep syntax Apple-like and readable.
  • Support v1 query capabilities:
  • filter (whose)
  • order (sorted by <field> normal|reverse)
  • limit (limit <n>)
  • Preserve backward compatibility with existing tell and query behavior.
  • Fail fast with explicit runtime errors when host capabilities are missing.

Non-Goals

  • SQL compatibility or SQL parsing.
  • joins, grouping, projections, and aggregates in v1.
  • host-specific raw-DSL parser callbacks in this proposal.
  • ordering keyword aliases in v1 (ordered by, descending, backwards).

User-Facing Syntax / Behavior

Canonical v1 forms:

every post whose author is "patrik" sorted by created reverse limit 5
every post sorted by created normal
first user whose id is user_id
name of first user whose id is user_id

Rules:

  • ordering must use sorted by.
  • direction must be exactly normal or reverse.
  • limit must be a positive integer expression.
  • clause order in v1:
  • base query
  • optional whose
  • optional sorted by
  • optional limit

Semantics

  • Query expressions compile to QuerySpec objects.
  • QuerySpec execution is delegated to the current tell host when the host supports QuerySpec execution.
  • If QuerySpec-only features are used and the host does not support QuerySpec execution, runtime raises an error (fail fast).
  • Existing simple query forms continue to work on legacy hosts.
  • first <kind> ... returns a single resolved item using existing object-specifier semantics.

Public API / Interface Changes

Add Hyperphp\Host\QuerySpecExecutableInterface:

  • executeQuerySpec(QuerySpec $spec, QueryExecutionContext $context): mixed

Add Hyperphp\Host\QuerySpec value object:

  • kind: string
  • mode: "one" | "many"
  • source: mixed|null
  • predicate: PredicateSpec|null
  • sort: SortSpec|null
  • limit: int|null

Add Hyperphp\Host\QueryExecutionContext value object:

  • applicationName: string
  • bindings: array<string,mixed>
  • eventContext: array<string,mixed>
  • tellPath: list<string> (optional diagnostics)

Parser / AST Changes

  • Add query AST node(s) that capture:
  • query kind and mode
  • optional source expression
  • optional predicate
  • optional sort clause
  • optional limit clause
  • Extend parser query path to support:
  • every <kind> ... sorted by ... limit ...
  • first <kind> whose ... sorted by ...
  • name of first <kind> whose ...
  • Reject non-canonical ordering aliases in v1.

Runtime Changes

  • Interpreter evaluates query AST into QuerySpec.
  • Interpreter builds QueryExecutionContext from current runtime state.
  • Dispatch flow:
  • If current tell object supports QuerySpecExecutableInterface, call executeQuerySpec.
  • Else:
    • if query is legacy-compatible, use existing resolver query flow.
    • if QuerySpec-only features are present, raise runtime error.

Error Model

Add explicit runtime errors for:

  • host does not support QuerySpec execution for requested features
  • invalid sort direction token
  • invalid or non-positive limit
  • malformed clause ordering

Errors must include line and column context.

Testing Plan

Parser tests:

  • parse canonical valid forms
  • reject alias keywords and directions in v1
  • reject duplicate or misordered clauses

Runtime tests:

  • QuerySpec host executes filter + sort + limit correctly
  • fail-fast on unsupported host capabilities
  • limit coercion and validation behavior

Regression tests:

  • existing every ... whose ..., object specifiers, and of chains remain green
  • existing hosts continue to work for non-QuerySpec scripts

Backward Compatibility

  • No breaking change for existing scripts unless they use new QuerySpec syntax.
  • Legacy hosts continue working with current command and query behavior.
  • QuerySpec support is opt-in through a new host interface.

Open Questions

  • Should singular sugar (user whose id is user_id) be included in v1 or deferred?
  • Should runtime in-memory fallback for unsupported sorted by or limit ever be allowed in a future phase?
  • Do we want explicit null behavior for empty singular queries, or strict errors only?

Rollout Plan

  1. Add QuerySpec interfaces and value objects in host layer.
  2. Add parser and AST support for canonical syntax.
  3. Add interpreter QuerySpec execution path and fail-fast errors.
  4. Add one reference host example demonstrating full QuerySpec execution.
  5. Update language and host docs.

Acceptance Criteria

  • New parser and runtime tests for QuerySpec pass.
  • Existing test suite remains green.
  • At least one reference host demonstrates filter + sort + limit behavior.
  • Documentation clearly states canonical syntax and explicit non-goals.