-
Notifications
You must be signed in to change notification settings - Fork 0
/
mgu.jl
92 lines (80 loc) · 3.2 KB
/
mgu.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
using Match
# Step 1
function check_predicates_match(expressions::Array)
exp = [exp[1] for exp in expressions]
return all(exp.==exp[1])
end
# Replace all instances of x with y in the expression
function replace_in_expression(x, y, exp)
replace(e) = begin
if e == x
return y
elseif isa(e, Symbol)
return e
else
return map(replace, e)
end
end
exp = replace(exp)
return exp
end
# Checks whether symbol occurs in the expression
function _occurs_in(symbol::Symbol, fx)
return @match fx begin
s::Symbol => s == symbol
[F, s::Symbol] => s == symbol
[F, x...] => symbol in fx || any([_occurs_in(symbol, e) for e in fx]) # It's a function, so go deeper
_ => false
end
end
function _unify(e1, e2, constants)
return _unify(e1, e2, Dict(), constants)
end
function _unify(e1, e2, substitutions::Dict, constants::Set)
@assert length(e1) == length(e2) "Arguments should be the same length"
err() = throw(ArgumentError("Not Unifiable"))
find_subs(pair) = @match pair begin
# Constants shouldn't be replaced by free variables
[s::Symbol, t::Symbol] => begin
if s in constants && t in constants
err()
elseif (s in constants)
[t, s]
elseif (t in constants)
[s, t] # Replace symbol with symbol
else
[s, t]
end
end
[s::Symbol, fx] || [fx, s::Symbol] => begin
if _occurs_in(s, fx); err() end # Occurs check
[s, fx]
end
[ [F, x], [G, y] ] => find_subs([x, y]) # If they are both functions, go inside
[ [F, x], [G, x] ] => err()
[[], _] || [_, []] => err()
end
for i = 1:length(e1)
pair = [e1[i], e2[i]]
if pair[1] != pair[2]
x, y = find_subs(pair)
e1 = replace_in_expression(x, y, e1)
e2 = replace_in_expression(x, y, e2)
substitutions[x] = y
end
end
return e1, e2, substitutions
end
function unify(e1, e2, constants::Set)
if !check_predicates_match([e1, e2])
throw(ArgumentError("Predicates don't match"))
end
predicate = e1[1]
e1, e2 = e1[2:end], e2[2:end]
e1, e2, subs = _unify(e1, e2, constants)
@assert e1 == e2 "Input is likely invalid" # Actually unecessary, as exception should be raised in _unify
return [predicate; e1], subs
end
function unify(e1, e2)
return unify(e1, e2, Set())
end