rubylog 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,6 +1,7 @@
1
1
  source "http://rubygems.org"
2
2
 
3
3
  group :development do
4
+ gem "ruby-prof"
4
5
  gem "rspec", ">= 2.8.0", " < 3"
5
6
  gem "yard", "~> 0.7"
6
7
  gem "bundler", ">= 1.0.0"
data/Gemfile.lock CHANGED
@@ -21,6 +21,7 @@ GEM
21
21
  rspec-expectations (2.13.0)
22
22
  diff-lcs (>= 1.1.3, < 2.0)
23
23
  rspec-mocks (2.13.1)
24
+ ruby-prof (0.13.0)
24
25
  simplecov (0.7.1)
25
26
  multi_json (~> 1.0)
26
27
  simplecov-html (~> 0.7.1)
@@ -34,5 +35,6 @@ DEPENDENCIES
34
35
  bundler (>= 1.0.0)
35
36
  jeweler (>= 1.8.3)
36
37
  rspec (>= 2.8.0, < 3)
38
+ ruby-prof
37
39
  simplecov
38
40
  yard (~> 0.7)
data/README.rdoc CHANGED
@@ -33,7 +33,6 @@ Another option is to use Kernel#rubylog:
33
33
 
34
34
 
35
35
  require 'rubylog'
36
- extend Rubylog::Context
37
36
 
38
37
  rubylog do
39
38
  # your code here
@@ -84,7 +83,7 @@ Declaring a predicate with arguments gives you three methods on the subject clas
84
83
 
85
84
  predicate_for String, ".likes()"
86
85
  'John'.likes('beer') # returns a structure object representing this logical statement
87
- 'John'.likes!('beer') # asserts that this statement as a fact
86
+ 'John'.likes!('beer') # asserts this statement as a fact
88
87
  'John'.likes?('beer') # tells if this statement is true (in this case, returns true)
89
88
 
90
89
  === Nullary predicates
@@ -271,7 +270,7 @@ These predicates also have a prefix form, which can be used to create more natur
271
270
  check none X.in([1,3]), X.even
272
271
  check iff X.in([2,4]), X.in(1..4).and(X.even)
273
272
 
274
- There is another quantifier, which only has a prefix form <tt>every(A, B)</tt>. This works similarly to <tt>.all()</tt>, but for each solution of A, creates a copy of B and chains them together with <tt>.and()</tt>. It can be useful for work with assumptions, see below. This is an experimental feature, and still contains bugs.
273
+ There is another quantifier <tt>A.every(B)</tt> or <tt>every(A,B)</tt>. This works similarly to <tt>.all()</tt>, but for each solution of A, creates a copy of B and chains them together with <tt>.and()</tt>. It can be useful for work with assumptions, see below. This is an experimental feature, and still contains bugs.
275
274
 
276
275
  === File system
277
276
 
@@ -335,6 +334,10 @@ You can turn on tracing with
335
334
 
336
335
  Rubylog.trace
337
336
 
337
+ And turn off with
338
+
339
+ Rubylog.trace false
340
+
338
341
  Or, you can trace a specific code block with
339
342
 
340
343
  Rubylog.trace do
data/RELEASE_NOTES.rdoc CHANGED
@@ -4,7 +4,11 @@
4
4
  * warning for singleton variables
5
5
  * predicate for checking bound/unbound variables
6
6
 
7
- == Version 2.0.0 (to be released)
7
+ == Version 2.0.1
8
+ * Bug fixes
9
+ * <tt>"a/b".file\_in("a")</tt> only succeeds once
10
+
11
+ == Version 2.0.0
8
12
  * Backwards incompatibilities
9
13
  * <tt>Rubylog.theory</tt> renamed to <tt>Kernel#rubylog</tt>.
10
14
  * <tt>file\_in</tt> includes directories and yields relative paths.
data/Rakefile CHANGED
@@ -51,4 +51,4 @@ task :yardserver do
51
51
  end
52
52
 
53
53
 
