rprec 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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