satre 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +4 -1
  3. data/README.md +9 -7
  4. data/bin/exercise +64 -8
  5. data/lib/satre.rb +1 -3
  6. data/lib/satre/errors.rb +3 -0
  7. data/lib/satre/errors/argument_error.rb +2 -0
  8. data/lib/satre/errors/expression_error.rb +2 -0
  9. data/lib/satre/errors/parser_error.rb +2 -0
  10. data/lib/satre/formula.rb +3 -9
  11. data/lib/satre/formula/first_order_logic.rb +4 -0
  12. data/lib/satre/formula/first_order_logic/exists.rb +32 -0
  13. data/lib/satre/formula/first_order_logic/fol_formula.rb +35 -0
  14. data/lib/satre/formula/first_order_logic/forall.rb +32 -0
  15. data/lib/satre/formula/first_order_logic/relation.rb +38 -0
  16. data/lib/satre/formula/formula.rb +1 -13
  17. data/lib/satre/formula/propositional_logic.rb +9 -0
  18. data/lib/satre/formula/propositional_logic/and.rb +37 -0
  19. data/lib/satre/formula/propositional_logic/atom.rb +44 -0
  20. data/lib/satre/formula/propositional_logic/entails.rb +42 -0
  21. data/lib/satre/formula/{false.rb → propositional_logic/false.rb} +9 -2
  22. data/lib/satre/formula/propositional_logic/iff.rb +38 -0
  23. data/lib/satre/formula/propositional_logic/imp.rb +38 -0
  24. data/lib/satre/formula/propositional_logic/not.rb +34 -0
  25. data/lib/satre/formula/propositional_logic/or.rb +37 -0
  26. data/lib/satre/formula/{true.rb → propositional_logic/true.rb} +11 -2
  27. data/lib/satre/formula/term.rb +3 -0
  28. data/lib/satre/formula/term/function.rb +33 -0
  29. data/lib/satre/formula/term/term.rb +6 -0
  30. data/lib/satre/formula/term/variable.rb +24 -0
  31. data/lib/satre/parser.rb +4 -15
  32. data/lib/satre/parser/formula_parser.rb +74 -0
  33. data/lib/satre/parser/lexer.rb +55 -0
  34. data/lib/satre/parser/parser.rb +80 -0
  35. data/lib/satre/parser/term_parser.rb +85 -0
  36. data/lib/satre/version.rb +1 -1
  37. data/satre.gemspec +1 -0
  38. metadata +43 -20
  39. data/lib/satre/expression.rb +0 -6
  40. data/lib/satre/expression/add.rb +0 -26
  41. data/lib/satre/expression/const.rb +0 -23
  42. data/lib/satre/expression/expression.rb +0 -22
  43. data/lib/satre/expression/expression_parser.rb +0 -56
  44. data/lib/satre/expression/mul.rb +0 -30
  45. data/lib/satre/expression/var.rb +0 -14
  46. data/lib/satre/formula/and.rb +0 -26
  47. data/lib/satre/formula/atom.rb +0 -28
  48. data/lib/satre/formula/entails.rb +0 -29
  49. data/lib/satre/formula/iff.rb +0 -27
  50. data/lib/satre/formula/imp.rb +0 -25
  51. data/lib/satre/formula/not.rb +0 -22
  52. data/lib/satre/formula/or.rb +0 -25
  53. data/lib/satre/formula_parser.rb +0 -92
  54. data/lib/satre/lexer.rb +0 -47
