dendroid 0.1.00 → 0.2.01

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/CHANGELOG.md +6 -0
  4. data/lib/dendroid/formatters/ascii_tree.rb +142 -0
  5. data/lib/dendroid/formatters/base_formatter.rb +24 -0
  6. data/lib/dendroid/formatters/bracket_notation.rb +50 -0
  7. data/lib/dendroid/grm_analysis/dotted_item.rb +45 -30
  8. data/lib/dendroid/grm_analysis/grm_analyzer.rb +10 -4
  9. data/lib/dendroid/grm_analysis/{choice_items.rb → rule_items.rb} +10 -10
  10. data/lib/dendroid/grm_dsl/base_grm_builder.rb +3 -4
  11. data/lib/dendroid/parsing/and_node.rb +54 -0
  12. data/lib/dendroid/parsing/chart_walker.rb +301 -0
  13. data/lib/dendroid/parsing/composite_parse_node.rb +21 -0
  14. data/lib/dendroid/parsing/empty_rule_node.rb +28 -0
  15. data/lib/dendroid/parsing/or_node.rb +46 -0
  16. data/lib/dendroid/parsing/parse_node.rb +26 -0
  17. data/lib/dendroid/parsing/parse_tree_visitor.rb +127 -0
  18. data/lib/dendroid/parsing/parser.rb +185 -0
  19. data/lib/dendroid/parsing/terminal_node.rb +32 -0
  20. data/lib/dendroid/parsing/walk_progress.rb +121 -0
  21. data/lib/dendroid/recognizer/chart.rb +3 -0
  22. data/lib/dendroid/recognizer/e_item.rb +21 -2
  23. data/lib/dendroid/recognizer/item_set.rb +7 -2
  24. data/lib/dendroid/recognizer/recognizer.rb +42 -23
  25. data/lib/dendroid/syntax/grammar.rb +5 -1
  26. data/lib/dendroid/syntax/rule.rb +71 -13
  27. data/spec/dendroid/grm_analysis/dotted_item_spec.rb +59 -47
  28. data/spec/dendroid/grm_analysis/{choice_items_spec.rb → rule_items_spec.rb} +5 -6
  29. data/spec/dendroid/parsing/chart_walker_spec.rb +250 -0
  30. data/spec/dendroid/parsing/terminal_node_spec.rb +36 -0
  31. data/spec/dendroid/recognizer/e_item_spec.rb +5 -5
  32. data/spec/dendroid/recognizer/item_set_spec.rb +16 -8
  33. data/spec/dendroid/recognizer/recognizer_spec.rb +56 -5
  34. data/spec/dendroid/support/sample_grammars.rb +2 -2
  35. data/spec/dendroid/syntax/grammar_spec.rb +16 -21
  36. data/spec/dendroid/syntax/rule_spec.rb +56 -7
  37. data/version.txt +1 -1
  38. metadata +20 -13
  39. data/lib/dendroid/grm_analysis/alternative_item.rb +0 -70
  40. data/lib/dendroid/grm_analysis/production_items.rb +0 -55
  41. data/lib/dendroid/syntax/choice.rb +0 -95
  42. data/lib/dendroid/syntax/production.rb +0 -82
  43. data/spec/dendroid/grm_analysis/alternative_item_spec.rb +0 -12
  44. data/spec/dendroid/grm_analysis/production_items_spec.rb +0 -68
  45. data/spec/dendroid/syntax/choice_spec.rb +0 -68
  46. data/spec/dendroid/syntax/production_spec.rb +0 -92
@@ -51,7 +51,7 @@ describe Dendroid::Recognizer::Recognizer do
51
51
  'm => t . @ 0',
52
52
  's => m . @ 0',
53
53
  # 'm => m . STAR t @ 0',
54
- 'p => s . @ 0',
54
+ 'p => s . @ 0', # Can be ruled out (next token != eos)
55
55
  's => s . PLUS m @ 0'
56
56
  ]
