rparsec2 1.1.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 +7 -0
- data/lib/rparsec/context.rb +86 -0
- data/lib/rparsec/error.rb +28 -0
- data/lib/rparsec/expressions.rb +186 -0
- data/lib/rparsec/functors.rb +282 -0
- data/lib/rparsec/id_monad.rb +17 -0
- data/lib/rparsec/keywords.rb +114 -0
- data/lib/rparsec/locator.rb +40 -0
- data/lib/rparsec/misc.rb +106 -0
- data/lib/rparsec/monad.rb +63 -0
- data/lib/rparsec/operators.rb +106 -0
- data/lib/rparsec/parser.rb +893 -0
- data/lib/rparsec/parser_monad.rb +23 -0
- data/lib/rparsec/parsers.rb +624 -0
- data/lib/rparsec/token.rb +43 -0
- data/lib/rparsec.rb +7 -0
- metadata +59 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5de30f07e3855c6c2168832879138e3341ce79a20272ddf2c92857f47414e1ea
|
4
|
+
data.tar.gz: 74c1ad503c94fe4a75a788d1384161b6f1b34a7e017a0740a0025d24f8a8518f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 26932d06bc2b3ec459ea0b3e22d091ebd1470d277bdac373b075821c97b82b4ab5936bdf4944150f21016cd9632f0a84d0428417f68446c31e33447734035e74
|
7
|
+
data.tar.gz: e776214089f74d14ce7f61d4fde624ca245127d7edf89ca87033fe96b2132ab25b74f4d36a8927a6c0c24145c0722b911334f7e1eb32f08064ed7e6b448fd33f
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
|
3
|
+
module RParsec
|
4
|
+
|
5
|
+
class ParseContext
|
6
|
+
attr_reader :error, :src, :index, :result
|
7
|
+
attr_writer :error, :index, :result
|
8
|
+
|
9
|
+
def initialize(src, index=0, error=nil)
|
10
|
+
@src, @index, @error = src, index, error
|
11
|
+
@scanner = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def scanner
|
15
|
+
@scanner = StringScanner.new(src) if @scanner.nil?
|
16
|
+
@scanner.pos= @index
|
17
|
+
@scanner
|
18
|
+
end
|
19
|
+
|
20
|
+
def prepare_error
|
21
|
+
@error.flatten! if @error.kind_of?(Array)
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_msg
|
25
|
+
return '' if @error.nil?
|
26
|
+
return @error.msg unless @error.kind_of?(Array)
|
27
|
+
@error.map{|e|e.msg}.join(' or ')
|
28
|
+
end
|
29
|
+
|
30
|
+
def error_input
|
31
|
+
return nil if @error.nil?
|
32
|
+
err = @error
|
33
|
+
err = err.last if err.kind_of? Array
|
34
|
+
err.input
|
35
|
+
end
|
36
|
+
|
37
|
+
def reset_error
|
38
|
+
@error = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def current
|
42
|
+
@src[@index]
|
43
|
+
end
|
44
|
+
|
45
|
+
def eof
|
46
|
+
@index >= @src.length
|
47
|
+
end
|
48
|
+
|
49
|
+
def available
|
50
|
+
@src.length - @index
|
51
|
+
end
|
52
|
+
|
53
|
+
def peek i
|
54
|
+
@src[@index+i]
|
55
|
+
end
|
56
|
+
|
57
|
+
def next
|
58
|
+
@index += 1
|
59
|
+
end
|
60
|
+
|
61
|
+
def advance n
|
62
|
+
@index += n
|
63
|
+
end
|
64
|
+
|
65
|
+
def retn(val)
|
66
|
+
@result = val
|
67
|
+
true
|
68
|
+
end
|
69
|
+
|
70
|
+
def failure(msg=nil)
|
71
|
+
@error = Failure.new(@index, get_current_input, msg)
|
72
|
+
return false
|
73
|
+
end
|
74
|
+
|
75
|
+
def expecting(expected=nil)
|
76
|
+
@error = Expected.new(@index, get_current_input, expected)
|
77
|
+
return false
|
78
|
+
end
|
79
|
+
|
80
|
+
def get_current_input
|
81
|
+
return nil if eof
|
82
|
+
current
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end # module
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rparsec/misc'
|
2
|
+
|
3
|
+
module RParsec
|
4
|
+
|
5
|
+
class ParserException < StandardError
|
6
|
+
extend DefHelper
|
7
|
+
def_readable :index
|
8
|
+
end
|
9
|
+
class Failure
|
10
|
+
def initialize(ind, input, message = nil)
|
11
|
+
@index, @input, @msg = ind, input, message
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :index, :input
|
15
|
+
attr_writer :index
|
16
|
+
|
17
|
+
def msg
|
18
|
+
return @msg.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
Precedence = 100
|
22
|
+
end
|
23
|
+
|
24
|
+
class Expected < Failure
|
25
|
+
Precedence = 100
|
26
|
+
end
|
27
|
+
|
28
|
+
end # module
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'rparsec/parser'
|
2
|
+
|
3
|
+
module RParsec
|
4
|
+
|
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.
|
74
|
+
#
|
75
|
+
def infixr(op, precedence)
|
76
|
+
add(:infixr, op, precedence)
|
77
|
+
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# Defines a non-associative infix operator that returns a binary Proc object with a precedence
|
81
|
+
# associated. Returns self.
|
82
|
+
#
|
83
|
+
def infixn(op, precedence)
|
84
|
+
add(:infixn, op, precedence)
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def add(*entry)
|
90
|
+
@operators << entry
|
91
|
+
self
|
92
|
+
end
|
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
|
108
|
+
end
|
109
|
+
result
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
KindPrecedence = array_to_dict Associativities
|
114
|
+
|
115
|
+
#
|
116
|
+
# build an expression parser using the given term parser
|
117
|
+
# and operator table.
|
118
|
+
# When _delim_ is specified, patterns recognized by _delim_
|
119
|
+
# is automatically ignored.
|
120
|
+
#
|
121
|
+
def self.build(term, table, delim = nil)
|
122
|
+
# sort so that higher precedence first.
|
123
|
+
apply_operators(term, prepare_suites(table).sort, delim)
|
124
|
+
end
|
125
|
+
|
126
|
+
class << self
|
127
|
+
private
|
128
|
+
|
129
|
+
def apply_operators(term, entries, delim)
|
130
|
+
# apply operators stored in [[precedence,associativity],[op...]] starting from beginning.
|
131
|
+
entries.inject(term) do |result, entry|
|
132
|
+
key, ops = *entry
|
133
|
+
_, kind_index = *key
|
134
|
+
op = ops[0]
|
135
|
+
op = Parsers.sum(*ops) if ops.length > 1
|
136
|
+
apply_operator(result, op, Associativities[kind_index], delim)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def apply_operator(term, op, kind, delim)
|
141
|
+
term, op = ignore_rest(term, delim), ignore_rest(op, delim)
|
142
|
+
# we could use send here,
|
143
|
+
# but explicit case stmt is more straight forward and less coupled with names.
|
144
|
+
# performance might be another benefit,
|
145
|
+
# though it is not clear whether meta-code is indeed slower than regular ones at all.
|
146
|
+
case kind
|
147
|
+
when :prefix
|
148
|
+
term.prefix(op)
|
149
|
+
when :postfix
|
150
|
+
term.postfix(op)
|
151
|
+
when :infixl
|
152
|
+
term.infixl(op)
|
153
|
+
when :infixr
|
154
|
+
term.infixr(op)
|
155
|
+
when :infixn
|
156
|
+
term.infixn(op)
|
157
|
+
else
|
158
|
+
raise ArgumentError, "unknown associativity: #{kind}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def ignore_rest(parser, delim)
|
163
|
+
return parser if delim.nil?
|
164
|
+
parser << delim
|
165
|
+
end
|
166
|
+
|
167
|
+
def prepare_suites(table)
|
168
|
+
# create a hash with [precedence, associativity] as key, and op as value.
|
169
|
+
suites = {}
|
170
|
+
table.operators.each do |entry|
|
171
|
+
kind, op, precedence = *entry
|
172
|
+
key = [-precedence, KindPrecedence[kind]]
|
173
|
+
suite = suites[key]
|
174
|
+
if suite.nil?
|
175
|
+
suite = [op]
|
176
|
+
suites[key] = suite
|
177
|
+
else
|
178
|
+
suite << op
|
179
|
+
end
|
180
|
+
end
|
181
|
+
suites
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
end # module
|
@@ -0,0 +1,282 @@
|
|
1
|
+
module RParsec
|
2
|
+
|
3
|
+
#
|
4
|
+
# This module provides frequently used functors.
|
5
|
+
#
|
6
|
+
module Functors
|
7
|
+
Id = proc { |x| x }
|
8
|
+
Idn = proc { |*x| x }
|
9
|
+
Neg = proc { |x| -x }
|
10
|
+
Inc = proc { |x| x + 1 }
|
11
|
+
Succ = proc { |x| x.succ }
|
12
|
+
Dec = proc { |x| x - 1 }
|
13
|
+
Plus = proc { |x, y| x + y }
|
14
|
+
Minus = proc { |x, y| x - y }
|
15
|
+
Mul = proc { |x, y| x * y }
|
16
|
+
Div = proc { |x, y| x / y }
|
17
|
+
Mod = proc { |x, y| x % y }
|
18
|
+
Power = proc { |x, y| x**y }
|
19
|
+
Not = proc { |x, _y| !x }
|
20
|
+
And = proc { |x, y| x && y }
|
21
|
+
Or = proc { |x, y| x || y }
|
22
|
+
Xor = proc { |x, y| x ^ y }
|
23
|
+
BitAnd = proc { |x, y| x & y }
|
24
|
+
Union = proc { |x, y| x | y }
|
25
|
+
Match = proc { |x, y| x =~ y }
|
26
|
+
Eq = proc { |x, y| x == y }
|
27
|
+
Ne = proc { |x, y| x != y }
|
28
|
+
Lt = proc { |x, y| x < y }
|
29
|
+
Gt = proc { |x, y| x > y }
|
30
|
+
Le = proc { |x, y| x <= y }
|
31
|
+
Ge = proc { |x, y| x >= y }
|
32
|
+
Compare = proc { |x, y| x <=> y }
|
33
|
+
Call = proc { |x, y| x.call(y) }
|
34
|
+
Feed = proc { |x, y| y.call(x) }
|
35
|
+
Fst = proc { |x, _| x }
|
36
|
+
Snd = proc { |_, x| x }
|
37
|
+
At = proc { |x, y| x[y] }
|
38
|
+
To_a = proc { |x| x.to_a }
|
39
|
+
To_s = proc { |x| x.to_s }
|
40
|
+
To_i = proc { |x| x.to_i }
|
41
|
+
To_sym = proc { |x| x.to_sym }
|
42
|
+
To_f = proc { |x| x.to_f }
|
43
|
+
|
44
|
+
#
|
45
|
+
# Get a Proc, when called, always return the given value.
|
46
|
+
#
|
47
|
+
def const(v)
|
48
|
+
proc { |_| v }
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# Get a Proc, when called, return the nth parameter.
|
53
|
+
#
|
54
|
+
def nth(n)
|
55
|
+
proc { |*args| args[n] }
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# Create a Proc, which expects the two parameters
|
60
|
+
# in the reverse order of _block_.
|
61
|
+
#
|
62
|
+
def flip(&block)
|
63
|
+
proc { |x, y| block.call(y, x) }
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Create a Proc, when called, the parameter is
|
68
|
+
# first passed into _f2_, _f1_ is called in turn
|
69
|
+
# with the return value from _other_.
|
70
|
+
#
|
71
|
+
def compose(f1, f2)
|
72
|
+
proc { |*x| f1.call(f2.call(*x)) }
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# Create a Proc that's curriable.
|
77
|
+
# When curried, parameters are passed in from left to right.
|
78
|
+
# i.e. curry(closure).call(a).call(b) is quivalent to closure.call(a,b) .
|
79
|
+
# _block_ is encapsulated under the hood to perform the actual
|
80
|
+
# job when currying is done.
|
81
|
+
# arity explicitly specifies the number of parameters to curry.
|
82
|
+
#
|
83
|
+
def curry(arity, &block)
|
84
|
+
fail "cannot curry for unknown arity" if arity < 0
|
85
|
+
Functors.make_curry(arity, &block)
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# Create a Proc that's curriable.
|
90
|
+
# When curried, parameters are passed in from right to left.
|
91
|
+
# i.e. reverse_curry(closure).call(a).call(b) is quivalent to closure.call(b,a) .
|
92
|
+
# _block_ is encapsulated under the hood to perform the actual
|
93
|
+
# job when currying is done.
|
94
|
+
# arity explicitly specifies the number of parameters to curry.
|
95
|
+
#
|
96
|
+
def reverse_curry(arity, &block)
|
97
|
+
fail "cannot curry for unknown arity" if arity < 0
|
98
|
+
Functors.make_reverse_curry(arity, &block)
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# Uncurry a curried closure.
|
103
|
+
#
|
104
|
+
def uncurry(&block)
|
105
|
+
return block unless block.arity == 1
|
106
|
+
proc do |*args|
|
107
|
+
result = block
|
108
|
+
args.each do |a|
|
109
|
+
result = result.call(a)
|
110
|
+
end
|
111
|
+
result
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# Uncurry a reverse curried closure.
|
117
|
+
#
|
118
|
+
def reverse_uncurry(&block)
|
119
|
+
return block unless block.arity == 1
|
120
|
+
proc do |*args|
|
121
|
+
result = block
|
122
|
+
args.reverse_each do |a|
|
123
|
+
result = result.call(a)
|
124
|
+
end
|
125
|
+
result
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# Create a Proc, when called,
|
131
|
+
# repeatedly call _block_ for _n_ times.
|
132
|
+
# The same arguments are passed to each invocation.
|
133
|
+
#
|
134
|
+
def repeat(n, &block)
|
135
|
+
proc do |*args|
|
136
|
+
result = nil
|
137
|
+
n.times { result = block.call(*args) }
|
138
|
+
result
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
#
|
143
|
+
# Create a Proc, when called,
|
144
|
+
# repeatedly call _block_ for _n_ times.
|
145
|
+
# At each iteration, return value from the previous iteration
|
146
|
+
# is used as parameter.
|
147
|
+
#
|
148
|
+
def power(n, &block)
|
149
|
+
return const(nil) if n <= 0
|
150
|
+
return block if n == 1
|
151
|
+
proc do |*args|
|
152
|
+
result = block.call(*args)
|
153
|
+
(n - 1).times { result = block.call(result) }
|
154
|
+
result
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
extend self
|
159
|
+
|
160
|
+
def self.make_curry(arity, &block)
|
161
|
+
return block if arity <= 1
|
162
|
+
proc do |x|
|
163
|
+
make_curry(arity - 1) do |*rest|
|
164
|
+
block.call(*rest.insert(0, x))
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def self.make_reverse_curry(arity, &block)
|
170
|
+
return block if arity <= 1
|
171
|
+
proc do |x|
|
172
|
+
make_reverse_curry(arity - 1) do |*rest|
|
173
|
+
block.call(*rest << x)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
#
|
180
|
+
# This module provides instance methods that
|
181
|
+
# manipulate closures in a functional style.
|
182
|
+
# It is typically included in Proc and Method.
|
183
|
+
#
|
184
|
+
module FunctorMixin
|
185
|
+
#
|
186
|
+
# Create a Proc, which expects the two parameters
|
187
|
+
# in the reverse order of _self_.
|
188
|
+
#
|
189
|
+
def flip
|
190
|
+
Functors.flip(&self)
|
191
|
+
end
|
192
|
+
|
193
|
+
#
|
194
|
+
# Create a Proc, when called, the parameter is
|
195
|
+
# first passed into _other_, _self_ is called in turn
|
196
|
+
# with the return value from _other_.
|
197
|
+
#
|
198
|
+
def compose(other)
|
199
|
+
Functors.compose(self, other)
|
200
|
+
end
|
201
|
+
|
202
|
+
alias << compose
|
203
|
+
|
204
|
+
#
|
205
|
+
# a >> b is equivalent to b << a
|
206
|
+
#
|
207
|
+
def >>(other)
|
208
|
+
other << self
|
209
|
+
end
|
210
|
+
|
211
|
+
#
|
212
|
+
# Create a Proc that's curriable.
|
213
|
+
# When curried, parameters are passed in from left to right.
|
214
|
+
# i.e. closure.curry.call(a).call(b) is quivalent to closure.call(a,b) .
|
215
|
+
# _self_ is encapsulated under the hood to perform the actual
|
216
|
+
# job when currying is done.
|
217
|
+
# _ary_ explicitly specifies the number of parameters to curry.
|
218
|
+
#
|
219
|
+
# *IMPORTANT*
|
220
|
+
# Proc and Method have built-in curry.
|
221
|
+
# but the arity always return -1.
|
222
|
+
# So, curry.reverse_curry does not work as expected.
|
223
|
+
# You need to use the "using FunctorMixin"
|
224
|
+
# See the "functor_test.rb"
|
225
|
+
[Proc, Method].each do |klass|
|
226
|
+
refine klass do
|
227
|
+
def curry(ary = arity)
|
228
|
+
Functors.curry(ary, &self)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
#
|
234
|
+
# Create a Proc that's curriable.
|
235
|
+
# When curried, parameters are passed in from right to left.
|
236
|
+
# i.e. closure.reverse_curry.call(a).call(b) is quivalent to closure.call(b,a) .
|
237
|
+
# _self_ is encapsulated under the hood to perform the actual
|
238
|
+
# job when currying is done.
|
239
|
+
# _ary_ explicitly specifies the number of parameters to curry.
|
240
|
+
#
|
241
|
+
def reverse_curry(ary = arity)
|
242
|
+
Functors.reverse_curry(ary, &self)
|
243
|
+
end
|
244
|
+
|
245
|
+
#
|
246
|
+
# Uncurry a curried closure.
|
247
|
+
#
|
248
|
+
def uncurry
|
249
|
+
Functors.uncurry(&self)
|
250
|
+
end
|
251
|
+
|
252
|
+
#
|
253
|
+
# Uncurry a reverse curried closure.
|
254
|
+
#
|
255
|
+
def reverse_uncurry
|
256
|
+
Functors.reverse_uncurry(&self)
|
257
|
+
end
|
258
|
+
|
259
|
+
#
|
260
|
+
# Create a Proc, when called,
|
261
|
+
# repeatedly call _self_ for _n_ times.
|
262
|
+
# The same arguments are passed to each invocation.
|
263
|
+
#
|
264
|
+
def repeat(n)
|
265
|
+
Functors.repeat(n, &self)
|
266
|
+
end
|
267
|
+
#
|
268
|
+
|
269
|
+
# Create a Proc, when called,
|
270
|
+
# repeatedly call _self_ for _n_ times.
|
271
|
+
# At each iteration, return value from the previous iteration
|
272
|
+
# is used as parameter.
|
273
|
+
#
|
274
|
+
def power(n)
|
275
|
+
Functors.power(n, &self)
|
276
|
+
end
|
277
|
+
|
278
|
+
alias ** power
|
279
|
+
alias * repeat
|
280
|
+
end
|
281
|
+
|
282
|
+
end # module
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'rparsec/parser'
|
2
|
+
|
3
|
+
module RParsec
|
4
|
+
|
5
|
+
#
|
6
|
+
# This class helps building lexers and parsers for keywords.
|
7
|
+
#
|
8
|
+
class Keywords
|
9
|
+
extend Parsers
|
10
|
+
|
11
|
+
private_class_method :new
|
12
|
+
|
13
|
+
#
|
14
|
+
# The symbol used to identify a keyword token
|
15
|
+
#
|
16
|
+
attr_reader :keyword_symbol
|
17
|
+
|
18
|
+
#
|
19
|
+
# The lexer that parses all the keywords represented
|
20
|
+
#
|
21
|
+
attr_reader :lexer
|
22
|
+
|
23
|
+
#
|
24
|
+
# Do we lex case sensitively?
|
25
|
+
#
|
26
|
+
def case_sensitive?
|
27
|
+
@case_sensitive
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# To create an instance that lexes the given keywords
|
32
|
+
# case sensitively.
|
33
|
+
# _default_lexer_ is used to lex a token first, the token text is then compared with
|
34
|
+
# the given keywords. If it matches any of the keyword, a keyword token is generated instead
|
35
|
+
# using _keyword_symbol_.
|
36
|
+
# The _block_ parameter, if present, is used to convert the token text to another object
|
37
|
+
# when the token is recognized during grammar parsing phase.
|
38
|
+
#
|
39
|
+
def self.case_sensitive(words, default_lexer = word.token(:word), keyword_symbol = :keyword, &block)
|
40
|
+
new(words, true, default_lexer, keyword_symbol, &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# To create an instance that lexes the given keywords
|
45
|
+
# case insensitively.
|
46
|
+
# _default_lexer_ is used to lex a token first, the token text is then compared with
|
47
|
+
# the given keywords. If it matches any of the keyword, a keyword token is generated instead
|
48
|
+
# using _keyword_symbol_.
|
49
|
+
# The _block_ parameter, if present, is used to convert the token text to another object
|
50
|
+
# when the token is recognized during parsing phase.
|
51
|
+
#
|
52
|
+
def self.case_insensitive(words, default_lexer = word.token(:word), keyword_symbol = :keyword, &block)
|
53
|
+
new(words, false, default_lexer, keyword_symbol, &block)
|
54
|
+
end
|
55
|
+
|
56
|
+
# scanner has to return a string
|
57
|
+
def initialize(words, case_sensitive, default_lexer, keyword_symbol, &block)
|
58
|
+
@default_lexer, @case_sensitive, @keyword_symbol = default_lexer, case_sensitive, keyword_symbol
|
59
|
+
# this guarantees that we have copy of the words array and all the word strings.
|
60
|
+
words = copy_words(words, case_sensitive)
|
61
|
+
@name_map = {}
|
62
|
+
@symbol_map = {}
|
63
|
+
word_map = {}
|
64
|
+
words.each do |w|
|
65
|
+
symbol = "#{keyword_symbol}:#{w}".to_sym
|
66
|
+
word_map[w] = symbol
|
67
|
+
parser = Parsers.token(symbol, &block)
|
68
|
+
@symbol_map["#{w}".to_sym] = parser
|
69
|
+
@name_map[w] = parser
|
70
|
+
end
|
71
|
+
@lexer = make_lexer(default_lexer, word_map)
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# Get the parser that recognizes the token of the given keyword during the parsing phase.
|
76
|
+
#
|
77
|
+
def parser(key)
|
78
|
+
result = nil
|
79
|
+
if key.kind_of? String
|
80
|
+
name = canonical_name(key)
|
81
|
+
result = @name_map[name]
|
82
|
+
else
|
83
|
+
result = @symbol_map[key]
|
84
|
+
end
|
85
|
+
raise ArgumentError, "parser not found for #{key}" if result.nil?
|
86
|
+
result
|
87
|
+
end
|
88
|
+
|
89
|
+
alias [] parser
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def make_lexer(default_lexer, word_map)
|
94
|
+
default_lexer.map do |tok|
|
95
|
+
text, ind = tok.text, tok.index
|
96
|
+
key = canonical_name(text)
|
97
|
+
my_symbol = word_map[key]
|
98
|
+
case when my_symbol.nil? then tok
|
99
|
+
else Token.new(my_symbol, text, ind) end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def canonical_name(name)
|
104
|
+
case when @case_sensitive then name else name.downcase end
|
105
|
+
end
|
106
|
+
|
107
|
+
def copy_words(words, case_sensitive)
|
108
|
+
words.map do |w|
|
109
|
+
case when case_sensitive then w.dup else w.downcase end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
end # module
|