@@ -0,0 +1,44 @@
1
+ require 'satre/formula'
2
+
3
+ # A parster for propositional statements
4
+ # and simple mathematical expressions
5
+ module Satre
6
+ # A propositional 'atomic' value
7
+ class Atom < Formula
8
+
9
+ attr_reader :value
10
+
11
+ def initialize(value)
12
+ @value = value.dup.freeze
13
+ end
14
+
15
+ def holds?(domain, func, predicate, valudation)
16
+ fail(OperationExceprion, 'Atomic value must be a relation') unless value.is_a?(Relation)
17
+ value.holds?(domain, func, predicate, valudation)
18
+ end
19
+
20
+ # A predicate p(x_1,...,x_n) is well-formed if
21
+ # (a) each term x_1,...,x_n is well-formed
22
+ # (b) there is a pair (q, m) in signature sig where q = p and n = m
23
+ def wellformed?(sig)
24
+ @value.wellformed?(sig)
25
+ end
26
+
27
+ def eval(valudation)
28
+ fail(Error, 'valudation must be a hash') unless valudation.is_a?(Hash)
29
+ fail(Error, 'all validations must be booleans') unless valudation.values.all?{|b|!!b == b}
30
+ valudation = valudation.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
31
+ fail(Error, "valudation for Atom #{value} not given") unless valudation.keys.include?(to_s.to_sym)
32
+ valudation[to_s.to_sym]
33
+ end
34
+
35
+ def to_s
36
+ value.to_s
37
+ end
38
+
39
+ def atoms
40
+ [value.to_s.to_sym]
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,42 @@
1
+ require 'satre/formula'
2
+
3
+ # A parster for propositional statements
4
+ # and simple mathematical expressions
5
+ module Satre
6
+ # A propositional logic 'and' value
7
+ class Entails < Formula
8
+ attr_reader :knowledge_base
9
+ attr_reader :logical_consequence
10
+
11
+ def initialize(knowledge_base, logical_consequence)
12
+ fail(ArgumentError, 'Argument must be a Formula') unless knowledge_base.is_a?(Formula)
13
+ fail(ArgumentError, 'Argument must be a Formula') unless logical_consequence.is_a?(Formula)
14
+ @knowledge_base = knowledge_base.dup.freeze
15
+ @logical_consequence = logical_consequence.dup.freeze
16
+ end
17
+
18
+ def to_s
19
+ "(#{knowledge_base} ⊨ #{logical_consequence})"
20
+ end
21
+
22
+ # p |= q is wellformed if p and q are well-formed
23
+ def wellformed?(sig)
24
+ knowledge_base.wellformed?(sig) && logical_consequence.wellformed?(sig)
25
+ end
26
+
27
+ def hold?(domain, func, predicate, valudation)
28
+ fail 'not implemented'
29
+ #And.new(knowledge_base, Not.new(logical_consequence)).unsatifiable?
30
+ end
31
+
32
+ def eval(*)
33
+ And.new(knowledge_base, Not.new(logical_consequence)).unsatifiable?
34
+ end
35
+
36
+ def atoms
37
+ atoms = knowledge_base.atoms + logical_consequence.atoms
38
+ atoms.uniq || []
39
+ end
40
+
41
+ end
42
+ end
@@ -3,8 +3,15 @@ require 'satre/formula'
3
3
  module Satre
4
4
  class False < Formula
5
5
 
6
- def initialize
7
- super '⊥'
6
+ def to_s
7
+ '⊥'
8
+ end
9
+
10
+ alias :holds? :eval
11
+
12
+ # False is well-formed
13
+ def wellformed?(sig)
14
+ true
8
15
  end
9
16
 
10
17
  def eval(*)
