Skip to content

Instantly share code, notes, and snippets.

@alucky0707
Created March 2, 2014 03:46
Show Gist options
  • Save alucky0707/9301598 to your computer and use it in GitHub Desktop.
Save alucky0707/9301598 to your computer and use it in GitHub Desktop.
CoffeeScriptでProlog的なことをやってみた ref: http://qiita.com/alucky0707/items/a8e08a3240be6de91104
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