57
57
  set2 = [ # 2 + . 3 * 4'
@@ -65,7 +65,7 @@ describe Dendroid::Recognizer::Recognizer do
65
65
  'm => t . @ 2',
66
66
  's => s PLUS m . @ 0',
67
67
  'm => m . STAR t @ 2',
68
- 'p => s . @ 0'
68
+ 'p => s . @ 0' # Can be ruled out (next token != eos)
69
69
  # 's => s . PLUS m @ 0'
70
70
  ]
71
71
  set4 = [ # 2 + 3 * . 4'
@@ -422,6 +422,54 @@ describe Dendroid::Recognizer::Recognizer do
422
422
  comp_expected_actuals(chart, expectations)
423
423
  end
424
424
 
425
+ it 'accepts an input with multiple levels of ambiguity' do
426
+ recognizer = described_class.new(grammar_l8, tokenizer_l8)
427
+ chart = recognizer.run('x x x x')
428
+ expect(chart).to be_successful
429
+
430
+ set0 = [ # . x x x x
431
+ 'S => . S S @ 0',
432
+ 'S => . x @ 0'
433
+ ]
434
+ set1 = [ # x . x x x
435
+ 'S => x . @ 0',
436
+ 'S => S . S @ 0',
437
+ 'S => . S S @ 1',
438
+ 'S => . x @ 1'
439
+ ]
440
+ set2 = [ # x x . x x
441
+ 'S => x . @ 1',
442
+ 'S => S S . @ 0',
443
+ 'S => S . S @ 1',
444
+ 'S => S . S @ 0',
445
+ 'S => . S S @ 2',
446
+ 'S => . x @ 2'
447
+ ]
448
+ set3 = [ # x x x . x
449
+ 'S => x . @ 2',
450
+ 'S => S S . @ 1',
451
+ 'S => S S . @ 0',
452
+ 'S => S . S @ 2',
453
+ 'S => S . S @ 1',
454
+ 'S => S . S @ 0',
455
+ 'S => . S S @ 3',
456
+ 'S => . x @ 3'
457
+ ]
458
+ set4 = [ # x x x x .
459
+ 'S => x . @ 3',
460
+ 'S => S S . @ 2',
461
+ 'S => S S . @ 1',
462
+ 'S => S S . @ 0', # Success entry
463
+ 'S => S . S @ 3',
464
+ 'S => S . S @ 2',
465
+ 'S => S . S @ 1',
466
+ 'S => S . S @ 0',
467
+ 'S => . S S @ 4'
468
+ ]
469
+ expectations = [set0, set1, set2, set3, set4]
470
+ comp_expected_actuals(chart, expectations)
471
+ end
472
+
425
473
  it 'swallows the input from an infinite ambiguity grammar' do
426
474
  recognizer = described_class.new(grammar_l9, tokenizer_l9)
427
475
  chart = recognizer.run('x x x')
@@ -441,7 +489,8 @@ describe Dendroid::Recognizer::Recognizer do
441
489
  'S => . S S @ 1',
442
490
  'S => . @ 1',
443
491
  'S => . x @ 1',
444
- 'S => S . S @ 1'
492
+ 'S => S . S @ 1',
493
+ 'S => S S . @ 1'
445
494
  ]
446
495
  set2 = [ # x x . x
447
496
  'S => x . @ 1',
@@ -452,7 +501,8 @@ describe Dendroid::Recognizer::Recognizer do
452
501
  'S => . S S @ 2',
453
502
  'S => . @ 2',
454
503
  'S => . x @ 2',
455
- 'S => S . S @ 2'
504
+ 'S => S . S @ 2',
505
+ 'S => S S . @ 2'
456
506
  ]
457
507
  set3 = [ # x x x .
458
508
  'S => x . @ 2',
@@ -465,7 +515,8 @@ describe Dendroid::Recognizer::Recognizer do
465
515
  'S => . S S @ 3',
466
516
  'S => . @ 3',
467
517
  # 'S => . x @ 3',
468
- 'S => S . S @ 3'
518
+ 'S => S . S @ 3',
519
+ 'S => S S . @ 3'
469
520
  ]
470
521
  expectations = [set0, set1, set2, set3]
471
522
  comp_expected_actuals(chart, expectations)