54
-
54
+ task :default => :spec
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.0
1
+ 2.0.1
@@ -0,0 +1,32 @@
1
+ # encoding: UTF-8
2
+ require "rubylog"
3
+ extend Rubylog::Context
4
+
5
+
6
+ class << primitives
7
+ def self.make_tree(parent, levels, s="")
8
+ return if levels.zero?
9
+
10
+ children = (1..DEGREES).map{random_person}
11
+
12
+ s << "a.rubylog_unify(#{parent.inspect}){\n"
13
+ children.each do |child|
14
+ s << "b.rubylog_unify(#{child.inspect}){yield}\n"
15
+ end
16
+ s << "}\n"
17
+ children.each do |child|
18
+ make_tree(child, levels-1, s)
19
+ end
20
+ s
21
+ end
22
+
23
+ eval "def parent_of a,b
24
+ #{make_tree(random_person, LEVELS)}
25
+ end"
26
+
27
+ def grandparent_of a,b
28
+ x = Rubylog::Variable.new
29
+ parent_of(a,x) { parent_of(x,b) { yield }}
30
+ end
31
+ end
32
+
@@ -0,0 +1,47 @@
1
+ # encoding: UTF-8
2
+ require "rubylog"
3
+ extend Rubylog::Context
4
+
5
+
6
+ class << primitives
7
+ def self.make_tree(parent, levels, all="", indexed= "")
8
+ return if levels.zero?
9
+
10
+ children = (1..DEGREES).map{random_person}
11
+
12
+ all << "a.rubylog_unify(#{parent.inspect}){\n"
13
+ children.each do |child|
14
+ all << "b.rubylog_unify(#{child.inspect}){yield}\n"
15
+ end
16
+ all << "}\n"
17
+
18
+ indexed << "when #{parent.inspect}\n"
19
+ children.each do |child|
20
+ indexed << "b.rubylog_unify(#{child.inspect}){yield}\n"
21
+ end
22
+
23
+ children.each do |child|
24
+ make_tree(child, levels-1, all, indexed)
25
+ end
26
+
27
+ end
28
+
29
+ all =""; indexed =""
30
+ make_tree(random_person, LEVELS, all, indexed)
31
+
32
+ eval "def parent_of a,b
33
+ a = a.rubylog_dereference
34
+ case a
35
+ when Rubylog::Variable
36
+ #{all}
37
+ #{indexed}
38
+ end
39
+ end"
40
+
41
+ def grandparent_of a,b
42
+ x = Rubylog::Variable.new
43
+ parent_of(a,x) { parent_of(x,b) { yield }}
44
+ end
45
+ end
46
+
47
+
@@ -0,0 +1,57 @@
1
+ class Rubylog::Procedure
2
+ def initialize functor, arity, rules=Array.new
3
+ super functor, arity
4
+ @rules = rules
5
+ @index = {}
6
+ end
7
+
8
+ def each(args)
9
+ index = args[0].rubylog_dereference
10
+ if !index.is_a? Rubylog::Variable
11
+ (@index[index] || []).each do |rule|
12
+ yield rule
13
+ end
14
+ else
15
+ @rules.each do |rule|
16
+ yield rule
17
+ end
18
+ end
19
+ end
20
+ rubylog_traceable :each
21
+
22
+ # accepts the *args of the called structure
23
+ def call *args
24
+ # catch cuts
25
+ catch :rubylog_cut do
26
+
27
+ # for each rule
28
+ each(args) do |rule|
29
+ # compile
30
+ rule = rule.rubylog_match_variables
31
+
32
+ # unify the head with the arguments
33
+ rule.head.args.rubylog_unify(args) do
34
+ # call the body
35
+ rule.body.prove do
36
+ yield
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ rubylog_traceable :call
43
+
44
+ # Asserts a rule with a given head and body, indexed
45
+ def assert head, body = :true
46
+ rule = Rubylog::Rule.new(head, body)
47
+
48
+ # update index
49
+ index = head[0].rubylog_dereference
50
+ if !index.is_a? Rubylog::Variable
51
+ (@index[index] ||= []).push rule
52
+ end
53
+
54
+ @rules.push rule
55
+ end
56
+ end
57
+
@@ -0,0 +1,39 @@
1
+ # encoding: UTF-8
2
+ #
3
+
4
+ puts "Native prolog"
5
+
6
+ def make_prolog_tree(parent, levels, s="")
7
+ return if levels.zero?
8
+
9
+ children = (1..DEGREES).map{random_person}
10
+
11
+ children.each do |child|
12
+ s << "parent_of(#{parent},#{child}).\n"
13
+ end
14
+ children.each do |child|
15
+ make_prolog_tree(child, levels-1, s)
16
+ end
17
+ s
18
+ end
19
+
20
+ s = make_prolog_tree(random_person, LEVELS)
21
+ s << <<EOT
22
+ grandparent_of(A,B) :- parent_of(A,X), parent_of(X,B).
23
+
24
+ :- dynamic(result/1).
25
+
26
+ main :- findall([A,B],grandparent_of(A,B),L), assertz(result(L)), halt.
27
+
28
+ :- initialization(main).
29
+ EOT
30
+
31
+ File.open(File.expand_path(__FILE__+"/../prolog.pl"), "w") do |f|
32
+ f << s
33
+ end
34
+
35
+ system 'gplc ./examples/benchmark/prolog.pl'
36
+ system 'bash -c "time ./examples/benchmark/prolog"'
37
+
38
+
39
+
@@ -0,0 +1,26 @@
1
+ # encoding: UTF-8
2
+ require "rubylog"
3
+ extend Rubylog::Context
4
+
5
+ predicate_for $person_class, ".parent_of() .grandparent_of()"
6
+
7
+ def make_tree(parent, levels)
8
+ return if levels.zero?
9
+
10
+ children = (1..DEGREES).map{random_person}
11
+
12
+ children.each do |child|
13
+ # add relationship
14
+ parent.parent_of!(child)
15
+ end
16
+
17
+ children.each do |child|
18
+ # make sub-tree
19
+ make_tree(child, levels-1)
20
+ end
21
+ end
22
+
23
+ make_tree(random_person, LEVELS)
24
+
25
+ A.grandparent_of(B).if A.parent_of(X).and X.parent_of(B)
26
+
@@ -0,0 +1,113 @@
1
+ # benchmark
2
+ #
3
+ # The task is to collect all grandparent-granchild relationships in a family
4
+ # tree to an array.
5
+ # First it is done with pure rubylog. Second, it is done with compiled rubylog
6
+ #
7
+ #
8
+
9
+ require "rubylog"
10
+ require "benchmark"
11
+ require "ruby-prof"
12
+
13
+
14
+ DEGREES = 3
15
+ LEVELS = 6
16
+ NAME_LENGTH = 5
17
+
18
+ class Person
19
+ def initialize name
20
+ @name = name
21
+ end
22
+
23
+ def inspect
24
+ # this is a quick solution for compiled*.rb code to retrieve the object
25
+ "ObjectSpace._id2ref(#{object_id})"
26
+ end
27
+
28
+ def to_s
29
+ # this is needed for comparing results
30
+ @name
31
+ end
32
+ end
33
+
34
+ # create a random person
35
+ def random_person
36
+ # create random name
37
+ name = ""
38
+ NAME_LENGTH.times { name << (97+$random.rand(26)).chr}
39
+ name
40
+
41
+ if $person_class == String
42
+ name
43
+ elsif $person_class == Symbol
44
+ name.to_sym
45
+ elsif $person_class = Person
46
+ Person.new(name)
47
+ end
48
+ end
49
+
50
+ def run_benchmark(id, person_class, name)
51
+ $person_class = person_class
52
+ $random = Random.new(1)
53
+
54
+ load "./examples/benchmark/#{id}.rb"
55
+
56
+ puts name
57
+
58
+ result = nil
59
+ time = Benchmark.realtime {
60
+ result = rubylog do
61
+ A.grandparent_of(B).map do
62
+ [A.to_s,B.to_s]
63
+ end
64
+ end
65
+ }
66
+ puts "%0.5f sec" % time
67
+
68
+
69
+ # forget result
70
+ result = result.inspect
71
+
72
+ # compare with last result
73
+ raise "#{$last_result} != #{result}" if $last_result && $last_result != result
74
+ $last_result = result
75
+
76
+ # make garbage collection
77
+ GC.start; sleep(time*1.2+1)
78
+ end
79
+
80
+ puts "Rubylog Grandparent Benchmark"
81
+ puts
82
+ puts "DEGREES = #{DEGREES}"
83
+ puts "LEVELS = #{LEVELS}"
84
+ puts "NAME_LENGTH = #{NAME_LENGTH}"
85
+ puts "NODES = #{DEGREES**LEVELS}"
86
+ puts
87
+
88
+
89
+
90
+ run_benchmark "pure", String, "Strings"
91
+ run_benchmark "pure", Person, "Objects"
92
+ run_benchmark "pure", Symbol, "Symbols"
93
+ puts
94
+
95
+ require "./examples/benchmark/indexed_procedure"
96
+
97
+ run_benchmark "pure", String, "Strings indexed"
98
+ run_benchmark "pure", Person, "Objects indexed"
99
+ run_benchmark "pure", Symbol, "Symbols indexed"
100
+ puts
101
+
102
+ run_benchmark "compiled_not_indexed", String, "Strings compiled"
103
+ run_benchmark "compiled_not_indexed", Person, "Objects compiled"
104
+ run_benchmark "compiled_not_indexed", Symbol, "Symbols compiled"
105
+ puts
106
+
107
+ run_benchmark "compiled_sequence_indexed", String, "Strings compiled, sequentially indexed"
108
+ #run_benchmark "compiled_sequence_indexed", Person, "Objects compiled, sequentially indexed"
109
+ run_benchmark "compiled_sequence_indexed", Symbol, "Symbols compiled, sequentially indexed"
110
+ puts
111
+
112
+ # native prolog
113
+ load "./examples/benchmark/prolog.rb"
data/examples/divisors.rb CHANGED
@@ -1,6 +1,4 @@
1
1
  $:.unshift File.dirname(__FILE__)+"/../lib"
