|
| 1 | +<script src="https://unpkg.com/monaco-editor@latest/min/vs/loader.js"></script> |
| 2 | + |
| 3 | +<script type="module"> |
| 4 | +const statusEl = document.getElementById('status'); |
| 5 | +const resultEl = document.getElementById('result'); |
| 6 | + |
| 7 | +let bindings = null; |
| 8 | +let stdin = ''; |
| 9 | +let stdinPosition = 0; |
| 10 | +// We use this to provide data into |
| 11 | +// the SWI stdin. |
| 12 | +const setStdin = (string) => { |
| 13 | + stdin = string; |
| 14 | + stdinPosition = 0; |
| 15 | +}; |
| 16 | +const readStdin = () => { |
| 17 | + if (stdinPosition >= stdin.length) { |
| 18 | + return null; |
| 19 | + } else { |
| 20 | + const code = stdin.charCodeAt(stdinPosition); |
| 21 | + stdinPosition++; |
| 22 | + return code; |
| 23 | + } |
| 24 | +}; |
| 25 | + |
| 26 | +// Helper function to call a query. |
| 27 | +const query = (bindings, input) => { |
| 28 | + // Show the query in the console output. |
| 29 | + const node = document.createTextNode(input + '\n'); |
| 30 | + resultEl.appendChild(node); |
| 31 | + setStdin(input); |
| 32 | + // This will execute one iteration of toplevel. |
| 33 | + call(bindings, 'break'); // see call.js |
| 34 | +} |
| 35 | + |
| 36 | +// Helper to print stdout from SWI. |
| 37 | +const print = (line) => { |
| 38 | + resultEl.appendChild(document.createTextNode(line + '\n')); |
| 39 | +}; |
| 40 | + |
| 41 | +// Helper to print stderr from SWI. |
| 42 | +const printErr = (line) => { |
| 43 | + const node = document.createElement('span'); |
| 44 | + node.className = 'output-error'; |
| 45 | + node.textContent = line + '\n'; |
| 46 | + resultEl.appendChild(node); |
| 47 | +}; |
| 48 | + |
| 49 | +// Creates bindings to the SWI foreign API. |
| 50 | +const createBindings = (module) => { |
| 51 | + return { |
| 52 | + PL_initialise: module.cwrap('PL_initialise', 'number', ['number', 'number']), |
| 53 | + PL_new_term_ref: module.cwrap('PL_new_term_ref', 'number', []), |
| 54 | + PL_chars_to_term: module.cwrap('PL_chars_to_term', 'number', ['string', 'number']), |
| 55 | + PL_call: module.cwrap('PL_call', 'number', ['number', 'number']) |
| 56 | + }; |
| 57 | +}; |
| 58 | + |
| 59 | +// Helper function to parse a JavaScript |
| 60 | +// string into a Prolog term and call is as a query. |
| 61 | +const call = (bindings, query) => { |
| 62 | + const ref = bindings.PL_new_term_ref(); |
| 63 | + if (!bindings.PL_chars_to_term(query, ref)) { |
| 64 | + throw new Error('Query has a syntax error: ' + query); |
| 65 | + } |
| 66 | + return !!bindings.PL_call(ref, 0); |
| 67 | +}; |
| 68 | + |
| 69 | +// This will set up the arguments necessary for the PL_initialise |
| 70 | +// function and will call it. |
| 71 | +// See http://www.swi-prolog.org/pldoc/doc_for?object=c(%27PL_initialise%27) |
| 72 | +const initialise = (bindings, module) => { |
| 73 | + const argvArray = [ |
| 74 | + module.allocate(module.intArrayFromString('swipl'), 'i8', module.ALLOC_NORMAL), |
| 75 | + module.allocate(module.intArrayFromString('-x'), 'i8', module.ALLOC_NORMAL), |
| 76 | + module.allocate(module.intArrayFromString('wasm-preload/swipl.prc'), 'i8', module.ALLOC_NORMAL), |
| 77 | + module.allocate(module.intArrayFromString('--nosignals'), 'i8', module.ALLOC_NORMAL) |
| 78 | + ]; |
| 79 | + const argvPtr = module._malloc(argvArray.length * 4); |
| 80 | + for (let i = 0; i < argvArray.length; i++) { |
| 81 | + module.setValue(argvPtr + i * 4, argvArray[i], '*'); |
| 82 | + } |
| 83 | + if (!bindings.PL_initialise(4, argvPtr)) { |
| 84 | + throw new Error('SWI-Prolog initialisation failed.'); |
| 85 | + } |
| 86 | + // Set the path of the preloaded (from swipl-web.dat) standard library. |
| 87 | + // This makes it possible to call use_module(library(lists)) and so on. |
| 88 | + call(bindings, "assert(user:file_search_path(library, 'wasm-preload/library'))."); |
| 89 | +}; |
| 90 | + |
| 91 | +// Stub Module object. Used by swipl-web.js to |
| 92 | +// populate the actual Module object. |
| 93 | +var Module = { |
| 94 | + noInitialRun: true, |
| 95 | + locateFile: (url) => { |
| 96 | + console.log('locateFile', url) |
| 97 | + return `https://cdn.jsdelivr.net/gh/SWI-Prolog/swipl-wasm@7e2e2aae7aabc74e9b7ab8a6e19a1c88be10325c/dist/${url}` |
| 98 | + }, |
| 99 | + print: print, |
| 100 | + printErr: printErr, |
| 101 | + preRun: [() => FS.init(readStdin)], // sets up stdin |
| 102 | + onRuntimeInitialized: () => { |
| 103 | + console.log("PROLOG time"); |
| 104 | + //document.getElementById('top').className = undefined; |
| 105 | + // Bind foreign functions to JavaScript. |
| 106 | + bindings = createBindings(Module); |
| 107 | + // Initialise SWI-Prolog. |
| 108 | + initialise(bindings, Module); |
| 109 | + } |
| 110 | +}; |
| 111 | +window.Module = Module; |
| 112 | + |
| 113 | +require.config({ |
| 114 | + paths: { |
| 115 | + 'vs': 'https://unpkg.com/monaco-editor@latest/min/vs' |
| 116 | + } |
| 117 | +}); |
| 118 | + |
| 119 | +const proxy = URL.createObjectURL(new Blob([` |
| 120 | + self.MonacoEnvironment = { |
| 121 | + baseUrl: 'https://unpkg.com/monaco-editor@latest/min/' |
| 122 | + }; |
| 123 | + importScripts('https://unpkg.com/monaco-editor@latest/min/vs/base/worker/workerMain.js'); |
| 124 | +`], { type: 'text/javascript' })); |
| 125 | + |
| 126 | +window.MonacoEnvironment = { getWorkerUrl: () => proxy }; |
| 127 | + |
| 128 | +const theme = window.matchMedia && |
| 129 | + window.matchMedia('(prefers-color-scheme: dark)').matches |
| 130 | + ? 'vs-dark' : undefined; |
| 131 | + |
| 132 | +const value = ` |
| 133 | +:- use_module(library(lists)). |
| 134 | + |
| 135 | +%% queens(+N, -Queens) is nondet. |
| 136 | +% |
| 137 | +% @param Queens is a list of column numbers for placing the queens. |
| 138 | +% @author Richard A. O'Keefe (The Craft of Prolog) |
| 139 | + |
| 140 | +queens(N, Queens) :- |
| 141 | + length(Queens, N), |
| 142 | + board(Queens, Board, 0, N, _, _), |
| 143 | + queens(Board, 0, Queens). |
| 144 | + |
| 145 | +board([], [], N, N, _, _). |
| 146 | +board([_|Queens], [Col-Vars|Board], Col0, N, [_|VR], VC) :- |
| 147 | + Col is Col0+1, |
| 148 | + functor(Vars, f, N), |
| 149 | + constraints(N, Vars, VR, VC), |
| 150 | + board(Queens, Board, Col, N, VR, [_|VC]). |
| 151 | + |
| 152 | +constraints(0, _, _, _) :- !. |
| 153 | +constraints(N, Row, [R|Rs], [C|Cs]) :- |
| 154 | + arg(N, Row, R-C), |
| 155 | + M is N-1, |
| 156 | + constraints(M, Row, Rs, Cs). |
| 157 | + |
| 158 | +queens([], _, []). |
| 159 | +queens([C|Cs], Row0, [Col|Solution]) :- |
| 160 | + Row is Row0+1, |
| 161 | + select(Col-Vars, [C|Cs], Board), |
| 162 | + arg(Row, Vars, Row-Row), |
| 163 | + queens(Board, Row, Solution). |
| 164 | +`.trim(); |
| 165 | + |
| 166 | +require(["vs/editor/editor.main"], function () { |
| 167 | + const typescript = monaco.languages.typescript; |
| 168 | + for (const lang of [typescript.typescriptDefaults, typescript.javascriptDefaults]) { |
| 169 | + lang.setCompilerOptions({ |
| 170 | + noSemanticValidation: true, |
| 171 | + noSyntaxValidation: false |
| 172 | + }); |
| 173 | + lang.setCompilerOptions({ |
| 174 | + target: monaco.languages.typescript.ScriptTarget.ESNext, |
| 175 | + allowNonTsExtensions: true, |
| 176 | + allowJs: true, |
| 177 | + }); |
| 178 | + /* FIXME: types.then(([uri, content]) => lang.addExtraLib(content, uri)); */ |
| 179 | + } |
| 180 | + const input = monaco.editor.create(document.getElementById('input'), { |
| 181 | + language: 'typescript', |
| 182 | + model: monaco.editor.createModel(value, 'prolog'), |
| 183 | + value, |
| 184 | + theme, |
| 185 | + minimap: false |
| 186 | + }); |
| 187 | + const output = monaco.editor.create(document.getElementById('output'), { |
| 188 | + language: 'javascript', |
| 189 | + value: '//', |
| 190 | + theme, |
| 191 | + readOnly: true, |
| 192 | + minimap: false |
| 193 | + }); |
| 194 | + const onEdit = () => { |
| 195 | + const body = input.getValue(); |
| 196 | + |
| 197 | + const encoder = new TextEncoder(); |
| 198 | + const data = encoder.encode(body); |
| 199 | + crypto.subtle.digest('SHA-256', data).then(hash => { |
| 200 | + const hex = Array.from(new Uint8Array(hash), chunk => chunk.toString(16).padStart(2, '0')).join(''); |
| 201 | + statusEl.textContent = `SHA256: ${hex}`; |
| 202 | + }); |
| 203 | + |
| 204 | + if (bindings) { |
| 205 | + FS.writeFile('/file.pl', body); |
| 206 | + query(bindings, "consult('/file.pl')."); |
| 207 | + } |
| 208 | + }; |
| 209 | + input.onDidChangeModelContent(onEdit); |
| 210 | + const model = input.getModel(); |
| 211 | + model.setValue(input.getValue() + "\n"); |
| 212 | +}); |
| 213 | + |
| 214 | +document.body.appendChild(Object.assign(document.createElement('script'), { |
| 215 | + async: true, |
| 216 | + defer: true, |
| 217 | + src: "https://cdn.jsdelivr.net/gh/SWI-Prolog/swipl-wasm@7e2e2aae7aabc74e9b7ab8a6e19a1c88be10325c/dist/swipl-web.js" |
| 218 | +})); |
| 219 | +</script> |
| 220 | + |
| 221 | +<!--<script async defer src="https://cdn.jsdelivr.net/gh/SWI-Prolog/swipl-wasm@7e2e2aae7aabc74e9b7ab8a6e19a1c88be10325c/dist/swipl-web.js"></script>--> |
| 222 | + |
| 223 | +<output id=status class="block text-xs opacity-50"></output> |
| 224 | +<output id=result class="block text-xs opacity-50"></output> |
| 225 | +<div class="flex-container" id="container" style="display: flex; min-height: 100vh;"> |
| 226 | + <div id="input" style="flex: 1;"></div> |
| 227 | + <div id="output" style="flex: 1;"></div> |
| 228 | +</div> |
0 commit comments