rparsec 0.4.1 → 0.4.2
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.
- data/rparsec.rb +2 -2
- data/rparsec/context.rb +81 -66
- data/rparsec/error.rb +22 -19
- data/rparsec/expressions.rb +179 -136
- data/rparsec/functors.rb +270 -249
- data/rparsec/id_monad.rb +13 -11
- data/rparsec/keywords.rb +101 -89
- data/rparsec/locator.rb +36 -32
- data/rparsec/misc.rb +109 -102
- data/rparsec/monad.rb +57 -51
- data/rparsec/operators.rb +116 -109
- data/rparsec/parser.rb +892 -794
- data/rparsec/parser_monad.rb +19 -16
- data/rparsec/parsers.rb +614 -603
- data/rparsec/token.rb +23 -20
- data/test/src/simple_monad_test.rb +21 -21
- metadata +49 -41
data/rparsec.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
%w{
|
2
|
-
parsers operators keywords expressions
|
1
|
+
%w{
|
2
|
+
parsers operators keywords expressions
|
3
3
|
}.each {|lib| require "rparsec/#{lib}"}
|
data/rparsec/context.rb
CHANGED
@@ -1,66 +1,81 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
class ParseContext
|
4
|
-
attr_reader :error, :src, :index, :result
|
5
|
-
attr_writer :error, :index, :result
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
@scanner
|
13
|
-
@scanner
|
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
|
-
def
|
39
|
-
@src
|
40
|
-
end
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
def
|
51
|
-
@
|
52
|
-
|
53
|
-
|
54
|
-
def
|
55
|
-
@
|
56
|
-
|
57
|
-
|
58
|
-
def
|
59
|
-
@
|
60
|
-
|
61
|
-
|
62
|
-
def
|
63
|
-
|
64
|
-
|
65
|
-
end
|
66
|
-
|
1
|
+
|
2
|
+
|
3
|
+
class ParseContext
|
4
|
+
attr_reader :error, :src, :index, :result
|
5
|
+
attr_writer :error, :index, :result
|
6
|
+
|
7
|
+
def initialize(src, index=0, error=nil)
|
8
|
+
@src, @index, @error = src, index, error
|
9
|
+
end
|
10
|
+
|
11
|
+
def scanner
|
12
|
+
@scanner = StringScanner.new(src) if @scanner.nil?
|
13
|
+
@scanner.pos= @index
|
14
|
+
@scanner
|
15
|
+
end
|
16
|
+
|
17
|
+
def prepare_error
|
18
|
+
@error.flatten! if @error.kind_of?(Array)
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_msg
|
22
|
+
return '' if @error.nil?
|
23
|
+
return @error.msg unless @error.kind_of?(Array)
|
24
|
+
@error.map{|e|e.msg}.join(' or ')
|
25
|
+
end
|
26
|
+
|
27
|
+
def error_input
|
28
|
+
return nil if @error.nil?
|
29
|
+
err = @error
|
30
|
+
err = err.last if err.kind_of? Array
|
31
|
+
err.input
|
32
|
+
end
|
33
|
+
|
34
|
+
def reset_error
|
35
|
+
@error = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def current
|
39
|
+
@src[@index]
|
40
|
+
end
|
41
|
+
|
42
|
+
def eof
|
43
|
+
@index >= @src.length
|
44
|
+
end
|
45
|
+
|
46
|
+
def available
|
47
|
+
@src.length - @index
|
48
|
+
end
|
49
|
+
|
50
|
+
def peek i
|
51
|
+
@src[@index+i]
|
52
|
+
end
|
53
|
+
|
54
|
+
def next
|
55
|
+
@index += 1
|
56
|
+
end
|
57
|
+
|
58
|
+
def advance n
|
59
|
+
@index += n
|
60
|
+
end
|
61
|
+
|
62
|
+
def retn(val)
|
63
|
+
@result = val
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
67
|
+
def failure(msg=nil)
|
68
|
+
@error = Failure.new(@index, get_current_input, msg)
|
69
|
+
return false
|
70
|
+
end
|
71
|
+
|
72
|
+
def expecting(expected=nil)
|
73
|
+
@error = Expected.new(@index, get_current_input, expected)
|
74
|
+
return false
|
75
|
+
end
|
76
|
+
|
77
|
+
def get_current_input
|
78
|
+
return nil if eof
|
79
|
+
current
|
80
|
+
end
|
81
|
+
end
|
data/rparsec/error.rb
CHANGED
@@ -1,20 +1,23 @@
|
|
1
|
-
require 'rparsec/misc'
|
2
|
-
class ParserException < StandardError
|
3
|
-
extend DefHelper
|
4
|
-
def_readable :index
|
5
|
-
end
|
6
|
-
class Failure
|
7
|
-
def initialize(ind, input,
|
8
|
-
@index, @input, @msg = ind, input,
|
9
|
-
end
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
|
19
|
-
|
1
|
+
require 'rparsec/misc'
|
2
|
+
class ParserException < StandardError
|
3
|
+
extend DefHelper
|
4
|
+
def_readable :index
|
5
|
+
end
|
6
|
+
class Failure
|
7
|
+
def initialize(ind, input, message=nil)
|
8
|
+
@index, @input, @msg = ind, input, message
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :index, :input
|
12
|
+
attr_writer :index
|
13
|
+
|
14
|
+
def msg
|
15
|
+
return @msg.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
Precedence = 100
|
19
|
+
end
|
20
|
+
|
21
|
+
class Expected < Failure
|
22
|
+
Precedence = 100
|
20
23
|
end
|
data/rparsec/expressions.rb
CHANGED
@@ -1,137 +1,180 @@
|
|
1
|
-
require 'rparsec/parser'
|
2
|
-
|
3
|
-
Associativities = [:prefix, :postfix, :infixn, :infixr, :infixl]
|
4
|
-
#
|
5
|
-
# This class holds information about operator precedences
|
6
|
-
# and associativities.
|
7
|
-
# prefix, postfix, infixl, infixr, infixn can be called
|
8
|
-
# to register operators.
|
9
|
-
#
|
10
|
-
class OperatorTable
|
11
|
-
#
|
12
|
-
# operators attribute is used internally. Do not access it.
|
13
|
-
#
|
14
|
-
attr_reader :operators
|
15
|
-
|
16
|
-
#
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
# tbl.
|
30
|
-
#
|
31
|
-
#
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
this
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
#
|
62
|
-
# that
|
63
|
-
#
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
def
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
private
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
end
|
135
|
-
|
136
|
-
|
1
|
+
require 'rparsec/parser'
|
2
|
+
|
3
|
+
Associativities = [:prefix, :postfix, :infixn, :infixr, :infixl]
|
4
|
+
#
|
5
|
+
# This class holds information about operator precedences
|
6
|
+
# and associativities.
|
7
|
+
# prefix, postfix, infixl, infixr, infixn can be called
|
8
|
+
# to register operators.
|
9
|
+
#
|
10
|
+
class OperatorTable
|
11
|
+
#
|
12
|
+
# operators attribute is used internally. Do not access it.
|
13
|
+
#
|
14
|
+
attr_reader :operators
|
15
|
+
|
16
|
+
#
|
17
|
+
# Re-initialize the operator table. Internal use only.
|
18
|
+
#
|
19
|
+
def reinit
|
20
|
+
@operators = []
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# To create an OperatorTable instance.
|
25
|
+
# If a block is given, it is invoked to do post-instantiation.
|
26
|
+
# For example:
|
27
|
+
#
|
28
|
+
# OperatorTable.new do |tbl|
|
29
|
+
# tbl.infixl(char(?+) >> Plus, 10)
|
30
|
+
# tbl.infixl(char(?-) >> Minus, 10)
|
31
|
+
# tbl.infixl(char(?*) >> Mul, 20)
|
32
|
+
# tbl.infixl(char(?/) >> Div, 20)
|
33
|
+
# tbl.prefix(char(?-) >> Neg, 50)
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
def self.new
|
37
|
+
this = allocate
|
38
|
+
this.reinit
|
39
|
+
if block_given?
|
40
|
+
yield this
|
41
|
+
end
|
42
|
+
this
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Defines a prefix operator that returns a unary Proc object with a precedence associated.
|
47
|
+
# Returns self.
|
48
|
+
#
|
49
|
+
def prefix(op, precedence)
|
50
|
+
add(:prefix, op, precedence)
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# Defines a postfix operator that returns a unary Proc object with a precedence associated.
|
55
|
+
# Returns self.
|
56
|
+
#
|
57
|
+
def postfix(op, precedence)
|
58
|
+
add(:postfix, op, precedence)
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# Defines a left associative infix operator that returns a binary Proc object with a precedence
|
63
|
+
# associated. Returns self.
|
64
|
+
#
|
65
|
+
def infixl(op, precedence)
|
66
|
+
add(:infixl, op, precedence)
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Defines a right associative infix operator that returns a binary Proc object with a precedence
|
71
|
+
# associated. Returns self.
|
72
|
+
#
|
73
|
+
def infixr(op, precedence)
|
74
|
+
add(:infixr, op, precedence)
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# Defines a non-associative infix operator that returns a binary Proc object with a precedence
|
79
|
+
# associated. Returns self.
|
80
|
+
#
|
81
|
+
def infixn(op, precedence)
|
82
|
+
add(:infixn, op, precedence)
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def add(*entry)
|
88
|
+
@operators << entry
|
89
|
+
self
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# This module helps build an expression parser
|
95
|
+
# using an OperatorTable instance and a parser
|
96
|
+
# that parses the term expression.
|
97
|
+
#
|
98
|
+
module Expressions
|
99
|
+
private
|
100
|
+
|
101
|
+
def self.array_to_dict arr
|
102
|
+
result = {}
|
103
|
+
arr.each_with_index do |key,i|
|
104
|
+
result [key] = i unless result.include? key
|
105
|
+
end
|
106
|
+
result
|
107
|
+
end
|
108
|
+
|
109
|
+
KindPrecedence = array_to_dict Associativities
|
110
|
+
|
111
|
+
public
|
112
|
+
|
113
|
+
#
|
114
|
+
# build an expression parser using the given term parser
|
115
|
+
# and operator table.
|
116
|
+
# When _delim_ is specified, patterns recognized by _delim_
|
117
|
+
# is automatically ignored.
|
118
|
+
#
|
119
|
+
def self.build(term, table, delim=nil)
|
120
|
+
# sort so that higher precedence first.
|
121
|
+
apply_operators(term, prepare_suites(table).sort, delim)
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def self.apply_operators(term, entries, delim)
|
127
|
+
# apply operators stored in [[precedence,associativity],[op...]] starting from beginning.
|
128
|
+
entries.inject(term) do |result, entry|
|
129
|
+
key, ops = *entry
|
130
|
+
null, kind_index = *key
|
131
|
+
op = ops[0]
|
132
|
+
op = Parsers.sum(*ops) if ops.length>1
|
133
|
+
apply_operator(result, op, Associativities[kind_index], delim)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.apply_operator(term, op, kind, delim)
|
138
|
+
term, op = ignore_rest(term, delim), ignore_rest(op, delim)
|
139
|
+
# we could use send here,
|
140
|
+
# but explicit case stmt is more straight forward and less coupled with names.
|
141
|
+
# performance might be another benefit,
|
142
|
+
# though it is not clear whether meta-code is indeed slower than regular ones at all.
|
143
|
+
case kind
|
144
|
+
when :prefix
|
145
|
+
term.prefix(op)
|
146
|
+
when :postfix
|
147
|
+
term.postfix(op)
|
148
|
+
when :infixl
|
149
|
+
term.infixl(op)
|
150
|
+
when :infixr
|
151
|
+
term.infixr(op)
|
152
|
+
when :infixn
|
153
|
+
term.infixn(op)
|
154
|
+
else
|
155
|
+
raise ArgumentError, "unknown associativity: #{kind}"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.ignore_rest(parser, delim)
|
160
|
+
return parser if delim.nil?
|
161
|
+
parser << delim
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.prepare_suites(table)
|
165
|
+
# create a hash with [precedence, associativity] as key, and op as value.
|
166
|
+
suites = {}
|
167
|
+
table.operators.each do |entry|
|
168
|
+
kind, op, precedence = *entry
|
169
|
+
key = [-precedence, KindPrecedence[kind]]
|
170
|
+
suite = suites[key]
|
171
|
+
if suite.nil?
|
172
|
+
suite = [op]
|
173
|
+
suites[key] = suite
|
174
|
+
else
|
175
|
+
suite << op
|
176
|
+
end
|
177
|
+
end
|
178
|
+
suites
|
179
|
+
end
|
137
180
|
end
|