Created
March 2, 2014 03:46
-
-
Save alucky0707/9301598 to your computer and use it in GitHub Desktop.
CoffeeScriptでProlog的なことをやってみた ref: http://qiita.com/alucky0707/items/a8e08a3240be6de91104
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
isSymbol = (t)-> t is t.toUpperCase?() | |
class Pred | |
constructor: (@name) -> | |
@defs = [] | |
is: (args...) -> | |
new Goal(@, args) | |
pred = (x) -> new Pred(x) | |
class Goal | |
constructor: (@pred, @args) -> | |
si: (rhs...) -> | |
@pred.defs.push([@, list(rhs...)]) | |
calls: (callback) -> | |
@pred.defs.push([@, callback]) | |
class Cons | |
constructor: (car, cdr) -> | |
[@[0], @[1]] = [car, cdr] | |
cons = (car, cdr) -> new Cons car, cdr | |
list = (xs...) -> xs.reduceRight (ls, x) -> | |
cons x, ls | |
, null | |
class Env | |
constructor: -> | |
@table = {} | |
put: (x, pair) -> | |
@table[x] = pair | |
get: (x) -> @table[x] | |
del: (x) -> delete @table[x] | |
clear: -> | |
@table = {} | |
dereference: (t) -> | |
env = @ | |
while isSymbol t | |
p = env.get t | |
break unless p | |
[t, env] = p | |
[t, env] | |
item: (t) -> | |
[t, env] = @dereference t | |
return switch | |
when t instanceof Goal then new Goal t.pred, env.item(t.args) | |
when t instanceof Cons then cons env.item(t[0], t[1]) | |
when t instanceof Array then t.map (e) -> env.item(e) | |
else t | |
_unify = (x, xEnv, y, yEnv, trail, tmpEnv) -> | |
loop | |
if isSymbol x | |
xp = xEnv.get x | |
unless xp | |
[y, yEnv] = yEnv.dereference y | |
unless x is y and xEnv is yEnv | |
xEnv.put x, [y, yEnv] | |
trail.push([x, xEnv]) unless xEnv is tmpEnv | |
return true | |
else | |
[x, xEnv] = xp | |
[x, xEnv] = xEnv.dereference x | |
else if isSymbol y | |
[x, xEnv, y, yEnv] = [y, yEnv, x, xEnv] | |
else | |
break | |
if x instanceof Goal and y instanceof Goal | |
return false unless x.pred is y.pred | |
[x, y] = [x.args, y.args] | |
if x instanceof Array and y instanceof Array | |
return false unless x.length is y.length | |
for i in [0 ... x.length] | |
return false unless _unify(x[i], xEnv, y[i], yEnv, trail, tmpEnv) | |
return true | |
else | |
return x is y | |
resolve = (goals...) -> | |
cb = goals.pop() | |
env = new Env | |
_resolveBody(list(goals...), env, [false], -> cb env) | |
_resolveBody = (body, env, cut, cb) -> | |
if body is null then cb() | |
else | |
[goal, rest] = body | |
if goal is 'CUT' | |
_resolveBody(rest, env, cut, cb) | |
cut[0] = true | |
else | |
dEnv = new Env | |
dCut = [false] | |
for [dHead, dBody] in goal.pred.defs | |
break if dCut[0] or cut[0] | |
trail = [] | |
if _unify(goal, env, dHead, dEnv, trail, dEnv) | |
if typeof dBody is 'function' | |
if dBody(new CallbackEnv(dEnv, trail)) | |
_resolveBody(rest, env, cut, cb) | |
else | |
_resolveBody(dBody, dEnv, dCut, -> | |
_resolveBody(rest, env, cut, cb) | |
dCut[0] ||= cut[0] | |
) | |
for [x, xEnv] in trail | |
xEnv.del(x) | |
class CallbackEnv | |
constructor: (@env, @trail) -> | |
item: (t) -> @env.item(t) | |
unify: (t, u) -> _unify(t, @env, u, @env, @trail, @env) | |
fizzbuzz = pred 'fizzbuzz' | |
assign = pred 'assign' | |
show = pred 'show' | |
eq = pred 'eq' | |
add = pred 'add' | |
mod = pred 'mod' | |
divable = pred 'divable' | |
print = pred 'print' | |
runFizzbuzz = pred 'runFizzbuzz' | |
runFizzbuzz2 = pred 'runFizzbuzz2' | |
assign.is('A', 'B').calls (env) -> | |
env.unify('A', 'B') | |
show.is('A', 'B').calls (env) -> | |
env.unify('A', env.item('B').toString?()) | |
eq.is('A', 'B').calls (env) -> | |
env.item('A') is env.item('B') | |
mod.is('A', 'B', 'X').calls (env) -> | |
env.unify('X', env.item('A') % env.item('B')) | |
add.is('A', 'B', 'X').calls (env) -> | |
env.unify('X', env.item('A') + env.item('B')) | |
print.is('X').calls (env) -> | |
console.log env.item('X') | |
true | |
divable.is('A', 'B').si mod.is('A', 'B', 'X'), eq.is('X', 0) | |
fizzbuzz.is('I', 'fizzbuzzhoge').si divable.is('I', 3), divable.is('I', 5), divable.is('I', 8), 'CUT' | |
fizzbuzz.is('I', 'buzzhoge').si divable.is('I', 5), divable.is('I', 8), 'CUT' | |
fizzbuzz.is('I', 'fizzhoge').si divable.is('I', 3), divable.is('I', 8), 'CUT' | |
fizzbuzz.is('I', 'fizzbuzz').si divable.is('I', 3), divable.is('I', 5), 'CUT' | |
fizzbuzz.is('I', 'hoge').si divable.is('I', 8), 'CUT' | |
fizzbuzz.is('I', 'fizz').si divable.is('I', 3), 'CUT' | |
fizzbuzz.is('I', 'buzz').si divable.is('I', 5), 'CUT' | |
fizzbuzz.is('I', 'I').si() | |
runFizzbuzz.is(101).si 'CUT' | |
runFizzbuzz.is('I').si fizzbuzz.is('I', 'S'), print.is('S'), add.is('I', 1, 'I1'), runFizzbuzz.is('I1') | |
runFizzbuzz2.is(200, '_').si 'CUT' | |
runFizzbuzz2.is('I', 'X').si fizzbuzz.is('I', 'X') | |
runFizzbuzz2.is('I', 'X').si add.is('I', 1, 'I1'), runFizzbuzz2.is('I1', 'X') | |
resolve runFizzbuzz2.is(1, 'X'), (env) -> | |
console.log env.item('X') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment