-
PVS (https://github.com/SRI-CSL/pvs)
- SBCL (https://github.com/sbcl/sbcl)
- Yices2 (https://github.com/SRI-CSL/yices2)
The following install script installs all dependencies (listed above):
make install
This install process is designed to be run on a Mac with Homebrew installed. On other systems, the Docker version of FHIR-Fly (see "Docker Container" below) is preferred.
./jslt_parser/defines a Python/Ply parser for JSLT and a walker to emit PVS specifications../jslt_pvs/defines PVS embeddings of JSON datatypes and JSLT.
See doc/architecture.pdf for a broad overview of the FHIR-Fly system and its design goals.
For given formats
The typechecking judgement
For example, let examples/ex1/A_schema.json):
{
...
"properties": {
"first_name": {
"description": "Human First Name",
"type": "string"
},
"last_name": {
"description": "Human Last Name",
"type": "string"
}
}
}
Let examples/ex1/B_schema.json):
{
...
"properties": {
"full_name": {
"description": "Human Full Name (`First Name` followed by `Last Name`)",
"type": "string"
}
}
}
and let examples/ex1/foo_bar_a.json):
{
"first_name": "foo",
"last_name": "bar"
}
Our JSLT transform would look like the following (examples/ex1/A_B.jslt):
{
"full_name": join([.first_name, " ", .last_name], "")
}
python3 main.py --input_schema examples/ex1/A_schema.json \
--transform examples/ex1/A_B.jslt \
--output_schema examples/ex1/B_schema.json \
--instance_name AB \
--pvs_location ./local-pvs/
This generates all the necessary verification conditions and saves it in [instance_name].pvs
(here, AB.pvs), alongside a verification strategy that tells PVS how to discharge the conditions.
At the moment, we utilize a custom decision procedure
jslt-verify-interpretthat expands hard-coded forms and applies PVS's internalgrindprocedure. Eventually, this would be swapped out with the Yices2 SMT solver and specialized decision procedures for strings/regular expressions.
To run the verifier,
cd ./test/ && ./PVS/pvs -batch -q -l AB.el
Now, the verification script will either print out Verification succeeded! or
Verification failed! depending on whether the decision procedures were able to establish
validity of the verification conditions.
To wrap both steps into a single command, run the tool with the extra flag --verify:
python3 main.py --input_schema examples/ex1/A_schema.json \
--transform examples/ex1/A_B.jslt \
--output_schema examples/ex1/B_schema.json \
--instance_name AB \
--pvs_location ./local-pvs/ \
--verify \
Alternately, you can run FHIR-Fly in a Docker container. To build the container, use:
./build_docker.shand to run it:
./run_docker.sh --input_schema workspace/ex1/A_schema.json \
--transform workspace/ex1/A_B.jslt \
--output_schema workspace/ex1/B_schema.json \
--instance_name AB \
--verifyThe run_docker.sh script automatically mounts ./workspace as ./workspace inside the container, examples/ex1 as workspace/ex1, and ./results as ./results. It also handles the PVS location, leaving you to specify only the command line arguments that differ from run to run.
In addition to the JSLT-based workflow described above, FHIR-Fly provides a direct PVS verification tool (pvs_main.py) that works directly with hand-written PVS theories without requiring JSLT as an intermediate language.
pvs_main.py: Command-line tool for direct PVS verificationprompt-transformsubcommand: Generate LLM prompt for synthesizing PVS transform functionsprompt-constraintssubcommand: Generate LLM prompt for synthesizing PVS constraint functionsgeneratesubcommand: Generate verification files (dry-run)verifysubcommand: Generate and run PVS verification
run_tests.py: Automated test runner for all examples- Docker scripts: Containerized versions of the above tools
The prompt-transform and prompt-constraints subcommands generate comprehensive prompts for an LLM to synthesize PVS transform and constraint functions. This is useful for automatically generating PVS theories from schema specifications.
Transform Synthesis:
# Generate transform prompt to stdout
python3 pvs_main.py prompt-transform \
--input-schema examples/ex2/A_schema.json \
--output-schema examples/ex2/B_schema.json
# Save transform prompt to a file with example
python3 pvs_main.py prompt-transform \
--input-schema examples/ex5/Drug_schema.json \
--output-schema examples/ex5/Prescription_schema.json \
--example-dir examples/ex3 \
--output-file drug_transform_prompt.txtThe transform prompt includes:
- Input and output JSON schemas
- PVS
jsondataAPI documentation - Transform structure template with correct syntax
- Optional example transform for reference
- Detailed instructions for the LLM
Constraints Synthesis:
# Generate constraints prompt to stdout
python3 pvs_main.py prompt-constraints \
--input-schema examples/ex2/A_schema.json \
--output-schema examples/ex2/B_schema.json \
--theory-name A_B_constraints
# Save constraints prompt to a file with example
python3 pvs_main.py prompt-constraints \
--input-schema examples/ex5/Drug_schema.json \
--output-schema examples/ex5/Prescription_schema.json \
--theory-name Drug_Prescription_constraints \
--example-dir examples/ex3 \
--output-file drug_constraints_prompt.txtThe constraints prompt includes:
- Input and output JSON schemas
- Explanation of
requires(precondition) andensures(postcondition) functions - PVS logical operators and JSON predicates
- Complete scaffolding with function signatures
- Optional example constraints for reference
- LLM only needs to fill in function bodies
# Generate verification files only
python3 pvs_main.py generate \
--transform examples/ex2/A_B.pvs \
--constraints examples/ex2/A_B_constraints.pvs \
--input-schema examples/ex2/A_schema.json \
--output-schema examples/ex2/B_schema.json \
--pvs ~/git/PVS/pvs \
--output results
# Generate and verify
python3 pvs_main.py verify \
--transform examples/ex2/A_B.pvs \
--constraints examples/ex2/A_B_constraints.pvs \
--input-schema examples/ex2/A_schema.json \
--output-schema examples/ex2/B_schema.json \
--pvs ~/git/PVS/pvs \
--output results
# Show full PVS log on failure
python3 pvs_main.py verify ... --verboseSet PVS_PATH to avoid specifying --pvs every time:
export PVS_PATH=~/git/PVS/pvs
python3 pvs_main.py verify \
--transform examples/ex2/A_B.pvs \
--constraints examples/ex2/A_B_constraints.pvs \
--input-schema examples/ex2/A_schema.json \
--output-schema examples/ex2/B_schema.jsonRun all PVS-based examples at once:
# Local execution
python3 run_tests.py
# With custom PVS path
PVS_PATH=~/git/PVS/pvs python3 run_tests.pyThe test runner automatically discovers all valid test cases in examples/ (excluding JSLT-based ex1) and reports pass/fail status with colored output.
# Normal build (uses cache)
./build_docker.sh
# Clean build (no cache, use after updating dependencies)
./build_docker.sh clean./run_pvs_docker.sh verify \
--transform examples/ex2/A_B.pvs \
--constraints examples/ex2/A_B_constraints.pvs \
--input-schema examples/ex2/A_schema.json \
--output-schema examples/ex2/B_schema.json \
--verboseThe run_pvs_docker.sh script automatically:
- Sets the correct PVS path for the container
- Mounts necessary directories (
workspace/,examples/,results/) - Handles platform requirements (amd64 for PVS/Yices)
./run_tests_docker.shEach example directory should contain:
A_B.pvs- Transform theory (theory name must match filename)A_B_constraints.pvs- Constraints theory withrequiresandensuresfunctionsA_schema.json- Input JSON schemaB_schema.json- Output JSON schema
Important: The theory name inside each .pvs file must match the filename (without extension). For example, A_B.pvs must contain A_B: THEORY, not A_B_transform: THEORY.
The jsondata theory in jslt_pvs/jsltlib/ provides helper functions:
json_get_key(obj, key)- Retrieve value from JSON object by keyjson_get_key_rec(obj, key, i)- Recursive implementation
Import with:
IMPORTING jsltlib@jsondata
See examples/ex2/ for a complete working example that demonstrates:
- Simple string concatenation transform
- Schema validation
- Basic constraints (TRUE requires, jdict? ensures)