lptp-mcp-server 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +29 -0
- package/README.md +91 -0
- package/build/backends/gprolog.d.ts +32 -0
- package/build/backends/gprolog.d.ts.map +1 -0
- package/build/backends/gprolog.js +113 -0
- package/build/backends/gprolog.js.map +1 -0
- package/build/backends/registry.d.ts +15 -0
- package/build/backends/registry.d.ts.map +1 -0
- package/build/backends/registry.js +61 -0
- package/build/backends/registry.js.map +1 -0
- package/build/backends/scryer.d.ts +33 -0
- package/build/backends/scryer.d.ts.map +1 -0
- package/build/backends/scryer.js +71 -0
- package/build/backends/scryer.js.map +1 -0
- package/build/backends/swipl.d.ts +24 -0
- package/build/backends/swipl.d.ts.map +1 -0
- package/build/backends/swipl.js +73 -0
- package/build/backends/swipl.js.map +1 -0
- package/build/backends/types.d.ts +27 -0
- package/build/backends/types.d.ts.map +1 -0
- package/build/backends/types.js +3 -0
- package/build/backends/types.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +355 -0
- package/build/index.js.map +1 -0
- package/build/lptpExecutor.d.ts +19 -0
- package/build/lptpExecutor.d.ts.map +1 -0
- package/build/lptpExecutor.js +520 -0
- package/build/lptpExecutor.js.map +1 -0
- package/jest.config.js +11 -0
- package/mcp-config-example.json +12 -0
- package/package.json +41 -0
- package/src/__tests__/mcp_lptp_server.test.ts +97 -0
- package/src/backends/gprolog.ts +87 -0
- package/src/backends/registry.ts +70 -0
- package/src/backends/scryer.ts +83 -0
- package/src/backends/swipl.ts +88 -0
- package/src/backends/types.ts +36 -0
- package/src/index.ts +369 -0
- package/src/lptpExecutor.ts +551 -0
- package/tsconfig.json +26 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Thierry Marianne
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
Note: This package provides a Model Context Protocol (MCP) server interface
|
|
26
|
+
to LPTP (Logic Program Theorem Prover), originally developed by Robert F.
|
|
27
|
+
Staerk (1996). The LPTP Prolog source code (src/lptp.pl) is distributed
|
|
28
|
+
under its own license terms. This MIT license applies only to the MCP server
|
|
29
|
+
TypeScript code in this package.
|
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# lptp-mcp-server
|
|
2
|
+
|
|
3
|
+
An [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server that exposes [LPTP (Logic Program Theorem Prover)](https://www.sciencedirect.com/science/article/pii/S0743106698100060 "Stärk, R.F. (1998). The theoretical foundations of LPTP. Journal of Logic Programming, 36(3):241-269.") operations as tools — proof verification, tactic application, definition querying, and ground representation compilation. Any MCP-compatible client (Claude Code, Claude Desktop, etc.) can connect and use these tools to interactively construct and verify formal proofs of Prolog programs.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- **Node.js** >= 18
|
|
8
|
+
- **LPTP source checkout** — the directory containing `src/lptp.pl` and the `lib/` hierarchy
|
|
9
|
+
- **At least one Prolog backend** on PATH:
|
|
10
|
+
- [SWI-Prolog](https://www.swi-prolog.org/) (`swipl`) — recommended, best resource-limit support
|
|
11
|
+
- [Scryer Prolog](https://www.scryer.pl/) (`scryer-prolog`) — ISO-compliant alternative
|
|
12
|
+
- GNU Prolog (`bin/lptp-gnu-prolog`) — pre-compiled binary with LPTP baked in
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install lptp-mcp-server
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or install from a local checkout:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
cd etc/lptp-mcp-server
|
|
24
|
+
npm install
|
|
25
|
+
npm run build
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Configuration
|
|
29
|
+
|
|
30
|
+
### Claude Code
|
|
31
|
+
|
|
32
|
+
Add to your `.mcp.json` (project or global):
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"mcpServers": {
|
|
37
|
+
"lptp-server": {
|
|
38
|
+
"type": "stdio",
|
|
39
|
+
"command": "npx",
|
|
40
|
+
"args": ["lptp-mcp-server"],
|
|
41
|
+
"env": {
|
|
42
|
+
"LPTP_ROOT_DIR": "/path/to/your/lptp/checkout"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Environment Variables
|
|
50
|
+
|
|
51
|
+
| Variable | Required | Description |
|
|
52
|
+
|----------|----------|-------------|
|
|
53
|
+
| `LPTP_ROOT_DIR` | Yes* | Path to the LPTP checkout root (directory containing `src/lptp.pl`). *If the server is installed inside the LPTP tree, it can auto-detect. |
|
|
54
|
+
| `LPTP_PROLOG_BACKEND` | No | Force a specific backend: `swipl`, `scryer`, or `gprolog`. Default: auto-detect in that order. |
|
|
55
|
+
|
|
56
|
+
## Available Tools
|
|
57
|
+
|
|
58
|
+
| Tool | Description |
|
|
59
|
+
|------|-------------|
|
|
60
|
+
| `get_lptp_grammar` | Returns LPTP grammar summary for LLM reasoning |
|
|
61
|
+
| `verify_lemma` | Validates LPTP proof code (lemma/3, theorem/3) |
|
|
62
|
+
| `check_proof` | Runs `check/1` on a `.pr` proof file on disk |
|
|
63
|
+
| `apply_tactic` | Applies a tactic to a formula, returns derivation tree |
|
|
64
|
+
| `tactic_auto` | Automatic proof with configurable depth and resource limits |
|
|
65
|
+
| `tactic_case` | Case splitting on disjunctions |
|
|
66
|
+
| `tactic_comp` | Completion formula (fixed-point semantics) |
|
|
67
|
+
| `tactic_elim` | Predicate definition elimination |
|
|
68
|
+
| `tactic_ex` | Existential quantifier instantiation |
|
|
69
|
+
| `tactic_fact` | Apply a known fact (lemma, theorem, corollary) |
|
|
70
|
+
| `tactic_ind` | Structural induction |
|
|
71
|
+
| `tactic_indqf` | Quantifier-free induction |
|
|
72
|
+
| `tactic_tot` | Totality (termination) case splitting |
|
|
73
|
+
| `tactic_unfold` | Unfold an abbreviation or definition |
|
|
74
|
+
| `tactic_debug` | Show available formulas and assumptions in context |
|
|
75
|
+
| `print_definition` | Print completion definition (IND(P) axioms) of a predicate |
|
|
76
|
+
| `list_facts` | List known theorems/lemmas/corollaries about a predicate |
|
|
77
|
+
| `compile_gr` | Compile `.pl` to LPTP ground representation (`.gr`) |
|
|
78
|
+
|
|
79
|
+
## Prolog Backend Selection
|
|
80
|
+
|
|
81
|
+
The server auto-detects backends in this order:
|
|
82
|
+
|
|
83
|
+
1. **SWI-Prolog** (`swipl`) — full resource limits (time, inference, depth)
|
|
84
|
+
2. **Scryer Prolog** (`scryer-prolog`) — inference limits only, Node.js timeout for time
|
|
85
|
+
3. **GNU Prolog** (`bin/lptp-gnu-prolog`) — Node.js timeout only
|
|
86
|
+
|
|
87
|
+
Override with `LPTP_PROLOG_BACKEND=swipl` (or `scryer`, `gprolog`).
|
|
88
|
+
|
|
89
|
+
## License
|
|
90
|
+
|
|
91
|
+
MIT — see [LICENSE](./LICENSE). The LPTP Prolog source (`src/lptp.pl`) has its own license (R.F. Staerk, 1996).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { PrologBackend, ResourceLimits } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* GNU Prolog backend.
|
|
4
|
+
*
|
|
5
|
+
* CLI pattern (from Makefile line 839):
|
|
6
|
+
* cat ./src/allg.pl | ./bin/lptp-gnu-prolog
|
|
7
|
+
*
|
|
8
|
+
* Key differences from SWI-Prolog:
|
|
9
|
+
* - Uses a pre-compiled binary (bin/lptp-gnu-prolog) with LPTP baked in
|
|
10
|
+
* - No consult needed — LPTP is compiled into the binary
|
|
11
|
+
* - Input is piped via stdin: echo "goal." | bin/lptp-gnu-prolog
|
|
12
|
+
* - No Prolog-level resource limits — Node.js timeout only
|
|
13
|
+
* - PREVENT_PRE_COMPILATION=true avoids .gr compilation on startup
|
|
14
|
+
*/
|
|
15
|
+
export declare class GprologBackend implements PrologBackend {
|
|
16
|
+
readonly id: "gprolog";
|
|
17
|
+
readonly displayName = "GNU Prolog";
|
|
18
|
+
private getBinaryPath;
|
|
19
|
+
isAvailable(): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* GNU Prolog has no Prolog-level resource limits.
|
|
22
|
+
* Returns the goal unchanged, wrapped in once/1 for determinism.
|
|
23
|
+
*/
|
|
24
|
+
wrapWithLimits(goal: string, _limits?: ResourceLimits): string;
|
|
25
|
+
buildExecCommand(_lptpSrc: string, tmpFile: string, _limits?: ResourceLimits): string;
|
|
26
|
+
buildCheckCommand(_lptpSrc: string, checkPath: string, _limits?: ResourceLimits): string;
|
|
27
|
+
buildCompileGrCommand(_lptpSrc: string, tmpFile: string, _limits?: ResourceLimits): string;
|
|
28
|
+
suppressWarningsGoal(): string;
|
|
29
|
+
nodeTimeout(limits?: ResourceLimits, defaultMs?: number): number;
|
|
30
|
+
execEnv(): Record<string, string>;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=gprolog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gprolog.d.ts","sourceRoot":"","sources":["../../src/backends/gprolog.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE7D;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAe,YAAW,aAAa;IAChD,QAAQ,CAAC,EAAE,EAAG,SAAS,CAAU;IACjC,QAAQ,CAAC,WAAW,gBAAgB;IAEpC,OAAO,CAAC,aAAa;IAkBrB,WAAW,IAAI,OAAO;IAStB;;;OAGG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,MAAM;IAI9D,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,MAAM;IAMrF,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,MAAM;IAKxF,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,MAAM;IAK1F,oBAAoB,IAAI,MAAM;IAI9B,WAAW,CAAC,MAAM,GAAE,cAAmB,EAAE,SAAS,GAAE,MAAe,GAAG,MAAM;IAQ5E,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CAGpC"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.GprologBackend = void 0;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
/**
|
|
39
|
+
* GNU Prolog backend.
|
|
40
|
+
*
|
|
41
|
+
* CLI pattern (from Makefile line 839):
|
|
42
|
+
* cat ./src/allg.pl | ./bin/lptp-gnu-prolog
|
|
43
|
+
*
|
|
44
|
+
* Key differences from SWI-Prolog:
|
|
45
|
+
* - Uses a pre-compiled binary (bin/lptp-gnu-prolog) with LPTP baked in
|
|
46
|
+
* - No consult needed — LPTP is compiled into the binary
|
|
47
|
+
* - Input is piped via stdin: echo "goal." | bin/lptp-gnu-prolog
|
|
48
|
+
* - No Prolog-level resource limits — Node.js timeout only
|
|
49
|
+
* - PREVENT_PRE_COMPILATION=true avoids .gr compilation on startup
|
|
50
|
+
*/
|
|
51
|
+
class GprologBackend {
|
|
52
|
+
id = 'gprolog';
|
|
53
|
+
displayName = 'GNU Prolog';
|
|
54
|
+
getBinaryPath() {
|
|
55
|
+
const root = process.env['LPTP_ROOT_DIR'];
|
|
56
|
+
if (root)
|
|
57
|
+
return path.join(root, 'bin', 'lptp-gnu-prolog');
|
|
58
|
+
// Walk up from __dirname to find bin/lptp-gnu-prolog
|
|
59
|
+
let dir = __dirname;
|
|
60
|
+
for (let i = 0; i < 8; i++) {
|
|
61
|
+
const candidate = path.join(dir, 'bin', 'lptp-gnu-prolog');
|
|
62
|
+
if (require('fs').existsSync(candidate))
|
|
63
|
+
return candidate;
|
|
64
|
+
dir = path.dirname(dir);
|
|
65
|
+
}
|
|
66
|
+
throw new Error('GNU Prolog binary (bin/lptp-gnu-prolog) not found. ' +
|
|
67
|
+
'Set LPTP_ROOT_DIR to your LPTP checkout root, or build the binary with: cd src && make gprolog');
|
|
68
|
+
}
|
|
69
|
+
isAvailable() {
|
|
70
|
+
try {
|
|
71
|
+
this.getBinaryPath();
|
|
72
|
+
return require('fs').existsSync(this.getBinaryPath());
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* GNU Prolog has no Prolog-level resource limits.
|
|
80
|
+
* Returns the goal unchanged, wrapped in once/1 for determinism.
|
|
81
|
+
*/
|
|
82
|
+
wrapWithLimits(goal, _limits) {
|
|
83
|
+
return `once(${goal})`;
|
|
84
|
+
}
|
|
85
|
+
buildExecCommand(_lptpSrc, tmpFile, _limits) {
|
|
86
|
+
const binary = this.getBinaryPath();
|
|
87
|
+
// LPTP is compiled into the binary — no consult needed
|
|
88
|
+
return `echo "exec('${tmpFile}')." | ${binary}`;
|
|
89
|
+
}
|
|
90
|
+
buildCheckCommand(_lptpSrc, checkPath, _limits) {
|
|
91
|
+
const binary = this.getBinaryPath();
|
|
92
|
+
return `echo "check('${checkPath}')." | ${binary}`;
|
|
93
|
+
}
|
|
94
|
+
buildCompileGrCommand(_lptpSrc, tmpFile, _limits) {
|
|
95
|
+
const binary = this.getBinaryPath();
|
|
96
|
+
return `echo "exec('${tmpFile}')." | ${binary}`;
|
|
97
|
+
}
|
|
98
|
+
suppressWarningsGoal() {
|
|
99
|
+
return '';
|
|
100
|
+
}
|
|
101
|
+
nodeTimeout(limits = {}, defaultMs = 30_000) {
|
|
102
|
+
// Node.js timeout is the only guard for GNU Prolog.
|
|
103
|
+
if (limits.timeLimitS != null) {
|
|
104
|
+
return limits.timeLimitS * 1000 + 5000;
|
|
105
|
+
}
|
|
106
|
+
return defaultMs;
|
|
107
|
+
}
|
|
108
|
+
execEnv() {
|
|
109
|
+
return { PREVENT_PRE_COMPILATION: 'true' };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
exports.GprologBackend = GprologBackend;
|
|
113
|
+
//# sourceMappingURL=gprolog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gprolog.js","sourceRoot":"","sources":["../../src/backends/gprolog.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAA6B;AAG7B;;;;;;;;;;;;GAYG;AACH,MAAa,cAAc;IACd,EAAE,GAAG,SAAkB,CAAC;IACxB,WAAW,GAAG,YAAY,CAAC;IAE5B,aAAa;QACjB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAC1C,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC;QAE3D,qDAAqD;QACrD,IAAI,GAAG,GAAG,SAAS,CAAC;QACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACzB,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC;YAC3D,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC;gBAAE,OAAO,SAAS,CAAC;YAC1D,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;QAED,MAAM,IAAI,KAAK,CACX,qDAAqD;YACrD,gGAAgG,CACnG,CAAC;IACN,CAAC;IAED,WAAW;QACP,IAAI,CAAC;YACD,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;QAC1D,CAAC;QAAC,MAAM,CAAC;YACL,OAAO,KAAK,CAAC;QACjB,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,cAAc,CAAC,IAAY,EAAE,OAAwB;QACjD,OAAO,QAAQ,IAAI,GAAG,CAAC;IAC3B,CAAC;IAED,gBAAgB,CAAC,QAAgB,EAAE,OAAe,EAAE,OAAwB;QACxE,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACpC,uDAAuD;QACvD,OAAO,eAAe,OAAO,UAAU,MAAM,EAAE,CAAC;IACpD,CAAC;IAED,iBAAiB,CAAC,QAAgB,EAAE,SAAiB,EAAE,OAAwB;QAC3E,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACpC,OAAO,gBAAgB,SAAS,UAAU,MAAM,EAAE,CAAC;IACvD,CAAC;IAED,qBAAqB,CAAC,QAAgB,EAAE,OAAe,EAAE,OAAwB;QAC7E,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACpC,OAAO,eAAe,OAAO,UAAU,MAAM,EAAE,CAAC;IACpD,CAAC;IAED,oBAAoB;QAChB,OAAO,EAAE,CAAC;IACd,CAAC;IAED,WAAW,CAAC,SAAyB,EAAE,EAAE,YAAoB,MAAM;QAC/D,oDAAoD;QACpD,IAAI,MAAM,CAAC,UAAU,IAAI,IAAI,EAAE,CAAC;YAC5B,OAAO,MAAM,CAAC,UAAU,GAAG,IAAI,GAAG,IAAI,CAAC;QAC3C,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,OAAO;QACH,OAAO,EAAE,uBAAuB,EAAE,MAAM,EAAE,CAAC;IAC/C,CAAC;CACJ;AAtED,wCAsEC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PrologBackend } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Returns the active Prolog backend.
|
|
4
|
+
*
|
|
5
|
+
* Selection logic:
|
|
6
|
+
* 1. If LPTP_PROLOG_BACKEND env var is set, use that backend (throw if unavailable).
|
|
7
|
+
* 2. Otherwise auto-detect in order: swipl -> scryer -> gprolog (first available wins).
|
|
8
|
+
* 3. If none found, throw a descriptive error.
|
|
9
|
+
*
|
|
10
|
+
* Result is cached for the lifetime of the server process.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getBackend(): PrologBackend;
|
|
13
|
+
/** Reset cached backend — useful for tests. */
|
|
14
|
+
export declare function resetBackendCache(): void;
|
|
15
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/backends/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,aAAa,EAAE,MAAM,SAAS,CAAC;AAexD;;;;;;;;;GASG;AACH,wBAAgB,UAAU,IAAI,aAAa,CAuC1C;AAED,+CAA+C;AAC/C,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getBackend = getBackend;
|
|
4
|
+
exports.resetBackendCache = resetBackendCache;
|
|
5
|
+
const swipl_1 = require("./swipl");
|
|
6
|
+
const scryer_1 = require("./scryer");
|
|
7
|
+
const gprolog_1 = require("./gprolog");
|
|
8
|
+
let cachedBackend = null;
|
|
9
|
+
function instantiate(id) {
|
|
10
|
+
switch (id) {
|
|
11
|
+
case 'swipl': return new swipl_1.SwiplBackend();
|
|
12
|
+
case 'scryer': return new scryer_1.ScryerBackend();
|
|
13
|
+
case 'gprolog': return new gprolog_1.GprologBackend();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Returns the active Prolog backend.
|
|
18
|
+
*
|
|
19
|
+
* Selection logic:
|
|
20
|
+
* 1. If LPTP_PROLOG_BACKEND env var is set, use that backend (throw if unavailable).
|
|
21
|
+
* 2. Otherwise auto-detect in order: swipl -> scryer -> gprolog (first available wins).
|
|
22
|
+
* 3. If none found, throw a descriptive error.
|
|
23
|
+
*
|
|
24
|
+
* Result is cached for the lifetime of the server process.
|
|
25
|
+
*/
|
|
26
|
+
function getBackend() {
|
|
27
|
+
if (cachedBackend)
|
|
28
|
+
return cachedBackend;
|
|
29
|
+
const envChoice = process.env['LPTP_PROLOG_BACKEND'];
|
|
30
|
+
if (envChoice) {
|
|
31
|
+
const validIds = ['swipl', 'scryer', 'gprolog'];
|
|
32
|
+
if (!validIds.includes(envChoice)) {
|
|
33
|
+
throw new Error(`LPTP_PROLOG_BACKEND="${envChoice}" is not valid. ` +
|
|
34
|
+
`Choose one of: ${validIds.join(', ')}`);
|
|
35
|
+
}
|
|
36
|
+
const backend = instantiate(envChoice);
|
|
37
|
+
if (!backend.isAvailable()) {
|
|
38
|
+
throw new Error(`LPTP_PROLOG_BACKEND="${envChoice}" requested but ` +
|
|
39
|
+
`${backend.displayName} is not available on this system.`);
|
|
40
|
+
}
|
|
41
|
+
cachedBackend = backend;
|
|
42
|
+
return cachedBackend;
|
|
43
|
+
}
|
|
44
|
+
// Auto-detect: try each backend in preference order
|
|
45
|
+
const probeOrder = ['swipl', 'scryer', 'gprolog'];
|
|
46
|
+
for (const id of probeOrder) {
|
|
47
|
+
const backend = instantiate(id);
|
|
48
|
+
if (backend.isAvailable()) {
|
|
49
|
+
cachedBackend = backend;
|
|
50
|
+
return cachedBackend;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
throw new Error('No Prolog backend found. Install SWI-Prolog (swipl), ' +
|
|
54
|
+
'Scryer Prolog (scryer-prolog), or build GNU Prolog binary ' +
|
|
55
|
+
'(cd src && make gprolog). Or set LPTP_PROLOG_BACKEND explicitly.');
|
|
56
|
+
}
|
|
57
|
+
/** Reset cached backend — useful for tests. */
|
|
58
|
+
function resetBackendCache() {
|
|
59
|
+
cachedBackend = null;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/backends/registry.ts"],"names":[],"mappings":";;AAyBA,gCAuCC;AAGD,8CAEC;AApED,mCAAuC;AACvC,qCAAyC;AACzC,uCAA2C;AAE3C,IAAI,aAAa,GAAyB,IAAI,CAAC;AAE/C,SAAS,WAAW,CAAC,EAAa;IAC9B,QAAQ,EAAE,EAAE,CAAC;QACT,KAAK,OAAO,CAAC,CAAE,OAAO,IAAI,oBAAY,EAAE,CAAC;QACzC,KAAK,QAAQ,CAAC,CAAC,OAAO,IAAI,sBAAa,EAAE,CAAC;QAC1C,KAAK,SAAS,CAAC,CAAC,OAAO,IAAI,wBAAc,EAAE,CAAC;IAChD,CAAC;AACL,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,UAAU;IACtB,IAAI,aAAa;QAAE,OAAO,aAAa,CAAC;IAExC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAA0B,CAAC;IAE9E,IAAI,SAAS,EAAE,CAAC;QACZ,MAAM,QAAQ,GAAgB,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;QAC7D,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACX,wBAAwB,SAAS,kBAAkB;gBACnD,kBAAkB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC1C,CAAC;QACN,CAAC;QACD,MAAM,OAAO,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACX,wBAAwB,SAAS,kBAAkB;gBACnD,GAAG,OAAO,CAAC,WAAW,mCAAmC,CAC5D,CAAC;QACN,CAAC;QACD,aAAa,GAAG,OAAO,CAAC;QACxB,OAAO,aAAa,CAAC;IACzB,CAAC;IAED,oDAAoD;IACpD,MAAM,UAAU,GAAgB,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC/D,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,aAAa,GAAG,OAAO,CAAC;YACxB,OAAO,aAAa,CAAC;QACzB,CAAC;IACL,CAAC;IAED,MAAM,IAAI,KAAK,CACX,uDAAuD;QACvD,4DAA4D;QAC5D,kEAAkE,CACrE,CAAC;AACN,CAAC;AAED,+CAA+C;AAC/C,SAAgB,iBAAiB;IAC7B,aAAa,GAAG,IAAI,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { PrologBackend, ResourceLimits } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Scryer Prolog backend.
|
|
4
|
+
*
|
|
5
|
+
* CLI pattern (from Makefile line 819):
|
|
6
|
+
* scryer-prolog -g "consult('./src/lptp.pl'),exec('file')." -g "halt."
|
|
7
|
+
*
|
|
8
|
+
* Key differences from SWI-Prolog:
|
|
9
|
+
* - Goals passed to -g need a trailing period
|
|
10
|
+
* - Supports call_with_inference_limit/3 from iso_ext module
|
|
11
|
+
* - No call_with_time_limit or call_with_depth_limit — Node.js timeout covers those
|
|
12
|
+
* - Quiet mode via QUIET_MODE=true environment variable
|
|
13
|
+
* - No message_hook suppression needed
|
|
14
|
+
*/
|
|
15
|
+
export declare class ScryerBackend implements PrologBackend {
|
|
16
|
+
readonly id: "scryer";
|
|
17
|
+
readonly displayName = "Scryer Prolog";
|
|
18
|
+
isAvailable(): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Wraps a Prolog goal with Scryer-specific resource limits.
|
|
21
|
+
* Only inference limit is supported at the Prolog level via
|
|
22
|
+
* call_with_inference_limit/3 (iso_ext module).
|
|
23
|
+
* Time and depth limits fall back to Node.js timeout.
|
|
24
|
+
*/
|
|
25
|
+
wrapWithLimits(goal: string, limits?: ResourceLimits): string;
|
|
26
|
+
buildExecCommand(lptpSrc: string, tmpFile: string, limits?: ResourceLimits): string;
|
|
27
|
+
buildCheckCommand(lptpSrc: string, checkPath: string, limits?: ResourceLimits): string;
|
|
28
|
+
buildCompileGrCommand(lptpSrc: string, tmpFile: string, limits?: ResourceLimits): string;
|
|
29
|
+
suppressWarningsGoal(): string;
|
|
30
|
+
nodeTimeout(limits?: ResourceLimits, defaultMs?: number): number;
|
|
31
|
+
execEnv(): Record<string, string>;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=scryer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scryer.d.ts","sourceRoot":"","sources":["../../src/backends/scryer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAI7D;;;;;;;;;;;;GAYG;AACH,qBAAa,aAAc,YAAW,aAAa;IAC/C,QAAQ,CAAC,EAAE,EAAG,QAAQ,CAAU;IAChC,QAAQ,CAAC,WAAW,mBAAmB;IAEvC,WAAW,IAAI,OAAO;IAKtB;;;;;OAKG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,cAAmB,GAAG,MAAM;IASjE,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM;IAQnF,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM;IAQtF,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM;IAQxF,oBAAoB,IAAI,MAAM;IAI9B,WAAW,CAAC,MAAM,GAAE,cAAmB,EAAE,SAAS,GAAE,MAAe,GAAG,MAAM;IAS5E,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CAGpC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ScryerBackend = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const DEFAULT_INFERENCE_LIMIT = 1_000_000;
|
|
6
|
+
/**
|
|
7
|
+
* Scryer Prolog backend.
|
|
8
|
+
*
|
|
9
|
+
* CLI pattern (from Makefile line 819):
|
|
10
|
+
* scryer-prolog -g "consult('./src/lptp.pl'),exec('file')." -g "halt."
|
|
11
|
+
*
|
|
12
|
+
* Key differences from SWI-Prolog:
|
|
13
|
+
* - Goals passed to -g need a trailing period
|
|
14
|
+
* - Supports call_with_inference_limit/3 from iso_ext module
|
|
15
|
+
* - No call_with_time_limit or call_with_depth_limit — Node.js timeout covers those
|
|
16
|
+
* - Quiet mode via QUIET_MODE=true environment variable
|
|
17
|
+
* - No message_hook suppression needed
|
|
18
|
+
*/
|
|
19
|
+
class ScryerBackend {
|
|
20
|
+
id = 'scryer';
|
|
21
|
+
displayName = 'Scryer Prolog';
|
|
22
|
+
isAvailable() {
|
|
23
|
+
try {
|
|
24
|
+
(0, child_process_1.execSync)('which scryer-prolog', { stdio: 'ignore' });
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Wraps a Prolog goal with Scryer-specific resource limits.
|
|
33
|
+
* Only inference limit is supported at the Prolog level via
|
|
34
|
+
* call_with_inference_limit/3 (iso_ext module).
|
|
35
|
+
* Time and depth limits fall back to Node.js timeout.
|
|
36
|
+
*/
|
|
37
|
+
wrapWithLimits(goal, limits = {}) {
|
|
38
|
+
let wrapped = `once(${goal})`;
|
|
39
|
+
const infLim = limits.inferenceLimit ?? DEFAULT_INFERENCE_LIMIT;
|
|
40
|
+
wrapped = `call_with_inference_limit((${wrapped}), ${infLim}, _InfRes)`;
|
|
41
|
+
return wrapped;
|
|
42
|
+
}
|
|
43
|
+
buildExecCommand(lptpSrc, tmpFile, limits) {
|
|
44
|
+
const wrappedGoal = this.wrapWithLimits(`(consult('${lptpSrc}'), exec('${tmpFile}'))`, limits);
|
|
45
|
+
return `scryer-prolog -g "${wrappedGoal}." -g "halt."`;
|
|
46
|
+
}
|
|
47
|
+
buildCheckCommand(lptpSrc, checkPath, limits) {
|
|
48
|
+
const wrappedGoal = this.wrapWithLimits(`(consult('${lptpSrc}'), check('${checkPath}'))`, limits ?? { inferenceLimit: 10_000_000 });
|
|
49
|
+
return `scryer-prolog -g "${wrappedGoal}." -g "halt."`;
|
|
50
|
+
}
|
|
51
|
+
buildCompileGrCommand(lptpSrc, tmpFile, limits) {
|
|
52
|
+
const wrappedGoal = this.wrapWithLimits(`(consult('${lptpSrc}'), exec('${tmpFile}'))`, limits);
|
|
53
|
+
return `scryer-prolog -g "${wrappedGoal}." -g "halt."`;
|
|
54
|
+
}
|
|
55
|
+
suppressWarningsGoal() {
|
|
56
|
+
return '';
|
|
57
|
+
}
|
|
58
|
+
nodeTimeout(limits = {}, defaultMs = 30_000) {
|
|
59
|
+
// Scryer has no Prolog-level time limit, so Node.js timeout is the primary guard.
|
|
60
|
+
// Use the timeLimitS hint if provided, otherwise fall back to defaultMs.
|
|
61
|
+
if (limits.timeLimitS != null) {
|
|
62
|
+
return limits.timeLimitS * 1000 + 5000;
|
|
63
|
+
}
|
|
64
|
+
return defaultMs;
|
|
65
|
+
}
|
|
66
|
+
execEnv() {
|
|
67
|
+
return { QUIET_MODE: 'true' };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.ScryerBackend = ScryerBackend;
|
|
71
|
+
//# sourceMappingURL=scryer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scryer.js","sourceRoot":"","sources":["../../src/backends/scryer.ts"],"names":[],"mappings":";;;AAAA,iDAAyC;AAGzC,MAAM,uBAAuB,GAAG,SAAS,CAAC;AAE1C;;;;;;;;;;;;GAYG;AACH,MAAa,aAAa;IACb,EAAE,GAAG,QAAiB,CAAC;IACvB,WAAW,GAAG,eAAe,CAAC;IAEvC,WAAW;QACP,IAAI,CAAC;YAAC,IAAA,wBAAQ,EAAC,qBAAqB,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;QAC1E,MAAM,CAAC;YAAC,OAAO,KAAK,CAAC;QAAC,CAAC;IAC3B,CAAC;IAED;;;;;OAKG;IACH,cAAc,CAAC,IAAY,EAAE,SAAyB,EAAE;QACpD,IAAI,OAAO,GAAG,QAAQ,IAAI,GAAG,CAAC;QAE9B,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,IAAI,uBAAuB,CAAC;QAChE,OAAO,GAAG,8BAA8B,OAAO,MAAM,MAAM,YAAY,CAAC;QAExE,OAAO,OAAO,CAAC;IACnB,CAAC;IAED,gBAAgB,CAAC,OAAe,EAAE,OAAe,EAAE,MAAuB;QACtE,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,CACnC,aAAa,OAAO,aAAa,OAAO,KAAK,EAC7C,MAAM,CACT,CAAC;QACF,OAAO,qBAAqB,WAAW,eAAe,CAAC;IAC3D,CAAC;IAED,iBAAiB,CAAC,OAAe,EAAE,SAAiB,EAAE,MAAuB;QACzE,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,CACnC,aAAa,OAAO,cAAc,SAAS,KAAK,EAChD,MAAM,IAAI,EAAE,cAAc,EAAE,UAAU,EAAE,CAC3C,CAAC;QACF,OAAO,qBAAqB,WAAW,eAAe,CAAC;IAC3D,CAAC;IAED,qBAAqB,CAAC,OAAe,EAAE,OAAe,EAAE,MAAuB;QAC3E,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,CACnC,aAAa,OAAO,aAAa,OAAO,KAAK,EAC7C,MAAM,CACT,CAAC;QACF,OAAO,qBAAqB,WAAW,eAAe,CAAC;IAC3D,CAAC;IAED,oBAAoB;QAChB,OAAO,EAAE,CAAC;IACd,CAAC;IAED,WAAW,CAAC,SAAyB,EAAE,EAAE,YAAoB,MAAM;QAC/D,kFAAkF;QAClF,yEAAyE;QACzE,IAAI,MAAM,CAAC,UAAU,IAAI,IAAI,EAAE,CAAC;YAC5B,OAAO,MAAM,CAAC,UAAU,GAAG,IAAI,GAAG,IAAI,CAAC;QAC3C,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,OAAO;QACH,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IAClC,CAAC;CACJ;AAhED,sCAgEC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { PrologBackend, ResourceLimits } from './types';
|
|
2
|
+
export declare class SwiplBackend implements PrologBackend {
|
|
3
|
+
readonly id: "swipl";
|
|
4
|
+
readonly displayName = "SWI-Prolog";
|
|
5
|
+
isAvailable(): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Wraps a SWI-Prolog goal with resource limits:
|
|
8
|
+
* once/1 — deterministic, no backtracking
|
|
9
|
+
* call_with_time_limit/2 — wall-clock timeout (throws time_limit_exceeded)
|
|
10
|
+
* call_with_inference_limit/3 — inference bound (binds Result)
|
|
11
|
+
* call_with_depth_limit/3 — depth bound (binds Result)
|
|
12
|
+
*
|
|
13
|
+
* If any limit is exceeded the process writes a diagnostic and halts with
|
|
14
|
+
* a non-zero exit code so the Node.js side captures it as an error.
|
|
15
|
+
*/
|
|
16
|
+
wrapWithLimits(goal: string, limits?: ResourceLimits): string;
|
|
17
|
+
buildExecCommand(lptpSrc: string, tmpFile: string, limits?: ResourceLimits): string;
|
|
18
|
+
buildCheckCommand(lptpSrc: string, checkPath: string, limits?: ResourceLimits): string;
|
|
19
|
+
buildCompileGrCommand(lptpSrc: string, tmpFile: string, limits?: ResourceLimits): string;
|
|
20
|
+
suppressWarningsGoal(): string;
|
|
21
|
+
nodeTimeout(limits?: ResourceLimits, defaultMs?: number): number;
|
|
22
|
+
execEnv(): Record<string, string>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=swipl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swipl.d.ts","sourceRoot":"","sources":["../../src/backends/swipl.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAK7D,qBAAa,YAAa,YAAW,aAAa;IAC9C,QAAQ,CAAC,EAAE,EAAG,OAAO,CAAU;IAC/B,QAAQ,CAAC,WAAW,gBAAgB;IAEpC,WAAW,IAAI,OAAO;IAKtB;;;;;;;;;OASG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,cAAmB,GAAG,MAAM;IAyBjE,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM;IAQnF,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM;IAStF,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM;IAQxF,oBAAoB,IAAI,MAAM;IAI9B,WAAW,CAAC,MAAM,GAAE,cAAmB,EAAE,SAAS,GAAE,MAAe,GAAG,MAAM;IAK5E,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CAGpC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SwiplBackend = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const DEFAULT_PROLOG_TIME_LIMIT_S = 25;
|
|
6
|
+
const DEFAULT_INFERENCE_LIMIT = 1_000_000;
|
|
7
|
+
class SwiplBackend {
|
|
8
|
+
id = 'swipl';
|
|
9
|
+
displayName = 'SWI-Prolog';
|
|
10
|
+
isAvailable() {
|
|
11
|
+
try {
|
|
12
|
+
(0, child_process_1.execSync)('which swipl', { stdio: 'ignore' });
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Wraps a SWI-Prolog goal with resource limits:
|
|
21
|
+
* once/1 — deterministic, no backtracking
|
|
22
|
+
* call_with_time_limit/2 — wall-clock timeout (throws time_limit_exceeded)
|
|
23
|
+
* call_with_inference_limit/3 — inference bound (binds Result)
|
|
24
|
+
* call_with_depth_limit/3 — depth bound (binds Result)
|
|
25
|
+
*
|
|
26
|
+
* If any limit is exceeded the process writes a diagnostic and halts with
|
|
27
|
+
* a non-zero exit code so the Node.js side captures it as an error.
|
|
28
|
+
*/
|
|
29
|
+
wrapWithLimits(goal, limits = {}) {
|
|
30
|
+
const timeS = limits.timeLimitS ?? DEFAULT_PROLOG_TIME_LIMIT_S;
|
|
31
|
+
const infLim = limits.inferenceLimit ?? DEFAULT_INFERENCE_LIMIT;
|
|
32
|
+
// Build from inside out:
|
|
33
|
+
// innermost: once(Goal)
|
|
34
|
+
// then: call_with_inference_limit(…, InfLim, InfRes)
|
|
35
|
+
// then: call_with_time_limit(TimeS, …)
|
|
36
|
+
// outermost: catch(…, time_limit_exceeded, diagnostic)
|
|
37
|
+
let wrapped = `once(${goal})`;
|
|
38
|
+
// Inference limit guard
|
|
39
|
+
wrapped = `call_with_inference_limit((${wrapped}), ${infLim}, _InfRes)`;
|
|
40
|
+
// Optional depth limit
|
|
41
|
+
if (limits.depthLimit != null) {
|
|
42
|
+
wrapped = `call_with_depth_limit((${wrapped}), ${limits.depthLimit}, _DepthRes)`;
|
|
43
|
+
}
|
|
44
|
+
// Time limit guard (outermost catch)
|
|
45
|
+
wrapped = `catch(call_with_time_limit(${timeS}, (${wrapped})), time_limit_exceeded, (write('TIME_LIMIT_EXCEEDED'), nl, halt(2)))`;
|
|
46
|
+
return wrapped;
|
|
47
|
+
}
|
|
48
|
+
buildExecCommand(lptpSrc, tmpFile, limits) {
|
|
49
|
+
const wrappedGoal = this.wrapWithLimits(`(consult('${lptpSrc}'), exec('${tmpFile}'))`, limits);
|
|
50
|
+
return `swipl -q -g "${wrappedGoal}, halt" -t "halt(1)"`;
|
|
51
|
+
}
|
|
52
|
+
buildCheckCommand(lptpSrc, checkPath, limits) {
|
|
53
|
+
const suppressWarnings = this.suppressWarningsGoal();
|
|
54
|
+
const wrappedGoal = this.wrapWithLimits(`(${suppressWarnings}, consult('${lptpSrc}'), check('${checkPath}'))`, limits ?? { timeLimitS: 110, inferenceLimit: 10_000_000 });
|
|
55
|
+
return `swipl -q -g "${wrappedGoal}, halt" -t "halt(1)"`;
|
|
56
|
+
}
|
|
57
|
+
buildCompileGrCommand(lptpSrc, tmpFile, limits) {
|
|
58
|
+
const wrappedGoal = this.wrapWithLimits(`(consult('${lptpSrc}'), exec('${tmpFile}'))`, limits);
|
|
59
|
+
return `swipl -q -g "${wrappedGoal}, halt" -t "halt(1)"`;
|
|
60
|
+
}
|
|
61
|
+
suppressWarningsGoal() {
|
|
62
|
+
return `asserta((user:message_hook(_,warning,_) :- !))`;
|
|
63
|
+
}
|
|
64
|
+
nodeTimeout(limits = {}, defaultMs = 30_000) {
|
|
65
|
+
const timeS = limits.timeLimitS ?? DEFAULT_PROLOG_TIME_LIMIT_S;
|
|
66
|
+
return timeS * 1000 + 5000;
|
|
67
|
+
}
|
|
68
|
+
execEnv() {
|
|
69
|
+
return {};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.SwiplBackend = SwiplBackend;
|
|
73
|
+
//# sourceMappingURL=swipl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swipl.js","sourceRoot":"","sources":["../../src/backends/swipl.ts"],"names":[],"mappings":";;;AAAA,iDAAyC;AAGzC,MAAM,2BAA2B,GAAG,EAAE,CAAC;AACvC,MAAM,uBAAuB,GAAO,SAAS,CAAC;AAE9C,MAAa,YAAY;IACZ,EAAE,GAAG,OAAgB,CAAC;IACtB,WAAW,GAAG,YAAY,CAAC;IAEpC,WAAW;QACP,IAAI,CAAC;YAAC,IAAA,wBAAQ,EAAC,aAAa,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;QAClE,MAAM,CAAC;YAAC,OAAO,KAAK,CAAC;QAAC,CAAC;IAC3B,CAAC;IAED;;;;;;;;;OASG;IACH,cAAc,CAAC,IAAY,EAAE,SAAyB,EAAE;QACpD,MAAM,KAAK,GAAI,MAAM,CAAC,UAAU,IAAI,2BAA2B,CAAC;QAChE,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,IAAI,uBAAuB,CAAC;QAEhE,yBAAyB;QACzB,0BAA0B;QAC1B,uDAAuD;QACvD,yCAAyC;QACzC,yDAAyD;QACzD,IAAI,OAAO,GAAG,QAAQ,IAAI,GAAG,CAAC;QAE9B,wBAAwB;QACxB,OAAO,GAAG,8BAA8B,OAAO,MAAM,MAAM,YAAY,CAAC;QAExE,uBAAuB;QACvB,IAAI,MAAM,CAAC,UAAU,IAAI,IAAI,EAAE,CAAC;YAC5B,OAAO,GAAG,0BAA0B,OAAO,MAAM,MAAM,CAAC,UAAU,cAAc,CAAC;QACrF,CAAC;QAED,qCAAqC;QACrC,OAAO,GAAG,8BAA8B,KAAK,MAAM,OAAO,uEAAuE,CAAC;QAElI,OAAO,OAAO,CAAC;IACnB,CAAC;IAED,gBAAgB,CAAC,OAAe,EAAE,OAAe,EAAE,MAAuB;QACtE,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,CACnC,aAAa,OAAO,aAAa,OAAO,KAAK,EAC7C,MAAM,CACT,CAAC;QACF,OAAO,gBAAgB,WAAW,sBAAsB,CAAC;IAC7D,CAAC;IAED,iBAAiB,CAAC,OAAe,EAAE,SAAiB,EAAE,MAAuB;QACzE,MAAM,gBAAgB,GAAG,IAAI,CAAC,oBAAoB,EAAE,CAAC;QACrD,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,CACnC,IAAI,gBAAgB,cAAc,OAAO,cAAc,SAAS,KAAK,EACrE,MAAM,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,cAAc,EAAE,UAAU,EAAE,CAC5D,CAAC;QACF,OAAO,gBAAgB,WAAW,sBAAsB,CAAC;IAC7D,CAAC;IAED,qBAAqB,CAAC,OAAe,EAAE,OAAe,EAAE,MAAuB;QAC3E,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,CACnC,aAAa,OAAO,aAAa,OAAO,KAAK,EAC7C,MAAM,CACT,CAAC;QACF,OAAO,gBAAgB,WAAW,sBAAsB,CAAC;IAC7D,CAAC;IAED,oBAAoB;QAChB,OAAO,gDAAgD,CAAC;IAC5D,CAAC;IAED,WAAW,CAAC,SAAyB,EAAE,EAAE,YAAoB,MAAM;QAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,IAAI,2BAA2B,CAAC;QAC/D,OAAO,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC;IAC/B,CAAC;IAED,OAAO;QACH,OAAO,EAAE,CAAC;IACd,CAAC;CACJ;AAjFD,oCAiFC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type BackendId = 'swipl' | 'scryer' | 'gprolog';
|
|
2
|
+
export interface ResourceLimits {
|
|
3
|
+
timeLimitS?: number;
|
|
4
|
+
inferenceLimit?: number;
|
|
5
|
+
depthLimit?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface PrologBackend {
|
|
8
|
+
readonly id: BackendId;
|
|
9
|
+
readonly displayName: string;
|
|
10
|
+
/** Returns true when the backend's binary / compiled artifact is on PATH or at expected location. */
|
|
11
|
+
isAvailable(): boolean;
|
|
12
|
+
/** Build shell command that runs `exec(TmpFile)` through LPTP. */
|
|
13
|
+
buildExecCommand(lptpSrc: string, tmpFile: string, limits?: ResourceLimits): string;
|
|
14
|
+
/** Build shell command that runs `check(CheckPath)` through LPTP. */
|
|
15
|
+
buildCheckCommand(lptpSrc: string, checkPath: string, limits?: ResourceLimits): string;
|
|
16
|
+
/** Build shell command that compiles a .gr file through LPTP. */
|
|
17
|
+
buildCompileGrCommand(lptpSrc: string, tmpFile: string, limits?: ResourceLimits): string;
|
|
18
|
+
/** Wrap a Prolog goal with backend-specific resource limits. */
|
|
19
|
+
wrapWithLimits(goal: string, limits?: ResourceLimits): string;
|
|
20
|
+
/** Return a Prolog goal that suppresses backend-specific warnings, or '' if N/A. */
|
|
21
|
+
suppressWarningsGoal(): string;
|
|
22
|
+
/** Compute Node.js exec timeout (ms) from limits. */
|
|
23
|
+
nodeTimeout(limits?: ResourceLimits, defaultMs?: number): number;
|
|
24
|
+
/** Extra environment variables for child_process.exec. */
|
|
25
|
+
execEnv(): Record<string, string>;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/backends/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEvD,MAAM,WAAW,cAAc;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,qGAAqG;IACrG,WAAW,IAAI,OAAO,CAAC;IAEvB,kEAAkE;IAClE,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM,CAAC;IAEpF,qEAAqE;IACrE,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM,CAAC;IAEvF,iEAAiE;IACjE,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM,CAAC;IAEzF,gEAAgE;IAChE,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM,CAAC;IAE9D,oFAAoF;IACpF,oBAAoB,IAAI,MAAM,CAAC;IAE/B,qDAAqD;IACrD,WAAW,CAAC,MAAM,CAAC,EAAE,cAAc,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAEjE,0DAA0D;IAC1D,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/backends/types.ts"],"names":[],"mappings":""}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|