@@ -211,6 +211,7 @@ module SampleGrammars
211
211
  end
212
212
 
213
213
  def grammar_l10
214
+ # Grammar with left recursive rule
214
215
  builder = Dendroid::GrmDSL::BaseGrmBuilder.new do
215
216
  declare_terminals('a')
216
217
 
@@ -230,6 +231,7 @@ module SampleGrammars
230
231
 
231
232
  def grammar_l11
232
233
  builder = Dendroid::GrmDSL::BaseGrmBuilder.new do
234
+ # Grammar with right-recursive rule
233
235
  declare_terminals('a')
234
236
 
235
237
  rule 'A' => ['a A', '']
@@ -254,9 +256,7 @@ module SampleGrammars
254
256
  declare_terminals('a', 'b')
255
257
 
256
258
  rule 'S' => ['A T', 'a T']
257
- # rule 'S' => 'a T'
258
259
  rule 'A' => ['a', 'B A']
259
- # rule 'A' => 'B A'
260
260
  rule 'B' => ''
261
261
  rule 'T' => 'b b b'
262
262
  end
@@ -4,8 +4,7 @@ require_relative '..\..\spec_helper'
4
4
  require_relative '..\..\..\lib\dendroid\syntax\terminal'
5
5
  require_relative '..\..\..\lib\dendroid\syntax\non_terminal'
6
6
  require_relative '..\..\..\lib\dendroid\syntax\symbol_seq'
7
- require_relative '..\..\..\lib\dendroid\syntax\production'
8
- require_relative '..\..\..\lib\dendroid\syntax\choice'
7
+ require_relative '..\..\..\lib\dendroid\syntax\rule'
9
8
  require_relative '..\..\..\lib\dendroid\syntax\grammar'
10
9
  require_relative '..\..\..\lib\dendroid\grm_dsl\base_grm_builder'
11
10
 
@@ -33,19 +32,15 @@ describe Dendroid::Syntax::Grammar do
33
32
  Dendroid::Syntax::SymbolSeq.new(symbols)
34
33
  end
35
34
 
36
- def build_production(lhs, symbols)
37
- Dendroid::Syntax::Production.new(lhs, build_symbol_seq(symbols))
38
- end
39
-
40
35
  def build_choice(lhs, sequences)
41
- Dendroid::Syntax::Choice.new(lhs, sequences.map { |arr| build_symbol_seq(arr) })
36
+ Dendroid::Syntax::Rule.new(lhs, sequences.map { |arr| build_symbol_seq(arr) })
42
37
  end
43
38
 
44
39
  def build_all_rules
45
- rule1 = build_production(p_symb, [s_symb]) # p => s
40
+ rule1 = build_choice(p_symb, [[s_symb]]) # p => s
46
41
  rule2 = build_choice(s_symb, [[s_symb, plus_symb, m_symb], [m_symb]]) # s => s + m | m
47
42
  rule3 = build_choice(m_symb, [[m_symb, star_symb, t_symb], [t_symb]]) # m => m * t
48
- rule4 = build_production(t_symb, [int_symb]) # t => INTEGER
43
+ rule4 = build_choice(t_symb, [[int_symb]]) # t => INTEGER
49
44
  [rule1, rule2, rule3, rule4]
50
45
  end
51
46
 
@@ -72,7 +67,7 @@ describe Dendroid::Syntax::Grammar do
72
67
 
73
68
  context 'Adding productions:' do
74
69
  it 'allows the addition of one production rule' do
75
- rule = build_production(p_symb, [s_symb])
70
+ rule = build_choice(p_symb, [[s_symb]])
76
71
  expect { subject.add_rule(rule) }.not_to raise_error
77
72
  expect(subject.rules.size).to eq(1)
78
73
  expect(subject.rules.first).to eq(rule)
@@ -141,10 +136,10 @@ describe Dendroid::Syntax::Grammar do
141
136
  nterm_e = build_nonterminal('E')
142
137
 
143
138
  instance = described_class.new([terminal_a])
