aboutsummaryrefslogtreecommitdiffstats
path: root/lib/world.mli
blob: 386826148bd9925cc618e1bc0dbe81ed2c27bf46 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
(** Worlds: the store of relations, and the loader that builds the
    standalone one.

    Decision 4 of the integration document: the VM is parameterized
    over a world. This module gives the module type every host
    satisfies, the standalone in-memory world, and the loader that
    admits a parsed program into it. Admission is where totality is
    enforced: a program that gets a world is a program whose queries
    terminate. *)

(** A compiled relational expression: what rule bodies and queries
    are made of after names are resolved. [Exists] is lowered away
    at loading: its variable is renamed apart and the form becomes a
    conjunction. *)
type expr =
  | Apply of string * Pattern.t Label.Map.t
  | And of expr * expr
  | Or of expr * expr
  | Not of expr * expr
      (** [u :not d]: the universe holds and the denial has no
          solutions. Decidable negation only: the VM runs the denial
          once its variables are ground. *)
  | Forall of expr * expr
      (** Every solution of the quantifier satisfies the body. The
          VM runs it when the quantifier is decidable, which keeps
          universals off infinite domains. *)

type rule = {
  rule_name : string;
  head : Pattern.t Label.Map.t;
  body : expr;
  witness : Label.t list;
      (** For a recursive rule, the lexicographic measure of its
          call-graph component: at some position the recursive
          argument strictly decreases, all earlier positions staying
          equal. The VM requires every position ground before
          unfolding, which is what turns the admission proof into
          runtime termination. Empty on non-recursive rules. *)
}

(** The primitive host-checked domains. *)
type primitive = D_integer | D_natural | D_rational | D_string

(** What a schema constrains an attribute by: a primitive domain, or
    a membership criterion, an arbitrary relational expression with
    exactly one free variable, the subject, bound to the attribute
    value at admission. Foreign keys are the criterion
    [([Department id: D])], nothing more special than that. *)
type domain =
  | Primitive of primitive
  | Criterion of { subject : string; body : expr }

(** How a relation is defined. *)
type def =
  | Table of { schema : (Label.t * domain) list; facts : Term.t Label.Map.t list }
      (** Extensional: a schema and finite stored tuples. *)
  | Rules of rule list  (** Intensional: defined by admitted rules. *)

val member_of_primitive : Term.t -> primitive -> bool
(** Host-decided membership in a primitive domain, with the literal
    inclusions of decision 12. Exposed so stateful worlds check
    schemas with exactly the loader's judgment. *)

(** What a transition relation says when consulted (decision 3:
    transitions are relations over states, provided by the world,
    never by the engine). *)
type transition_result =
  | T_state of Term.t
      (** The successor state; the VM unifies it with [after:]. *)
  | T_fail  (** No such transition: integrity violation and kin. *)
  | T_stuck  (** Arguments not ground yet; the planner defers. *)

(** What a host must answer for the VM to run: which relation lives
    under a name, optionally at a state ([at:] reads, decision 7),
    and which names are transition relations. Builtins are the VM's
    own and are not consulted here. The standalone world has one
    implicit state and no transitions (decision 5). *)
module type WORLD = sig
  type t

  val find : t -> ?at:Term.t -> string -> def option

  val transition :
    t -> string -> Pattern.t Label.Map.t -> transition_result option
  (** [None] when the name is not a transition relation of this
      world. Bindings arrive resolved. *)
end

type error

val pp_error : Format.formatter -> error -> unit

(** A fact attribute awaiting a criterion check. The loader cannot
    run the solver (the solver sits above it), so criterion
    admission is finished by {!Program}, against the fully loaded
    world. *)
type pending = {
  pending_relation : string;
  pending_label : Label.t;
  pending_body : expr;
      (** The criterion with its subject already bound to the
          attribute value; admissible iff it has a solution. *)
}

val expr_of_node : Ast.node -> (expr, error) result
(** Compiles one surface expression: names resolve by context,
    [Exists] lowers, quantified variables rename apart. This is the
    embedding hosts' way in: they compile queries themselves and
    answer for their own relations, so no standalone reference
    checking happens here. *)

(** The standalone world: an immutable catalog loaded from program
    text. This is Sol as a separate language; there are no branches,
    no transitions, only the pure fragment. *)
module Standalone : sig
  type t

  val find : t -> ?at:Term.t -> string -> def option
  (** [at:] is accepted and ignored: the standalone world is its own
      single, eternal state. *)

  val transition : t -> string -> Pattern.t Label.Map.t -> transition_result option
  (** Always [None]: no branches, no transitions, the pure
      fragment. *)

  val load : Ast.statement list -> (t * expr list * pending list, error) result
  (** Admits a program: registers relations, checks every fact
      against its schema and primitive domains, compiles rules and
      queries, and enforces totality. Rejected, not deferred: a
      recursive rule without a structural decrease witness, a mutual
      recursion cycle, an unknown relation, or an ill-typed fact is
      an [error], and no world exists. Returns the world, the query
      expressions in program order, and the criterion checks for
      {!Program} to finish. *)
end