2
2
  require "rubylog"
3
3
 
4
- extend Rubylog::Context
5
-
6
- p (P.is(672).and A.in{1..P}.and P.product_of(A,B).and{A<=B}).count
4
+ p rubylog { P.is(672).and A.in{1..P}.and P.product_of(A,B).and{A<=B} }.count
@@ -6,11 +6,12 @@ rubylog do
6
6
 
7
7
  0.factorial! 1
8
8
  N[thats > 0].factorial(K).if \
9
- N1.is{N-1}.
9
+ N.sum_of(N1,1).
10
10
  and N1.factorial(K1).
11
- and K.is{N*K1}
11
+ and K.product_of(K1,N)
12
12
 
13
13
  7.factorial(N).solve {puts N}
14
14
  end
15
15
 
16
16
 
17
+
@@ -1,14 +1,16 @@
1
1
  require "rubylog"
2
2
  extend Rubylog::Context
3
3
 
4
- predicate_for String, "FILE.found_in(DIR)"
4
+ [true,false].each do |b|
5
+ puts "Files which #{b ? 'have' : 'do not have'} spec:"
5
6
 
6
- FILE.found_in(DIR).if FILE.file_in(DIR)
7
- FILE.found_in(DIR).if DIR2[thats_not =~ /\/\./].dir_in(DIR).and FILE.found_in(DIR2)
7
+ "lib/#{X}.rb".file_in("lib/**").each do
8
+ if b == "spec/#{X}_spec.rb".file_in?("spec/**")
9
+ puts X
10
+ end
11
+ end
8
12
 
9
- "lib/#{X}.rb".found_in("lib").and "lib/#{X}"
10
- "#{X}/spec/#{S}_spec.rb".found_in("spec").each do
11
- puts S
12
- end
13
+ puts
13
14
 
15
+ end
14
16
 
data/examples/hanoi.rb CHANGED
@@ -1,23 +1,22 @@
1
1
  $:.unshift File.dirname(__FILE__)+"/../lib"
2
2
  require "rubylog"
3
+ extend Rubylog::Context
3
4
 
4
- rubylog do
5
- predicate_for Integer, ".move(,,)", ".hanoi"
5
+ predicate_for Integer, ".move(,,)", ".hanoi"
6
6
 
7
- 0.move(ANY,ANY,ANY).if :cut!
8
- N.move(A,B,C).if (
9
- M.is{N-1}.and \
10
- M.move(A,C,B).and \
11
- { puts "move disc #{N} from the #{A} pole to the #{B} pole" or true }.and \
12
- M.move(C,B,A)
13
- )
14
- N.hanoi.if N.move('left', 'right', 'center')
7
+ 0.move(ANY,ANY,ANY).if :cut!
8
+ N.move(A,B,C).if (
9
+ M.is{N-1}.and \
10
+ M.move(A,C,B).and \
11
+ { puts "move disc #{N} from the #{A} pole to the #{B} pole" or true }.and \
12
+ M.move(C,B,A)
13
+ )
14
+ N.hanoi.if N.move('left', 'right', 'center')
15
15
 
