factbase 0.0.30 → 0.0.31

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 58a162c50ba7d2b9d21ca0ff2d62e34acd0c46d2666b596ceffcceb2dc4215c0
4
- data.tar.gz: 61514fa0fa904ea71540c0f9d4776ea75d5405aa5a1b73d32e051c29606b5fdb
3
+ metadata.gz: 016b53cf00bc9efec24f50c333ab1fc14266151487fab61ce7d7ca533ba602a6
4
+ data.tar.gz: 89845aa58068bf78189d62602282dbcffe8f99a5d4b772dcf8bfd3ee4b3698d4
5
5
  SHA512:
6
- metadata.gz: 7b3841073dba72e3fcc6e1fd089235d93829264504447557b3b23f6eb3fa74a4ebccad0d27062af84dc00ab115ca06d9dcc9621bf245d54e09b121bb21983737
7
- data.tar.gz: aed653f93b7505db410e81d9fefd8fe9d38fc6b89947f89c952992bcdd76dab1d5f9e4a8cd941df426cb7b200855f26fb2981a9698ac35e8e6ea682c693787e7
6
+ metadata.gz: 0d2fecf64f6b62d4ad8796469f4aa937a63e2398f032a812fc15b95ea6be48f0aa25fe032bc469e4afd4ced351f6ba8fbc914bf1dc074dd07ae881b481e39d0b
7
+ data.tar.gz: 9843fdc79bf8b53aae6b366d1ea133f92185a85dc37ed5f6779d478d030655e621d5431cf793ee162f79fddc4c51ae49403850f25849cbcefb7ac793cc4975ae
@@ -0,0 +1,57 @@
1
+ # Copyright (c) 2024 Yegor Bugayenko
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the 'Software'), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ # SOFTWARE.
20
+ ---
21
+ name: license
22
+ 'on':
23
+ push:
24
+ branches:
25
+ - master
26
+ pull_request:
27
+ branches:
28
+ - master
29
+ jobs:
30
+ license:
31
+ runs-on: ubuntu-22.04
32
+ steps:
33
+ - uses: actions/checkout@v4
34
+ - shell: bash
35
+ run: |
36
+ header="Copyright (c) $(date +%Y) Yegor Bugayenko"
37
+ failed="false"
38
+ while IFS= read -r file; do
39
+ if ! grep -q "${header}" "${file}"; then
40
+ failed="true"
41
+ echo "⚠️ Copyright header is not found in: ${file}"
42
+ else
43
+ echo "File looks good: ${file}"
44
+ fi
45
+ done < <(find . -type f \( \
46
+ -name "Dockerfile" -o \
47
+ -name "LICENSE.txt" -o \
48
+ -name "Makefile" -o \
49
+ -name "Rakefile" -o \
50
+ -name "*.sh" -o \
51
+ -name "*.rb" -o \
52
+ -name "*.fe" -o \
53
+ -name "*.yml" \
54
+ \) -print)
55
+ if [ "${failed}" = "true" ]; then
56
+ exit 1
57
+ fi
data/Gemfile CHANGED
@@ -23,10 +23,10 @@
23
23
  source 'https://rubygems.org'
24
24
  gemspec
25
25
 
26
- gem 'minitest', '5.23.0', require: false
26
+ gem 'minitest', '5.23.1', require: false
27
27
  gem 'rake', '13.2.1', require: false
28
28
  gem 'rspec-rails', '6.1.2', require: false
29
- gem 'rubocop', '1.63.5', require: false
29
+ gem 'rubocop', '1.64.0', require: false
30
30
  gem 'rubocop-performance', '1.21.0', require: false
31
31
  gem 'rubocop-rspec', '2.29.2', require: false
32
32
  gem 'simplecov', '0.22.0', require: false
data/Gemfile.lock CHANGED
@@ -59,7 +59,7 @@ GEM
59
59
  crass (~> 1.0.2)
60
60
  nokogiri (>= 1.12.0)
61
61
  loog (0.5.1)
62
- minitest (5.23.0)
62
+ minitest (5.23.1)
63
63
  mutex_m (0.2.0)
64
64
  nokogiri (1.16.5-arm64-darwin)
65
65
  racc (~> 1.4)
@@ -75,7 +75,7 @@ GEM
75
75
  racc
76
76
  psych (5.1.2)
77
77
  stringio
78
- racc (1.7.3)
78
+ racc (1.8.0)
79
79
  rack (3.0.11)
80
80
  rack-session (2.0.0)
81
81
  rack (>= 3.0.0)
@@ -125,7 +125,7 @@ GEM
125
125
  rspec-mocks (~> 3.13)
