rparsec2 1.1.0 → 1.2.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.
- checksums.yaml +4 -4
- data/lib/rparsec/context.rb +80 -78
- data/lib/rparsec/error.rb +21 -18
- data/lib/rparsec/expressions.rb +75 -163
- data/lib/rparsec/functor_mixin.rb +102 -0
- data/lib/rparsec/functors.rb +151 -255
- data/lib/rparsec/id_monad.rb +13 -11
- data/lib/rparsec/keywords.rb +95 -93
- data/lib/rparsec/locator.rb +27 -25
- data/lib/rparsec/misc.rb +14 -97
- data/lib/rparsec/monad.rb +53 -50
- data/lib/rparsec/operator_table.rb +89 -0
- data/lib/rparsec/operators.rb +85 -81
- data/lib/rparsec/parser.rb +397 -819
- data/lib/rparsec/parser_monad.rb +18 -16
- data/lib/rparsec/parsers.rb +922 -499
- data/lib/rparsec/token.rb +32 -30
- data/lib/rparsec.rb +7 -4
- metadata +11 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4561cdd86a68228d3d19811f6d8cf77a2664c21c61fac3b368035d6be7d55df3
|
4
|
+
data.tar.gz: bc34dfa71df4b91c505b6a27d5d3b23c3f6acc6c2542d77cebad140d78b670a9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a9731ba718453a5814d896bccc2221b7473c577a16a62b710fe4e723225ea616c3067022e7e7cd902d217d290c9f47553184de4197a0421dd2b3d96418f6ccad
|
7
|
+
data.tar.gz: 89b2f4f0678b636570d6eb9d3b7ac0ac83555c64036a3ae9031c74df068a7603d028770bda66960c0933fa517a4c2b63249b772b18e4abae9f7b0523b3d8e49b
|
data/lib/rparsec/context.rb
CHANGED
@@ -1,86 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'strscan'
|
2
4
|
|
3
5
|
module RParsec
|
4
6
|
|
5
|
-
class ParseContext
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(src, index=0, error=nil)
|
10
|
-
@src, @index, @error = src, index, error
|
11
|
-
@scanner = nil
|
12
|
-
end
|
7
|
+
class ParseContext # :nodoc:
|
8
|
+
attr_reader :error, :src, :index, :result
|
9
|
+
attr_writer :error, :index, :result
|
13
10
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
11
|
+
def initialize(src, index=0, error=nil)
|
12
|
+
@src, @index, @error = src, index, error
|
13
|
+
@scanner = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def scanner
|
17
|
+
@scanner = StringScanner.new(src) if @scanner.nil?
|
18
|
+
@scanner.pos = @index
|
19
|
+
@scanner
|
20
|
+
end
|
21
|
+
|
22
|
+
def prepare_error
|
23
|
+
@error.flatten! if @error.kind_of?(Array)
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_msg
|
27
|
+
return '' if @error.nil?
|
28
|
+
return @error.msg unless @error.kind_of?(Array)
|
29
|
+
@error.map { |e| e.msg }.join(' or ')
|
30
|
+
end
|
31
|
+
|
32
|
+
def error_input
|
33
|
+
return nil if @error.nil?
|
34
|
+
err = @error
|
35
|
+
err = err.last if err.kind_of? Array
|
36
|
+
err.input
|
37
|
+
end
|
38
|
+
|
39
|
+
def reset_error
|
40
|
+
@error = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def current
|
44
|
+
@src[@index]
|
45
|
+
end
|
46
|
+
|
47
|
+
def eof
|
48
|
+
@index >= @src.length
|
49
|
+
end
|
50
|
+
|
51
|
+
def available
|
52
|
+
@src.length - @index
|
53
|
+
end
|
54
|
+
|
55
|
+
def peek i
|
56
|
+
@src[@index + i]
|
57
|
+
end
|
58
|
+
|
59
|
+
def next
|
60
|
+
@index += 1
|
61
|
+
end
|
62
|
+
|
63
|
+
def advance n
|
64
|
+
@index += n
|
65
|
+
end
|
66
|
+
|
67
|
+
def retn(val)
|
68
|
+
@result = val
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
72
|
+
def failure(msg=nil)
|
73
|
+
@error = Failure.new(@index, get_current_input, msg)
|
74
|
+
return false
|
75
|
+
end
|
76
|
+
|
77
|
+
def expecting(expected=nil)
|
78
|
+
@error = Expected.new(@index, get_current_input, expected)
|
79
|
+
return false
|
80
|
+
end
|
81
|
+
|
82
|
+
def get_current_input
|
83
|
+
return nil if eof
|
84
|
+
current
|
85
|
+
end
|
83
86
|
end
|
84
|
-
end
|
85
87
|
|
86
88
|
end # module
|
data/lib/rparsec/error.rb
CHANGED
@@ -1,28 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rparsec/misc'
|
2
4
|
|
3
5
|
module RParsec
|
4
6
|
|
5
|
-
class ParserException < StandardError
|
6
|
-
|
7
|
-
|
8
|
-
end
|
9
|
-
class Failure
|
10
|
-
def initialize(ind, input, message = nil)
|
11
|
-
@index, @input, @msg = ind, input, message
|
7
|
+
class ParserException < StandardError # :nodoc:
|
8
|
+
extend DefHelper
|
9
|
+
def_readable :index
|
12
10
|
end
|
13
11
|
|
14
|
-
|
15
|
-
|
12
|
+
class Failure # :nodoc:
|
13
|
+
def initialize(ind, input, message = nil)
|
14
|
+
@index, @input, @msg = ind, input, message
|
15
|
+
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
end
|
17
|
+
attr_reader :index, :input
|
18
|
+
attr_writer :index
|
20
19
|
|
21
|
-
|
22
|
-
|
20
|
+
def msg
|
21
|
+
return @msg.to_s
|
22
|
+
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
Precedence = 100
|
25
|
+
end
|
26
|
+
|
27
|
+
class Expected < Failure # :nodoc:
|
28
|
+
Precedence = 100
|
29
|
+
end
|
27
30
|
|
28
|
-
end # module
|
31
|
+
end # module
|
data/lib/rparsec/expressions.rb
CHANGED
@@ -1,186 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rparsec/parser'
|
2
4
|
|
3
5
|
module RParsec
|
4
6
|
|
5
|
-
Associativities = [:prefix, :postfix, :infixn, :infixr, :infixl]
|
6
|
-
#
|
7
|
-
# This class holds information about operator precedences
|
8
|
-
# and associativities.
|
9
|
-
# prefix, postfix, infixl, infixr, infixn can be called
|
10
|
-
# to register operators.
|
11
|
-
#
|
12
|
-
class OperatorTable
|
13
|
-
#
|
14
|
-
# operators attribute is used internally. Do not access it.
|
15
|
-
#
|
16
|
-
attr_reader :operators
|
17
|
-
|
18
|
-
#
|
19
|
-
# Re-initialize the operator table. Internal use only.
|
20
|
-
#
|
21
|
-
def reinit
|
22
|
-
@operators = []
|
23
|
-
end
|
24
|
-
|
25
|
-
#
|
26
|
-
# To create an OperatorTable instance.
|
27
|
-
# If a block is given, it is invoked to do post-instantiation.
|
28
|
-
# For example:
|
29
|
-
#
|
30
|
-
# OperatorTable.new do |tbl|
|
31
|
-
# tbl.infixl(char(?+) >> Plus, 10)
|
32
|
-
# tbl.infixl(char(?-) >> Minus, 10)
|
33
|
-
# tbl.infixl(char(?*) >> Mul, 20)
|
34
|
-
# tbl.infixl(char(?/) >> Div, 20)
|
35
|
-
# tbl.prefix(char(?-) >> Neg, 50)
|
36
|
-
# end
|
37
|
-
#
|
38
|
-
def self.new
|
39
|
-
this = allocate
|
40
|
-
this.reinit
|
41
|
-
if block_given?
|
42
|
-
yield this
|
43
|
-
end
|
44
|
-
this
|
45
|
-
end
|
46
|
-
|
47
|
-
#
|
48
|
-
# Defines a prefix operator that returns a unary Proc object with a precedence associated.
|
49
|
-
# Returns self.
|
50
|
-
#
|
51
|
-
def prefix(op, precedence)
|
52
|
-
add(:prefix, op, precedence)
|
53
|
-
end
|
54
|
-
|
55
|
-
#
|
56
|
-
# Defines a postfix operator that returns a unary Proc object with a precedence associated.
|
57
|
-
# Returns self.
|
58
|
-
#
|
59
|
-
def postfix(op, precedence)
|
60
|
-
add(:postfix, op, precedence)
|
61
|
-
end
|
62
|
-
|
63
|
-
#
|
64
|
-
# Defines a left associative infix operator that returns a binary Proc object with a precedence
|
65
|
-
# associated. Returns self.
|
66
|
-
#
|
67
|
-
def infixl(op, precedence)
|
68
|
-
add(:infixl, op, precedence)
|
69
|
-
end
|
70
|
-
|
71
|
-
#
|
72
|
-
# Defines a right associative infix operator that returns a binary Proc object with a precedence
|
73
|
-
# associated. Returns self.
|
7
|
+
Associativities = [:prefix, :postfix, :infixn, :infixr, :infixl]
|
74
8
|
#
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
9
|
+
# This module helps build an expression parser
|
10
|
+
# using an OperatorTable instance and a parser
|
11
|
+
# that parses the term expression.
|
79
12
|
#
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
def infixn(op, precedence)
|
84
|
-
add(:infixn, op, precedence)
|
85
|
-
end
|
86
|
-
|
87
|
-
private
|
13
|
+
module Expressions
|
14
|
+
class << self
|
15
|
+
private
|
88
16
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
end
|
94
|
-
|
95
|
-
#
|
96
|
-
# This module helps build an expression parser
|
97
|
-
# using an OperatorTable instance and a parser
|
98
|
-
# that parses the term expression.
|
99
|
-
#
|
100
|
-
module Expressions
|
101
|
-
class << self
|
102
|
-
private
|
103
|
-
|
104
|
-
def array_to_dict arr
|
105
|
-
result = {}
|
106
|
-
arr.each_with_index do |key, i|
|
107
|
-
result[key] = i unless result.include? key
|
17
|
+
def array_to_dict arr
|
18
|
+
result = {}
|
19
|
+
arr.each_with_index do |key, i|
|
20
|
+
result[key] = i unless result.include? key
|
21
|
+
end
|
22
|
+
result
|
108
23
|
end
|
109
|
-
result
|
110
24
|
end
|
111
|
-
end
|
112
25
|
|
113
|
-
|
26
|
+
KindPrecedence = array_to_dict Associativities
|
114
27
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
125
|
-
|
126
|
-
class << self
|
127
|
-
private
|
28
|
+
#
|
29
|
+
# build an expression parser using the given +term+ parser and
|
30
|
+
# operator +table+. When +delim+ is specified, patterns
|
31
|
+
# recognized by +delim+ is automatically ignored.
|
32
|
+
#
|
33
|
+
def self.build(term, table, delim = nil)
|
34
|
+
# sort so that higher precedence first.
|
35
|
+
apply_operators(term, prepare_suites(table).sort, delim)
|
36
|
+
end
|
128
37
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
38
|
+
class << self
|
39
|
+
private
|
40
|
+
|
41
|
+
def apply_operators(term, entries, delim)
|
42
|
+
# apply operators stored in [[precedence,associativity],[op...]] starting from beginning.
|
43
|
+
entries.inject(term) do |result, entry|
|
44
|
+
key, ops = *entry
|
45
|
+
_, kind_index = *key
|
46
|
+
op = ops[0]
|
47
|
+
op = Parsers.sum(*ops) if ops.length > 1
|
48
|
+
apply_operator(result, op, Associativities[kind_index], delim)
|
49
|
+
end
|
137
50
|
end
|
138
|
-
end
|
139
51
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
52
|
+
def apply_operator(term, op, kind, delim)
|
53
|
+
term, op = ignore_rest(term, delim), ignore_rest(op, delim)
|
54
|
+
# we could use send here,
|
55
|
+
# but explicit case stmt is more straight forward and less coupled with names.
|
56
|
+
# performance might be another benefit,
|
57
|
+
# though it is not clear whether meta-code is indeed slower than regular ones at all.
|
58
|
+
case kind
|
59
|
+
when :prefix
|
60
|
+
term.prefix(op)
|
61
|
+
when :postfix
|
62
|
+
term.postfix(op)
|
63
|
+
when :infixl
|
64
|
+
term.infixl(op)
|
65
|
+
when :infixr
|
66
|
+
term.infixr(op)
|
67
|
+
when :infixn
|
68
|
+
term.infixn(op)
|
69
|
+
else
|
70
|
+
raise ArgumentError, "unknown associativity: #{kind}"
|
71
|
+
end
|
159
72
|
end
|
160
|
-
end
|
161
73
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
74
|
+
def ignore_rest(parser, delim)
|
75
|
+
return parser if delim.nil?
|
76
|
+
parser << delim
|
77
|
+
end
|
166
78
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
79
|
+
def prepare_suites(table)
|
80
|
+
# create a hash with [precedence, associativity] as key, and op as value.
|
81
|
+
suites = {}
|
82
|
+
table.operators.each do |entry|
|
83
|
+
kind, op, precedence = *entry
|
84
|
+
key = [-precedence, KindPrecedence[kind]]
|
85
|
+
suite = suites[key]
|
86
|
+
if suite.nil?
|
87
|
+
suite = [op]
|
88
|
+
suites[key] = suite
|
89
|
+
else
|
90
|
+
suite << op
|
91
|
+
end
|
179
92
|
end
|
93
|
+
suites
|
180
94
|
end
|
181
|
-
suites
|
182
95
|
end
|
183
96
|
end
|
184
|
-
end
|
185
97
|
|
186
98
|
end # module
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module RParsec
|
2
|
+
#
|
3
|
+
# This module provides instance methods that
|
4
|
+
# manipulate closures in a functional style.
|
5
|
+
# It is typically included in Proc and Method.
|
6
|
+
#
|
7
|
+
module FunctorMixin
|
8
|
+
#
|
9
|
+
# Create a +Proc+, which expects the two parameters in the reverse
|
10
|
+
# order of +self+.
|
11
|
+
#
|
12
|
+
def flip
|
13
|
+
Functors.flip(&self)
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# Create a +Proc+, when called, the parameter is first passed into
|
18
|
+
# +other+, +self+ is called in turn with the return value from
|
19
|
+
# +other+.
|
20
|
+
#
|
21
|
+
def compose(other)
|
22
|
+
Functors.compose(self, other)
|
23
|
+
end
|
24
|
+
|
25
|
+
alias << compose
|
26
|
+
|
27
|
+
#
|
28
|
+
# <tt>a >> b</tt> is equivalent to <tt>b << a</tt>. See also #<<.
|
29
|
+
#
|
30
|
+
def >>(other)
|
31
|
+
other << self
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Create a Proc that's curriable.
|
36
|
+
# When curried, parameters are passed in from left to right.
|
37
|
+
# i.e. closure.curry.call(a).call(b) is quivalent to closure.call(a,b) .
|
38
|
+
# _self_ is encapsulated under the hood to perform the actual
|
39
|
+
# job when currying is done.
|
40
|
+
# _ary_ explicitly specifies the number of parameters to curry.
|
41
|
+
#
|
42
|
+
# *IMPORTANT*
|
43
|
+
# Proc and Method have built-in curry.
|
44
|
+
# but the arity always return -1.
|
45
|
+
# So, curry.reverse_curry does not work as expected.
|
46
|
+
# You need to use the "using FunctorMixin"
|
47
|
+
# See the "functor_test.rb"
|
48
|
+
[Proc, Method].each do |klass|
|
49
|
+
refine klass do
|
50
|
+
def curry(ary = arity)
|
51
|
+
Functors.curry(ary, &self)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Create a +Proc+ that's curriable. When curried, parameters are
|
58
|
+
# passed in from right to left.
|
59
|
+
# i.e. <tt>closure.reverse_curry.call(a).call(b)</tt> is quivalent
|
60
|
+
# to <tt>closure.call(b, a)</tt>. +self+ is encapsulated under
|
61
|
+
# the hood to perform the actual job when currying is done. +ary+
|
62
|
+
# explicitly specifies the number of parameters to curry.
|
63
|
+
#
|
64
|
+
def reverse_curry(ary = arity)
|
65
|
+
Functors.reverse_curry(ary, &self)
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Uncurry a curried closure.
|
70
|
+
#
|
71
|
+
def uncurry
|
72
|
+
Functors.uncurry(&self)
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# Uncurry a reverse curried closure.
|
77
|
+
#
|
78
|
+
def reverse_uncurry
|
79
|
+
Functors.reverse_uncurry(&self)
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# Create a +Proc+, when called, repeatedly call +self+ for +n+
|
84
|
+
# times. The same arguments are passed to each invocation.
|
85
|
+
#
|
86
|
+
def repeat(n)
|
87
|
+
Functors.repeat(n, &self)
|
88
|
+
end
|
89
|
+
#
|
90
|
+
|
91
|
+
# Create a +Proc+, when called, repeatedly call +self+ for +n+
|
92
|
+
# times. At each iteration, return value from the previous
|
93
|
+
# iteration is used as parameter.
|
94
|
+
#
|
95
|
+
def power(n)
|
96
|
+
Functors.power(n, &self)
|
97
|
+
end
|
98
|
+
|
99
|
+
alias ** power
|
100
|
+
alias * repeat
|
101
|
+
end
|
102
|
+
end
|