16
- puts "\nWhat's the solution for a single disc?"
17
- solve 1.hanoi
16
+ puts "\nWhat's the solution for a single disc?"
17
+ solve 1.hanoi
18
18
 
19
- puts "\n\nWhat's the solution for 5 discs?"
20
- solve 5.hanoi
19
+ puts "\n\nWhat's the solution for 5 discs?"
20
+ solve 5.hanoi
21
21
 
22
22
 
23
- end
@@ -0,0 +1,18 @@
1
+ # encoding: UTF-8
2
+ require "rubylog"
3
+ extend Rubylog::Context
4
+
5
+ predicate_for String, ".parent_of() .grandparent_of()"
6
+
7
+ # ki kinek a szülője
8
+ 'Bob'.parent_of!('Greg')
9
+ 'Greg'.parent_of!('John')
10
+ 'Greg'.parent_of!('Jane')
11
+
12
+ # "A" nagyszülője B-nek, ha "A" szülője X-nek és X szülője B-nek
13
+ A.grandparent_of(B).if A.parent_of(X).and X.parent_of(B)
14
+
15
+ # Az összes nagyszülő-unoka kapcsolat kiírása
16
+ A.grandparent_of(B).each do
17
+ puts "#{A} unokája #{B}."
18
+ end
data/examples/mice2.rb CHANGED
@@ -1,19 +1,37 @@
1
1
  require "rubylog"
2
2
  extend Rubylog::Context
3
3
 
4
- predicate_for Array, ".array(N) Peek.matches(Guess)"
4
+ predicate_for Rubylog::Variable, ".bound"
5
+ A.bound.if { A.is_a? Rubylog::Variable }
5
6
 
6
- [].peek!(0)
7
- [K,*REST].mice(N[thats>0]).if K.in([0,1]).and N.sum_of(N1,1).and REST.mice(N1)
7
+ class Array
8
+ extend Rubylog::Context
8
9
 
9
- [].seen!([])
10
- [0,*REST].seen([nil,*REST2]).if REST.seen(REST2)
11
- [1,*REST].seen([A,*REST2]).if A.in([0,1]).and REST.seen(REST2)
10
+ predicate ".can_have_neighbours"
11
+ [*ANY,:yes,:yes,*ANY].can_have_neighbours!
12
+
13
+ predicate ".has_neighbours"
14
+ [*ANY,A,B,*ANY].has_neighbours.if { A == :yes && B == :yes}
15
+
16
+ predicate ".cannot_have_neighbours"
17
+ A.cannot_have_neighbours.if A.can_have_neighbours.false
18
+
19
+ predicate ".decidable"
20
+ A.decidable.if A.has_neighbours.or A.cannot_have_neighbours
21
+
22
+
23
+ predicate ".guessed"
24
+ A.guessed.if every X.in(A), X.is(:yes).or(X.is(:no)).or(:true)
25
+
26
+ predicate ".easy"
27
+ A.easy.if A.guessed.and A.decidable.and any X.in(A), X.bound.false
28
+
29
+ Rubylog.trace
30
+ N.in(0..6).each do
31
+ puts "#{N}: #{([ANY]*N).easy?}"
32
+ end
33
+
34
+ end
12
35
 
13
- [].guess!([])
14
- [nil,*REST].guess([A,*REST2]).if A.in([0,1]).and REST.guess(REST2)
15
- [0,*REST].guess([0,*REST2]).if A.in([0,1]).and REST.guess(REST2)
16
- [1,*REST].guess([1,*REST2]).if A.in([0,1]).and REST.guess(REST2)
17
36
 
18
- solve N.is(6).and any Peek.peek(N), all(Peek.seen(Seen), all(Guess.guess(Seen), Guess.has_neighbors.false).or(all(Guess.guess(Seen), Guess.has_neighbors)))
19
37
 
data/examples/sudoku.rb CHANGED
@@ -1,52 +1,17 @@
1
1
  $:.unshift File.dirname(__FILE__)+"/../lib"
2
2
  require "rubylog"
3
3
 
4
- class Sudoku
5
- def initialize
6
- @rows = (1..9).map{(1..9).map{Rubylog::Variable.new}}
7
- end
8
-
9
- attr_reader :rows
10
-
11
- def columns
12
- @rows.transpose
13
- end
14
-
15
- def blocks
16
- (0..2).map {|a| (0..2).map {|b| (0..2).map {|c| (0..2).map {|d|
17
- @rows[a*3+c][b*3+d]
18
- }}.flatten(1) }}.flatten(1)
19
- end
20
-
21
- extend Rubylog::Context
22
-
23
- predicate_for Array, ".unique"
24
- L.unique.unless L.is [*ANY, X, *ANY, X, *ANY]
25
-
26
- predicate ".solved .good .shown .given(s)"
27
- S.solved.if every L.in{S.rows}.and(F.in(L)), F.in(1..9)
28
- S.good.if all(T.in{[S.rows, S.columns, S.blocks]}.and(L.in{T}), L.unique)
4
+ def blocks_of s
5
+ (0..2).map {|a| (0..2).map {|b| (0..2).map {|c| (0..2).map {|d|
6
+ s[a*3+c][b*3+d]
7
+ }}.flatten(1) }}.flatten(1)
8
+ end
29
9
 
30
- S.shown.if do
31
- L.in{S.rows}.each do
32
- p L
33
- end
34
- puts
35
- true
36
- end
10
+ predicate_for Array, ".sudoku .row_unique .col_unique .block_unique .unique"
37
11
 