144
- instance.add_rule(build_production(nterm_s_prime, [nterm_s]))
145
- instance.add_rule(build_production(nterm_s, [nterm_a, nterm_a, nterm_a, nterm_a]))
139
+ instance.add_rule(build_choice(nterm_s_prime, [[nterm_s]]))
140
+ instance.add_rule(build_choice(nterm_s, [[nterm_a, nterm_a, nterm_a, nterm_a]]))
146
141
  instance.add_rule(build_choice(nterm_a, [[terminal_a], [nterm_e]]))
147
- instance.add_rule(build_production(nterm_e, []))
142
+ instance.add_rule(build_choice(nterm_e, [[]]))
148
143
 
149
144
  instance.complete!
150
145
  all_nonterminals = subject.symbols.reject(&:terminal?)
@@ -160,7 +155,7 @@ describe Dendroid::Syntax::Grammar do
160
155
  # Let add's unreachable symbols
161
156
  zed_symb = build_nonterminal('Z')
162
157
  question_symb = build_nonterminal('?')
163
- bad_rule = build_production(zed_symb, [zed_symb, question_symb, int_symb]) # Z => Z ? INTEGER
158
+ bad_rule = build_choice(zed_symb, [[zed_symb, question_symb, int_symb]]) # Z => Z ? INTEGER
164
159
  subject.add_rule(bad_rule)
165
160
  unreachable = subject.send(:unreachable_symbols)
166
161
  expect(unreachable).not_to be_empty
@@ -175,7 +170,7 @@ describe Dendroid::Syntax::Grammar do
175
170
  expect(t_symb).to be_productive
176
171
  expect(p_symb).to be_productive
177
172
 
178
- # Grammar with non-productive symbols
173
+ # # Grammar with non-productive symbols
179
174
  term_a = build_terminal('a')
180
175
  term_b = build_terminal('b')
181
176
  term_c = build_terminal('c')
@@ -191,12 +186,12 @@ describe Dendroid::Syntax::Grammar do
191
186
  nterm_S = build_nonterminal('S')
192
187
  instance = described_class.new([term_a, term_b, term_c, term_d, term_e, term_f])
193
188
  instance.add_rule(build_choice(nterm_S, [[nterm_A, nterm_B], [nterm_D, nterm_E]]))
194
- instance.add_rule(build_production(nterm_A, [term_a]))
195
- instance.add_rule(build_production(nterm_B, [term_b, nterm_C]))
196
- instance.add_rule(build_production(nterm_C, [term_c]))
197
- instance.add_rule(build_production(nterm_D, [term_d, nterm_F]))
198
- instance.add_rule(build_production(nterm_E, [term_e]))
199
- instance.add_rule(build_production(nterm_F, [term_f, nterm_D]))
189
+ instance.add_rule(build_choice(nterm_A, [[term_a]]))
190
+ instance.add_rule(build_choice(nterm_B, [[term_b, nterm_C]]))
191
+ instance.add_rule(build_choice(nterm_C, [[term_c]]))
192
+ instance.add_rule(build_choice(nterm_D, [[term_d, nterm_F]]))
193
+ instance.add_rule(build_choice(nterm_E, [[term_e]]))
194
+ instance.add_rule(build_choice(nterm_F, [[term_f, nterm_D]]))
200
195
  nonproductive = instance.send(:mark_non_productive_symbols)
201
196
  expect(nonproductive).not_to be_empty
202
197
  expect(nonproductive).to eq([nterm_D, nterm_F])
@@ -3,28 +3,77 @@
3
3
  require_relative '..\..\spec_helper'
4
4
  require_relative '..\..\..\lib\dendroid\syntax\terminal'
5
5
  require_relative '..\..\..\lib\dendroid\syntax\non_terminal'
6
+ require_relative '..\..\..\lib\dendroid\syntax\symbol_seq'
6
7
  require_relative '..\..\..\lib\dendroid\syntax\rule'
7
8
 
8
9
  describe Dendroid::Syntax::Rule do
9
10
  let(:num_symb) { Dendroid::Syntax::Terminal.new('NUMBER') }