126
126
  rspec-support (~> 3.13)
127
127
  rspec-support (3.13.1)
128
- rubocop (1.63.5)
128
+ rubocop (1.64.0)
129
129
  json (~> 2.3)
130
130
  language_server-protocol (>= 3.17.0)
131
131
  parallel (~> 1.10)
@@ -181,10 +181,10 @@ PLATFORMS
181
181
 
182
182
  DEPENDENCIES
183
183
  factbase!
184
- minitest (= 5.23.0)
184
+ minitest (= 5.23.1)
185
185
  rake (= 13.2.1)
186
186
  rspec-rails (= 6.1.2)
187
- rubocop (= 1.63.5)
187
+ rubocop (= 1.64.0)
188
188
  rubocop-performance (= 1.21.0)
189
189
  rubocop-rspec (= 2.29.2)
190
190
  simplecov (= 0.22.0)
data/README.md CHANGED
@@ -14,7 +14,8 @@
14
14
  This Ruby gem manages an in-memory database of facts.
15
15
  A fact is simply a map of properties and values.
16
16
  The values are either atomic literals or non-empty sets of literals.
17
- It is possible to delete a fact, but impossible to delete a property from a fact.
17
+ It is possible to delete a fact, but impossible to delete a property
18
+ from a fact.
18
19
 
19
20
  **ATTENTION**: The current implemention is naive and,
20
21
  because of that, very slow. I will be very happy
@@ -73,12 +74,14 @@ All terms available in a query:
73
74
 
74
75
  There are also terms that match the entire factbase:
75
76
 
76
- * `(max k)` returns true if the value of `k` property is the largest in the entire factbase
77
+ * `(max k)` returns true if the value of `k` property
78
+ is the largest in the entire factbase
77
79
  * `(min k)` returns true if the value of `k` is the smallest
78
80
 
79
81
  ## How to contribute
80
82
 
