Skip to content

Commit 1092d78

Browse files
committed
Add prolog wasm page
1 parent 3e222fd commit 1092d78

File tree

3 files changed

+230
-1
lines changed

3 files changed

+230
-1
lines changed

apps/components_guide_web/lib/components_guide_web/controllers/react_typescript_controller.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ defmodule ComponentsGuideWeb.ReactTypescriptController do
77
|> render("index.html", article: "tips")
88
end
99

10-
@articles ["testing", "forms", "event-handlers", "logical-clocks", "editor"]
10+
@articles ["testing", "forms", "event-handlers", "logical-clocks", "editor", "editor-prolog"]
1111

1212
def show(conn, %{"article" => article}) when article in @articles do
1313
render(conn, "index.html", article: article)
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
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>

apps/components_guide_web/lib/components_guide_web/views/react_typescript_view.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ defmodule ComponentsGuideWeb.ReactTypescriptView do
1818
end
1919

2020
def article_content_class("editor"), do: "content text-xl"
21+
def article_content_class("editor-prolog"), do: "content text-xl"
2122
def article_content_class(_article), do: "content max-w-4xl mx-auto py-8 text-xl"
2223

2324
def collected_image(conn, name) do

0 commit comments

Comments
 (0)