@@ -0,0 +1,38 @@
1
+ require 'satre/formula'
2
+
3
+ module Satre
4
+ class Iff < Formula
5
+ attr_reader :left_conditional
6
+ attr_reader :right_conditional
7
+
8
+ def initialize(left_conditional, right_conditional)
9
+ fail(ArgumentError, 'Argument must be a Formula') unless left_conditional.is_a?(Formula)
10
+ fail(ArgumentError, 'Argument must be a Formula') unless right_conditional.is_a?(Formula)
11
+ @left_conditional = left_conditional.dup.freeze
12
+ @right_conditional = right_conditional.dup.freeze
13
+ end
14
+
15
+ def holds?(domain, func, pred, valudation)
16
+ left_conditional.holds?(domain, func, pred, valudation) == right_conditional.holds?(domain, func, pred, valudation)
17
+ end
18
+
19
+ # p <=> q is well-formed if p and q are well-formed
20
+ def wellformed?(sig)
21
+ left_conjunct.wellformed?(sig) && right_conjunct.wellformed(sig)
22
+ end
23
+
24
+ def to_s
25
+ "(#{left_conditional} ⇔ #{right_conditional})"
26
+ end
27
+
28
+ def eval(valudation)
29
+ left_conditional.eval(valudation) == right_conditional.eval(valudation)
30
+ end
31
+
32
+ def atoms
33
+ atoms = left_conditional.atoms + right_conditional.atoms
34
+ atoms.uniq || []
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,38 @@
1
+ require 'satre/formula'
2
+
3
+ module Satre
4
+ class Imp < Formula
5
+ attr_reader :antendence
6
+ attr_reader :consequence
7
+
8
+ def initialize(antendence, consequence)
9
+ fail(ArgumentError, "Argument must be a Formula p:#{p}") unless antendence.is_a?(Formula)
10
+ fail(ArgumentError, "Argument must be a Formula q:#{q}") unless consequence.is_a?(Formula)
11
+ @antendence = antendence.dup.freeze
12
+ @consequence = consequence.dup.freeze
13
+ end
14
+
15
+ def to_s
16
+ "(#{antendence} → #{consequence})"
17
+ end
18
+
19
+ # p ==> q is well-formed if p and q are well-formed
20
+ def wellformed?(sig)
21
+ antendence.wellformed?(sig) && consequence.wellformed?(sig)
22
+ end
23
+
24
+ # | Imp(p,q) -> not(holds m v p) or (holds m v q)
25
+ def holds?(domain, func, pred, valudation)
26
+ (! antendence.holds?(domain, func, pred, valudation)) || (consequence.holds?(domain, func, pred, valudation))
27
+ end
28
+
29
+ def eval(valudation)
30
+ (! antendence.eval(valudation)) || (consequence.eval(valudation))
31
+ end
32
+
33
+ def atoms
34
+ atoms = antendence.atoms + consequence.atoms
35
+ atoms.uniq || []
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,34 @@
1
+ require 'satre/formula'
2
+
3
+ module Satre
4
+ class Not < Formula
5
+ attr_reader :literal
6
+
7
+ def initialize(literal)
8
+ fail(ArgumentError, 'Argument must be a Formula') unless literal.is_a?(Formula)
9
+ @literal = literal.dup.freeze
10
+ end
11
+
12
+ def to_s
13
+ "(¬#{literal})"
14
+ end
15
+
16
+ def holds?(domain, func, pred, valudation)
17
+ ! literal.holds?(domain, func, pred, valudation)
18
+ end
19
+
20
+ # ~p is well-formed if p is well-formed
21
+ def wellformed?(sig)
22
+ literal.wellformed?(sig)
23
+ end
24
+
25
+ def eval(valudation)
26
+ ! literal.eval(valudation)
27
+ end
28
+
29
+ def atoms
30
+ literal.atoms || []
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,37 @@
1
+ require 'satre/formula'
2
+
3
+ module Satre
4
+ class Or < Formula
5
+ attr_reader :left_disjunct
6
+ attr_reader :right_disjunct
7
+
8
+ def initialize(left_disjunct, right_disjunct)
9
+ fail(ArgumentError, 'Argument must be a Formula') unless left_disjunct.is_a?(Formula)
10
+ fail(ArgumentError, 'Argument must be a Formula') unless right_disjunct.is_a?(Formula)
11
+ @left_disjunct = left_disjunct.dup.freeze
12
+ @right_disjunct = right_disjunct.dup.freeze
13
+ end
14
+
15
+ def to_s
16
+ "(#{left_disjunct} ∨ #{right_disjunct})"
17
+ end
18
+
19
+ def holds?(domain, func, pred, valudation)
20
+ left_disjunct.holds?(domain, func, pred, valudation) or right_disjunct.holds?(domain, func, pred, valudation)
21
+ end
22
+
23
+ # p \// q is well-formed if p and q are well-formed
24
+ def wellformed?(sig)
25
+ left_disjunct.wellformed?(sig) && right_disjunct.wellformed?(sig)
26
+ end
27
+
28
+ def eval(valudation)
29
+ left_disjunct.eval(valudation) or right_disjunct.eval(valudation)
30
+ end
31
+
32
+ def atoms
33
+ atoms = left_disjunct.atoms + right_disjunct.atoms
34
+ atoms.uniq || []
35
+ end
36
+ end
37
+ end
@@ -2,8 +2,17 @@ require 'satre/formula'
2
2
 
