Created
October 2, 2009 01:16
-
-
Save creationix/199372 to your computer and use it in GitHub Desktop.
The Ruby Turtle DSL metaprogramming example ported to JavaScript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Add some ruby-like methods to some of the builtins | |
Object.prototype.instance_eval = function (block) { | |
// Convert the function to a string so that we can rebind it | |
if (typeof block === 'function') { | |
block = "(" + block + ").call(this)"; | |
} | |
// Eval using "this" as the "with" scope | |
return eval("with(this) { " + block + "}"); | |
}; | |
Object.prototype.keys = function () { | |
var key, keys = []; | |
for (key in this) { | |
if (this.hasOwnProperty(key)) { | |
keys.push(key); | |
} | |
} | |
return keys; | |
}; | |
Number.prototype.times = function (block) { | |
for (var i = 0; i < this; i += 1) { | |
block(i); | |
} | |
}; | |
Number.prototype.is_even = function () { | |
return (this % 2) === 0; | |
}; | |
Number.prototype.upto = function (other, block) { | |
for (var i = this; i <= other; i+= 1) { | |
block(i); | |
} | |
}; | |
Array.prototype.minmax = function () { | |
var min, max; | |
this.forEach(function (item) { | |
if (min === undefined || item < min) { | |
min = item; | |
} | |
if (max === undefined || item > max) { | |
max = item; | |
} | |
}); | |
return [min, max]; | |
}; | |
function Turtle() { | |
// directions: 0 = E, 1 = S, 2 = W, 3 = N | |
// axis: 0 = x, 1 = y | |
this.board = {}; | |
this.x = 0; | |
this.y = 0; | |
this.direction = 0; | |
this.pen_up(); | |
} | |
Turtle.prototype = { | |
pen_up: function () { | |
this.is_pen_down = false; | |
}, | |
pen_down: function () { | |
this.is_pen_down = true; | |
this.mark_current_location(); | |
}, | |
forward: function (n) { | |
with(this) { | |
if (n === undefined) { | |
n = 1; | |
} | |
n.times(function () { | |
move(); | |
}); | |
} | |
}, | |
left: function () { | |
this.direction -= 1; | |
if (this.direction < 0) { | |
this.direction = 3; | |
} | |
}, | |
right: function () { | |
this.direction += 1; | |
if (this.direction > 3) { | |
this.direction = 0; | |
} | |
}, | |
walk: function (block) { | |
this.instance_eval(block); | |
}, | |
draw: function () { | |
var xrange, yrange, | |
puts = require('sys').puts; | |
with (this) { | |
xrange = board.keys().map(function (key) { | |
return parseInt(key.split(',')[0]); | |
}).minmax(); | |
yrange = board.keys().map(function (key) { | |
return parseInt(key.split(',')[1]); | |
}).minmax(); | |
yrange[0].upto(yrange[1], function (y) { | |
var row = ""; | |
xrange[0].upto(xrange[1], function (x) { | |
row += board[[x,y]] || " "; | |
}); | |
puts(row); | |
}); | |
} | |
}, | |
move: function () { | |
var increment = this.direction > 1 ? -1 : 1; | |
if (this.direction.is_even()) { | |
this.x += increment; | |
} else { | |
this.y += increment; | |
} | |
this.mark_current_location(); | |
}, | |
mark_current_location: function () { | |
if (this.is_pen_down) { | |
this.board[[this.x, this.y]] = "#"; | |
} | |
} | |
}; | |
// Use the dsl (Domain Specific Language) | |
var turtle = new Turtle(); | |
turtle.walk(function () { | |
(3).times(function () { | |
forward(8); | |
pen_down(); | |
(4).times(function () { | |
forward(4); | |
left(); | |
}); | |
pen_up(); | |
}); | |
}); | |
turtle.draw(); | |
// Here's the output if you don't have node | |
// Timothy-Caswells-MacBook-Pro:Desktop tim$ node turtle.js | |
// ##### ##### ##### | |
// # # # # # # | |
// # # # # # # | |
// # # # # # # | |
// ##### ##### ##### |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment