Theory Server
theory Server
imports Base
begin
chapter ‹The Isabelle server›
text ‹
An Isabelle session requires at least two processes, which are both rather
heavy: Isabelle/Scala for the system infrastructure and Isabelle/ML for the
logic session (e.g.\ HOL). In principle, these processes can be invoked
directly on the command-line, e.g.\ via @{tool java}, @{tool scala}, @{tool
process}, @{tool console}, but this approach is inadequate for reactive
applications that require quick responses from the prover.
In contrast, the Isabelle server exposes Isabelle/Scala as a
``terminate-stay-resident'' application that manages multiple logic
∗‹sessions› and concurrent tasks to use ∗‹theories›. This is analogous to
\<^ML>‹Thy_Info.use_theories› in Isabelle/ML, with proper support for
concurrent invocations.
The client/server arrangement via TCP sockets also opens possibilities for
remote Isabelle services that are accessed by local applications, e.g.\ via
an SSH tunnel.
›
section ‹Command-line tools›
subsection ‹Server \label{sec:tool-server}›
text ‹
The @{tool_def server} tool manages resident server processes:
@{verbatim [display]
‹Usage: isabelle server [OPTIONS]
Options are:
-L FILE logging on FILE
-c console interaction with specified server
-l list servers (alternative operation)
-n NAME explicit server name (default: isabelle)
-p PORT explicit server port
-s assume existing server, no implicit startup
-x exit specified server (alternative operation)
Manage resident Isabelle servers.›}
The main operation of ▩‹isabelle server› is to ensure that a named server is
running, either by finding an already running process (according to the
central database file \<^path>‹$ISABELLE_HOME_USER/servers.db›) or by
becoming itself a new server that accepts connections on a particular TCP
socket. The server name and its address are printed as initial output line.
If another server instance is already running, the current
▩‹isabelle server› process will terminate; otherwise, it keeps running as a
new server process until an explicit ▩‹shutdown› command is received.
Further details of the server socket protocol are explained in
\secref{sec:server-protocol}.
Other server management operations are invoked via options ▩‹-l› and ▩‹-x›
(see below).
┉
Option ▩‹-n› specifies an alternative server name: at most one process for
each name may run, but each server instance supports multiple connections
and logic sessions.
┉
Option ▩‹-p› specifies an explicit TCP port for the server socket (which is
always on ▩‹localhost›): the default is to let the operating system assign a
free port number.
┉
Option ▩‹-s› strictly assumes that the specified server process is already
running, skipping the optional server startup phase.
┉
Option ▩‹-c› connects the console in/out channels after the initial check
for a suitable server process. Also note that the @{tool client} tool
(\secref{sec:tool-client}) provides a command-line editor to interact with
the server.
┉
Option ▩‹-L› specifies a log file for exceptional output of internal server
and session operations.
┉
Operation ▩‹-l› lists all active server processes with their connection
details.
┉
Operation ▩‹-x› exits the specified server process by sending it a
▩‹shutdown› command.
›
subsection ‹Client \label{sec:tool-client}›
text ‹
The @{tool_def client} tool provides console interaction for Isabelle
servers:
@{verbatim [display]
‹Usage: isabelle client [OPTIONS]
Options are:
-n NAME explicit server name
-p PORT explicit server port
Console interaction for Isabelle server (with line-editor).›}
This is a wrapper to ▩‹isabelle server -s -c› for interactive
experimentation, which uses @{setting ISABELLE_LINE_EDITOR} if available.
The server name is sufficient for identification, as the client can
determine the connection details from the local database of active servers.
┉
Option ▩‹-n› specifies an explicit server name as in @{tool server}.
┉
Option ▩‹-p› specifies an explicit server port as in @{tool server}.
›
subsection ‹Examples›
text ‹
Ensure that a particular server instance is running in the background:
@{verbatim [display] ‹isabelle server -n test &›}
The first line of output presents the connection details:⁋‹This information
may be used in other TCP clients, without access to Isabelle/Scala and the
underlying database of running servers.›
@{verbatim [display] ‹server "test" = 127.0.0.1:4711 (password "XYZ")›}
┉
List available server processes:
@{verbatim [display] ‹isabelle server -l›}
┉
Connect the command-line client to the above test server:
@{verbatim [display] ‹isabelle client -n test›}
Interaction now works on a line-by-line basis, with commands like ▩‹help› or
▩‹echo›. For example, some JSON values may be echoed like this:
@{verbatim [display]
‹echo 42
echo [1, 2, 3]
echo {"a": "text", "b": true, "c": 42}›}
Closing the connection (via CTRL-D) leaves the server running: it is
possible to reconnect again, and have multiple connections at the same time.
┉
Exit the named server on the command-line:
@{verbatim [display] ‹isabelle server -n test -x›}
›
section ‹Protocol messages \label{sec:server-protocol}›
text ‹
The Isabelle server listens on a regular TCP socket, using a line-oriented
protocol of structured messages. Input ∗‹commands› and output ∗‹results›
(via ▩‹OK› or ▩‹ERROR›) are strictly alternating on the toplevel, but
commands may also return a ∗‹task› identifier to indicate an ongoing
asynchronous process that is joined later (via ▩‹FINISHED› or ▩‹FAILED›).
Asynchronous ▩‹NOTE› messages may occur at any time: they are independent of
the main command-result protocol.
For example, the synchronous ▩‹echo› command immediately returns its
argument as ▩‹OK› result. In contrast, the asynchronous ▩‹session_build›
command returns ▩‹OK {"task":›‹id›▩‹}› and continues in the background. It
will eventually produce ▩‹FINISHED {"task":›‹id›▩‹,›‹…›▩‹}› or
▩‹FAILED {"task":›‹id›▩‹,›‹…›▩‹}› with the final result. Intermediately, it
may emit asynchronous messages of the form ▩‹NOTE {"task":›‹id›▩‹,›‹…›▩‹}›
to inform about its progress. Due to the explicit task identifier, the
client can show these messages in the proper context, e.g.\ a GUI window for
this particular session build job.
\medskip Subsequently, the protocol message formats are described in further
detail.
›
subsection ‹Byte messages \label{sec:byte-messages}›
text ‹
The client-server connection is a raw byte-channel for bidirectional
communication, but the Isabelle server always works with messages of a
particular length. Messages are written as a single chunk that is flushed
immediately.
Message boundaries are determined as follows:
▪ A ∗‹short message› consists of a single line: it is a sequence of
arbitrary bytes excluding CR (13) and LF (10), and terminated by CR-LF or
just LF.
▪ A ∗‹long message› starts with a single line consisting of decimal
digits: these are interpreted as length of the subsequent block of
arbitrary bytes. A final line-terminator (as above) may be included here,
but is not required.
Messages in JSON format (see below) always fit on a single line, due to
escaping of newline characters within string literals. This is convenient
for interactive experimentation, but it can impact performance for very long
messages. If the message byte-length is given on the preceding line, the
server can read the message more efficiently as a single block.
›
subsection ‹Text messages›
text ‹
Messages are read and written as byte streams (with byte lengths), but the
content is always interpreted as plain text in terms of the UTF-8
encoding.⁋‹See also the ``UTF-8 Everywhere Manifesto''
🌐‹https://utf8everywhere.org›.›
Note that line-endings and other formatting characters are invariant wrt.
UTF-8 representation of text: thus implementations are free to determine the
overall message structure before or after applying the text encoding.
›
subsection ‹Input and output messages \label{sec:input-output-messages}›
text ‹
The uniform format for server input and output messages is ‹name argument›,
such that:
▪ ‹name› is the longest prefix consisting of ASCII letters, digits,
``▩‹_›'', ``▩‹.›'',
▪ the separator between ‹name› and ‹argument› is the longest possible
sequence of ASCII blanks (it could be empty, e.g.\ when the argument
starts with a quote or bracket),
▪ ‹argument› is the rest of the message without line terminator.
┉
Input messages are sent from the client to the server. Here the ‹name›
specifies a ∗‹server command›: the list of known commands may be
retrieved via the ▩‹help› command.
┉
Output messages are sent from the server to the client. Here the ‹name›
specifies the ∗‹server reply›, which always has a specific meaning as
follows:
▪ synchronous results: ▩‹OK› or ▩‹ERROR›
▪ asynchronous results: ▩‹FINISHED› or ▩‹FAILED›
▪ intermediate notifications: ▩‹NOTE›
┉
The ‹argument› format is uniform for both input and output messages:
▪ empty argument (Scala type ▩‹Unit›)
▪ XML element in YXML notation (Scala type ▩‹XML.Elem›)
▪ JSON value (Scala type ▩‹JSON.T›)
JSON values may consist of objects (records), arrays (lists), strings,
numbers, bools, or null.⁋‹See also the official specification
🌐‹https://www.json.org› and unofficial explorations ``Parsing JSON is a
Minefield'' 🌐‹http://seriot.ch/parsing_json.php›.› Since JSON requires
explicit quotes and backslash-escapes to represent arbitrary text, the YXML
notation for XML trees (\secref{sec:yxml-vs-xml}) works better
for large messages with a lot of PIDE markup.
Nonetheless, the most common commands use JSON by default: big chunks of
text (theory sources etc.) are taken from the underlying file-system and
results are pre-formatted for plain-text output, without PIDE markup
information. This is a concession to simplicity: the server imitates the
appearance of command-line tools on top of the Isabelle/PIDE infrastructure.
›
subsection ‹Initial password exchange›
text ‹
Whenever a new client opens the server socket, the initial message needs to
be its unique password as a single line, without length indication (i.e.\ a
``short message'' in the sense of \secref{sec:byte-messages}).
The server replies either with ▩‹OK› (and some information about the
Isabelle version) or by silent disconnection of what is considered an
illegal connection attempt. Note that @{tool client} already presents the
correct password internally.
Server passwords are created as Universally Unique Identifier (UUID) in
Isabelle/Scala and stored in a per-user database, with restricted
file-system access only for the current user. The Isabelle/Scala server
implementation is careful to expose the password only on private output
channels, and not on a process command-line (which is accessible to other
users, e.g.\ via the ▩‹ps› command).
›
subsection ‹Synchronous commands›
text ‹
A ∗‹synchronous command› corresponds to regular function application in
Isabelle/Scala, with single argument and result (regular or error). Both the
argument and the result may consist of type ▩‹Unit›, ▩‹XML.Elem›, ▩‹JSON.T›.
An error result typically consists of a JSON object with error message and
potentially further result fields (this resembles exceptions in Scala).
These are the protocol exchanges for both cases of command execution:
\begin{center}
\begin{tabular}{rl}
❙‹input:› & ‹command argument› \\
(a) regular ❙‹output:› & ▩‹OK› ‹result› \\
(b) error ❙‹output:› & ▩‹ERROR› ‹result› \\
\end{tabular}
\end{center}
›
subsection ‹Asynchronous commands›
text ‹
An ∗‹asynchronous command› corresponds to an ongoing process that finishes
or fails eventually, while emitting arbitrary notifications in between.
Formally, it starts as synchronous command with immediate result ▩‹OK›
giving the ▩‹task› identifier, or an immediate ▩‹ERROR› that indicates bad
command syntax. For a running task, the termination is indicated later by
▩‹FINISHED› or ▩‹FAILED›, together with its ultimate result value.
These are the protocol exchanges for various cases of command task
execution:
\begin{center}
\begin{tabular}{rl}
❙‹input:› & ‹command argument› \\
immediate ❙‹output:› & ▩‹OK {"task":›‹id›▩‹}› \\
intermediate ❙‹output:› & ▩‹NOTE {"task":›‹id›▩‹,›‹…›▩‹}› \\
(a) regular ❙‹output:› & ▩‹FINISHED {"task":›‹id›▩‹,›‹…›▩‹}› \\
(b) error ❙‹output:› & ▩‹FAILED {"task":›‹id›▩‹,›‹…›▩‹}› \\[3ex]
❙‹input:› & ‹command argument› \\
immediate ❙‹output:› & ▩‹ERROR›~‹…› \\
\end{tabular}
\end{center}
All asynchronous messages are decorated with the task identifier that was
revealed in the immediate (synchronous) result. Thus the client can
invoke further asynchronous commands and still dispatch the resulting stream of
asynchronous messages properly.
The synchronous command ▩‹cancel {"task":›~‹id›▩‹}› tells the specified task
to terminate prematurely: usually causing a ▩‹FAILED› result, but this is
not guaranteed: the cancel event may come too late or the running process
may just ignore it.
›
section ‹Types for JSON values \label{sec:json-types}›
text ‹
In order to specify concrete JSON types for command arguments and result
messages, the following type definition language shall be used:
\<^rail>‹
@{syntax type_def}: @'type' @{syntax name} '=' @{syntax type}
;
@{syntax type}: @{syntax name} | @{syntax value} | 'any' | 'null' |
'bool' | 'int' | 'long' | 'double' | 'string' | '[' @{syntax type} ']' |
'{' (@{syntax field_type} ',' *) '}' |
@{syntax type} '⊕' @{syntax type} |
@{syntax type} '|' @{syntax type} |
'(' @{syntax type} ')'
;
@{syntax field_type}: @{syntax name} ('?'?) ':' @{syntax type}
›
This is a simplified variation of TypeScript
interfaces.⁋‹🌐‹https://www.typescriptlang.org/docs/handbook/interfaces.html››
The meaning of these types is specified wrt. the Isabelle/Scala
implementation as follows.
▪ A ‹name› refers to a type defined elsewhere. The environment of type
definitions is given informally: put into proper foundational order, it
needs to specify a strongly normalizing system of syntactic abbreviations;
type definitions may not be recursive.
▪ A ‹value› in JSON notation represents the singleton type of the given
item. For example, the string ▩‹"error"› can be used as type for a slot that
is guaranteed to contain that constant.
▪ Type ‹any› is the super type of all other types: it is an untyped slot in
the specification and corresponds to ▩‹Any› or ▩‹JSON.T› in Isabelle/Scala.
▪ Type ‹null› is the type of the improper value ‹null›; it corresponds to
type ▩‹Null› in Scala and is normally not used in Isabelle/Scala.⁋‹See also
``Null References: The Billion Dollar Mistake'' by Tony Hoare
🌐‹https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare›.›
▪ Type ‹bool› is the type of the truth values ▩‹true› and ▩‹false›; it
corresponds to ▩‹Boolean› in Scala.
▪ Types ‹int›, ‹long›, ‹double› are specific versions of the generic
‹number› type, corresponding to ▩‹Int›, ▩‹Long›, ▩‹Double› in Scala, but
▩‹Long› is limited to 53 bit precision.⁋‹Implementations of JSON typically
standardize ‹number› to ▩‹Double›, which can absorb ▩‹Int› faithfully, but
not all of ▩‹Long›.›
▪ Type ‹string› represents Unicode text; it corresponds to type ▩‹String› in
Scala.
▪ Type ‹[t]› is the array (or list) type over ‹t›; it corresponds to
▩‹List[t]› in Scala. The list type is co-variant as usual (i.e.\ monotonic
wrt. the subtype relation).
▪ Object types describe the possible content of JSON records, with field
names and types. A question mark after a field name means that it is
optional. In Scala this could refer to an explicit type ▩‹Option[t]›, e.g.\
‹{a: int, b?: string}› corresponding to a Scala case class with arguments
▩‹a: Int›, ▩‹b: Option[String]›.
Alternatively, optional fields can have a default value. If nothing else is
specified, a standard ``empty value'' is used for each type, i.e.\ ▩‹0› for
the number types, ▩‹false› for ‹bool›, or the empty string, array, object
etc.
Object types are ∗‹permissive› in the sense that only the specified field
names need to conform to the given types, but unspecified fields may be
present as well.
▪ The type expression ‹t⇩1 ⊕ t⇩2› only works for two object types with
disjoint field names: it is the concatenation of the respective @{syntax
field_type} specifications taken together. For example: ‹{task: string} ⊕
{ok: bool}› is the equivalent to ‹{task: string, ok: bool}›.
▪ The type expression ‹t⇩1 | t⇩2› is the disjoint union of two types, either
one of the two cases may occur.
▪ Parentheses ‹(t)› merely group type expressions syntactically.
These types correspond to JSON values in an obvious manner, which is not
further described here. For example, the JSON array ▩‹[1, 2, 3]› conforms to
types ‹[int]›, ‹[long]›, ‹[double]›, ‹[any]›, ‹any›.
Note that JSON objects require field names to be quoted, but the type
language omits quotes for clarity. Thus the object ▩‹{"a": 42, "b": "xyz"}›
conforms to the type ‹{a: int, b: string}›, for example.
┉
The absence of an argument or result is represented by the Scala type
▩‹Unit›: it is written as empty text in the message ‹argument›
(\secref{sec:input-output-messages}). This is not part of the JSON language.
Server replies have name tags like ▩‹OK›, ▩‹ERROR›: these are used literally
together with type specifications to indicate the particular name with the
type of its argument, e.g.\ ▩‹OK›~‹[string]› for a regular result that is a
list (JSON array) of strings.
━
Here are some common type definitions, for use in particular specifications
of command arguments and results.
▪ ❙‹type›~‹position = {line?: int, offset?: int, end_offset?: int, file?:
string, id?: long}› describes a source position within Isabelle text. Only
the ‹line› and ‹file› fields make immediate sense to external programs.
Detailed ‹offset› and ‹end_offset› positions are counted according to
Isabelle symbols, see \<^ML_type>‹Symbol.symbol› in Isabelle/ML \<^cite>‹"isabelle-implementation"›. The position ‹id› belongs to the representation
of command transactions in the Isabelle/PIDE protocol: it normally does not
occur in externalized positions.
▪ ❙‹type›~‹message = {kind: string, message: string, pos?: position}› where
the ‹kind› provides some hint about the role and importance of the message.
The main message kinds are ▩‹writeln› (for regular output), ▩‹warning›,
▩‹error›.
▪ ❙‹type›~‹error_message = {kind:›~▩‹"error"›‹, message: string}› refers to
error messages in particular. These occur routinely with ▩‹ERROR› or
▩‹FAILED› replies, but also as initial command syntax errors (which are
omitted in the command specifications below).
▪ ❙‹type›~‹theory_progress = {kind:›~▩‹"writeln"›‹, message: string, theory:
string, session: string, percentage?: int}› reports formal progress in
loading theories (e.g.\ when building a session image). Apart from a regular
output message, it also reveals the formal theory name (e.g.\ ▩‹"HOL.Nat"›)
and session name (e.g.\ ▩‹"HOL"›). Note that some rare theory names lack a
proper session prefix, e.g.\ theory ▩‹"Main"› in session ▩‹"HOL"›. The
optional percentage has the same meaning as in ❙‹type›~‹node_status› below.
▪ ❙‹type›~‹timing = {elapsed: double, cpu: double, gc: double}› refers to
common Isabelle timing information in seconds, usually with a precision of
three digits after the point (whole milliseconds).
▪ ❙‹type›~‹uuid = string› refers to a Universally Unique Identifier (UUID)
as plain text.⁋‹See 🌐‹https://www.ietf.org/rfc/rfc4122.txt› and
🌐‹https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/UUID.html›.› Such
identifiers are created as private random numbers of the server and only
revealed to the client that creates a certain resource (e.g.\ task or
session). A client may disclose this information for use in a different
client connection: this allows to share sessions between multiple
connections.
Client commands need to provide syntactically wellformed UUIDs: this is
trivial to achieve by using only identifiers that have been produced by the
server beforehand.
▪ ❙‹type›~‹task = {task: uuid}› identifies a newly created asynchronous task
and thus allows the client to control it by the ▩‹cancel› command. The same
task identification is included in all messages produced by this task.
▪ ❙‹type› ‹session_id = {session_id: uuid}› identifies a newly created PIDE
session managed by the server. Sessions are independent of client
connections and may be shared by different clients, as long as the internal
session identifier is known.
▪ ❙‹type› ‹node = {node_name: string, theory_name: string}› represents the
internal node name of a theory. The ‹node_name› is derived from the
canonical theory file-name (e.g.\ ▩‹"~~/src/HOL/Examples/Seq.thy"› after
normalization within the file-system). The ‹theory_name› is the
session-qualified theory name (e.g.\ ▩‹HOL-Examples.Seq›).
▪ ❙‹type› ‹node_status = {ok: bool, total: int, unprocessed: int, running:
int, warned: int, failed: int, finished: int, canceled: bool, consolidated:
bool, percentage: int}› represents a formal theory node status of the PIDE
document model as follows.
▪ Fields ‹total›, ‹unprocessed›, ‹running›, ‹warned›, ‹failed›, ‹finished›
account for individual commands within a theory node; ‹ok› is an
abstraction for ‹failed = 0›.
▪ The ‹canceled› flag tells if some command in the theory has been
spontaneously canceled (by an Interrupt exception that could also
indicate resource problems).
▪ The ‹consolidated› flag indicates whether the outermost theory command
structure has finished (or failed) and the final ⬚‹end› command has been
checked.
▪ The ‹percentage› field tells how far the node has been processed. It
ranges between 0 and 99 in normal operation, and reaches 100 when the node
has been formally consolidated as described above.
›
section ‹Server commands and results›
text ‹
Here follows an overview of particular Isabelle server commands with their
results, which are usually represented as JSON values with types according
to \secref{sec:json-types}. The general format of input and output messages
is described in \secref{sec:input-output-messages}. The relevant
Isabelle/Scala source files are:
┉
\begin{tabular}{l}
🗏‹$ISABELLE_HOME/src/Pure/Tools/server_commands.scala› \\
🗏‹$ISABELLE_HOME/src/Pure/Tools/server.scala› \\
🗏‹$ISABELLE_HOME/src/Pure/General/json.scala› \\
\end{tabular}
›
subsection ‹Command ▩‹help››
text ‹
\begin{tabular}{ll}
regular result: & ▩‹OK› ‹[string]› \\
\end{tabular}
┉
The ▩‹help› command has no argument and returns the list of server command
names. This is occasionally useful for interactive experimentation (see also
@{tool client} in \secref{sec:tool-client}).
›
subsection ‹Command ▩‹echo››
text ‹
\begin{tabular}{ll}
argument: & ‹any› \\
regular result: & ▩‹OK› ‹any› \\
\end{tabular}
┉
The ▩‹echo› command is the identity function: it returns its argument as
regular result. This is occasionally useful for testing and interactive
experimentation (see also @{tool client} in \secref{sec:tool-client}).
The Scala type of ▩‹echo› is actually more general than given above:
▩‹Unit›, ▩‹XML.Elem›, ▩‹JSON.T› work uniformly. Note that ▩‹XML.Elem› might
be difficult to type on the console in its YXML syntax
(\secref{sec:yxml-vs-xml}).
›
subsection ‹Command ▩‹shutdown››
text ‹
\begin{tabular}{ll}
regular result: & ▩‹OK› \\
\end{tabular}
┉
The ▩‹shutdown› command has no argument and result value. It forces a
shutdown of the connected server process, stopping all open sessions and
closing the server socket. This may disrupt pending commands on other
connections!
┉
The command-line invocation ▩‹isabelle server -x› opens a server connection
and issues a ▩‹shutdown› command (see also \secref{sec:tool-server}).
›
subsection ‹Command ▩‹cancel››
text ‹
\begin{tabular}{ll}
argument: & ‹task› \\
regular result: & ▩‹OK› \\
\end{tabular}
┉
The command ▩‹cancel {"task":›~‹id›▩‹}› attempts to cancel the specified
task.
Cancellation is merely a hint that the client prefers an ongoing process to
be stopped. The command always succeeds formally, but it may get ignored by
a task that is still running; it might also refer to a non-existing or
no-longer existing task (without producing an error).
Successful cancellation typically leads to an asynchronous failure of type
▩‹FAILED {›‹task: uuid, message:›~▩‹"Interrupt"}›. A different message is
also possible, depending how the task handles the event.
›
subsection ‹Command ▩‹session_build› \label{sec:command-session-build}›
text ‹
\begin{tabular}{lll}
argument: & ‹session_build_args› \\
immediate result: & ▩‹OK› ‹task› \\
notifications: & ▩‹NOTE› ‹task ⊕ (theory_progress | message)› \\
regular result: & ▩‹FINISHED› ‹task ⊕ session_build_results› \\
error result: & ▩‹FAILED› ‹task ⊕ error_message ⊕ session_build_results› \\[2ex]
\end{tabular}
\begin{tabular}{lll}
❙‹type› ‹session_build_args =› \\
\quad‹{session: string,› \\
\quad~~‹preferences?: string,› & ❙‹default:› server preferences \\
\quad~~‹options?: [string],› \\
\quad~~‹dirs?: [string],› \\
\quad~~‹include_sessions: [string],› \\
\quad~~‹verbose?: bool}› \\[2ex]
\end{tabular}
\begin{tabular}{ll}
❙‹type› ‹session_build_result =› \\
\quad‹{session: string,› \\
\quad~~‹ok: bool,› \\
\quad~~‹return_code: int,› \\
\quad~~‹timeout: bool,› \\
\quad~~‹timing: timing}› \\[2ex]
❙‹type› ‹session_build_results =› \\
\quad‹{ok: bool,› \\
\quad~~‹return_code: int,› \\
\quad~~‹sessions: [session_build_result]}› \\
\end{tabular}
›
text ‹
The ▩‹session_build› command prepares a session image for interactive use of
theories. This is a limited version of command-line tool @{tool build}
(\secref{sec:tool-build}), with specific options to request a formal context
for an interactive PIDE session.
The build process is asynchronous, with notifications that inform about the
progress of loaded theories. Some further informative messages are output as
well.
Coordination of independent build processes is at the discretion of the
client (or end-user), just as for @{tool build} and @{tool jedit}. There is
no built-in coordination of conflicting builds with overlapping hierarchies
of session images. In the worst case, a session image produced by one task
may get overwritten by another task!
›
subsubsection ‹Arguments›
text ‹
The ‹session› field specifies the target session name. The build process
will produce all required ancestor images according to the overall session
graph.
┉
The environment of Isabelle system options is determined from ‹preferences›
that are augmented by ‹options›, which is a list individual updates of the
form the ‹name›▩‹=›‹value› or ‹name› (the latter abbreviates
‹name›▩‹=true›); see also command-line option ▩‹-o› for @{tool build}. The
preferences are loaded from the file
\<^path>‹$ISABELLE_HOME_USER/etc/preferences› by default, but the client may
provide alternative contents for it (as text, not a file-name). This could
be relevant in situations where client and server run in different
operating-system contexts.
┉
The ‹dirs› field specifies additional directories for session ROOT and ROOTS
files (\secref{sec:session-root}). This augments the name space of available
sessions; see also option ▩‹-d› in @{tool build}.
┉
The ‹include_sessions› field specifies sessions whose theories should be
included in the overall name space of session-qualified theory names. This
corresponds to a ❙‹sessions› specification in ROOT files
(\secref{sec:session-root}). It enables the ▩‹use_theories› command
(\secref{sec:command-use-theories}) to refer to sources from other sessions
in a robust manner, instead of relying on directory locations.
›
subsubsection ‹Intermediate output›
text ‹
The asynchronous notifications of command ▩‹session_build› mainly serve as
progress indicator: the output resembles that of the session build window of
Isabelle/jEdit after startup \<^cite>‹"isabelle-jedit"›.
For the client it is usually sufficient to print the messages in plain text,
but note that ‹theory_progress› also reveals formal ‹theory› and
‹session› names directly.
›
subsubsection ‹Results›
text ‹
The overall ‹session_build_results› contain both a summary and an entry
‹session_build_result› for each session in the build hierarchy. The result
is always provided, independently of overall success (▩‹FINISHED› task) or
failure (▩‹FAILED› task).
The ‹ok› field tells abstractly, whether all required session builds came
out as ‹ok›, i.e.\ with zero ‹return_code›. A non-zero ‹return_code›
indicates an error according to usual POSIX conventions for process exit.
The individual ‹session_build_result› entries provide extra fields:
▪ ‹timeout› tells if the build process was aborted after running too long,
▪ ‹timing› gives the overall process timing in the usual Isabelle format
with elapsed, CPU, GC time.
›
subsubsection ‹Examples›
text ‹
Build of a session image from the Isabelle distribution:
@{verbatim [display] ‹session_build {"session": "HOL-Algebra"}›}
Build a session image from the Archive of Formal Proofs:
@{verbatim [display] ‹session_build {"session": "Coinductive", "dirs": ["$AFP_BASE/thys"]}›}
›
subsection ‹Command ▩‹session_start› \label{sec:command-session-start}›
text ‹
\begin{tabular}{lll}
argument: & ‹session_build_args ⊕ {print_mode?: [string]}› \\
immediate result: & ▩‹OK› ‹task› \\
notifications: & ▩‹NOTE› ‹task ⊕ (theory_progress | message)› \\
regular result: & ▩‹FINISHED› ‹task ⊕ session_id ⊕ {tmp_dir: string}› \\
error result: & ▩‹FAILED› ‹task ⊕ error_message› \\[2ex]
\end{tabular}
┉
The ▩‹session_start› command starts a new Isabelle/PIDE session with
underlying Isabelle/ML process, based on a session image that it produces on
demand using ▩‹session_build›. Thus it accepts all ‹session_build_args› and
produces similar notifications, but the detailed ‹session_build_results› are
omitted.
The session build and startup process is asynchronous: when the task is
finished, the session remains active for commands, until a ▩‹session_stop›
or ▩‹shutdown› command is sent to the server.
Sessions are independent of client connections: it is possible to start a
session and later apply ▩‹use_theories› on different connections, as long as
the internal session identifier is known: shared theory imports will be used
only once (and persist until purged explicitly).
›
subsubsection ‹Arguments›
text ‹
Most arguments are shared with ▩‹session_build›
(\secref{sec:command-session-build}).
┉
The ‹print_mode› field adds identifiers of print modes to be made active for
this session. For example, ▩‹"print_mode": ["ASCII"]› prefers ASCII
replacement syntax over mathematical Isabelle symbols. See also option ▩‹-m›
in @{tool process} (\secref{sec:tool-process}).
›
subsubsection ‹Results›
text ‹
The ‹session_id› provides the internal identification of the session object
within the server process. It can remain active as long as the server is
running, independently of the current client connection.
┉
The ‹tmp_dir› field refers to a temporary directory that is specifically
created for this session and deleted after it has been stopped. This may
serve as auxiliary file-space for the ▩‹use_theories› command, but
concurrent use requires some care in naming temporary files, e.g.\ by
using sub-directories with globally unique names.
As ‹tmp_dir› is the default ‹master_dir› for commands ▩‹use_theories› and
▩‹purge_theories›, theory files copied there may be used without further
path specification.
›
subsubsection ‹Examples›
text ‹
Start a default Isabelle/HOL session:
@{verbatim [display] ‹session_start {"session": "HOL"}›}
Start a session from the Archive of Formal Proofs:
@{verbatim [display] ‹session_start {"session": "Coinductive", "dirs": ["$AFP_BASE/thys"]}›}
Start a session with fine-tuning of options:
@{verbatim [display] ‹session_start {"session": "HOL",
"options": ["headless_consolidate_delay=0.5", "headless_prune_delay=5"]}›}
›
subsection ‹Command ▩‹session_stop››
text ‹
\begin{tabular}{ll}
argument: & ‹session_id› \\
immediate result: & ▩‹OK› ‹task› \\
regular result: & ▩‹FINISHED› ‹task ⊕ session_stop_result› \\
error result: & ▩‹FAILED› ‹task ⊕ error_message ⊕ session_stop_result› \\[2ex]
\end{tabular}
\begin{tabular}{l}
❙‹type› ‹session_stop_result = {ok: bool, return_code: int}›
\end{tabular}
┉
The ▩‹session_stop› command forces a shutdown of the identified PIDE
session. This asynchronous tasks usually finishes quickly. Failure only
happens in unusual situations, according to the return code of the
underlying Isabelle/ML process.
›
subsubsection ‹Arguments›
text ‹
The ‹session_id› provides the UUID originally created by the server for this
session.
›
subsubsection ‹Results›
text ‹
The ‹ok› field tells abstractly, whether the Isabelle/ML process has
terminated properly.
The ‹return_code› field expresses this information according to usual POSIX
conventions for process exit.
›
subsection ‹Command ▩‹use_theories› \label{sec:command-use-theories}›
text ‹
\begin{tabular}{ll}
argument: & ‹use_theories_arguments› \\
immediate result: & ▩‹OK› ‹task› \\
regular result: & ▩‹FINISHED› ‹use_theories_results› \\
\end{tabular}
\begin{tabular}{ll}
❙‹type› ‹use_theories_arguments =› \\
\quad‹{session_id: uuid,› \\
\quad~~‹theories: [string],› \\
\quad~~‹master_dir?: string,› & ❙‹default:› session ‹tmp_dir› \\
\quad~~‹pretty_margin?: double,› & ❙‹default:› ▩‹76› \\
\quad~~‹unicode_symbols?: bool,› \\
\quad~~‹export_pattern?: string,› \\
\quad~~‹check_delay?: double,› & ❙‹default:› ▩‹0.5› \\
\quad~~‹check_limit?: int,› \\
\quad~~‹watchdog_timeout?: double,› & ❙‹default:› ▩‹600.0› \\
\quad~~‹nodes_status_delay?: double}› & ❙‹default:› ▩‹-1.0› \\
\end{tabular}
\begin{tabular}{ll}
❙‹type› ‹export =› \\
\quad~~‹{name: string, base64: bool, body: string}› \\
❙‹type› ‹node_results =› \\
\quad~~‹{status: node_status, messages: [message], exports: [export]}› \\
❙‹type› ‹nodes_status =› \\
\quad~~‹[node ⊕ {status: node_status}]› \\
❙‹type› ‹use_theories_results =› \\
\quad‹{ok: bool,› \\
\quad~~‹errors: [message],› \\
\quad~~‹nodes: [node ⊕ node_results]}› \\
\end{tabular}
┉
The ▩‹use_theories› command updates the identified session by adding the
current version of theory files to it, while dependencies are resolved
implicitly. The command succeeds eventually, when all theories have status
∗‹terminated› or ∗‹consolidated› in the sense of ‹node_status›
(\secref{sec:json-types}).
Already used theories persist in the session until purged explicitly
(\secref{sec:command-purge-theories}). This also means that repeated
invocations of ▩‹use_theories› are idempotent: it could make sense to do
that with different values for ‹pretty_margin› or ‹unicode_symbols› to get
different formatting for ‹errors› or ‹messages›.
┉ A non-empty ‹export_pattern› means that theory ‹exports› are retrieved
(see \secref{sec:tool-export}). An ‹export› ‹name› roughly follows
file-system standards: ``▩‹/›'' separated list of base names (excluding
special names like ``▩‹.›'' or ``▩‹..›''). The ‹base64› field specifies the
format of the ‹body› string: it is true for a byte vector that cannot be
represented as plain text in UTF-8 encoding, which means the string needs to
be decoded as in ▩‹java.util.Base64.getDecoder.decode(String)›.
┉ The status of PIDE processing is checked every ‹check_delay› seconds, and
bounded by ‹check_limit› attempts (default: 0, i.e.\ unbounded). A
‹check_limit > 0› effectively specifies a global timeout of ‹check_delay ×
check_limit› seconds.
┉ If ‹watchdog_timeout› is greater than 0, it specifies the timespan (in
seconds) after the last command status change of Isabelle/PIDE, before
finishing with a potentially non-terminating or deadlocked execution.
┉ A non-negative ‹nodes_status_delay› enables continuous notifications of
kind ‹nodes_status›, with a field of name and type ‹nodes_status›. The time
interval is specified in seconds; by default it is negative and thus
disabled.
›
subsubsection ‹Arguments›
text ‹
The ‹session_id› is the identifier provided by the server, when the session
was created (possibly on a different client connection).
┉
The ‹theories› field specifies theory names as in theory ⬚‹imports› or in
ROOT ❙‹theories›.
┉
The ‹master_dir› field specifies the master directory of imported theories:
it acts like the ``current working directory'' for locating theory files.
This is irrelevant for ‹theories› with an absolute path name (e.g.\
▩‹"~~/src/HOL/Examples/Seq.thy"›) or session-qualified theory name (e.g.\
▩‹"HOL-Examples.Seq"›).
┉
The ‹pretty_margin› field specifies the line width for pretty-printing. The
default is suitable for classic console output. Formatting happens at the
end of ▩‹use_theories›, when all prover messages are exported to the client.
┉
The ‹unicode_symbols› field set to ▩‹true› renders message output for direct
output on a Unicode capable channel, ideally with the Isabelle fonts as in
Isabelle/jEdit. The default is to keep the symbolic representation of
Isabelle text, e.g.\ ▩‹∀› instead of its rendering as ‹∀›. This means the
client needs to perform its own rendering before presenting it to the
end-user.
›
subsubsection ‹Results›
text ‹
The ‹ok› field indicates overall success of processing the specified
theories with all their dependencies.
When ‹ok› is ▩‹false›, the ‹errors› field lists all errors cumulatively
(including imported theories). The messages contain position information for
the original theory nodes.
┉
The ‹nodes› field provides detailed information about each imported theory
node. The individual fields are as follows:
▪ ‹node_name›: the canonical name for the theory node, based on its
file-system location;
▪ ‹theory_name›: the logical theory name;
▪ ‹status›: the overall node status, e.g.\ see the visualization in the
‹Theories› panel of Isabelle/jEdit \<^cite>‹"isabelle-jedit"›;
▪ ‹messages›: the main bulk of prover messages produced in this theory
(with kind ▩‹writeln›, ▩‹warning›, ▩‹error›).
›
subsubsection ‹Examples›
text ‹
Process some example theory from the Isabelle distribution, within the
context of an already started session for Isabelle/HOL (see also
\secref{sec:command-session-start}):
@{verbatim [display] ‹use_theories {"session_id": ..., "theories": ["~~/src/HOL/Examples/Seq"]}›}
┉
Process some example theories in the context of their (single) parent
session:
@{verbatim [display] ‹session_start {"session": "HOL-Library"}
use_theories {"session_id": ..., "theories": ["~~/src/HOL/Unix/Unix"]}
session_stop {"session_id": ...}›}
┉
Process some example theories that import other theories via
session-qualified theory names:
@{verbatim [display] ‹session_start {"session": "HOL", "include_sessions": ["HOL-Unix"]}
use_theories {"session_id": ..., "theories": ["HOL-Unix.Unix"]}
session_stop {"session_id": ...}›}
›
subsection ‹Command ▩‹purge_theories› \label{sec:command-purge-theories}›
text ‹
\begin{tabular}{ll}
argument: & ‹purge_theories_arguments› \\
regular result: & ▩‹OK› ‹purge_theories_result› \\
\end{tabular}
\begin{tabular}{lll}
❙‹type› ‹purge_theories_arguments =› \\
\quad‹{session_id: uuid,› \\
\quad~~‹theories: [string],› \\
\quad~~‹master_dir?: string,› & ❙‹default:› session ‹tmp_dir› \\
\quad~~‹all?: bool}› \\[2ex]
\end{tabular}
\begin{tabular}{ll}
❙‹type› ‹purge_theories_result = {purged: [string]}› \\
\end{tabular}
┉
The ▩‹purge_theories› command updates the identified session by removing
theories that are no longer required: theories that are used in pending
▩‹use_theories› tasks or imported by other theories are retained.
›
subsubsection ‹Arguments›
text ‹
The ‹session_id› is the identifier provided by the server, when the session
was created (possibly on a different client connection).
┉
The ‹theories› field specifies theory names to be purged: imported
dependencies are ∗‹not› completed. Instead it is possible to provide the
already completed import graph returned by ▩‹use_theories› as ‹nodes› /
‹node_name›.
┉
The ‹master_dir› field specifies the master directory as in ▩‹use_theories›.
This is irrelevant, when passing fully-qualified theory node names (e.g.\
‹node_name› from ‹nodes› in ‹use_theories_results›).
┉
The ‹all› field set to ▩‹true› attempts to purge all presently loaded
theories.
›
subsubsection ‹Results›
text ‹
The ‹purged› field gives the theory nodes that were actually removed.
┉
The ‹retained› field gives the remaining theory nodes, i.e.\ the complement
of ‹purged›.
›
end