rprec 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.
data/lib/rprec/prec.rb ADDED
@@ -0,0 +1,215 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RPrec
4
+ # `Prec` is an operator precedence.
5
+ class Prec
6
+ # @param name [Symbol]
7
+ # @param succs [Array<Symbol>]
8
+ # @param ops [Array<Op>]
9
+ def initialize(name, succs, ops)
10
+ @name = name
11
+ @succs = succs
12
+ @ops = ops
13
+ end
14
+
15
+ # @dynamic name, succs, ops
16
+
17
+ # @return [Symbol]
18
+ attr_reader :name
19
+ # @return [Array<Symbol>]
20
+ attr_reader :succs
21
+ # @return [Array<RPrec::Op>]
22
+ attr_reader :ops
23
+
24
+ # @api private
25
+ # @return [Hash{String => RPrec::Op}]
26
+ attr_reader :prefix_table
27
+ # @api private
28
+ # @return [Hash{String => RPrec::Op}]
29
+ attr_reader :postfix_table
30
+
31
+ # @api private
32
+ # @param grammar [RPrec::Grammar]
33
+ # @return [void]
34
+ def setup(grammar)
35
+ return unless @prefix_table.nil? && @postfix_table.nil?
36
+
37
+ @prefix_table = {}
38
+ @postfix_table = {}
39
+
40
+ @ops.each do |op|
41
+ case op.type
42
+ when :prefix, :non_assoc_prefix, :closed
43
+ raise ArgumentError, "Conflict with the key token '#{op.key}'" if @prefix_table.include?(op.key)
44
+
45
+ @prefix_table[op.key] = op
46
+ when :postfix, :non_assoc_postfix, :left_assoc, :right_assoc, :non_assoc
47
+ raise ArgumentError, "Conflict with the key token '#{op.key}'" if @postfix_table.include?(op.key)
48
+
49
+ @postfix_table[op.key] = op
50
+ end
51
+ end
52
+
53
+ grammar.setup(@succs)
54
+
55
+ @ops.each do |op|
56
+ parts = op.parts.filter_map { _1.is_a?(Symbol) ? _1 : nil }
57
+ grammar.setup(parts)
58
+ end
59
+
60
+ @all_prefix_keys = @prefix_table.keys
61
+ @all_postfix_keys = @postfix_table.keys
62
+ @prefix_keys = @prefix_table.filter { |_, op| op.type == :prefix }.keys
63
+ @right_assoc_keys = @postfix_table.filter { |_, op| op.type == :right_assoc }.keys
64
+ @postfix_and_left_assoc_keys = @postfix_table.filter { |_, op| op.type == :postfix || op.type == :left_assoc }.keys
65
+ end
66
+
67
+ # @api private
68
+ # @param grammar [RPrec::Grammar]
69
+ # @param stream [RPrec::Stream]
70
+ # @return [Object, nil]
71
+ def parse(grammar, stream)
72
+ op = @prefix_table[stream.current.type]
73
+
74
+ if op
75
+ key_token = stream.current
76
+ stream.next
77
+
78
+ case op.type
79
+ when :prefix
80
+ parts = grammar.parse_parts(op.parts, stream)
81
+ return parse_prefix_and_right_assoc(grammar, stream, [[op, [key_token, *parts]]])
82
+
83
+ when :non_assoc_prefix
84
+ parts = grammar.parse_parts(op.parts, stream)
85
+ node = grammar.parse_precs(@succs, stream)
86
+ return op.build(key_token, *parts, node)
87
+
88
+ when :closed
89
+ parts = grammar.parse_parts(op.parts, stream)
90
+ return op.build(key_token, *parts)
91
+
92
+ else raise ScriptError, 'Unreachable'
93
+ end
94
+ end
95
+ stream.expected(@all_prefix_keys)
96
+
97
+ node = grammar.parse_precs(@succs, stream)
98
+ key_token = stream.current
99
+ op = @postfix_table[key_token.type]
100
+ unless op
101
+ stream.expected(@all_postfix_keys)
102
+ return node
103
+ end
104
+ stream.next
105
+
106
+ case op.type
107
+ when :postfix
108
+ parts = grammar.parse_parts(op.parts, stream)
109
+ node = op.build(node, key_token, *parts)
110
+ parse_postfix_and_left_assoc(grammar, stream, node)
111
+
112
+ when :non_assoc_postfix
113
+ parts = grammar.parse_parts(op.parts, stream)
114
+ op.build(node, key_token, *parts)
115
+
116
+ when :left_assoc
117
+ parts = grammar.parse_parts(op.parts, stream)
118
+ right = grammar.parse_precs(@succs, stream)
119
+ node = op.build(node, key_token, *parts, right)
120
+ parse_postfix_and_left_assoc(grammar, stream, node)
121
+
122
+ when :right_assoc
123
+ parts = grammar.parse_parts(op.parts, stream)
124
+ parse_prefix_and_right_assoc(grammar, stream, [[op, [node, key_token, *parts]]])
125
+
126
+ when :non_assoc
127
+ parts = grammar.parse_parts(op.parts, stream)
128
+ right = grammar.parse_precs(@succs, stream)
129
+ op.build(node, key_token, *parts, right)
130
+
131
+ else raise ScriptError, 'Unreachable'
132
+ end
133
+ end
134
+
135
+ # @param grammar [RPrec::Grammar]
136
+ # @param stream [RPrec::Stream]
137
+ # @param stack [Array<Array<RPrec::Op, RPrec::Token, Object>>]
138
+ # @return [Object]
139
+ private def parse_prefix_and_right_assoc(grammar, stream, stack)
140
+ continue = true
141
+ # TODO: this untyped var seems ugly. How to fix it?
142
+ # @type var node: untyped
143
+ node = nil
144
+ while continue
145
+ key_token = stream.current
146
+ op = @prefix_table[key_token.type]
147
+ while op.is_a?(Op) && op.type == :prefix
148
+ stream.next
149
+ parts = grammar.parse_parts(op.parts, stream)
150
+ stack << [op, [key_token, *parts]]
151
+ key_token = stream.current
152
+ op = @prefix_table[key_token.type]
153
+ end
154
+ stream.expected(@prefix_keys)
155
+
156
+ node = grammar.parse_precs(@succs, stream)
157
+ key_token = stream.current
158
+ op = @postfix_table[key_token.type]
159
+ if op.is_a?(Op) && op.type == :right_assoc
160
+ stream.next
161
+ parts = grammar.parse_parts(op.parts, stream)
162
+ stack << [op, [node, key_token, *parts]]
163
+ continue = true
164
+ else
165
+ stream.expected(@right_assoc_keys)
166
+ continue = false
167
+ end
168
+ end
169
+
170
+ stack.reverse_each do |(parsed_op, args)|
171
+ node = parsed_op.build(*args, node)
172
+ end
173
+ node
174
+ end
175
+
176
+ # @param grammar [RPrec::Grammar]
177
+ # @param stream [RPrec::Stream]
178
+ # @param node [Object]
179
+ # @return [Object]
180
+ private def parse_postfix_and_left_assoc(grammar, stream, node)
181
+ key_token = stream.current
182
+ op = @postfix_table[key_token.type]
183
+ while op.is_a?(Op) && (op.type == :postfix || op.type == :left_assoc)
184
+ stream.next
185
+ case op.type
186
+ when :postfix
187
+ parts = grammar.parse_parts(op.parts, stream)
188
+ node = op.build(node, key_token, *parts)
189
+ when :left_assoc
190
+ parts = grammar.parse_parts(op.parts, stream)
191
+ right = grammar.parse_precs(@succs, stream)
192
+ node = op.build(node, key_token, *parts, right)
193
+ end
194
+ key_token = stream.current
195
+ op = @postfix_table[key_token.type]
196
+ end
197
+ stream.expected(@postfix_and_left_assoc_keys)
198
+ node
199
+ end
200
+
201
+ # @return [String]
202
+ def inspect
203
+ result = "prec #{name.inspect}"
204
+ result << " => #{succs.inspect}" unless succs.empty?
205
+ unless ops.empty?
206
+ result << " do\n"
207
+ ops.each do |op|
208
+ result << " #{op.inspect}\n"
209
+ end
210
+ result << 'end'
211
+ end
212
+ result
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'stream'
4
+
5
+ module RPrec
6
+ # `RegexpLexer` is a lexer using regexp patterns.
7
+ class RegexpLexer
8
+ # @param skip [Regexp]
9
+ # @param pattern [Regexp]
10
+ # @param block [Proc]
11
+ def initialize(skip:, pattern:, &block)
12
+ @skip = /\G#{skip}/
13
+ @pattern = /\G#{pattern}/
14
+ @block = block
15
+ end
16
+
17
+ # @dynamic skip, pattern, block
18
+
19
+ # @return [Regexp]
20
+ attr_reader :skip
21
+ # @return [Regexp]
22
+ attr_reader :pattern
23
+ # @return [Proc]
24
+ attr_reader :block
25
+
26
+ # @param source [String]
27
+ # @return [RPrec::RegexpStream]
28
+ def lex(source)
29
+ RegexpStream.new(self, source)
30
+ end
31
+ end
32
+
33
+ # `RegexpStream` is a stream generated by a `RegexpLexer`.
34
+ #
35
+ # @api private
36
+ class RegexpStream < Stream
37
+ # @param lexer [RPrec::Lexer]
38
+ # @param source [String]
39
+ def initialize(lexer, source)
40
+ super()
41
+ @lexer = lexer
42
+ @source = source
43
+ @offset = 0
44
+ self.next
45
+ end
46
+
47
+ # @return [RPrec::Token]
48
+ attr_reader :current
49
+
50
+ # @return [void]
51
+ def next
52
+ super
53
+
54
+ skip = @source.match(@lexer.skip, @offset)
55
+ @offset += skip.to_s.size if skip
56
+
57
+ if eof?
58
+ @current = Token.new('EOF', loc: @offset...@offset)
59
+ return
60
+ end
61
+
62
+ match = @source.match(@lexer.pattern, @offset)
63
+ raise ParseError.new("Unexpected character: #{@source[@offset].inspect}", loc: @offset...@offset + 1) unless match
64
+
65
+ type_and_value = @lexer.block.call(match)
66
+ type, value =
67
+ if type_and_value.is_a?(Array)
68
+ [type_and_value[0], type_and_value[1]]
69
+ else
70
+ [type_and_value, nil]
71
+ end
72
+ loc = @offset...@offset + match.to_s.size
73
+ @offset += match.to_s.size
74
+ @current = Token.new(type, value, loc:)
75
+ end
76
+
77
+ # @return [Boolean]
78
+ def eof?
79
+ @source.size <= @offset
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RPrec
4
+ # `Stream` is a token stream.
5
+ #
6
+ # @abstract `current`, `next` and `eof?` must be implemented.
7
+ class Stream
8
+ def initialize
9
+ @expected = []
10
+ end
11
+
12
+ # @return [RPrec::Token]
13
+ def current
14
+ raise ScriptError, 'Not implemented'
15
+ end
16
+
17
+ # @return [void]
18
+ def next
19
+ @expected = []
20
+ end
21
+
22
+ # @return [Boolean]
23
+ def eof?
24
+ raise ScriptError, 'Not implemented'
25
+ end
26
+
27
+ # @param tokens [Array<String>]
28
+ # @return [void]
29
+ def expected(tokens)
30
+ @expected += tokens
31
+ end
32
+
33
+ # @return [void]
34
+ def unexpected
35
+ token = current
36
+ raise ParseError.new("Unexpected token '#{token.type}'", loc: token.loc) if @expected.empty?
37
+
38
+ expected = @expected.sort.map { |tok| "'#{tok}'" }.join(', ')
39
+ raise ParseError.new("Expected token(s) #{expected}, but the unexpected token '#{token.type}' comes", loc: token.loc)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RPrec
4
+ # `Token` is a token.
5
+ class Token
6
+ # @param type [String]
7
+ # @param value [Object]
8
+ # @param loc [Range, nil]
9
+ def initialize(type, value = nil, loc: nil)
10
+ @type = type
11
+ @value = value
12
+ @loc = loc
13
+ end
14
+
15
+ # @dynamic type, value, loc
16
+
17
+ # @return [String]
18
+ attr_reader :type
19
+ # @return [Object]
20
+ attr_reader :value
21
+ # @return [Range, nil]
22
+ attr_reader :loc
23
+ end
24
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RPrec
4
+ # The version.
5
+ VERSION = '1.0.0'
6
+ end
data/lib/rprec.rb ADDED
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ # `RPrec` is an implementation of operator-precedence parsing.
4
+ # The operator-precedence parsing is also known as Pratt parsing.
5
+ # This implementation is extended for mixfix operators which are operators
6
+ # consists of multi parts (e.g. `... ? ... : ...` operator).
7
+ #
8
+ # ## Usage
9
+ #
10
+ # `RPrec::Grammar` is a grammar of the target language and also behaves
11
+ # its parser.
12
+ #
13
+ # We have a `RPrec::DSL` to easily build a `RPrec::Grammar` instance.
14
+ # The below is an example grammar for simple arithmetic expressions.
15
+ #
16
+ # ```
17
+ # grammar = RPrec::DSL.build do
18
+ # prec :main => :add_sub
19
+ #
20
+ # prec :add_sub => :mul_div do
21
+ # left_assoc '+' do |left, op_tok, right|
22
+ # [:add, left, right]
23
+ # end
24
+ # left_assoc '-' do |left, op_tok, right|
25
+ # [:sub, left, right]
26
+ # end
27
+ # end
28
+ #
29
+ # prec :mul_div => :unary do
30
+ # left_assoc '*' do |left, op_tok, right|
31
+ # [:mul, left, right]
32
+ # end
33
+ # left_assoc '/' do |left, op_tok, right|
34
+ # [:div, left, right]
35
+ # end
36
+ # end
37
+ #
38
+ # prec :unary => :atom do
39
+ # prefix '+' do |op_tok, expr|
40
+ # [:plus, expr]
41
+ # end
42
+ # prefix '-' do |op_tok, expr|
43
+ # [:minus, expr]
44
+ # end
45
+ # end
46
+ #
47
+ # prec :atom do
48
+ # closed 'nat' do |nat_tok|
49
+ # [:nat, nat_tok.value]
50
+ # end
51
+ # closed '(', :add_sub, ')' do |lpar_tok, expr, rpar_tok|
52
+ # [:paren, expr]
53
+ # end
54
+ # end
55
+ # end
56
+ # ```
57
+ #
58
+ # Here, `prec` is the only available method in the `RPrec::DSL` context.
59
+ # This method is for defining a precedence and takes the argument of one of the
60
+ # following forms: `name => succs`, `name => succs`, and `name`.
61
+ # `prec name => succ` is an alias for `prec name => [succ]` and `prec name` is
62
+ # an alias for `prec name => []`. `succs` is an array of precedence names which
63
+ # have higher binding powers than this precedence's. Precedence names must be
64
+ # symbols.
65
+ #
66
+ # `prec` also can take a block to define operators belonging to this
67
+ # precedence. Operators can be defined by the following eight methods:
68
+ #
69
+ # - `prefix key, *parts, &block`
70
+ # - `postfix key, *parts, &block`
71
+ # - `non_assoc_prefix key, *parts, &block`
72
+ # - `non_assoc_postfix key, *parts, &block`
73
+ # - `closed key, *parts, &block`
74
+ # - `left_assoc key, *parts, &block`
75
+ # - `right_assoc key, *parts, &block`
76
+ # - `non_assoc key, *parts, &block`
77
+ #
78
+ # They define operators with their own name associativity. The first argument
79
+ # `key` specifies the key token that is used to determine which the operator to
80
+ # be parsed. The rest arguments are `parts`: they consist of precedence names
81
+ # or tokens, meaning that they are required after the key token. They can take
82
+ # a block, which is an action called on parsed.
83
+ #
84
+ # To parse a string using this grammar, we can use the `RPrec::Grammar#parse`
85
+ # method. This method takes a `RPrec::Stream` object. `RPrec::Stream`
86
+ # is a token stream (an array) with convenient methods for parsing. It can be
87
+ # created easily by using `RPrec::ArrayStream` class.
88
+ #
89
+ # ```
90
+ # # A stream for the source `"1 + 2 * 3"`.
91
+ # stream = RPrec::ArrayStream.new([
92
+ # RPrec::Token.new('nat', 1),
93
+ # RPrec::Token.new('+'),
94
+ # RPrec::Token.new('nat', 2),
95
+ # RPrec::Token.new('*'),
96
+ # RPrec::Token.new('nat', 3),
97
+ # ])
98
+ #
99
+ # grammar.parse(stream)
100
+ # # => [:add, [:nat, 1], [:mul, [:nat, 2], [:nat, 3]]]
101
+ # ```
102
+ #
103
+ # ## Limitations
104
+ #
105
+ # This implementation assumes that an operator can be determined by the current
106
+ # token on parsing. If a grammar is not so, this reports the "Conflict with the
107
+ # key token ..." error.
108
+ #
109
+ # For example, the following grammar is rejected because the key token `'['`
110
+ # is conflicted.
111
+ #
112
+ # ```
113
+ # grammar = RPrec::DSL.build do
114
+ # prec :range do
115
+ # closed '[', :int, ',', :int, ']'
116
+ # closed '[', :int, ',', :int, ')'
117
+ # end
118
+ # end
119
+ # # raises "Conflict with the key token '['"
120
+ # ```
121
+ #
122
+ # ## References
123
+ #
124
+ # - [Operator-precedence parser - Wikipedia](https://en.wikipedia.org/wiki/Operator-precedence_parser)
125
+ # - [Parsing Mixfix Operators | Springer Link](https://link.springer.com/chapter/10.1007/978-3-642-24452-0_5)
126
+ module RPrec
127
+ # `Error` is an error for `RPrec`.
128
+ class Error < StandardError
129
+ end
130
+ end
131
+
132
+ require_relative 'rprec/array_stream'
133
+ require_relative 'rprec/dsl'
134
+ require_relative 'rprec/grammar'
135
+ require_relative 'rprec/op'
136
+ require_relative 'rprec/parse_error'
137
+ require_relative 'rprec/prec'
138
+ require_relative 'rprec/regexp_lexer'
139
+ require_relative 'rprec/stream'
140
+ require_relative 'rprec/token'
141
+ require_relative 'rprec/version'
@@ -0,0 +1,12 @@
1
+ ---
2
+ path: ".gem_rbs_collection"
3
+ gems:
4
+ - name: minitest
5
+ version: '0'
6
+ source:
7
+ type: stdlib
8
+ - name: mutex_m
9
+ version: '0'
10
+ source:
11
+ type: stdlib
12
+ gemfile_lock_path: Gemfile.lock
@@ -0,0 +1,20 @@
1
+ sources:
2
+ - type: git
3
+ name: ruby/gem_rbs_collection
4
+ remote: https://github.com/ruby/gem_rbs_collection.git
5
+ revision: main
6
+ repo_dir: gems
7
+
8
+ path: .gem_rbs_collection
9
+
10
+ gems:
11
+ - name: rake
12
+ ignore: true
13
+ - name: rbs
14
+ ignore: true
15
+ - name: redcarpet
16
+ ignore: true
17
+ - name: steep
18
+ ignore: true
19
+ - name: yard
20
+ ignore: true
@@ -0,0 +1,7 @@
1
+ module RPrec
2
+ class ArrayStream[T] < Stream[T]
3
+ def initialize: (Array[Token[T]]) -> void
4
+ @tokens: Array[Token[T]]
5
+ @index: Integer
6
+ end
7
+ end
data/sig/rprec/dsl.rbs ADDED
@@ -0,0 +1,26 @@
1
+ module RPrec
2
+ class DSL[T, R < Object]
3
+ def self.build: [T, R < Object] (?Symbol) { (DSL[T, R]) [self: DSL[T, R]] -> void } -> Grammar[T, R]
4
+ def initialize: () -> void
5
+ attr_reader precs: Hash[Symbol, Prec[T, R]]
6
+ def prec:
7
+ (Symbol | Hash[Symbol, Symbol | Array[Symbol]]) ?{ (PrecDSL[T, R]) [self: PrecDSL[T, R]] -> void }
8
+ -> void
9
+
10
+ class PrecDSL[T, R < Object]
11
+ def self.build:
12
+ [T, R < Object] (Symbol, Array[Symbol]) ?{ (PrecDSL[T, R]) [self: PrecDSL[T, R]] -> void }
13
+ -> Prec[T, R]
14
+ def initialize: () -> void
15
+ attr_reader ops: Array[Op[T, R]]
16
+ def prefix: (String, *(String | Symbol)) { (*(Token[T] | R)) -> R } -> void
17
+ def postfix: (String, *(String | Symbol)) { (*(Token[T] | R)) -> R } -> void
18
+ def non_assoc_prefix: (String, *(String | Symbol)) { (*(Token[T] | R)) -> R } -> void
19
+ def non_assoc_postfix: (String, *(String | Symbol)) { (*(Token[T] | R)) -> R } -> void
20
+ def closed: (String, *(String | Symbol)) { (*(Token[T] | R)) -> R } -> void
21
+ def left_assoc: (String, *(String | Symbol)) { (*(Token[T] | R)) -> R } -> void
22
+ def right_assoc: (String, *(String | Symbol)) { (*(Token[T] | R)) -> R } -> void
23
+ def non_assoc: (String, *(String | Symbol)) { (*(Token[T] | R)) -> R } -> void
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ module RPrec
2
+ class Grammar[T, R < Object]
3
+ def initialize: (Symbol, Hash[Symbol, Prec[T, R]]) -> void
4
+ def []: (Symbol) -> Prec[T, R]
5
+ def setup: (?Array[Symbol]) -> void
6
+ def parse: (Stream[T], ?check_eof: bool) -> R
7
+ def parse_prec: (Symbol, Stream[T]) -> R?
8
+ def parse_precs: (Array[Symbol], Stream[T]) -> R
9
+ def parse_parts: (Array[String | Symbol], Stream[T]) -> Array[Token[T] | R]
10
+ @main: Symbol
11
+ @precs: Hash[Symbol, Prec[T, R]]
12
+ end
13
+ end
data/sig/rprec/op.rbs ADDED
@@ -0,0 +1,15 @@
1
+ module RPrec
2
+ type op_type =
3
+ :prefix | :postfix | :non_assoc_prefix | :non_assoc_postfix | :closed |
4
+ :left_assoc | :right_assoc | :non_assoc
5
+
6
+ class Op[T, R < Object]
7
+ def initialize:
8
+ (op_type, String, Array[String | Symbol], ^(*(Token[T] | R)) -> R) -> void
9
+ attr_reader type: op_type
10
+ attr_reader key: String
11
+ attr_reader parts: Array[String | Symbol]
12
+ def build: (*(Token[T] | R)) -> R
13
+ @build: ^(*(Token[T] | R)) -> R
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ module RPrec
2
+ class ParseError < Error
3
+ def initialize: (String, ?loc: Range[Integer]?) -> void
4
+ attr_reader loc: Range[Integer]?
5
+ end
6
+ end
@@ -0,0 +1,21 @@
1
+ module RPrec
2
+ class Prec[T, R < Object]
3
+ def initialize: (Symbol, Array[Symbol], Array[Op[T, R]]) -> void
4
+ attr_reader name: Symbol
5
+ attr_reader succs: Array[Symbol]
6
+ attr_reader ops: Array[Op[T, R]]
7
+ def setup: (Grammar[T, R]) -> void
8
+ def parse: (Grammar[T, R], Stream[T]) -> R?
9
+ private def parse_prefix_and_right_assoc:
10
+ (Grammar[T, R], Stream[T], Array[[Op[T, R], Array[Token[T] | R]]]) -> R
11
+ private def parse_postfix_and_left_assoc:
12
+ (Grammar[T, R], Stream[T], R) -> R
13
+ @prefix_table: Hash[String, Op[T, R]]
14
+ @postfix_table: Hash[String, Op[T, R]]
15
+ @all_prefix_keys: Array[String]
16
+ @all_postfix_keys: Array[String]
17
+ @prefix_keys: Array[String]
18
+ @right_assoc_keys: Array[String]
19
+ @postfix_and_left_assoc_keys: Array[String]
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ module RPrec
2
+ class RegexpLexer[T]
3
+ def initialize:
4
+ (skip: Regexp, pattern: Regexp) { (MatchData) -> (String | [String, T]) }
5
+ -> void
6
+ def lex: (String) -> Stream[T]
7
+ attr_reader skip: Regexp
8
+ attr_reader pattern: Regexp
9
+ attr_reader block: ^(MatchData) -> (String | [String, T])
10
+ end
11
+
12
+ class RegexpStream[T] < Stream[T]
13
+ def initialize: (RegexpLexer[T], String) -> void
14
+ @lexer: RegexpLexer[T]
15
+ @source: String
16
+ @offset: Integer
17
+ @current: Token[T]
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ module RPrec
2
+ class Stream[T]
3
+ def initialize: () -> void
4
+ def current: () -> Token[T]
5
+ def next: () -> void
6
+ def eof?: () -> bool
7
+ def expected: (Array[String]) -> void
8
+ def unexpected: () -> bot
9
+ @expected: Array[String]
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ module RPrec
2
+ class Token[T]
3
+ def initialize: (String, ?(T | nil), ?loc: Range[Integer]?) -> void
4
+
5
+ attr_reader type: String
6
+ attr_reader value: T?
7
+ attr_reader loc: Range[Integer]?
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module RPrec
2
+ VERSION: String
3
+ end