rbprolog 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|