rley 0.7.06 → 0.8.01

Sign up to get free protection for your applications and to get access to all the features.
Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +362 -62
  3. data/.travis.yml +6 -6
  4. data/CHANGELOG.md +20 -4
  5. data/LICENSE.txt +1 -1
  6. data/README.md +7 -7
  7. data/examples/NLP/engtagger.rb +193 -190
  8. data/examples/NLP/nano_eng/nano_en_demo.rb +7 -11
  9. data/examples/NLP/nano_eng/nano_grammar.rb +21 -21
  10. data/examples/NLP/pico_en_demo.rb +2 -2
  11. data/examples/data_formats/JSON/cli_options.rb +1 -1
  12. data/examples/data_formats/JSON/json_ast_builder.rb +21 -27
  13. data/examples/data_formats/JSON/json_ast_nodes.rb +12 -21
  14. data/examples/data_formats/JSON/json_demo.rb +1 -2
  15. data/examples/data_formats/JSON/json_grammar.rb +13 -13
  16. data/examples/data_formats/JSON/json_lexer.rb +8 -8
  17. data/examples/data_formats/JSON/json_minifier.rb +1 -1
  18. data/examples/general/calc_iter1/calc_ast_builder.rb +13 -10
  19. data/examples/general/calc_iter1/calc_ast_nodes.rb +23 -37
  20. data/examples/general/calc_iter1/calc_grammar.rb +7 -6
  21. data/examples/general/calc_iter1/calc_lexer.rb +6 -4
  22. data/examples/general/calc_iter1/spec/calculator_spec.rb +5 -5
  23. data/examples/general/calc_iter2/calc_ast_builder.rb +5 -3
  24. data/examples/general/calc_iter2/calc_ast_nodes.rb +27 -43
  25. data/examples/general/calc_iter2/calc_grammar.rb +12 -12
  26. data/examples/general/calc_iter2/calc_lexer.rb +11 -10
  27. data/examples/general/calc_iter2/spec/calculator_spec.rb +26 -26
  28. data/examples/general/left.rb +2 -2
  29. data/examples/general/right.rb +2 -2
  30. data/lib/rley.rb +1 -1
  31. data/lib/rley/base/dotted_item.rb +28 -31
  32. data/lib/rley/base/grm_items_builder.rb +6 -0
  33. data/lib/rley/constants.rb +2 -2
  34. data/lib/rley/engine.rb +22 -25
  35. data/lib/rley/formatter/asciitree.rb +3 -3
  36. data/lib/rley/formatter/bracket_notation.rb +1 -8
  37. data/lib/rley/formatter/debug.rb +6 -6
  38. data/lib/rley/formatter/json.rb +2 -2
  39. data/lib/rley/gfg/call_edge.rb +1 -1
  40. data/lib/rley/gfg/edge.rb +5 -5
  41. data/lib/rley/gfg/end_vertex.rb +2 -6
  42. data/lib/rley/gfg/epsilon_edge.rb +1 -5
  43. data/lib/rley/gfg/grm_flow_graph.rb +27 -23
  44. data/lib/rley/gfg/item_vertex.rb +10 -10
  45. data/lib/rley/gfg/non_terminal_vertex.rb +4 -4
  46. data/lib/rley/gfg/scan_edge.rb +1 -1
  47. data/lib/rley/gfg/shortcut_edge.rb +2 -2
  48. data/lib/rley/gfg/start_vertex.rb +4 -8
  49. data/lib/rley/gfg/vertex.rb +43 -39
  50. data/lib/rley/interface.rb +16 -0
  51. data/lib/rley/lexical/token_range.rb +6 -6
  52. data/lib/rley/notation/all_notation_nodes.rb +2 -0
  53. data/lib/rley/notation/ast_builder.rb +191 -0
  54. data/lib/rley/notation/ast_node.rb +44 -0
  55. data/lib/rley/notation/ast_visitor.rb +113 -0
  56. data/lib/rley/notation/grammar.rb +49 -0
  57. data/lib/rley/notation/grammar_builder.rb +504 -0
  58. data/lib/rley/notation/grouping_node.rb +23 -0
  59. data/lib/rley/notation/parser.rb +56 -0
  60. data/lib/rley/notation/sequence_node.rb +35 -0
  61. data/lib/rley/notation/symbol_node.rb +29 -0
  62. data/lib/rley/notation/tokenizer.rb +192 -0
  63. data/lib/rley/parse_forest_visitor.rb +5 -5
  64. data/lib/rley/parse_rep/ast_base_builder.rb +48 -11
  65. data/lib/rley/parse_rep/cst_builder.rb +5 -6
  66. data/lib/rley/parse_rep/parse_forest_builder.rb +22 -18
  67. data/lib/rley/parse_rep/parse_forest_factory.rb +3 -3
  68. data/lib/rley/parse_rep/parse_rep_creator.rb +14 -16
  69. data/lib/rley/parse_rep/parse_tree_builder.rb +4 -4
  70. data/lib/rley/parse_rep/parse_tree_factory.rb +27 -27
  71. data/lib/rley/parse_tree_visitor.rb +1 -1
  72. data/lib/rley/parser/error_reason.rb +4 -5
  73. data/lib/rley/parser/gfg_chart.rb +118 -26
  74. data/lib/rley/parser/gfg_parsing.rb +22 -33
  75. data/lib/rley/parser/parse_entry.rb +25 -31
  76. data/lib/rley/parser/parse_entry_set.rb +19 -16
  77. data/lib/rley/parser/parse_entry_tracker.rb +4 -4
  78. data/lib/rley/parser/parse_tracer.rb +13 -13
  79. data/lib/rley/parser/parse_walker_factory.rb +23 -28
  80. data/lib/rley/ptree/non_terminal_node.rb +7 -5
  81. data/lib/rley/ptree/parse_tree.rb +3 -3
  82. data/lib/rley/ptree/parse_tree_node.rb +5 -5
  83. data/lib/rley/ptree/terminal_node.rb +7 -7
  84. data/lib/rley/rley_error.rb +12 -12
  85. data/lib/rley/sppf/alternative_node.rb +6 -6
  86. data/lib/rley/sppf/composite_node.rb +7 -7
  87. data/lib/rley/sppf/epsilon_node.rb +3 -3
  88. data/lib/rley/sppf/leaf_node.rb +3 -3
  89. data/lib/rley/sppf/parse_forest.rb +16 -16
  90. data/lib/rley/sppf/sppf_node.rb +7 -8
  91. data/lib/rley/sppf/token_node.rb +3 -3
  92. data/lib/rley/syntax/{grammar_builder.rb → base_grammar_builder.rb} +61 -23
  93. data/lib/rley/syntax/grammar.rb +5 -5
  94. data/lib/rley/syntax/grm_symbol.rb +7 -7
  95. data/lib/rley/syntax/match_closest.rb +43 -0
  96. data/lib/rley/syntax/non_terminal.rb +9 -15
  97. data/lib/rley/syntax/production.rb +16 -10
  98. data/lib/rley/syntax/symbol_seq.rb +7 -9
  99. data/lib/rley/syntax/terminal.rb +4 -5
  100. data/lib/rley/syntax/verbatim_symbol.rb +3 -3
  101. data/lib/support/base_tokenizer.rb +19 -18
  102. data/spec/rley/base/dotted_item_spec.rb +2 -2
  103. data/spec/rley/engine_spec.rb +23 -21
  104. data/spec/rley/formatter/asciitree_spec.rb +7 -7
  105. data/spec/rley/formatter/bracket_notation_spec.rb +13 -13
  106. data/spec/rley/formatter/json_spec.rb +1 -1
  107. data/spec/rley/gfg/end_vertex_spec.rb +5 -5
  108. data/spec/rley/gfg/grm_flow_graph_spec.rb +2 -2
  109. data/spec/rley/gfg/item_vertex_spec.rb +10 -10
  110. data/spec/rley/gfg/non_terminal_vertex_spec.rb +3 -3
  111. data/spec/rley/gfg/shortcut_edge_spec.rb +1 -1
  112. data/spec/rley/gfg/start_vertex_spec.rb +5 -5
  113. data/spec/rley/gfg/vertex_spec.rb +3 -3
  114. data/spec/rley/lexical/token_range_spec.rb +16 -16
  115. data/spec/rley/lexical/token_spec.rb +2 -2
  116. data/spec/rley/notation/grammar_builder_spec.rb +302 -0
  117. data/spec/rley/notation/parser_spec.rb +184 -0
  118. data/spec/rley/notation/tokenizer_spec.rb +370 -0
  119. data/spec/rley/parse_forest_visitor_spec.rb +165 -163
  120. data/spec/rley/parse_rep/ambiguous_parse_spec.rb +44 -44
  121. data/spec/rley/parse_rep/ast_builder_spec.rb +6 -7
  122. data/spec/rley/parse_rep/cst_builder_spec.rb +5 -5
  123. data/spec/rley/parse_rep/groucho_spec.rb +24 -26
  124. data/spec/rley/parse_rep/parse_forest_builder_spec.rb +27 -27
  125. data/spec/rley/parse_rep/parse_forest_factory_spec.rb +8 -8
  126. data/spec/rley/parse_rep/parse_tree_factory_spec.rb +3 -3
  127. data/spec/rley/parse_tree_visitor_spec.rb +10 -8
  128. data/spec/rley/parser/dangling_else_spec.rb +445 -0
  129. data/spec/rley/parser/error_reason_spec.rb +6 -6
  130. data/spec/rley/parser/gfg_earley_parser_spec.rb +120 -12
  131. data/spec/rley/parser/gfg_parsing_spec.rb +6 -13
  132. data/spec/rley/parser/parse_entry_spec.rb +19 -19
  133. data/spec/rley/parser/parse_walker_factory_spec.rb +10 -10
  134. data/spec/rley/ptree/non_terminal_node_spec.rb +5 -3
  135. data/spec/rley/ptree/parse_tree_node_spec.rb +4 -4
  136. data/spec/rley/ptree/terminal_node_spec.rb +6 -6
  137. data/spec/rley/sppf/alternative_node_spec.rb +6 -6
  138. data/spec/rley/sppf/non_terminal_node_spec.rb +3 -3
  139. data/spec/rley/sppf/token_node_spec.rb +4 -4
  140. data/spec/rley/support/ambiguous_grammar_helper.rb +4 -5
  141. data/spec/rley/support/grammar_abc_helper.rb +3 -5
  142. data/spec/rley/support/grammar_ambig01_helper.rb +5 -6
  143. data/spec/rley/support/grammar_arr_int_helper.rb +5 -6
  144. data/spec/rley/support/grammar_b_expr_helper.rb +5 -6
  145. data/spec/rley/support/grammar_int_seq_helper.rb +51 -0
  146. data/spec/rley/support/grammar_l0_helper.rb +14 -17
  147. data/spec/rley/support/grammar_pb_helper.rb +8 -7
  148. data/spec/rley/support/grammar_sppf_helper.rb +3 -3
  149. data/spec/rley/syntax/{grammar_builder_spec.rb → base_grammar_builder_spec.rb} +35 -16
  150. data/spec/rley/syntax/grammar_spec.rb +6 -6
  151. data/spec/rley/syntax/grm_symbol_spec.rb +1 -1
  152. data/spec/rley/syntax/match_closest_spec.rb +46 -0
  153. data/spec/rley/syntax/non_terminal_spec.rb +8 -8
  154. data/spec/rley/syntax/production_spec.rb +17 -13
  155. data/spec/rley/syntax/symbol_seq_spec.rb +2 -2
  156. data/spec/rley/syntax/terminal_spec.rb +5 -5
  157. data/spec/rley/syntax/verbatim_symbol_spec.rb +1 -1
  158. data/spec/spec_helper.rb +0 -12
  159. data/spec/support/base_tokenizer_spec.rb +7 -2
  160. metadata +48 -74
  161. data/.simplecov +0 -7
  162. data/lib/rley/parser/parse_state.rb +0 -83
  163. data/lib/rley/parser/parse_state_tracker.rb +0 -59
  164. data/lib/rley/parser/state_set.rb +0 -101
  165. data/spec/rley/parser/parse_state_spec.rb +0 -125
  166. data/spec/rley/parser/parse_tracer_spec.rb +0 -200
  167. data/spec/rley/parser/state_set_spec.rb +0 -130