11
+ let(:plus_symb) { Dendroid::Syntax::Terminal.new('PLUS') }
12
+ let(:minus_symb) { Dendroid::Syntax::Terminal.new('MINUS') }
10
13
  let(:expr_symb) { Dendroid::Syntax::NonTerminal.new('expression') }
14
+ let(:foo_symb) { Dendroid::Syntax::NonTerminal.new('foo') }
15
+ let(:alt1) { Dendroid::Syntax::SymbolSeq.new([num_symb, plus_symb, num_symb]) }
16
+ let(:alt2) { Dendroid::Syntax::SymbolSeq.new([num_symb, minus_symb, num_symb]) }
17
+ let(:empty_body) { Dendroid::Syntax::SymbolSeq.new([]) }
11
18
 
12
- subject { described_class.new(expr_symb) }
19
+ # Implements a choice rule:
20
+ # expression => NUMBER PLUS NUMBER
21
+ # | NUMBER MINUS NUMBER
22
+ # | epsilon
23
+ subject { described_class.new(expr_symb, [alt1, alt2, empty_body]) }
13
24
 
14
25
  context 'Initialization:' do
15
- it 'is initialized with a non-terminal' do
16
- expect { described_class.new(expr_symb) }.not_to raise_error
26
+ it 'is initialized with a head and alternatives' do
27
+ expect { described_class.new(expr_symb, [alt1, alt2, empty_body]) }.not_to raise_error
17
28
  end
18
29
 
19
30
  it 'knows its head (aka lhs)' do
20
31
  expect(subject.head).to eq(expr_symb)
21
32
  end
33
+
34
+ it 'knows its alternatives' do
35
+ expect(subject.alternatives).to eq([alt1, alt2, empty_body])
36
+ end
37
+
38
+ it 'renders a String representation of itself' do
39
+ expectation = 'expression => NUMBER PLUS NUMBER | NUMBER MINUS NUMBER | '
40
+ expect(subject.to_s).to eq(expectation)
41
+ end
42
+ end # context
43
+
44
+ context 'Provided services:' do
45
+ it 'knows its terminal members' do
46
+ expect(subject.terminals).to eq([num_symb, plus_symb, minus_symb])
47
+ end
48
+
49
+ it 'knows its non-terminal members' do
50
+ expect(subject.nonterminals).to be_empty
51
+
52
+ my_alt1 = Dendroid::Syntax::SymbolSeq.new([expr_symb, plus_symb, expr_symb])
53
+ my_alt2 = Dendroid::Syntax::SymbolSeq.new([foo_symb, minus_symb, expr_symb])
54
+ instance = described_class.new(foo_symb, [my_alt1, my_alt2])
55
+ expect(instance.nonterminals).to eq([expr_symb, foo_symb])
56
+ end
22
57
  end # context
23
58
 
24
59
  context 'Errors:' do
25
- it 'fails when initialized with a terminal' do
26
- msg = "Terminal symbol 'NUMBER' may not be on left-side of a rule."
27
- expect { described_class.new(num_symb) }.to raise_error(StandardError, msg)
60
+ context 'Errors:' do
61
+ it 'fails when initialized with a terminal' do
62
+ msg = "Terminal symbol 'NUMBER' may not be on left-side of a rule."
63
+ expect { described_class.new(num_symb, []) }.to raise_error(StandardError, msg)
64
+ end
65
+ end
66
+
67
+ it 'fails when initialized with one alternative only' do
68
+ err = StandardError
69
+ err_msg = 'The choice for `expression` must have at least one alternative.'
70
+ expect { described_class.new(expr_symb, []) }.to raise_error(err, err_msg)
28
71
  end
29
- end
72
+
73
+ it 'fails in presence of duplicate rhs' do
74
+ err = StandardError
75
+ err_msg = 'Duplicate alternatives: expression => NUMBER PLUS NUMBER'
76
+ expect { described_class.new(expr_symb, [alt1, alt2, alt1]) }.to raise_error(err, err_msg)
77
+ end
78
+ end # context
30
79
  end # describe