3
3
  module Satre
4
4
  class True < Formula
5
- def initialize
6
- super '⊤'
5
+
6
+ def to_s
7
+ '⊤'
8
+ end
9
+
10
+ # true holds
11
+ alias :holds? :eval
12
+
13
+ # True is wellformed
14
+ def wellformed?(_)
15
+ true
7
16
  end
8
17
 
9
18
  def eval(*)
@@ -0,0 +1,3 @@
1
+ require 'satre/formula/term/term.rb'
2
+ require 'satre/formula/term/function.rb'
3
+ require 'satre/formula/term/variable.rb'
@@ -0,0 +1,33 @@
1
+ require 'satre/formula/term/term'
2
+
3
+ module Satre
4
+ class Function < Term
5
+ attr_reader :function
6
+ attr_reader :term_list
7
+
8
+ def initialize(function, term_list=[])
9
+ #fail(ArgumentError, '...') unless function.is_a?(String)
10
+ #fail(ArgumentError, '...') unless term_list.is_a?(Array)
11
+ @function = function.dup.freeze
12
+ @term_list = term_list.dup.freeze
13
+ end
14
+
15
+ # A term $f(x_1,...,x_n)$ is well-formed if
16
+ # (a) Each term $x_1,...,x_n$ is well formed
17
+ # (b) There is a pair (a, m) in signature sig where s = f and n = m
18
+ #
19
+ # sig is a hash containing the signature domain
20
+ def wellformed?(sig)
21
+ term_list.all? { |x| x.wellformed?(sig) } && sig[function.to_sym] == term_list.length
22
+ end
23
+
24
+ # | Fn(f,args) -> func f (map (termval m v), args);;
25
+ def validate(func, pred, valudation)
26
+ func.call(f, term_list.map { |t| t.validate(func, pred, valudation) })
27
+ end
28
+
29
+ def to_s
30
+ "#{function}(#{term_list.map(&:to_s).join(',')})"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,6 @@
1
+ require 'satre/formula/formula'
2
+
3
+ module Satre
4
+ class Term < Formula
5
+ end
6
+ end
@@ -0,0 +1,24 @@
1
+ require 'satre/formula/term/term'
2
+
3
+ module Satre
4
+ class Variable < Term
5
+ attr_reader :variable
6
+
7
+ def initialize(variable)
8
+ @variable = variable.dup.freeze
9
+ end
10
+
11
+ # A variable is well-formed
12
+ def wellformed?(_)
13
+ true
14
+ end
15
+
16
+ def validate(_, _, valudation)
17
+ valudation[variable.to_sym]
18
+ end
19
+
20
+ def to_s
21
+ variable.to_s
22
+ end
23
+ end
24
+ end
@@ -1,15 +1,4 @@
1
- require 'satre/lexer'
2
-
3
- module Satre
4
- class Parser
5
- class << self
6
- def make_parser
7
- lambda do |pfn, inp|
8
- expr, rest = pfn.call(Lexer.lex(inp))
9
- return expr if rest == []
10
- fail(ParserError, 'Unparsed input')
11
- end
12
- end
13
- end
14
- end
15
- end
1
+ require 'satre/parser/lexer'
2
+ require 'satre/parser/parser'
3
+ require 'satre/parser/formula_parser'
4
+ require 'satre/parser/term_parser'
@@ -0,0 +1,74 @@
1
+ require 'satre/parser/parser'
2
+
3
+ module Satre
4
+ class FormulaParser < Parser
5
+ class << self
6
+
7
+ def parse_formula(ifn, afn, vs, inp)
8
+ parse_Entails = parse_right_infix("|=", ->(p,q) { Entails.new(p,q)})
9
+ parse_Iff = parse_right_infix("<=>", ->(p,q) { Iff.new(p,q)})
10
+ parse_Imp = parse_right_infix("==>", ->(p,q) { Imp.new(p,q)})
11
+ parse_Or = parse_right_infix("\\/", ->(p,q) { Or.new(p,q)})
12
+ parse_And = parse_right_infix("/\\", ->(p,q) { And.new(p,q)})
13
+ parse_Atom = method(:parse_atomic_formula).curry.call(ifn, afn, vs)
14
+ parse_Entails.call(parse_Imp.call(parse_Or.call(parse_And.call(parse_Iff.call(parse_Atom)))),inp)
15
+ end
16
+
17
+ # absorbs the list of variables allowing the convention of
18
+ # repeated quantifiers
19
+ def parse_quant(ifn, afn, vs, qcon, x, inp)
20
+ fail(ArgumentError, 'Body of quantified term expected') if inp == []
21
+ head = inp.first
22
+ rest = inp[1..-1]
23
+ ast, rest = if head == '.' then parse_formula(ifn, afn, vs, rest) else parse_quant(ifn, afn, vs.push(head), qcon, head, rest) end
24
+ papply( ->(fm) { qcon.call(x,fm) }, ast, rest )
25
+ end
26
+
27
+ # ifn: restricted parser for infix atoms
28
+ # afn: more general parser for arbitary atoms
29
+ def parse_atomic_formula(ifn, afn, vs, inp)
30
+ fail(ArgumentError, 'Expected an formula at end of input') if inp == []
31
+ head = inp.first
32
+ rest = inp[1..-1]
33
+ case head
34
+ when "false" then return False.new, rest
35
+ when "true" then return True.new, rest
36
+ when "("
37
+ begin
38
+ ifn.call(vs, inp)
39
+ rescue
40
+ parse_bracketed(method(:parse_formula).curry.call(ifn, afn, vs), ")", rest)
41
+ end
42
+ when "~"
43
+ ast, rest = parse_atomic_formula(ifn, afn, vs, rest)
44
+ papply(->(p) { Not.new(p) }, ast, rest)
45
+ when "forall" then
46
+ parse_quant(ifn, afn, vs.push(rest[0]), ->(x,p) { Forall.new(x,p) }, rest[0], rest[1..-1])
47
+ when "exists" then parse_quant(ifn, afn, vs.push(rest[0]), ->(x,p) { Exists.new(x,p) }, rest[0], rest[1..-1])
48
+ else afn.call(vs, inp)
49
+ end
50
+ end
51
+
52
+ def parse_infix_atom(vs, inp)
53
+ tm, rest = TermParser.parse_term(vs, inp)
54
+ if rest != [] && ["=", '<', '<=', '>', ">="].include?(rest.first)
55
+ ast, ost = TermParser.parse_term(vs, rest[1..-1])
56
+ papply( ->(tm_) {Atom.new(Relation.new(rest[0], [tm, tm_])) }, ast, ost )
57
+ else fail ParserError, 'calculated' # Excepion erfinden
58
+ end
59
+ end
60
+
61
+ def parse(inp)
62
+ make_parser(method(:parse_formula).curry.call(method(:parse_infix_atom),
63
+ TermParser.method(:parse_atom), []), inp)
64
+ end
65
+
66
+ end
67
+ end
68
+ end
69
+
70
+ class String
71
+ def to_formula
72
+ Satre::FormulaParser.parse(self)
73
+ end
74
+ end