satre 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +4 -1
- data/README.md +9 -7
- data/bin/exercise +64 -8
- data/lib/satre.rb +1 -3
- data/lib/satre/errors.rb +3 -0
- data/lib/satre/errors/argument_error.rb +2 -0
- data/lib/satre/errors/expression_error.rb +2 -0
- data/lib/satre/errors/parser_error.rb +2 -0
- data/lib/satre/formula.rb +3 -9
- data/lib/satre/formula/first_order_logic.rb +4 -0
- data/lib/satre/formula/first_order_logic/exists.rb +32 -0
- data/lib/satre/formula/first_order_logic/fol_formula.rb +35 -0
- data/lib/satre/formula/first_order_logic/forall.rb +32 -0
- data/lib/satre/formula/first_order_logic/relation.rb +38 -0
- data/lib/satre/formula/formula.rb +1 -13
- data/lib/satre/formula/propositional_logic.rb +9 -0
- data/lib/satre/formula/propositional_logic/and.rb +37 -0
- data/lib/satre/formula/propositional_logic/atom.rb +44 -0
- data/lib/satre/formula/propositional_logic/entails.rb +42 -0
- data/lib/satre/formula/{false.rb → propositional_logic/false.rb} +9 -2
- data/lib/satre/formula/propositional_logic/iff.rb +38 -0
- data/lib/satre/formula/propositional_logic/imp.rb +38 -0
- data/lib/satre/formula/propositional_logic/not.rb +34 -0
- data/lib/satre/formula/propositional_logic/or.rb +37 -0
- data/lib/satre/formula/{true.rb → propositional_logic/true.rb} +11 -2
- data/lib/satre/formula/term.rb +3 -0
- data/lib/satre/formula/term/function.rb +33 -0
- data/lib/satre/formula/term/term.rb +6 -0
- data/lib/satre/formula/term/variable.rb +24 -0
- data/lib/satre/parser.rb +4 -15
- data/lib/satre/parser/formula_parser.rb +74 -0
- data/lib/satre/parser/lexer.rb +55 -0
- data/lib/satre/parser/parser.rb +80 -0
- data/lib/satre/parser/term_parser.rb +85 -0
- data/lib/satre/version.rb +1 -1
- data/satre.gemspec +1 -0
- metadata +43 -20
- data/lib/satre/expression.rb +0 -6
- data/lib/satre/expression/add.rb +0 -26
- data/lib/satre/expression/const.rb +0 -23
- data/lib/satre/expression/expression.rb +0 -22
- data/lib/satre/expression/expression_parser.rb +0 -56
- data/lib/satre/expression/mul.rb +0 -30
- data/lib/satre/expression/var.rb +0 -14
- data/lib/satre/formula/and.rb +0 -26
- data/lib/satre/formula/atom.rb +0 -28
- data/lib/satre/formula/entails.rb +0 -29
- data/lib/satre/formula/iff.rb +0 -27
- data/lib/satre/formula/imp.rb +0 -25
- data/lib/satre/formula/not.rb +0 -22
- data/lib/satre/formula/or.rb +0 -25
- data/lib/satre/formula_parser.rb +0 -92
- data/lib/satre/lexer.rb +0 -47
@@ -0,0 +1,55 @@
|
|
1
|
+
module Satre
|
2
|
+
class Lexer
|
3
|
+
class << self
|
4
|
+
def matches
|
5
|
+
lambda { |pattern, str| str.split("").all? { |c| pattern.include? c } }
|
6
|
+
end
|
7
|
+
|
8
|
+
def matches(pattern, str)
|
9
|
+
str.split("").all? { |c| pattern.include? c }
|
10
|
+
end
|
11
|
+
|
12
|
+
def space?(str)
|
13
|
+
matches(" \t\n\r", str)
|
14
|
+
end
|
15
|
+
|
16
|
+
def punctuation?(str)
|
17
|
+
matches("()[]{}", str)
|
18
|
+
end
|
19
|
+
|
20
|
+
def symbolic?(str)
|
21
|
+
matches("~`!@#%$^&*-+<=>\\/|", str)
|
22
|
+
end
|
23
|
+
|
24
|
+
def numeric?(str)
|
25
|
+
matches("0123456789", str)
|
26
|
+
end
|
27
|
+
|
28
|
+
def alpanumeric?(str)
|
29
|
+
matches("abcdefghijklmnopqrstuvwxyz_'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", str)
|
30
|
+
end
|
31
|
+
|
32
|
+
def refute(*)
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
def lexwhile(prop, inp)
|
37
|
+
tokl = inp.split("").take_while { |c| self.send(prop, c) }.inject(:+)
|
38
|
+
tokl = "" if tokl.nil?
|
39
|
+
return tokl, inp[tokl.length..-1]
|
40
|
+
end
|
41
|
+
|
42
|
+
def lex(inp)
|
43
|
+
inp, rest = self.lexwhile(:space?, inp)
|
44
|
+
return [] if rest == "" || rest.nil?
|
45
|
+
c = rest[0]
|
46
|
+
cs = rest[1..-1]
|
47
|
+
prop = if self.alpanumeric?(c) then :alpanumeric?
|
48
|
+
elsif self.symbolic?(c) then :symbolic?
|
49
|
+
else :refute end
|
50
|
+
toktl, rest = self.lexwhile(prop, cs)
|
51
|
+
[c+toktl] + lex(rest)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'satre/parser/lexer'
|
2
|
+
require 'satre/formula'
|
3
|
+
require 'satre/errors'
|
4
|
+
|
5
|
+
module Satre
|
6
|
+
class Parser
|
7
|
+
class << self
|
8
|
+
def make_parser(parsing_function, input)
|
9
|
+
expr, rest = parsing_function.call(Lexer.lex(input))
|
10
|
+
fail(ParserError, "Unparsed input: #{rest}") unless rest == []
|
11
|
+
return expr
|
12
|
+
end
|
13
|
+
|
14
|
+
def is_const_name(string)
|
15
|
+
string.split('').all? { |c| Lexer.numeric?(c) } || string.nil?
|
16
|
+
end
|
17
|
+
|
18
|
+
###
|
19
|
+
# Generic parsing function
|
20
|
+
# sof: function -> takes the current input and combines it in some way with the items arravied at so far
|
21
|
+
# opupdate: function -> modifies the function appropriately when an new item is parsed.
|
22
|
+
###
|
23
|
+
def parse_ginfix(opsym, opupdate, sof, subparser, inp)
|
24
|
+
e1, inp1 = subparser.call(inp)
|
25
|
+
if inp1 != [] && inp1[0] == opsym
|
26
|
+
parse_ginfix(opsym, opupdate, opupdate.curry.call(sof, e1), subparser, inp1[1..-1])
|
27
|
+
else
|
28
|
+
return sof.call(e1), inp1
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# parse a list of items and combine them in a left associated manner
|
33
|
+
# returns a lambda
|
34
|
+
def parse_left_infix(opsym, opcon)
|
35
|
+
opupdate = ->(f, e1 ,e2) {opcon.call(f.call(e1), e2) }
|
36
|
+
sof = ->(x) { x }
|
37
|
+
method(:parse_ginfix).curry.call(opsym, opupdate, sof)
|
38
|
+
end
|
39
|
+
|
40
|
+
# parse a list of items and combine them in a right associated manner
|
41
|
+
# returns a lambda
|
42
|
+
def parse_right_infix(opsym, opcon)
|
43
|
+
opupdate = ->(f,e1,e2) { f.call(opcon.call(e1,e2)) }
|
44
|
+
sof = ->(x) { x }
|
45
|
+
method(:parse_ginfix).curry.call(opsym, opupdate, sof)
|
46
|
+
end
|
47
|
+
|
48
|
+
# parse a list of items and collect them in a list
|
49
|
+
def parse_list(opsym)
|
50
|
+
opupdate = ->(f,e1,e2) { f.call(e1) << e2 }
|
51
|
+
sof = ->(x) { [x] }
|
52
|
+
method(:parse_ginfix).curry.call(opsym, opupdate, sof)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Applies a function to the first element of a pair.
|
56
|
+
# The idea being to modify the returned abstract syntax tree while leaving
|
57
|
+
# the `unparsed input` alone
|
58
|
+
def papply(f, ast, rest)
|
59
|
+
return f.call(ast), rest
|
60
|
+
end
|
61
|
+
|
62
|
+
# Checks if the head of a list (typically the list of unparsed input)
|
63
|
+
# is some particular item, but also first checks that the list is nonempty
|
64
|
+
# before looking at its head
|
65
|
+
def nextin(inp, tok)
|
66
|
+
inp != [] && inp.first == tok
|
67
|
+
end
|
68
|
+
|
69
|
+
# Deals with the common situation of syntastic items enclosed in brackets
|
70
|
+
# It simply calls the subparser and then checks and eliminates
|
71
|
+
# the closing bracket. In principle, the therminating character can be anything,
|
72
|
+
# so this function could equally be used for other purposes.
|
73
|
+
def parse_bracketed(subparser, closing_character, inp)
|
74
|
+
ast, rest = subparser.call(inp)
|
75
|
+
return ast, rest[1..-1] if nextin(rest, closing_character)
|
76
|
+
fail(ExpressionError, "Expected closing character '#{closing_character}'")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'satre/parser/parser'
|
2
|
+
|
3
|
+
module Satre
|
4
|
+
class TermParser < Parser
|
5
|
+
class << self
|
6
|
+
|
7
|
+
# vs: set of bounds variables in the current scope
|
8
|
+
# inp: current input
|
9
|
+
def parse_atomic_term(vs, inp)
|
10
|
+
fail(ArgumentError, 'Term expected') if inp == []
|
11
|
+
head = inp.first
|
12
|
+
rest = inp[1..-1]
|
13
|
+
if head == '(' then parse_bracketed(method(:parse_term).curry.call(vs), ")", rest)
|
14
|
+
elsif head == '-' then papply( ->(t) { Function.new("-", [t])}, method(:parse_atomic_term).curry.call(vs), rest )
|
15
|
+
elsif Lexer.alpanumeric?(head) && rest[0] == '('
|
16
|
+
if rest[1] == ')'
|
17
|
+
papply( -> { Function.new(head, []) }, rest[2..-1])
|
18
|
+
else
|
19
|
+
ast, rest = parse_bracketed(parse_list(',').call(method(:parse_term).curry.call(vs)) , ')', rest[1..-1])
|
20
|
+
papply(->(args) { Function.new(head, args) }, ast, rest)
|
21
|
+
end
|
22
|
+
else
|
23
|
+
a = if is_const_name(head) && !vs.include?(head) then Function.new(head,[]) else Variable.new(head) end
|
24
|
+
return a, rest
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# build up parsing of general terms via parsing of the various infix operations
|
29
|
+
def parse_term(vs, inp)
|
30
|
+
colon = parse_right_infix("::", ->(e1,e2) { Function.new('::', [e1, e2]) })
|
31
|
+
plus = parse_right_infix("+", ->(e1,e2) { Function.new('+', [e1, e2]) })
|
32
|
+
minus = parse_left_infix("-", ->(e1,e2) { Function.new('-', [e1, e2]) })
|
33
|
+
mul = parse_right_infix("*", ->(e1,e2) { Function.new('*', [e1, e2]) })
|
34
|
+
div = parse_left_infix("/", ->(e1,e2) { Function.new('/', [e1, e2]) })
|
35
|
+
power = parse_left_infix("^", ->(e1,e2) { Function.new('^', [e1, e2]) })
|
36
|
+
atom = method(:parse_atomic_term).curry.call(vs)
|
37
|
+
colon.call(plus.call(minus.call(mul.call(div.call(power.call(atom))))), inp)
|
38
|
+
end
|
39
|
+
|
40
|
+
def is_const_name(string)
|
41
|
+
string.split('').all? { |c| Lexer.numeric?(c) } || string.nil?
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def parse_atom(vs, inp)
|
46
|
+
FormulaParser.parse_infix_atom(vs, inp)
|
47
|
+
rescue ParserError
|
48
|
+
head = inp.first
|
49
|
+
rest = inp[1..-1]
|
50
|
+
if rest[0] == '('
|
51
|
+
rest = rest[1..-1]
|
52
|
+
if rest[0] == ')'
|
53
|
+
return Atom.new(Relation.new(head, [])), rest[1..-1]
|
54
|
+
else
|
55
|
+
ast, rest = parse_bracketed(parse_list(',').call(method(:parse_term).curry.call(vs)),')', rest)
|
56
|
+
papply( ->(args) { Atom.new(Relation.new(head,args))}, ast, rest)
|
57
|
+
end
|
58
|
+
else
|
59
|
+
return Atom.new(Relation.new(head, [])), rest if head != '('
|
60
|
+
fail(ParserError, 'Parse Atom')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse_formula(ifn, afn, vs, inp)
|
65
|
+
parse_Entails = parse_right_infix("|=", ->(p,q) { Entails.new(p,q)})
|
66
|
+
parse_Iff = parse_right_infix("<=>", ->(p,q) { Iff.new(p,q)})
|
67
|
+
parse_Imp = parse_right_infix("==>", ->(p,q) { Imp.new(p,q)})
|
68
|
+
parse_Or = parse_right_infix("\\/", ->(p,q) { Or.new(p,q)})
|
69
|
+
parse_And = parse_right_infix("/\\", ->(p,q) { And.new(p,q)})
|
70
|
+
parse_Atom = method(:parse_atomic_formula).curry.call(ifn, afn, vs)
|
71
|
+
parse_Entails.call(parse_Imp.call(parse_Or.call(parse_And.call(parse_Iff.call(parse_Atom)))),inp)
|
72
|
+
end
|
73
|
+
|
74
|
+
def parse(inp)
|
75
|
+
make_parser(method(:parse_term).curry.call([]), inp)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class String
|
82
|
+
def to_term
|
83
|
+
Satre::TermParser.parse(self)
|
84
|
+
end
|
85
|
+
end
|
data/lib/satre/version.rb
CHANGED
data/satre.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: satre
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roman C. Podolski
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-01-
|
11
|
+
date: 2016-01-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: terminal-table
|
@@ -164,6 +164,20 @@ dependencies:
|
|
164
164
|
- - ">="
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: codeclimate-test-reporter
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
167
181
|
description: "\n I think therefore I am.\n \n Satre is a library for proportional
|
168
182
|
and first order logic.\n It was inspired by the book 'Handbook of practical logic
|
169
183
|
and automated reasoning' by Harrison, J (2009).\n \n This project originated at
|
@@ -188,27 +202,36 @@ files:
|
|
188
202
|
- bin/exercise
|
189
203
|
- bin/setup
|
190
204
|
- lib/satre.rb
|
191
|
-
- lib/satre/
|
192
|
-
- lib/satre/
|
193
|
-
- lib/satre/
|
194
|
-
- lib/satre/
|
195
|
-
- lib/satre/expression/expression_parser.rb
|
196
|
-
- lib/satre/expression/mul.rb
|
197
|
-
- lib/satre/expression/var.rb
|
205
|
+
- lib/satre/errors.rb
|
206
|
+
- lib/satre/errors/argument_error.rb
|
207
|
+
- lib/satre/errors/expression_error.rb
|
208
|
+
- lib/satre/errors/parser_error.rb
|
198
209
|
- lib/satre/formula.rb
|
199
|
-
- lib/satre/formula/
|
200
|
-
- lib/satre/formula/
|
201
|
-
- lib/satre/formula/
|
202
|
-
- lib/satre/formula/
|
210
|
+
- lib/satre/formula/first_order_logic.rb
|
211
|
+
- lib/satre/formula/first_order_logic/exists.rb
|
212
|
+
- lib/satre/formula/first_order_logic/fol_formula.rb
|
213
|
+
- lib/satre/formula/first_order_logic/forall.rb
|
214
|
+
- lib/satre/formula/first_order_logic/relation.rb
|
203
215
|
- lib/satre/formula/formula.rb
|
204
|
-
- lib/satre/formula/
|
205
|
-
- lib/satre/formula/
|
206
|
-
- lib/satre/formula/
|
207
|
-
- lib/satre/formula/
|
208
|
-
- lib/satre/formula/
|
209
|
-
- lib/satre/
|
210
|
-
- lib/satre/
|
216
|
+
- lib/satre/formula/propositional_logic.rb
|
217
|
+
- lib/satre/formula/propositional_logic/and.rb
|
218
|
+
- lib/satre/formula/propositional_logic/atom.rb
|
219
|
+
- lib/satre/formula/propositional_logic/entails.rb
|
220
|
+
- lib/satre/formula/propositional_logic/false.rb
|
221
|
+
- lib/satre/formula/propositional_logic/iff.rb
|
222
|
+
- lib/satre/formula/propositional_logic/imp.rb
|
223
|
+
- lib/satre/formula/propositional_logic/not.rb
|
224
|
+
- lib/satre/formula/propositional_logic/or.rb
|
225
|
+
- lib/satre/formula/propositional_logic/true.rb
|
226
|
+
- lib/satre/formula/term.rb
|
227
|
+
- lib/satre/formula/term/function.rb
|
228
|
+
- lib/satre/formula/term/term.rb
|
229
|
+
- lib/satre/formula/term/variable.rb
|
211
230
|
- lib/satre/parser.rb
|
231
|
+
- lib/satre/parser/formula_parser.rb
|
232
|
+
- lib/satre/parser/lexer.rb
|
233
|
+
- lib/satre/parser/parser.rb
|
234
|
+
- lib/satre/parser/term_parser.rb
|
212
235
|
- lib/satre/version.rb
|
213
236
|
- satre.gemspec
|
214
237
|
homepage: http://in.tum.de
|
data/lib/satre/expression.rb
DELETED
data/lib/satre/expression/add.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
require 'satre/expression'
|
2
|
-
|
3
|
-
# A parster for propositional statements
|
4
|
-
# and simple mathematical expressions
|
5
|
-
module Satre
|
6
|
-
class Add < Expression
|
7
|
-
attr_reader :p
|
8
|
-
attr_reader :q
|
9
|
-
|
10
|
-
def initialize(p,q)
|
11
|
-
fail(ArgumentError, 'p must be a expression') unless p.is_a?(Expression)
|
12
|
-
fail(ArgumentError, 'q must be a expression') unless p.is_a?(Expression)
|
13
|
-
@p = p.dup.freeze
|
14
|
-
@q = q.dup.freeze
|
15
|
-
super "Add (#{@p},#{@q})"
|
16
|
-
end
|
17
|
-
|
18
|
-
def eval
|
19
|
-
p.eval + q.eval
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.parse(e)
|
23
|
-
fail 'not yet implemented'
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
require 'satre/expression'
|
2
|
-
|
3
|
-
# A parster for propositional statements
|
4
|
-
# and simple mathematical expressions
|
5
|
-
module Satre
|
6
|
-
class Const < Expression
|
7
|
-
attr_reader :i
|
8
|
-
|
9
|
-
def initialize(i)
|
10
|
-
ArgumentError 'argument must be an integer' unless i.is_a? Integer
|
11
|
-
@i = i
|
12
|
-
super "Const #{i}"
|
13
|
-
end
|
14
|
-
|
15
|
-
def eval
|
16
|
-
i
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.parse(e)
|
20
|
-
fail 'not yet implemented'
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module Satre
|
2
|
-
class Expression
|
3
|
-
attr_reader :base
|
4
|
-
|
5
|
-
def initialize(base)
|
6
|
-
fail(ArgumentError, 'argument must be a string') unless base.is_a?(String)
|
7
|
-
@base = base.dup.freeze
|
8
|
-
end
|
9
|
-
|
10
|
-
def to_s
|
11
|
-
base
|
12
|
-
end
|
13
|
-
|
14
|
-
def eval # abstract method
|
15
|
-
fail 'abstract'
|
16
|
-
end
|
17
|
-
|
18
|
-
def simplify
|
19
|
-
fail 'not yet implemented'
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
require 'satre/parser'
|
2
|
-
|
3
|
-
module Satre
|
4
|
-
class ExpressionParser < Parser
|
5
|
-
class << self
|
6
|
-
def parse_expression
|
7
|
-
lambda do |inp|
|
8
|
-
e1, i1 = parse_product(inp)
|
9
|
-
if i1[0] == '+'
|
10
|
-
e2, i2 = parse_expression.call(i1.drop(1))
|
11
|
-
return Add.new(e1, e2), i2
|
12
|
-
end
|
13
|
-
return e1, i1
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def parse_product(inp)
|
18
|
-
e1, i1 = parse_atom(inp)
|
19
|
-
if i1[0] == '*'
|
20
|
-
e2,i2 = parse_expression.call(i1.drop(1))
|
21
|
-
return Mul.new(e1, e2), i2
|
22
|
-
end
|
23
|
-
return e1, i1
|
24
|
-
end
|
25
|
-
|
26
|
-
def parse_atom(inp)
|
27
|
-
fail(ArgumentError, 'Expected an expression at end of input') if inp == []
|
28
|
-
if inp[0] == '('
|
29
|
-
e2, i2 = parse_expression.call(inp.drop(1))
|
30
|
-
if i2[0] == ')'
|
31
|
-
return e2, i2.drop(1)
|
32
|
-
else
|
33
|
-
fail(ExpressionError, 'Expected closing bracket')
|
34
|
-
end
|
35
|
-
else
|
36
|
-
if Lexer.numeric?.call(inp[0])
|
37
|
-
return Const.new(inp[0].to_i), inp.drop(1)
|
38
|
-
else
|
39
|
-
return Var.new(inp[0]), inp.drop(1)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def parse(inp)
|
45
|
-
make_parser.curry.call(parse_expression).call(inp)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
class String
|
53
|
-
def to_expression
|
54
|
-
Satre::ExpressionParser.parse(self)
|
55
|
-
end
|
56
|
-
end
|