data/version.txt CHANGED
@@ -1 +1 @@
1
- 0.1.00
1
+ 0.2.01
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dendroid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.00
4
+ version: 0.2.01
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-03 00:00:00.000000000 Z
11
+ date: 2023-12-17 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: WIP. A Ruby implementation of an Earley parser
14
14
  email: famished.tiger@yahoo.com
@@ -24,47 +24,54 @@ files:
24
24
  - bin/dendroid
25
25
  - dendroid.gemspec
26
26
  - lib/dendroid.rb
27
- - lib/dendroid/grm_analysis/alternative_item.rb
28
- - lib/dendroid/grm_analysis/choice_items.rb
27
+ - lib/dendroid/formatters/ascii_tree.rb
28
+ - lib/dendroid/formatters/base_formatter.rb
29
+ - lib/dendroid/formatters/bracket_notation.rb
29
30
  - lib/dendroid/grm_analysis/dotted_item.rb
30
31
  - lib/dendroid/grm_analysis/grm_analyzer.rb
31
- - lib/dendroid/grm_analysis/production_items.rb
32
+ - lib/dendroid/grm_analysis/rule_items.rb
32
33
  - lib/dendroid/grm_dsl/base_grm_builder.rb
33
34
  - lib/dendroid/lexical/literal.rb
34
35
  - lib/dendroid/lexical/token.rb
35
36
  - lib/dendroid/lexical/token_position.rb
37
+ - lib/dendroid/parsing/and_node.rb
38
+ - lib/dendroid/parsing/chart_walker.rb
39
+ - lib/dendroid/parsing/composite_parse_node.rb
40
+ - lib/dendroid/parsing/empty_rule_node.rb
41
+ - lib/dendroid/parsing/or_node.rb
42
+ - lib/dendroid/parsing/parse_node.rb
43
+ - lib/dendroid/parsing/parse_tree_visitor.rb
44
+ - lib/dendroid/parsing/parser.rb
45
+ - lib/dendroid/parsing/terminal_node.rb
46
+ - lib/dendroid/parsing/walk_progress.rb
36
47
  - lib/dendroid/recognizer/chart.rb
37
48
  - lib/dendroid/recognizer/e_item.rb
38
49
  - lib/dendroid/recognizer/item_set.rb
39
50
  - lib/dendroid/recognizer/recognizer.rb
40
- - lib/dendroid/syntax/choice.rb
41
51
  - lib/dendroid/syntax/grammar.rb
42
52
  - lib/dendroid/syntax/grm_symbol.rb
43
53
  - lib/dendroid/syntax/non_terminal.rb
44
- - lib/dendroid/syntax/production.rb
45
54
  - lib/dendroid/syntax/rule.rb
46
55
  - lib/dendroid/syntax/symbol_seq.rb
47
56
  - lib/dendroid/syntax/terminal.rb
48
57
  - lib/dendroid/utils/base_tokenizer.rb
49
- - spec/dendroid/grm_analysis/alternative_item_spec.rb
50
- - spec/dendroid/grm_analysis/choice_items_spec.rb
51
58
  - spec/dendroid/grm_analysis/dotted_item_spec.rb
52
59
  - spec/dendroid/grm_analysis/grm_analyzer_spec.rb
53
- - spec/dendroid/grm_analysis/production_items_spec.rb
60
+ - spec/dendroid/grm_analysis/rule_items_spec.rb
54
61
  - spec/dendroid/grm_dsl/base_grm_builder_spec.rb
55
62
  - spec/dendroid/lexical/literal_spec.rb
56
63
  - spec/dendroid/lexical/token_position_spec.rb
57
64
  - spec/dendroid/lexical/token_spec.rb
65
+ - spec/dendroid/parsing/chart_walker_spec.rb
66
+ - spec/dendroid/parsing/terminal_node_spec.rb
58
67
  - spec/dendroid/recognizer/chart_spec.rb
59
68
  - spec/dendroid/recognizer/e_item_spec.rb
60
69
  - spec/dendroid/recognizer/item_set_spec.rb
61
70
  - spec/dendroid/recognizer/recognizer_spec.rb
62
71
  - spec/dendroid/support/sample_grammars.rb
