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
ML_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 🪙‹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 🍋‹$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:
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. ›
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:
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:
▪ 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 specificationand corresponds to🍋‹Any› or 🍋‹JSON.T›in Isabelle/Scala.
▪ 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 to53 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 andtypes. 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.java.lang.NullPointerException ‹{a: int, b?: string}› corresponding to a Scala caseclasswith 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 ‹t1⊕ t2› only works for two object typeswith
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 ‹t1 | t2›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 textin 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, forusein particular specifications
of command arguments and results.
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.