rbprolog 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +5 -1
- data/lib/rbprolog/deduction.rb +2 -1
- data/lib/rbprolog/evaluation.rb +26 -0
- data/lib/rbprolog/rule.rb +3 -13
- data/lib/rbprolog/version.rb +1 -1
- data/lib/rbprolog.rb +66 -5
- data/rbprolog.gemspec +1 -0
- data/test/test_evaluation.rb +29 -0
- data/test/test_helper.rb +2 -0
- data/test/test_rbprolog.rb +1 -0
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d723ef7238bd43d66ba6107479b1b3ceb3f2c343
|
4
|
+
data.tar.gz: bfa0dc55e11633ec5d83ad1dcbbb02a5008dfa4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da369f5d469d14ee459e145ac505a4b513aa3db32f1617bf5262268c08b2b19c46960bf54eff4b577afaf063c91b561a1432bfe44bed9b8a168eeb0837b3c977
|
7
|
+
data.tar.gz: 1c8216be63234451c9ac110cf1b02173198ec3491ffba96bcc6a6d5159550b82f7f819ee2c2de2753fbc4bb3ebd525ad94561d8077310eedb523b71fcfaab3f4
|
data/Rakefile
CHANGED
data/lib/rbprolog/deduction.rb
CHANGED
@@ -14,7 +14,8 @@ module Rbprolog
|
|
14
14
|
context.scope(self) do |scoped_args|
|
15
15
|
puts " => #{@sym}?(#{scoped_args.join(', ')})" if rule_index == 0
|
16
16
|
|
17
|
-
|
17
|
+
# rules.reject is to avoid endless loop caused by self reference
|
18
|
+
rule.each_match(rules.reject {|item| item == rule}, *scoped_args, id + [rule_index]) do |hash|
|
18
19
|
context.scope(self) do
|
19
20
|
rule.args.each_with_index do |rule_arg, rule_arg_index|
|
20
21
|
if Var === scoped_args[rule_arg_index]
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Rbprolog
|
2
|
+
class Evaluation
|
3
|
+
#FIXME the design is not good to have an empty array as args
|
4
|
+
attr_accessor :args
|
5
|
+
|
6
|
+
def initialize(expression)
|
7
|
+
@args = []
|
8
|
+
@expression = expression
|
9
|
+
end
|
10
|
+
|
11
|
+
def each_deduce(context, rules, id)
|
12
|
+
print "#{"\t" * id.size}#{id.join('.')} ?(#{@args.join(', ')})"
|
13
|
+
|
14
|
+
context.scope(self) do |scoped_args|
|
15
|
+
puts " => ?(#{scoped_args.join(', ')})"
|
16
|
+
|
17
|
+
kclass = Class.new
|
18
|
+
kclass.send(:define_singleton_method, :const_missing) do |sym|
|
19
|
+
context.deduce(sym)
|
20
|
+
end
|
21
|
+
|
22
|
+
yield context.binds if kclass.class_eval(@expression)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/rbprolog/rule.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module Rbprolog
|
2
|
-
|
3
2
|
#when fail at one predicate, the output values need be reset, and remove from the parent rule/s context
|
4
3
|
class Rule
|
5
4
|
attr_accessor :args, :sym
|
@@ -7,7 +6,7 @@ module Rbprolog
|
|
7
6
|
def initialize(sym, *args, deductions)
|
8
7
|
@sym = sym
|
9
8
|
@args = args
|
10
|
-
@deductions = [deductions].flatten
|
9
|
+
@deductions = [deductions].flatten.map {|deduction| Deduction === deduction ? deduction : Evaluation.new(deduction.call)}
|
11
10
|
end
|
12
11
|
|
13
12
|
def each_match(rules, *args, id)
|
@@ -42,18 +41,9 @@ module Rbprolog
|
|
42
41
|
yield
|
43
42
|
else
|
44
43
|
deduction = deductions.shift
|
45
|
-
|
46
|
-
|
47
|
-
deduce_deductions(context, rules, *deductions, id, &block)
|
48
|
-
end
|
49
|
-
else
|
50
|
-
@logic.send(:define_singleton_method, :const_missing) do |sym|
|
51
|
-
context[sym]
|
52
|
-
end
|
53
|
-
|
54
|
-
deduction.call && deduce_deductions(context, rules, *deductions, id, &block)
|
44
|
+
deduction.each_deduce(context, rules, id + [@deductions.size - deductions.size - 1]) do |hash|
|
45
|
+
deduce_deductions(context, rules, *deductions, id, &block)
|
55
46
|
end
|
56
|
-
|
57
47
|
end
|
58
48
|
end
|
59
49
|
end
|
data/lib/rbprolog/version.rb
CHANGED
data/lib/rbprolog.rb
CHANGED
@@ -1,8 +1,54 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
require "rbprolog/version"
|
2
|
+
require 'rbprolog/context'
|
3
|
+
require 'rbprolog/rule'
|
4
|
+
require 'rbprolog/deduction'
|
5
|
+
require 'rbprolog/evaluation'
|
6
|
+
require 'rbprolog/var'
|
7
|
+
|
8
|
+
=begin rdoc
|
9
|
+
Simulate the prolog logic processing partially by using ruby based DSL
|
10
|
+
|
11
|
+
= Representations (Conventions)
|
12
|
+
[rule - class level]
|
13
|
+
* name arg0, arg1, ..., {:if => [deduction0, deduction1, ...]}
|
14
|
+
* name: defined in the keywords
|
15
|
+
* arg: use const to present the variable, and non-const for value
|
16
|
+
* deduction: see below
|
17
|
+
|
18
|
+
[fact - class level]
|
19
|
+
* name: arg0, arg1, ...
|
20
|
+
|
21
|
+
[deduction - class level]
|
22
|
+
* name? arg0, arg1, ...
|
23
|
+
* return: true or false
|
24
|
+
|
25
|
+
[question - instance level]
|
26
|
+
* name? arg0, arg1, ...
|
27
|
+
|
28
|
+
[enumerator - instance level]
|
29
|
+
* name! arg0, arg1, ...
|
30
|
+
* Deduce all possible answers
|
31
|
+
|
32
|
+
= Example
|
33
|
+
class FriendLogic
|
34
|
+
include Rbprolog
|
35
|
+
|
36
|
+
keywords :likes, :friends
|
37
|
+
|
38
|
+
likes 'p1', 's1'
|
39
|
+
likes 'p1', 's2'
|
40
|
+
likes 'p2', 's2'
|
41
|
+
likes 'p3', 's1'
|
42
|
+
likes 'p4', X
|
43
|
+
|
44
|
+
friends 'p1', W, :if => likes?(W, 's2')
|
45
|
+
friends X, Y, :if => [likes?(X, Z), likes?(Y, Z)]
|
46
|
+
end
|
47
|
+
|
48
|
+
l = FriendLogic.new
|
49
|
+
l.likes?('p1', 's1') #=> true
|
50
|
+
l.friends?('p1', 'p4') #=> true
|
51
|
+
=end
|
6
52
|
|
7
53
|
module Rbprolog
|
8
54
|
def self.included(mod)
|
@@ -12,6 +58,18 @@ module Rbprolog
|
|
12
58
|
end
|
13
59
|
end
|
14
60
|
|
61
|
+
#
|
62
|
+
#Initialize the rbprolog instance, each instance can have its
|
63
|
+
#own fact and rules. The definition can be passed in as string or block.
|
64
|
+
#string is required when variable such as X is used.
|
65
|
+
# l = FriendLogic.new do
|
66
|
+
# likes 'p5', 's1'
|
67
|
+
# end
|
68
|
+
#or
|
69
|
+
# l = FriendLogic.new %q{
|
70
|
+
# friends 'p2', X, :if => likes?(X, 's1')
|
71
|
+
# }
|
72
|
+
#
|
15
73
|
def initialize(string = nil, &block)
|
16
74
|
if string || block
|
17
75
|
self.extend(Rbprolog)
|
@@ -27,6 +85,7 @@ module Rbprolog
|
|
27
85
|
end
|
28
86
|
|
29
87
|
module ClassMethods
|
88
|
+
#Define the vocabulary of rules and facts
|
30
89
|
def keywords(*syms)
|
31
90
|
raise if syms.any? {|sym| sym.to_s.end_with? '?'}
|
32
91
|
|
@@ -38,6 +97,7 @@ module Rbprolog
|
|
38
97
|
Var.new(sym)
|
39
98
|
end
|
40
99
|
|
100
|
+
#Generate rule, fact and deduction based on conventions
|
41
101
|
def method_missing(sym, *args)
|
42
102
|
if self.syms.include? sym
|
43
103
|
Hash === args.last ? rule(sym, *args) : rule(sym, *args, :if => [])
|
@@ -48,6 +108,7 @@ module Rbprolog
|
|
48
108
|
end
|
49
109
|
end
|
50
110
|
|
111
|
+
#Internal class method to install instance methods for question and enumerator
|
51
112
|
def rule(sym, *args, options)
|
52
113
|
self.rules ||= []
|
53
114
|
self.rules << Rule.new(sym, *args, options[:if])
|
data/rbprolog.gemspec
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'rbprolog'
|
4
|
+
|
5
|
+
class EvaluationLogic
|
6
|
+
include Rbprolog
|
7
|
+
|
8
|
+
keywords :must, :can, :can_not, :must_not
|
9
|
+
|
10
|
+
%w(a b c).each {|t| must 'win', t}
|
11
|
+
must_not 'linux', 'd'
|
12
|
+
can_not X, Y, :if => [must?(U, Y), ->{'U != X'}]
|
13
|
+
can_not X, Y, :if => must_not?(X, Y)
|
14
|
+
#can X, Y, :if => !can_not?(X, Y)
|
15
|
+
end
|
16
|
+
|
17
|
+
class TestEvaluation < Test::Unit::TestCase
|
18
|
+
def test_evaluation
|
19
|
+
l = EvaluationLogic.new
|
20
|
+
|
21
|
+
assert_equal true, l.must?('win', 'a')
|
22
|
+
assert_equal true, l.can_not?('linux', 'd')
|
23
|
+
assert_equal true, l.can_not?('linux', 'a')
|
24
|
+
assert_equal true, l.can_not?('linux', 'b')
|
25
|
+
assert_equal true, l.can_not?('linux', 'c')
|
26
|
+
|
27
|
+
assert_equal(['a', 'b', 'c', 'd'], l.can_not!('linux', Rbprolog::Var.new(:G)).map {|hash| hash[:G]}.uniq.sort)
|
28
|
+
end
|
29
|
+
end
|
data/test/test_helper.rb
ADDED
data/test/test_rbprolog.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbprolog
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ruijia Li
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - '>='
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: simplecov
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
description: A ruby implementation to simulate prolog partially
|
42
56
|
email:
|
43
57
|
- ruijia.li@gmail.com
|
@@ -53,10 +67,13 @@ files:
|
|
53
67
|
- lib/rbprolog.rb
|
54
68
|
- lib/rbprolog/context.rb
|
55
69
|
- lib/rbprolog/deduction.rb
|
70
|
+
- lib/rbprolog/evaluation.rb
|
56
71
|
- lib/rbprolog/rule.rb
|
57
72
|
- lib/rbprolog/var.rb
|
58
73
|
- lib/rbprolog/version.rb
|
59
74
|
- rbprolog.gemspec
|
75
|
+
- test/test_evaluation.rb
|
76
|
+
- test/test_helper.rb
|
60
77
|
- test/test_rbprolog.rb
|
61
78
|
homepage: https://github.com/rli9/rbprolog
|
62
79
|
licenses:
|
@@ -83,4 +100,6 @@ signing_key:
|
|
83
100
|
specification_version: 4
|
84
101
|
summary: A prolog DSL in ruby to do AI logic analysis
|
85
102
|
test_files:
|
103
|
+
- test/test_evaluation.rb
|
104
|
+
- test/test_helper.rb
|
86
105
|
- test/test_rbprolog.rb
|