38
- _=ANY
39
- solve S.is{Sudoku.new}.and lambda{S.rows}.is([
40
- [5,_,_,_,2,4,7,_,_],
41
- [_,_,2,_,_,_,8,_,_],
42
- [1,_,_,7,_,3,9,_,2],
43
- [_,_,8,_,7,2,_,4,9],
44
- [_,2,_,9,8,_,_,7,_],
45
- [7,9,_,_,_,_,_,8,_],
46
- [_,_,_,_,3,_,5,_,6],
47
- [9,6,_,_,1,_,3,_,_],
48
- [_,5,_,6,9,_,_,1,_]
49
- ]).and S.solved.and S.good.and S.shown rescue p $!
50
- end
12
+ S.sudoku.if S.is([[ANY]*9]*9).and S.row_unique.and S.col_unique.and S.block_unique.and every(ROW.in(S).and(X.in(ROW)), X.in(1..9))
51
13
 
14
+ S.row_unique.if every ROW.in(S), ROW.unique
15
+ S.col_unique.if every COL.in{S.transpose}, COL.unique
16
+ S.col_unique.if every BLOCK.in{blocks_of(S)}, BLOCK.unique
52
17
 
@@ -14,7 +14,7 @@ rubylog do
14
14
  File.basename(x) !~ /\A\.\.?\z/
15
15
  }
16
16
  }
17
- X.file_in(D).if proc{! X.is_a? Rubylog::Variable and File.exist?(X)}.and D.is { File.dirname(X) }
17
+ X.file_in(D).if proc{! X.is_a? Rubylog::Variable and D.is_a?(Rubylog::Variable) and File.exist?(X)}.and D.is { File.dirname(X) }
18
18
 
19
19
  # If D is not a variable, succeeds if X unifies with a directory in directory D. X
20
20
  # necessarily starts with "#{D}/". X can contain variables, D cannot.
data/rubylog.gemspec CHANGED
@@ -5,17 +5,16 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "rubylog"
8
- s.version = "2.0.0"
8
+ s.version = "2.0.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Bern\u{e1}t Kall\u{f3}"]
12
- s.date = "2013-05-07"
12
+ s.date = "2013-05-16"
13
13
  s.description = "Rubylog is a Prolog-like DSL for Ruby."
14
14
  s.email = "kallo.bernat@gmail.com"
15
15
  s.extra_rdoc_files = [
16
16
  "LICENSE.txt",
17
- "README.rdoc",
18
- "README.rdoc.orig"
17
+ "README.rdoc"
19
18
  ]
20
19
  s.files = [
21
20
  ".document",
@@ -29,6 +28,12 @@ Gem::Specification.new do |s|
29
28
  "TODO.txt",
30
29
  "VERSION",
31
30
  "examples/a_plus_b.rb",
31
+ "examples/benchmark.rb",
32
+ "examples/benchmark/compiled_not_indexed.rb",
33
+ "examples/benchmark/compiled_sequence_indexed.rb",
34
+ "examples/benchmark/indexed_procedure.rb",
35
+ "examples/benchmark/prolog.rb",
36
+ "examples/benchmark/pure.rb",
32
37
  "examples/checkmate.rb",
33
38
  "examples/combination.rb",
34
39
  "examples/dcg.rb",
@@ -41,6 +46,7 @@ Gem::Specification.new do |s|
41
46
  "examples/file_search.rb",
42
47
  "examples/hanoi.rb",
43
48
  "examples/hello.rb",
49
+ "examples/hu/csaladfa.rb",
44
50
  "examples/mice.rb",
45
51
  "examples/mice2.rb",
46
52
  "examples/n_queens.rb",
@@ -170,7 +176,6 @@ Gem::Specification.new do |s|
170
176
  "spec/inriasuite_spec.rb",
171
177
  "spec/integration/custom_classes_spec.rb",
172
178
  "spec/integration/dsl_spec.rb",
173
- "spec/integration/recursion_spec.rb",
174
179
  "spec/integration/theory_as_module_spec.rb",
175
180
  "spec/integration/theory_as_module_with_include_spec.rb",
176
181
  "spec/rspec/rubylog_spec.rb",
@@ -215,12 +220,14 @@ Gem::Specification.new do |s|
215
220
  s.specification_version = 3
216
221
 
217
222
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
223
+ s.add_development_dependency(%q<ruby-prof>, [">= 0"])
218
224
  s.add_development_dependency(%q<rspec>, ["< 3", ">= 2.8.0"])
219
225
  s.add_development_dependency(%q<yard>, ["~> 0.7"])
220
226
  s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
221
227
  s.add_development_dependency(%q<jeweler>, [">= 1.8.3"])
222
228
  s.add_development_dependency(%q<simplecov>, [">= 0"])
223
229
  else
230
+ s.add_dependency(%q<ruby-prof>, [">= 0"])
224
231
  s.add_dependency(%q<rspec>, ["< 3", ">= 2.8.0"])
225
232
  s.add_dependency(%q<yard>, ["~> 0.7"])
226
233
  s.add_dependency(%q<bundler>, [">= 1.0.0"])
@@ -228,6 +235,7 @@ Gem::Specification.new do |s|
228
235
  s.add_dependency(%q<simplecov>, [">= 0"])
229
236
  end
230
237
  else
238
+ s.add_dependency(%q<ruby-prof>, [">= 0"])
231
239
  s.add_dependency(%q<rspec>, ["< 3", ">= 2.8.0"])
232
240
  s.add_dependency(%q<yard>, ["~> 0.7"])
233
241
  s.add_dependency(%q<bundler>, [">= 1.0.0"])
@@ -37,4 +37,7 @@ describe "file system builtins", :rubylog=>true do
37
37
  "./lib/#{A}.rb".file_in("./lib").map{A}.should == ["rubylog"]
38
38
  end
39
39
 
40
+ specify "works with nonvars" do
41
+ "./lib/rubylog.rb".file_in("./lib").to_a.should == [nil]
42
+ end
40
43
  end
@@ -145,6 +145,20 @@ describe Rubylog::Variable, :rubylog=>true do
145
145
  check 3.is(Rubylog::Variable.new(:var1 )).and(4.is(Rubylog::Variable.new(:var1 ))).false
146
146
  end
147
147
 
148
+ describe "variables support recursion" do
149
+ predicate_for Integer, ".factorial()"
150
+ 0.factorial! 1
151
+ N[thats > 0].factorial(K).if N1.is {N-1} .and N1.factorial(K1).and K.is{ N*K1 }
152
+
153
+ check 0.factorial 1
154
+ check 1.factorial 1
155
+ check 2.factorial 2
156
+ check 3.factorial 6
157
+ check 4.factorial 24
158
+ check 7.factorial 5040
159
+ end
160
+
161
+
148
162
  describe "dont-care variables support recursion" do
149
163
  predicate_for Integer, ".factorial()"
150
164
  0.factorial! 1
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubylog
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-07 00:00:00.000000000Z
12
+ date: 2013-05-16 00:00:00.000000000Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ruby-prof
16
+ requirement: &79320110 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *79320110
14
25
  - !ruby/object:Gem::Dependency
15
26
  name: rspec
16
- requirement: &80384320 !ruby/object:Gem::Requirement
27
+ requirement: &79319690 !ruby/object:Gem::Requirement
17
28
  none: false
18
29
  requirements:
19
30
  - - <
@@ -24,10 +35,10 @@ dependencies:
24
35
  version: 2.8.0
25
36
  type: :development
26
37
  prerelease: false
27
- version_requirements: *80384320
38
+ version_requirements: *79319690
28
39
  - !ruby/object:Gem::Dependency
29
40
  name: yard
30
- requirement: &80383710 !ruby/object:Gem::Requirement
41
+ requirement: &79319150 !ruby/object:Gem::Requirement
31
42
  none: false
32
43
  requirements:
33
44
  - - ~>
@@ -35,10 +46,10 @@ dependencies:
35
46
  version: '0.7'
36
47
  type: :development
37
48
  prerelease: false
38
- version_requirements: *80383710
49
+ version_requirements: *79319150
39
50
  - !ruby/object:Gem::Dependency
40
51
  name: bundler
41
- requirement: &80383340 !ruby/object:Gem::Requirement
52
+ requirement: &79318590 !ruby/object:Gem::Requirement
42
53
  none: false
43
54
  requirements:
44
55
  - - ! '>='
@@ -46,10 +57,10 @@ dependencies:
46
57
  version: 1.0.0
47
58
  type: :development
48
59
  prerelease: false
49
- version_requirements: *80383340
60
+ version_requirements: *79318590
50
61
  - !ruby/object:Gem::Dependency
51
62
  name: jeweler
52
- requirement: &80382950 !ruby/object:Gem::Requirement
63
+ requirement: &79318130 !ruby/object:Gem::Requirement
53
64
  none: false
54
65
  requirements:
55
66
  - - ! '>='
@@ -57,10 +68,10 @@ dependencies:
57
68
  version: 1.8.3
58
69
  type: :development
59
70
  prerelease: false
60
- version_requirements: *80382950
71
+ version_requirements: *79318130
61
72
  - !ruby/object:Gem::Dependency
62
73
  name: simplecov
63
- requirement: &80382600 !ruby/object:Gem::Requirement
74
+ requirement: &79317710 !ruby/object:Gem::Requirement
64
75
  none: false
65
76
  requirements:
66
77
  - - ! '>='
@@ -68,7 +79,7 @@ dependencies:
68
79
  version: '0'
69
80
  type: :development
70
81
  prerelease: false
71
- version_requirements: *80382600
82
+ version_requirements: *79317710
72
83
  description: Rubylog is a Prolog-like DSL for Ruby.
73
84
  email: kallo.bernat@gmail.com
74
85
  executables: []
@@ -76,7 +87,6 @@ extensions: []
76
87
  extra_rdoc_files:
77
88
  - LICENSE.txt
78
89
  - README.rdoc
79
- - README.rdoc.orig
80
90
  files:
81
91
  - .document
82
92
  - .rspec
@@ -89,6 +99,12 @@ files:
89
99
  - TODO.txt
90
100
  - VERSION
91
101
  - examples/a_plus_b.rb
102
+ - examples/benchmark.rb
103
+ - examples/benchmark/compiled_not_indexed.rb
104
+ - examples/benchmark/compiled_sequence_indexed.rb
105
+ - examples/benchmark/indexed_procedure.rb
106
+ - examples/benchmark/prolog.rb
107
+ - examples/benchmark/pure.rb
92
108
  - examples/checkmate.rb
93
109
  - examples/combination.rb
94
110
  - examples/dcg.rb
@@ -101,6 +117,7 @@ files:
101
117
  - examples/file_search.rb
102
118
  - examples/hanoi.rb
103
119
  - examples/hello.rb
120
+ - examples/hu/csaladfa.rb
104
121
  - examples/mice.rb
105
122
  - examples/mice2.rb
106
123
  - examples/n_queens.rb
@@ -230,7 +247,6 @@ files:
230
247
  - spec/inriasuite_spec.rb
231
248
  - spec/integration/custom_classes_spec.rb
232
249
  - spec/integration/dsl_spec.rb
233
- - spec/integration/recursion_spec.rb
234
250
  - spec/integration/theory_as_module_spec.rb
235
251
  - spec/integration/theory_as_module_with_include_spec.rb
236
252
  - spec/rspec/rubylog_spec.rb
@@ -264,7 +280,6 @@ files:
264
280
  - spec/rubylog/variable_spec.rb
265
281
  - spec/spec_helper.rb
266
282
  - vimrc
267
- - README.rdoc.orig
268
283
  homepage: https://github.com/cie/rubylog
269
284
  licenses:
270
285
  - MIT
@@ -280,7 +295,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
280
295
  version: '0'
281
296
  segments:
282
297
  - 0
283
- hash: -961282381
298
+ hash: 393356129
284
299
  required_rubygems_version: !ruby/object:Gem::Requirement
285
300
  none: false
286
301
  requirements:
data/README.rdoc.orig DELETED
@@ -1,284 +0,0 @@
1
- = Rubylog - Prolog interpreter for ruby
2
-
3
- Rubylog is a Prolog-like DSL for Ruby. The language is inspired by {Jamis
4
- Buck}[http://weblog.jamisbuck.org/2006/10/28/prolog-in-ruby], and the
5
- implementation is based on {Yield Prolog}[http://yieldprolog.sourceforge.net/],
6
- with lots of sintactic and semantic additions.
7
-
8
- See the {wiki}[https://github.com/cie/rubylog/wiki] for online documentation.
9
-
10
- == Getting started
11
-
12
- First, install the gem
13
-
14
- $ gem install rubylog
15
-
16
- or, if you use +bundler+, add this line to your +Gemfile+:
17
-
18
- gem 'rubylog', '~>2.0pre1'
19
-
20
-
21
-
22
- First, you need a Rubylog context. The simplest you can do is to extend Rubylog::Context into the main object.
23
-
24
- require 'rubylog'
25
- extend Rubylog::Context
26
-
27
-
28
- === Data types
29
-
30
- Rubylog is similar to Prolog, but there are quite a few differences. In Rubylog,
31
- you can use any Ruby object as data.
32
-
33
- Rubylog variables are (undefined) constant names:
34
-
35
- A, B, ANYTHING
36
-
37
- A variables whose name starts with +ANY...+ (case-insensitive) is a don't-care
38
- variable (like +_+ in Prolog).
39
-
40
- <<<<<<< HEAD
41
- Structures are in a different order than they are in prolog:
42
-
43
- functor_for String, :likes
44
- 'John'.likes('beer')
45
-
46
- which would be <tt>likes('John','beer')</tt> in prolog.
47
-
48
- =======
49
- >>>>>>> develop
50
- Lists are just Ruby arrays:
51
-
52
- [1, 2, 3]
53
-
54
- They can have splats:
55
-
56
- [1, 2, *T]
57
-
58
- Which would be <tt>[1,2|T]</tt> in Prolog, however, in Rubylog, splats are not limited
59
- to the end.
60
-
61
- === Predicates
62
- As in prolog, predicates are the buinding blocks of your program. However, the arguments are in a different order than they are in prolog:
63
-
64
- predicate_for String, ".likes()"
65
- 'John'.likes!('beer')
66
-
67
- which would be <tt>likes('John','beer').</tt> in prolog. In Rubylog, predicates must be declared. The string indicating the predicate syntax is <tt>".likes()"</tt>. The format is <tt>:asdf .asdf .asdf() .asdf(,) .asdf(,,)</tt> for predicates with 0,1,2,3,4 arguments.
68
-
69
- You can assert a rule with the +if+ method:
70
-
71
- predicate_for String, ".drinks() .has()"
72
- X.drinks(Y).if X.has(Y).and X.likes(Y)
73
-
74
- This would be +drinks(X,Y) :- has(X,Y), likes(X,Y)+ in Prolog.
75
-
76
- You can assert facts with <tt>if(:true)</tt>, or, as a shorthand you can use the bang
77
- syntax:
78
-
79
- 'John'.likes! 'milk'
80
-
81
- Bang assertions return their first argument (which is <tt>'John'</tt> in this case), so they can be chained:
82
-
83
- 'John'.likes!('beer').has!('beer')
84
-
85
- You can also use +unless+:
86
-
87
- predicate_for String, ".good .bad"
88
- A.good.unless A.bad
89
-
90
- Nullary predicates are symbols, similar to Prolog:
91
-
92
- 'John'.drinks('beer').if :false.and(:cut!).or(:true)
93
-
94
-
95
-
96
- === Built-in predicates
97
-
98
- Some built-in predicates and their Prolog equivalents:
99
-
100
- <<<<<<< HEAD
101
- Prolog Rubylog
102
- ------ -------
103
- true/0 :true
104
- fail/0 :fail
105
- ','/2 .and()
106
- ';'/2 .or()
107
- '\+'/1 .false
108
- '='/2 .is()
109
- '=/='/2 .is_not()
110
- member/2 .in()
111
- '!'/0 :cut!
112
- =======
113
- Rubylog Prolog
114
- ------- ------
115
- :true true
116
- :fail fail
117
- .and() ,
118
- .or() ;
119
- :false \+
120
- .is() =
121
- .is_not() =/=
122
- .in() member
123
- :cut! !
124
- >>>>>>> develop
125
-
126
- There are some new ones:
127
-
128
- not_in, all, any, one, none, iff
129
-
130
- You can see reference of these in <tt>lib/rubylog/builtins/logic.rb</tt> and <tt>lib/rubylog/builtins/term.rb</tt>
131
-
132
-
133
- See the documentation in <tt>lib/rubylog/builtins/</tt>
134
-
135
- === Unification
136
-
137
- In Rubylog, unification works quite the same in Prolog, with the +is+ functor.
138
-
139
- A.is(B)
140
-
141
- Using arrays, you can benefit the splats:
142
-
143
- [1,2,3,4].is([A,B,*T]) # [1,2,3,4] = [A,B|T] in prolog
144
- [1,2,3,4].is([*H,*T]) # append(H, T, [1,2,3,4]) in prolog
145
-
146
- The +in+ predicate unifies the first argument with any member of the collection:
147
-
148
- 4.in([1,2,3,4])
149
-
150
- You can use guards:
151
-
152
- A[String].in(["asdf",5,nil]).each { p A } # outputs "asdf"
153
- A[/x/].in(["asdf","xyz"]).each { p A } # outputs "xyz"
154
- A[thats < 5].in([4,5,6]).each { p A } # outputs 4
155
-
156
- === Moving between Ruby and Rubylog
157
- ==== Running a query
158
-
159
- If you want to run a query, you have many different syntaxes:
160
-
161
- prove ('John'.drinks 'beer') # => true
162
- true? ('John'.drinks 'beer') # => true
163
- ('John'.drinks 'beer').true? # => true
164
- 'John'.drinks? 'beer' # => true
165
-
166
- ==== Enumerations
167
-
168
- +Structure+ implements +Enumerable+, and yields the solutions. Within the
169
- enumeration block, you can access the values of your variables.
170
-
171
- 'John'.drinks! 'beer'
172
- ('John'.drinks X).each {p X} # outputs 'beer'
173
- ('John'.drinks X).map{X} # => ['beer']
174
- ('John'.drinks X).count # => 1
175
-
176
- ==== Procs as predicates
177
-
178
- You can invoke Ruby codes in Rubylog rules with a proc:
179
-
180
- 'John'.likes(Y).if proc{ Y =~ /ale/ }
181
-
182
- or in most cases you can use just a block:
183
-
184
- 'John'.likes(Y).if { Y =~ /ale/ }
185
-
186
- The predicate succeeds if the block returns a true value.
187
-
188
- ==== Procs as functions
189
-
190
- +is+ and +in+ can take a proc or block argument, which they execute and take its return value:
191
-
192
- X.good.if X.is { 'BEER'.downcase }
193
- X.good.if X.in { get_good_drinks() }
194
-
195
- ==== The two modes of Rubylog
196
-
197
- Rubylog has two modes, DSL and native. DSL code is executed only once
198
- at compile time, and is used for describing the Rubylog program. Native code is
199
- executed runtime. Any block passed to Rubylog structures native code.
200
-
201
- ('John'.drinks X).and { X != 'beer'}.each { p X }
202
- ^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^ dsl mode
203
- ^^^^^^^^^^^^ ^^^^^ native mode
204
-
205
- In dsl mode, variables are +Rubylog::Variable+ objects. In native mode,
206
- variables are substituted with their respecitve value (or +nil+ if they are not
207
- bound).
208
-
209
- All built-in rubylog predicates are clean logical programming predicates witout
210
- a side-effect. If you want some side-effects, you always go into native mode.
211
-
212
- === Rubylog as a test suite
213
-
214
- You can write simple tests using the +check+ method:
215
-
216
- theory do
217
- check :true.or :false
218
- check A.is(3).and A.in [1,2,3]
219
- check { 5+5 == 10 }
220
- end
221
-
222
- You sould put this file in <tt>"./logic/something\_logic.rb"</tt>. Then you can run it
223
- with
224
-
225
- rubylog logic/something_logic.rb
226
-
227
- Or you can run all files in <tt>logic/**/*\_logic.rb</tt> with
228
-
229
- rubylog
230
-
231
- === Other built-in libraries
232
-
233
- ==== File system
234
-
235
- You can make some queries on the file system:
236
-
237
- require "rubylog/builtins/file_system"
238
-
239
- theory do
240
- check "README".filename_in "."
241
-
242
- X.dirname_in(".").each { puts X }
243
- end
244
-
245
-
246
- ==== Reflection
247
-
248
- You can make some metaprogramming with Rubylog
249
-
250
-
251
- require "rubylog/builtins/reflection"
252
-
253
- theory do
254
- functor_for String, :likes
255
-
256
- check "John".likes("Jane").structure(:likes, ["John", "Jane"])
257
-
258
- "John".likes(X).if X.likes("John")
259
- "Jane".likes!("John")
260
- check "John".likes("Jane").follows_from "Jane".likes("John")
261
-
262
- "John".likes!("milk")
263
- check "John".likes("milk").fact
264
- check "John".likes("beer").fact.false
265
-
266
- end
267
-
268
- == Contributing
269
-
270
- === To the language
271
-
272
- * Post your own examples to the {wiki}[https://github.com/cie/rubylog/wiki/Examples].
273
- * Improve others' examples.
274
- * If you have a suggestion for the language, submit an issue.
275
-
276
- === Reporting bugs or requesting features
277
-
278
- * Create an issue on the {issue tracker}[https://github.com/cie/rubylog/issues].
279
-
280
- == Copyright
281
-
282
- Copyright (c) 2013 Bernát Kalló. See LICENSE.txt for
283
- further details.
284
-
@@ -1,14 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe "Recursion", :rubylog=>true do
4
- predicate_for Integer, ".factorial()"
5
- 0.factorial! 1
6
- N[thats > 0].factorial(K).if N1.is {N-1} .and N1.factorial(K1).and K.is{ N*K1 }
7
-
8
- check 0.factorial 1
9
- check 1.factorial 1
10
- check 2.factorial 2
11
- check 3.factorial 6
12
- check 4.factorial 24
13
- check 7.factorial 5040
14
- end