63
- - spec/dendroid/syntax/choice_spec.rb
64
72
  - spec/dendroid/syntax/grammar_spec.rb
65
73
  - spec/dendroid/syntax/grm_symbol_spec.rb
66
74
  - spec/dendroid/syntax/non_terminal_spec.rb
67
- - spec/dendroid/syntax/production_spec.rb
68
75
  - spec/dendroid/syntax/rule_spec.rb
69
76
  - spec/dendroid/syntax/symbol_seq_spec.rb
70
77
  - spec/dendroid/syntax/terminal_spec.rb
@@ -90,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
97
  - !ruby/object:Gem::Version
91
98
  version: '0'
92
99
  requirements: []
93
- rubygems_version: 3.3.7
100
+ rubygems_version: 3.5.1
94
101
  signing_key:
95
102
  specification_version: 4
96
103
  summary: WIP. A Ruby implementation of an Earley parser
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'dotted_item'
4
-
5
- module Dendroid
6
- module GrmAnalysis
7
- # A specialization of DottedItem specific for Choice (rule)
8
- class AlternativeItem < DottedItem
9
- # @return [Integer] the alternative number
10
- attr_reader :alt_index
11
-
12
- # Constructor.
13
- # @param aChoice [Dendroid::Syntax::Choice]
14
- # @param aPosition [Integer] Position of the dot in rhs of production.
15
- # @param index [Integer] the rank of the alternative at hand
16
- def initialize(aChoice, aPosition, index)
17
- @alt_index = index
18
- super(aChoice, aPosition)
19
- end
20
-
21
- # Return a String representation of the alternative item.
22
- # @return [String]
23
- def to_s
24
- rhs_names = rule.alternatives[alt_index].members.map(&:to_s)
25
- dotted_rhs = rhs_names.insert(position, '.')
26
- "#{rule.head} => #{dotted_rhs.join(' ')}"
27
- end
28
-
29
- # Indicate whether the rhs of the alternative is empty
30
- # @return [Boolean]
31
- def empty?
32
- rule.alternatives[alt_index].empty?
33
- end
34
-
35
- # Indicate whether the dot is at the start of rhs
36
- # @return [Boolean]
37
- def final_pos?
38
- empty? || position == rule.alternatives[alt_index].size
39
- end
40
-
41
- alias completed? final_pos?
42
-
43
- # Return the symbol right after the dot (if any)
44
- # @return [Dendroid::Syntax::GrmSymbol, NilClass]
45
- def next_symbol
46
- return nil if empty? || completed?
47
-
48
- rule.alternatives[alt_index].members[position]
49
- end
50
-
51
- # Test for equality with another dotted item.
52
- # Two dotted items are equal if they refer to the same rule and
53
- # have both the same rhs and dot positions.
54
- # @return [Boolean]
55
- def ==(other)
56
- return true if eql?(other)
57
-
58
- (position == other.position) && rule.eql?(other.rule) && (alt_index == other.alt_index)
59
- end
60
-
61
- private
62
-
63
- def valid_position(aPosition)
64
- raise StandardError if aPosition.negative? || aPosition > rule.alternatives[alt_index].size
65
-
66
- aPosition
67
- end
68
- end # class
69
- end # module
70
- end # module
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'dotted_item'
4
-
5
- module Dendroid
6
- module GrmAnalysis
7
- # Mix-in module for extending the Dendroid::Syntax::Production class
8
- # with dotted items manipulation methods and an attribute named `items`.
9
- module ProductionItems
10
- # Build the dotted items for this production and assign them
11
- # to the `items` attributes
12
- # @return [Array<GrmAnalysis::DottedItem>]
13
- def build_items
14
- @items = if empty?
15
- [DottedItem.new(self, 0)]
16
- else
17
- (0..body.size).reduce([]) do |result, pos|
18
- result << GrmAnalysis::DottedItem.new(self, pos)
19
- end
20
- end
21
- end
22
-
23
- # Read accessor for the `items` attribute.
24
- # Return the dotted items for this production
25
- # @return [Array<GrmAnalysis::DottedItem>]
26
- def items
27
- @items
28
- end
29
-
30
- # Return the predicted item (i.e. the dotted item with the dot at start)
31
- # for this production.
32
- # @return [Array<GrmAnalysis::DottedItem>]
33
- def predicted_items
34
- [@items.first]
35
- end
36
-
37
- # Return the reduce item (i.e. the dotted item with the dot at end)
38
- # for this production.
39
- # @return [Array<GrmAnalysis::DottedItem>]
40
- def reduce_items
41
- [@items.last]
42
- end
43
-
44
- # Return the next item given the provided item.
45
- # In other words, advance the dot by one position.
46
- # @param anItem [GrmAnalysis::DottedItem]
47
- # @return [GrmAnalysis::DottedItem|NilClass]
48
- def next_item(anItem)
49
- return nil if anItem == @items.last
50
-
51
- @items[anItem.position + 1]
52
- end
53
- end # module
54
- end # module
55
- end # module
@@ -1,95 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'rule'
4
-
5
- module Dendroid
6
- module Syntax
7
- # A specialization of the Rule class.
8
- # A choice is a rule with multiple rhs
9
- class Choice < Rule
10
- # @return [Array<Dendroid::Syntax::SymbolSeq>]
11
- attr_reader :alternatives
12
-
13
- # Create a Choice instance.
14
- # @param lhs [Dendroid::Syntax::NonTerminal] The left-hand side of the rule.
15
- # @param alt [Array<Dendroid::Syntax::SymbolSeq>] the alternatives (each as a sequence of symbols).
16
- def initialize(lhs, alt)
17
- super(lhs)
18
- @alternatives = valid_alternatives(alt)
19
- end
20
-
21
- # Predicate method to check whether the rule has alternatives
22
- # @return [TrueClass]
23
- def choice?
24
- true
25
- end
26
-
27
- # Return the text representation of the choice
28
- # @return [String]
29
- def to_s
30
- "#{head} => #{alternatives.join(' | ')}"
31
- end
32
-
33
- # Predicate method to check whether the choice rule body is productive.
34
- # It is productive when at least of its alternative is productive.
35
- # @return [Boolean]
36
- def productive?
37
- productive_alts = alternatives.select(&:productive?)
38
- return false if productive_alts.empty?
39
-
40
- @productive = Set.new(productive_alts)
41
- head.productive = true
42
- end
43
-
44
- # Predicate method to check whether the rule has at least one empty alternative.
45
- # @return [Boolean]
46
- def empty?
47
- alternatives.any?(&:empty?)
48
- end
49
-
50
- # Returns an array with the symbol sequence of its alternatives
51
- # @return [Array<Dendroid::Syntax::SymbolSeq>]
52
- def rhs
53
- alternatives
54
- end
55
-
56
- # Equality operator
57
- # Two production rules are equal when their head and alternatives are equal.
58
- # @return [Boolean]
59
- def ==(other)
60
- return true if equal?(other)
61
- return false if other.is_a?(Production)
62
-
63
- (head == other.head) && (alternatives == other.alternatives)
64
- end
65
-
66
- private
67
-
68
- def valid_alternatives(alt)
69
- raise StandardError, "Expecting an Array, found a #{rhs.class} instead." unless alt.is_a?(Array)
70
-
71
- if alt.size < 2
72
- # A choice must have at least two alternatives
73
- raise StandardError, "The choice for `#{head}` must have at least two alternatives."
74
- end
75
-
76
- # Verify that each array element is a valid symbol sequence
77
- alt.each { |elem| valid_sequence(elem) }
78
-
79
- # Fail when duplicate rhs found
80
- alt_texts = alt.map(&:to_s)
81
- no_duplicate = alt_texts.uniq
82
- if alt_texts.size > no_duplicate.size
83
- alt_texts.each_with_index do |str, i|
84
- next if str == no_duplicate[i]
85
-
86
- err_msg = "Duplicate alternatives: #{head} => #{alt_texts[i]}"
87
- raise StandardError, err_msg
88
- end
89
- end
90
-
91
- alt
92
- end
93
- end # class
94
- end # module
95
- end # module