81
- Read [these guidelines](https://www.yegor256.com/2014/04/15/github-guidelines.html).
83
+ Read
84
+ [these guidelines](https://www.yegor256.com/2014/04/15/github-guidelines.html).
82
85
  Make sure you build is green before you contribute
83
86
  your pull request. You will need to have
84
87
  [Ruby](https://www.ruby-lang.org/en/) 3.2+ and
@@ -39,17 +39,26 @@ class Factbase::Syntax
39
39
  # Convert it to a term.
40
40
  # @return [Term] The term detected
41
41
  def to_term
42
+ build.simplify
43
+ rescue StandardError => e
44
+ raise "#{e.message} in \"#{@query}\""
45
+ end
46
+
47
+ private
48
+
49
+ # Convert it to a term.
50
+ # @return [Term] The term detected
51
+ def build
42
52
  @tokens ||= to_tokens
53
+ raise 'No tokens' if @tokens.empty?
43
54
  @ast ||= to_ast(@tokens, 0)
44
- raise "Too many terms: #{@query}" if @ast[1] != @tokens.size
55
+ raise 'Too many terms' if @ast[1] != @tokens.size
45
56
  term = @ast[0]
46
- raise "No terms found: #{@query}" if term.nil?
47
- raise "Not a term: #{@query}" unless term.is_a?(Factbase::Term)
57
+ raise 'No terms found' if term.nil?
58
+ raise 'Not a term' unless term.is_a?(Factbase::Term)
48
59
  term
49
60
  end
50
61
 
51
- private
52
-
53
62
  # Reads the stream of tokens, starting at the +at+ position. If the
54
63
  # token at the position is not a literal (like 42 or "Hello") but a term,
55
64
  # the function recursively calls itself.
@@ -58,7 +67,7 @@ class Factbase::Syntax
58
67
  # is the term/literal and the second one is the position where the
59
68
  # scanning should continue.
60
69
  def to_ast(tokens, at)
61
- raise "Closing too soon at ##{at}: #{@query}" if tokens[at] == :close
70
+ raise "Closing too soon at ##{at}" if tokens[at] == :close
62
71
  return [tokens[at], at + 1] unless tokens[at] == :open
63
72
  at += 1
64
73
  op = tokens[at]
@@ -66,11 +75,11 @@ class Factbase::Syntax
66
75
  operands = []
67
76
  at += 1
68
77
  loop do
69
- raise "End of token stream at ##{at}: #{@query}" if tokens[at].nil?
78
+ raise "End of token stream at ##{at}" if tokens[at].nil?
70
79
  break if tokens[at] == :close
71
80
  (operand, at1) = to_ast(tokens, at)
72
- raise "Stuck at position ##{at}: #{@query}" if at == at1
73
- raise "Jump back at position ##{at}: #{@query}" if at1 < at
81
+ raise "Stuck at position ##{at}" if at == at1
82
+ raise "Jump back at position ##{at}" if at1 < at
74
83
  at = at1
75
84
  operands << operand
76
85
  break if tokens[at] == :close
@@ -110,12 +119,12 @@ class Factbase::Syntax
110
119
  acc += c
111
120
  end
112
121
  end
113
- raise "String not closed: : #{@query}" if string
122
+ raise 'String not closed' if string
114
123
  list.map do |t|
115
124
  if t.is_a?(Symbol)
116
125
  t
117
126
  elsif t.start_with?('\'', '"')
118
- raise "String literal can't be empty: #{@query}" if t.length <= 2
127
+ raise 'String literal can\'t be empty' if t.length <= 2
119
128
  t[1..-2]
120
129
  elsif t.match?(/^[0-9]+$/)
121
130
  t.to_i
@@ -124,7 +133,7 @@ class Factbase::Syntax
124
133
  elsif t.match?(/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$/)
125
134
  Time.parse(t)
126
135
  else
127
- raise "Wrong symbol format (#{t}): #{@query}" unless t.match?(/^[a-z][a-zA-Z0-9_]*$/)
136
+ raise "Wrong symbol format (#{t})" unless t.match?(/^[a-z][a-zA-Z0-9_]*$/)
128
137
  t.to_sym
129
138
  end
130
139
  end
data/lib/factbase/term.rb CHANGED
@@ -57,6 +57,17 @@ class Factbase::Term
57
57
  self
58
58
  end
59
59
 
60
+ # Simplify it if possible.
61
+ # @return [Factbase::Term] New term or itself
62
+ def simplify
63
+ m = "#{@op}_simplify"
64
+ if respond_to?(m, true)
65
+ send(m)
66
+ else
67
+ self
68
+ end
69
+ end
70
+
60
71
  # Turns it into a string.
61
72
  # @return [String] The string of it
62
73
  def to_s
@@ -100,6 +111,28 @@ class Factbase::Term
100
111
  true
101
112
  end
102
113
 
114
+ def and_or_simplify
115
+ strs = []
116
+ ops = []
117
+ @operands.each do |o|
118
+ o = o.simplify
119
+ s = o.to_s
120
+ next if strs.include?(s)
121
+ strs << s
122
+ ops << o
123
+ end
124
+ return ops[0] if ops.size == 1
125
+ Factbase::Term.new(@op, ops)
126
+ end
127
+
128
+ def and_simplify
129
+ and_or_simplify
130
+ end
131
+
132
+ def or_simplify
133
+ and_or_simplify
134
+ end
135
+
103
136
  def when(fact)
104
137
  assert_args(2)
105
138
  a = @operands[0]
data/lib/factbase.rb CHANGED
@@ -29,7 +29,7 @@ require 'yaml'
29
29
  # License:: MIT
30
30
  class Factbase
31
31
  # Current version of the gem (changed by .rultor.yml on every release)
32
- VERSION = '0.0.30'
32
+ VERSION = '0.0.31'
33
33
 
34
34
  # Constructor.
35
35
  def initialize(facts = [])
@@ -106,9 +106,21 @@ class TestSyntax < Minitest::Test
106
106
  ')',
107
107
  '"'
108
108
  ].each do |q|
109
- assert_raises(q) do
110
- Factbase::Syntax.new(q).to_term
111
- end
109
+ assert(
110
+ assert_raises(q) do
111
+ Factbase::Syntax.new(q).to_term
112
+ end.message.include?(q)
113
+ )
114
+ end
115
+ end
116
+
117
+ def test_simplification
118
+ {
119
+ '(foo)' => '(foo)',
120
+ '(and (foo) (foo))' => '(foo)',
121
+ '(and (foo) (or (and (eq a 1))) (eq a 1) (foo))' => '(and (foo) (eq a 1))'
122
+ }.each do |s, t|
123
+ assert_equal(t, Factbase::Syntax.new(s).to_term.to_s)
112
124
  end
113
125
  end
114
126
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: factbase
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.30
4
+ version: 0.0.31
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-05-21 00:00:00.000000000 Z
11
+ date: 2024-05-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -78,6 +78,7 @@ files:
78
78
  - ".gitattributes"
79
79
  - ".github/workflows/actionlint.yml"
80
80
  - ".github/workflows/codecov.yml"
81
+ - ".github/workflows/license.yml"
81
82
  - ".github/workflows/markdown-lint.yml"
82
83
  - ".github/workflows/pdd.yml"
83
84
  - ".github/workflows/rake.yml"