Skip to content

Instantly share code, notes, and snippets.

@tatut
Created April 27, 2023 15:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tatut/d947e2d62a5a6fbb07d150f3ecf78948 to your computer and use it in GitHub Desktop.
Save tatut/d947e2d62a5a6fbb07d150f3ecf78948 to your computer and use it in GitHub Desktop.
Definite Clause Grammars, toy Logo interpreter
<!DOCTYPE>
<html>
<head>
<script src="swipl-bundle.js"></script>
<script type="text/prolog">
:- use_module(library(dcg/basics)).
:- set_prolog_flag(double_quotes, chars).
turtle([]) --> []. % the empty program
turtle([Cmd|Cmds]) --> blanks, turtle_command(Cmd), blanks, turtle(Cmds).
turtle_command(Cmd) --> fd(Cmd) | rt(Cmd) | pen(Cmd) | repeat(Cmd).
fd(fd(N)) --> "fd", blanks, integer(N).
rt(rt(N)) --> "rt", blanks, integer(N).
pen(pen(R,G,B)) --> "pen", blanks, integer(R), blanks, integer(G), blanks, integer(B).
repeat(repeat(N,Cmds)) --> "repeat", blanks, integer(N), blanks, "[", turtle(Cmds), "]".
% Basic DCG state helper nonterminals
state(S), [S] --> [S].
state(S0, S), [S] --> [S0].
eval_all([]) --> [].
eval_all([Cmd|Cmds]) -->
eval(Cmd),
eval_all(Cmds).
eval(fd(N)) -->
% Previous state to new state
state(t(X1,Y1,C,Ang), t(X2,Y2,C,Ang)),
{ Rad is Ang * pi/180,
X2 is X1 + N * cos(Rad),
Y2 is Y1 + N * sin(Rad),
% Call JS interop on the JS global CTX
_ := 'CTX'.beginPath(),
_ := setcolor(C),
_ := 'CTX'.moveTo(X1,Y1),
_ := 'CTX'.lineTo(X2,Y2),
_ := 'CTX'.stroke()
}.
eval(rt(N)) -->
state(t(X,Y,C,Ang1), t(X,Y,C,Ang2)),
{ Ang2 is (Ang1 + N) mod 360 }.
eval(pen(R,G,B)) -->
state(t(X,Y,_,Ang), t(X,Y,[R,G,B],Ang)).
eval(repeat(0,_)) --> [].
eval(repeat(N,Cmds)) -->
eval_all(Cmds),
{ N1 is N - 1 },
eval(repeat(N1, Cmds)).
run(ProgramString) :-
phrase(turtle(T), ProgramString),
writeln(parsed_program(T)),
phrase(eval_all(T), [t(160,100,[0, 200, 0],0)], [FinalState]).
</script>
<script>
new SWIPL({}).then((ok,_) => {
P = ok.prolog;
P.load_scripts();
CTX = document.getElementById("logo").getContext('2d');
});
function setcolor([R,G,B]) {
CTX.strokeStyle = `rgb(${R},${G},${B})`;
}
function run() {
P.call(`run("${document.getElementById("program").value}")`)
}
</script>
</head>
<body>
<canvas id="logo" style="border: solid 1px black;" width="320" height="200"></canvas>
<div>
<input id="program" type="text" placeholder="Enter program">
<button onclick="run()">Run</button>
</div>
</body>
</html>
@tatut
Copy link
Author

tatut commented Apr 27, 2023

See https://www.swi-prolog.org/pldoc/man?section=wasm on using SWI-Prolog from the browser.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment