rubylog 2.0.0 → 2.0.1

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.
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