JSFiddle - React, Tailwind, and code Playground
by Ben Gillbanks
HTML
<script src="https://unpkg.com/ohm-js@17/dist/ohm.min.js"></script>
JavaScript
// Create the grammar using Ohm
const grammar = ohm.grammar(`
LogoScript {
program = command*
command = moveCommand | penCommand
moveCommand = ("FORWARD" | "BACKWARD") value
penCommand = ("PENUP" | "PENDOWN")
value = number
number = "-"? digit+
space := " " | "\\t" | "\\n"
}
`);
// Create the turtle object to store the state
const turtle = {
x: 0,
y: 0,
penDown: true,
direction: 0, // Angle in degrees, 0 points to the right
output: [],
};
// Helper function to move the turtle
function move(distance) {
const radians = (turtle.direction * Math.PI) / 180;
const dx = distance * Math.cos(radians);
const dy = distance * Math.sin(radians);
turtle.x += dx;
turtle.y += dy;
if (turtle.penDown) {
turtle.output.push(`DRAW ${dx} ${dy}`);
}
}
// Helper function to turn the turtle
function turn(angle) {
turtle.direction += angle;
turtle.output.push(`TURN ${angle}`);
}
// Helper function to put the pen up
function penUp() {
turtle.penDown = false;
turtle.output.push('PENUP');
}
// Helper function to put the pen down
function penDown() {
turtle.penDown = true;
turtle.output.push('PENDOWN');
}
// Create the interpreter
const semantics = grammar.createSemantics().addOperation('run', {
program(commands) {
return commands.run();
},
moveCommand(direction, value) {
const distance = Number(value.sourceString);
if (direction.sourceString === 'FORWARD') {
move(distance);
} else {
move(-distance);
}
},
penCommand(cmd) {
if (cmd.sourceString === 'PENUP') {
penUp();
} else {
penDown();
}
},
});
// Define a helper function to execute Logo-like code
function runLogoCode(code) {
const match = grammar.match(code);
if (match.succeeded()) {
turtle.output = [];
semantics(match).run();
return turtle.output;
} else {
throw new Error('Syntax Error: ' + match.message);
}
}
// Example usage:
const logoCode = `
FORWARD 50
TURN 90
...