@@ -0,0 +1,184 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../spec_helper' # Use the RSpec framework
4
+
5
+ require_relative '../../../lib/rley/notation/ast_builder'
6
+ # Load the class under test
7
+ require_relative '../../../lib/rley/notation/parser'
8
+
9
+ module Rley
10
+ module Notation
11
+ describe Parser do
12
+ subject { Parser.new }
13
+
14
+ # Utility method to walk towards deeply nested node
15
+ # @param aNTNode [Rley::PTree::NonTerminalNode]
16
+ # @param subnodePath[Array<Integer>] An Array of subnode indices
17
+ def walk_subnodes(aNTNode, subnodePath)
18
+ curr_node = aNTNode
19
+ subnodePath.each do |index|
20
+ curr_node = curr_node.subnodes[index]
21
+ end
22
+
23
+ curr_node
24
+ end
25
+
26
+ context 'Initialization:' do
27
+ it 'should be initialized without argument' do
28
+ expect { Parser.new }.not_to raise_error
29
+ end
30
+
31
+ it 'should have its parse engine initialized' do
32
+ expect(subject.engine).to be_kind_of(Rley::Engine)
33
+ end
34
+ end # context
35
+
36
+ context 'Parsing into CST:' do
37
+ subject do
38
+ instance = Parser.new
39
+ instance.engine.configuration.repr_builder = Rley::ParseRep::CSTBuilder
40
+
41
+ instance
42
+ end
43
+
44
+ it 'should parse single symbol names' do
45
+ samples = %w[IF ifCondition statement]
46
+
47
+ # One drawback od CSTs: they have a deeply nested structure
48
+ samples.each do |source|
49
+ ptree = subject.parse(source)
50
+ expect(ptree.root).to be_kind_of(Rley::PTree::NonTerminalNode)
51
+ expect(ptree.root.symbol.name).to eq('notation')
52
+ expect(ptree.root.subnodes[0]).to be_kind_of(Rley::PTree::NonTerminalNode)
53
+ expect(ptree.root.subnodes[0].symbol.name).to eq('rhs')
54
+ expect(ptree.root.subnodes[0].subnodes[0]).to be_kind_of(Rley::PTree::NonTerminalNode)
55
+ member_seq = ptree.root.subnodes[0].subnodes[0]
56
+ expect(member_seq.symbol.name).to eq('member_seq')
57
+ expect(member_seq.subnodes[0]).to be_kind_of(Rley::PTree::NonTerminalNode)
58
+ expect(member_seq.subnodes[0].symbol.name).to eq('member')
59
+ expect(member_seq.subnodes[0].subnodes[0]).to be_kind_of(Rley::PTree::NonTerminalNode)
60
+ expect(member_seq.subnodes[0].subnodes[0].symbol.name).to eq('strait_member')
61
+ strait_member = member_seq.subnodes[0].subnodes[0]
62
+ expect(strait_member.subnodes[0]).to be_kind_of(Rley::PTree::NonTerminalNode)
63
+ expect(strait_member.subnodes[0].symbol.name).to eq('base_member')
64
+ expect(strait_member.subnodes[0].subnodes[0]).to be_kind_of(Rley::PTree::TerminalNode)
65
+ expect(strait_member.subnodes[0].subnodes[0].token.lexeme).to eq(source)
66
+ end
67
+ end
68
+
69
+ end # context
70
+
71
+ context 'Parsing into AST:' do
72
+ subject do
73
+ instance = Parser.new
74
+ instance.engine.configuration.repr_builder = ASTBuilder
75
+
76
+ instance
77
+ end
78
+
79
+ it 'should parse single symbol names' do
80
+ samples = %w[IF ifCondition statement]
81
+
82
+ samples.each do |source|
83
+ ptree = subject.parse(source)
84
+ expect(ptree.root).to be_kind_of(SymbolNode)
85
+ expect(ptree.root.name).to eq(source)
86
+ expect(ptree.root.repetition).to eq(:exactly_one)
87
+ end
88
+ end
89
+
90
+ it 'should parse a sequence of symbols' do
91
+ sequence = 'INT_LIT ELLIPSIS INT_LIT'
92
+
93
+ ptree = subject.parse(sequence)
94
+ expect(ptree.root).to be_kind_of(SequenceNode)
95
+ expect(ptree.root.subnodes[0]).to be_kind_of(SymbolNode)
96
+ expect(ptree.root.subnodes[0].name).to eq('INT_LIT')
97
+ expect(ptree.root.subnodes[1]).to be_kind_of(SymbolNode)
98
+ expect(ptree.root.subnodes[1].name).to eq('ELLIPSIS')
99
+ expect(ptree.root.subnodes[2]).to be_kind_of(SymbolNode)
100
+ expect(ptree.root.subnodes[2].name).to eq('INT_LIT')
101
+ end
102
+
103
+ it 'should parse an optional symbol' do
104
+ optional = 'member_seq?'
105
+
106
+ ptree = subject.parse(optional)
107
+ expect(ptree.root).to be_kind_of(SymbolNode)
108
+ expect(ptree.root.name).to eq('member_seq')
109
+ expect(ptree.root.repetition).to eq(:zero_or_one)
110
+ end
111
+
112
+ it 'should parse a symbol with a + modifier' do
113
+ one_or_more = 'member+'
114
+
115
+ ptree = subject.parse(one_or_more)
116
+ expect(ptree.root).to be_kind_of(SymbolNode)
117
+ expect(ptree.root.name).to eq('member')
118
+ expect(ptree.root.repetition).to eq(:one_or_more)
119
+ end
120
+
121
+ it 'should parse a symbol with a * modifier' do
122
+ zero_or_more = 'declaration* EOF'
123
+
124
+ ptree = subject.parse(zero_or_more)
125
+ expect(ptree.root).to be_kind_of(SequenceNode)
126
+ expect(ptree.root.subnodes[0]).to be_kind_of(SymbolNode)
127
+ expect(ptree.root.subnodes[0].name).to eq('declaration')
128
+ expect(ptree.root.subnodes[0].repetition).to eq(:zero_or_more)
129
+ expect(ptree.root.subnodes[1]).to be_kind_of(SymbolNode)
130
+ expect(ptree.root.subnodes[1].name).to eq('EOF')
131
+ expect(ptree.root.subnodes[1].repetition).to eq(:exactly_one)
132
+ end
133
+
134
+ it 'should parse a grouping with a modifier' do
135
+ input = "IF ifCondition statement (ELSE statement)?"
136
+
137
+ ptree = subject.parse(input)
138
+ expect(ptree.root).to be_kind_of(SequenceNode)
139
+ expect(ptree.root.subnodes[0]).to be_kind_of(SymbolNode)
140
+ expect(ptree.root.subnodes[0].name).to eq('IF')
141
+ expect(ptree.root.subnodes[1]).to be_kind_of(SymbolNode)
142
+ expect(ptree.root.subnodes[1].name).to eq('ifCondition')
143
+ expect(ptree.root.subnodes[2]).to be_kind_of(SymbolNode)
144
+ expect(ptree.root.subnodes[2].name).to eq('statement')
145
+ expect(ptree.root.subnodes[3]).to be_kind_of(SequenceNode)
146
+ expect(ptree.root.subnodes[3].repetition).to eq(:zero_or_one)
147
+ expect(ptree.root.subnodes[3].subnodes[0]).to be_kind_of(SymbolNode)
148
+ expect(ptree.root.subnodes[3].subnodes[0].name).to eq('ELSE')
149
+ expect(ptree.root.subnodes[3].subnodes[1]).to be_kind_of(SymbolNode)
150
+ expect(ptree.root.subnodes[3].subnodes[1].name).to eq('statement')
151
+ end
152
+
153
+ it 'should parse an annotated symbol' do
154
+ optional = 'member_seq{repeat: 0..1}'
155
+
156
+ ptree = subject.parse(optional)
157
+ expect(ptree.root).to be_kind_of(SymbolNode)
158
+ expect(ptree.root.name).to eq('member_seq')
159
+ expect(ptree.root.repetition).to eq(:zero_or_one)
160
+ end
161
+
162
+ it 'should parse a grouping with embedded annotation' do
163
+ if_stmt = "IF ifCondition statement ( ELSE { match_closest: 'IF' } statement )?"
164
+
165
+ ptree = subject.parse(if_stmt)
166
+ expect(ptree.root).to be_kind_of(SequenceNode)
167
+ expect(ptree.root.subnodes[0]).to be_kind_of(SymbolNode)
168
+ expect(ptree.root.subnodes[0].name).to eq('IF')
169
+ expect(ptree.root.subnodes[1]).to be_kind_of(SymbolNode)
170
+ expect(ptree.root.subnodes[1].name).to eq('ifCondition')
171
+ expect(ptree.root.subnodes[2]).to be_kind_of(SymbolNode)
172
+ expect(ptree.root.subnodes[2].name).to eq('statement')
173
+ optional = ptree.root.subnodes[3]
174
+ expect(optional).to be_kind_of(SequenceNode)
175
+ expect(optional.repetition).to eq(:zero_or_one)
176
+ expect(optional.subnodes[0]).to be_kind_of(SymbolNode)
177
+ expect(optional.subnodes[0].name).to eq('ELSE')
178
+ expect(optional.subnodes[0].annotation).to eq({'match_closest' => 'IF'})
179
+ expect(optional.subnodes[1].name).to eq('statement')
180
+ end
181
+ end # context
182
+ end # describe
183
+ end # module
184
+ end # module
@@ -0,0 +1,370 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../spec_helper'
4
+
5
+ # Load the class under test
6
+ require_relative '../../../lib/rley/notation/tokenizer'
7
+
8
+ module Rley # Open this namespace to avoid module qualifier prefixes
9
+ module Notation # Open this namespace to avoid module qualifier prefixes
10
+ describe Tokenizer do
11
+ # Utility method for comparing actual and expected token
12
+ # sequence. The final EOF is removed from the input sequence.
13
+ def match_expectations(aScanner, theExpectations)
14
+ tokens = aScanner.tokens
15
+
16
+ tokens.each_with_index do |token, i|
17
+ terminal, lexeme = theExpectations[i]
18
+ expect(token.terminal).to eq(terminal)
19
+ expect(token.lexeme).to eq(lexeme)
20
+ end
21
+ end
22
+
23
+ context 'Initialization:' do
24
+ let(:sample_text) { 'begin-object member-list end-object' }
25
+ subject { Tokenizer.new }
26
+
27
+ it 'could be initialized with a text to tokenize or...' do
28
+ expect { Tokenizer.new(sample_text) }.not_to raise_error
29
+ end
30
+
31
+ it 'could be initialized without argument...' do
32
+ expect { Tokenizer.new }.not_to raise_error
33
+ end
34
+
35
+ it 'should have its scanner initialized' do
36
+ expect(subject.scanner).to be_kind_of(StringScanner)
37
+ end
38
+ end # context
39
+
40
+ context 'Input tokenization:' do
41
+ it 'should recognize single special character token' do
42
+ input = '(){}?*+,'
43
+ subject.start_with(input)
44
+ expectations = [
45
+ # [token lexeme]
46
+ %w[LEFT_PAREN (],
47
+ %w[RIGHT_PAREN )],
48
+ %w[LEFT_BRACE {],
49
+ %w[RIGHT_BRACE }],
50
+ %w[QUESTION_MARK ?],
51
+ %w[STAR *],
52
+ %w[PLUS +],
53
+ %w[COMMA ,]
54
+ ]
55
+ match_expectations(subject, expectations)
56
+ end
57
+
58
+ it 'should recognize one or two special character tokens' do
59
+ input = '..'
60
+ subject.start_with(input)
61
+ expectations = [
62
+ # [token lexeme]
63
+ %w[ELLIPSIS ..]
64
+ ]
65
+ match_expectations(subject, expectations)
66
+ end
67
+
68
+ it 'should treat ? * + as symbols if they occur as suffix' do
69
+ input = 'a+ + b* * 3 ?'
70
+ subject.start_with(input)
71
+ expectations = [
72
+ # [token lexeme]
73
+ %w[SYMBOL a],
74
+ %w[PLUS +],
75
+ %w[SYMBOL +],
76
+ %w[SYMBOL b],
77
+ %w[STAR *],
78
+ %w[SYMBOL *],
79
+ %w[INT_LIT 3],
80
+ %w[SYMBOL ?]
81
+ ]
82
+ match_expectations(subject, expectations)
83
+ end
84
+
85
+ it 'should recognize annotation keywords' do
86
+ keywords = 'match_closest: repeat:'
87
+ subject.start_with(keywords)
88
+ expectations = [
89
+ # [token lexeme]
90
+ %w[KEY match_closest],
91
+ %w[KEY repeat]
92
+ ]
93
+ match_expectations(subject, expectations)
94
+ end
95
+
96
+ it 'should recognize ordinal integer values' do
97
+ input = <<-RLEY_END
98
+ 3 123
99
+ 987654 0
100
+ RLEY_END
101
+
102
+ expectations = [
103
+ ['3', 3],
104
+ ['123', 123],
105
+ ['987654', 987654],
106
+ ['0', 0]
107
+ ]
108
+
109
+ subject.start_with(input)
110
+ subject.tokens[0..-2].each_with_index do |tok, i|
111
+ expect(tok).to be_kind_of(Rley::Lexical::Token)
112
+ expect(tok.terminal).to eq('INT_LIT')
113
+ (lexeme, val) = expectations[i]
114
+ expect(tok.lexeme).to eq(lexeme)
115
+ end
116
+ end
117
+
118
+ it 'should recognize string literals' do
119
+ input = <<-RLEY_END
120
+ ""
121
+ "string"
122
+ "123"
123
+ ''
124
+ 'string'
125
+ '123'
126
+ RLEY_END
127
+
128
+ expectations = [
129
+ '',
130
+ 'string',
131
+ '123'
132
+ ] * 2
133
+
134
+ subject.start_with(input)
135
+ subject.tokens.each_with_index do |str, i|
136
+ expect(str).to be_kind_of(Rley::Lexical::Token)
137
+ expect(str.terminal).to eq('STR_LIT')
138
+ (lexeme, val) = expectations[i]
139
+ expect(str.lexeme).to eq(lexeme)
140
+ end
141
+ end
142
+
143
+ it 'should recognize a sequence of symbols' do
144
+ input = "IF ifCondition statement ELSE statement"
145
+ expectations = [
146
+ 'IF',
147
+ 'ifCondition',
148
+ 'statement',
149
+ 'ELSE',
150
+ 'statement'
151
+ ]
152
+
153
+ subject.start_with(input)
154
+ subject.tokens.each_with_index do |str, i|
155
+ expect(str).to be_kind_of(Rley::Lexical::Token)
156
+ expect(str.terminal).to eq('SYMBOL')
157
+ (lexeme, val) = expectations[i]
158
+ expect(str.lexeme).to eq(lexeme)
159
+ end
160
+ end
161
+
162
+ it 'should recognize an optional symbol' do
163
+ input = "RETURN expression? SEMICOLON"
164
+ expectations = [
165
+ ['RETURN', 'SYMBOL'],
166
+ ['expression', 'SYMBOL'],
167
+ ['?', 'QUESTION_MARK'],
168
+ ['SEMICOLON', 'SYMBOL'],
169
+ ]
170
+
171
+ subject.start_with(input)
172
+ subject.tokens.each_with_index do |str, i|
173
+ expect(str).to be_kind_of(Rley::Lexical::Token)
174
+ (lexeme, token) = expectations[i]
175
+ expect(str.lexeme).to eq(lexeme)
176
+ expect(str.terminal).to eq(token)
177
+ end
178
+ end
179
+
180
+ it 'should recognize a symbol with a star quantifier' do
181
+ input = "declaration* EOF"
182
+ expectations = [
183
+ ['declaration', 'SYMBOL'],
184
+ ['*', 'STAR'],
185
+ ['EOF', 'SYMBOL'],
186
+ ]
187
+
188
+ subject.start_with(input)
189
+ subject.tokens.each_with_index do |str, i|
190
+ expect(str).to be_kind_of(Rley::Lexical::Token)
191
+ (lexeme, token) = expectations[i]
192
+ expect(str.lexeme).to eq(lexeme)
193
+ expect(str.terminal).to eq(token)
194
+ end
195
+ end
196
+
197
+ it 'should recognize a symbol with a plus quantifier' do
198
+ input = "declaration+ EOF"
199
+ expectations = [
200
+ ['declaration', 'SYMBOL'],
201
+ ['+', 'PLUS'],
202
+ ['EOF', 'SYMBOL'],
203
+ ]
204
+
205
+ subject.start_with(input)
206
+ subject.tokens.each_with_index do |str, i|
207
+ expect(str).to be_kind_of(Rley::Lexical::Token)
208
+ (lexeme, token) = expectations[i]
209
+ expect(str.lexeme).to eq(lexeme)
210
+ expect(str.terminal).to eq(token)
211
+ end
212
+ end
213
+
214
+ it 'should recognize a grouping with a quantifier' do
215
+ input = "IF ifCondition statement (ELSE statement)?"
216
+ expectations = [
217
+ ['IF', 'SYMBOL'],
218
+ ['ifCondition', 'SYMBOL'],
219
+ ['statement', 'SYMBOL'],
220
+ ['(', 'LEFT_PAREN'],
221
+ ['ELSE', 'SYMBOL'],
222
+ ['statement', 'SYMBOL'],
223
+ [')', 'RIGHT_PAREN'],
224
+ ['?', 'QUESTION_MARK']
225
+ ]
226
+
227
+ subject.start_with(input)
228
+ subject.tokens.each_with_index do |str, i|
229
+ expect(str).to be_kind_of(Rley::Lexical::Token)
230
+ (lexeme, token) = expectations[i]
231
+ expect(str.lexeme).to eq(lexeme)
232
+ expect(str.terminal).to eq(token)
233
+ end
234
+ end
235
+
236
+ it 'should recognize a match closest constraint' do
237
+ input = "IF ifCondition statement ELSE { match_closest: 'IF' } statement"
238
+ expectations = [
239
+ ['IF', 'SYMBOL'],
240
+ ['ifCondition', 'SYMBOL'],
241
+ ['statement', 'SYMBOL'],
242
+ ['ELSE', 'SYMBOL'],
243
+ ['{', 'LEFT_BRACE'],
244
+ ['match_closest', 'KEY'],
245
+ ['IF', 'STR_LIT'],
246
+ ['}', 'RIGHT_BRACE'],
247
+ ['statement', 'SYMBOL']
248
+ ]
249
+
250
+ subject.start_with(input)
251
+ subject.tokens.each_with_index do |str, i|
252
+ expect(str).to be_kind_of(Rley::Lexical::Token)
253
+ (lexeme, token) = expectations[i]
254
+ expect(str.lexeme).to eq(lexeme)
255
+ expect(str.terminal).to eq(token)
256
+ end
257
+ end
258
+
259
+ it 'should recognize a repeat constraint' do
260
+ input = "IF ifCondition statement { repeat: 1 } ELSE statement"
261
+ expectations = [
262
+ ['IF', 'SYMBOL'],
263
+ ['ifCondition', 'SYMBOL'],
264
+ ['statement', 'SYMBOL'],
265
+ ['{', 'LEFT_BRACE'],
266
+ ['repeat', 'KEY'],
267
+ ['1', 'INT_LIT'],
268
+ ['}', 'RIGHT_BRACE'],
269
+ ['ELSE', 'SYMBOL'],
270
+ ['statement', 'SYMBOL']
271
+ ]
272
+
273
+ subject.start_with(input)
274
+ subject.tokens.each_with_index do |str, i|
275
+ expect(str).to be_kind_of(Rley::Lexical::Token)
276
+ (lexeme, token) = expectations[i]
277
+ expect(str.lexeme).to eq(lexeme)
278
+ expect(str.terminal).to eq(token)
279
+ end
280
+ end
281
+
282
+ it 'should recognize a grouping with a repeat constraint' do
283
+ input = "IF ifCondition statement ( ELSE statement ){ repeat: 0..1 }"
284
+ expectations = [
285
+ ['IF', 'SYMBOL'],
286
+ ['ifCondition', 'SYMBOL'],
287
+ ['statement', 'SYMBOL'],
288
+ ['(', 'LEFT_PAREN'],
289
+ ['ELSE', 'SYMBOL'],
290
+ ['statement', 'SYMBOL'],
291
+ [')', 'RIGHT_PAREN'],
292
+ ['{', 'LEFT_BRACE'],
293
+ ['repeat', 'KEY'],
294
+ ['0', 'INT_LIT'],
295
+ ['..', 'ELLIPSIS'],
296
+ ['1', 'INT_LIT'],
297
+ ['}', 'RIGHT_BRACE']
298
+ ]
299
+
300
+ subject.start_with(input)
301
+ subject.tokens.each_with_index do |str, i|
302
+ expect(str).to be_kind_of(Rley::Lexical::Token)
303
+ (lexeme, token) = expectations[i]
304
+ expect(str.lexeme).to eq(lexeme)
305
+ expect(str.terminal).to eq(token)
306
+ end
307
+ end
308
+
309
+ it 'should recognize a combination of constraints' do
310
+ input = "IF ifCondition statement ELSE { repeat: 1, match_closest: 'IF' } statement"
311
+ expectations = [
312
+ ['IF', 'SYMBOL'],
313
+ ['ifCondition', 'SYMBOL'],
314
+ ['statement', 'SYMBOL'],
315
+ ['ELSE', 'SYMBOL'],
316
+ ['{', 'LEFT_BRACE'],
317
+ ['repeat', 'KEY'],
318
+ ['1', 'INT_LIT'],
319
+ [',', 'COMMA'],
320
+ ['match_closest', 'KEY'],
321
+ ['IF', 'STR_LIT'],
322
+ ['}', 'RIGHT_BRACE'],
323
+ ['statement', 'SYMBOL']
324
+ ]
325
+
326
+ subject.start_with(input)
327
+ subject.tokens.each_with_index do |str, i|
328
+ expect(str).to be_kind_of(Rley::Lexical::Token)
329
+ (lexeme, token) = expectations[i]
330
+ expect(str.lexeme).to eq(lexeme)
331
+ expect(str.terminal).to eq(token)
332
+ end
333
+ end
334
+
335
+ it 'should recognize a grouping with a nested constraint' do
336
+ input = "IF ifCondition statement ( ELSE { match_closest: 'IF' } statement ){ repeat: 0..1 }"
337
+ expectations = [
338
+ ['IF', 'SYMBOL'],
339
+ ['ifCondition', 'SYMBOL'],
340
+ ['statement', 'SYMBOL'],
341
+ ['(', 'LEFT_PAREN'],
342
+ ['ELSE', 'SYMBOL'],
343
+ ['{', 'LEFT_BRACE'],
344
+ ['match_closest', 'KEY'],
345
+ ['IF', 'STR_LIT'],
346
+ ['}', 'RIGHT_BRACE'],
347
+ ['statement', 'SYMBOL'],
348
+ [')', 'RIGHT_PAREN'],
349
+ ['{', 'LEFT_BRACE'],
350
+ ['repeat', 'KEY'],
351
+ ['0', 'INT_LIT'],
352
+ ['..', 'ELLIPSIS'],
353
+ ['1', 'INT_LIT'],
354
+ ['}', 'RIGHT_BRACE']
355
+ ]
356
+
357
+ subject.start_with(input)
358
+ subject.tokens.each_with_index do |str, i|
359
+ expect(str).to be_kind_of(Rley::Lexical::Token)
360
+ (lexeme, token) = expectations[i]
361
+ expect(str.lexeme).to eq(lexeme)
362
+ expect(str.terminal).to eq(token)
363
+ end
364
+ end
365
+ end # context
366
+ end # describe
367
+ end # module
368
+ end # module
369
+
370
+ # End of file