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
@@ -1,82 +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 production is a rule with a single rhs
9
- class Production < Rule
10
- # @return [Dendroid::Syntax::SymbolSeq]
11
- attr_reader :body
12
-
13
- # Create a Production instance.
14
- # @param lhs [Dendroid::Syntax::NonTerminal] The left-hand side of the rule.
15
- # @param rhs [Dendroid::Syntax::SymbolSeq] the sequence of symbols on rhs.
16
- def initialize(lhs, rhs)
17
- super(lhs)
18
- @body = valid_sequence(rhs)
19
- end
20
-
21
- # Predicate method to check whether the rule body (its rhs) is empty.
22
- # @return [Boolean]
23
- def empty?
24
- body.empty?
25
- end
26
-
27
- # Predicate method to check whether the rule has alternatives
28
- # @return [FalseClass]
29
- def choice?
30
- false
31
- end
32
-
33
- # Predicate method to check whether the production rule body is productive.
34
- # It is productive when it is empty or all of its rhs members are productive too.
35
- # @return [Boolean, NilClass]
36
- def productive?
37
- if @productive.nil?
38
- if body.productive?
39
- self.productive = true
40
- else
41
- nil
42
- end
43
- else
44
- @productive
45
- end
46
- end
47
-
48
- # Mark the production rule as non-productive.
49
- def non_productive
50
- self.productive = false
51
- end
52
-
53
- # Return the text representation of the production rule
54
- # @return [String]
55
- def to_s
56
- "#{head} => #{body}"
57
- end
58
-
59
- # Equality operator
60
- # Two production rules are equal when their head and rhs are equal.
61
- # @return [Boolean]
62
- def ==(other)
63
- return true if equal?(other)
64
-
65
- (head == other.head) && (body == other.body)
66
- end
67
-
68
- # Returns an array with the symbol sequence of its rhs
69
- # @return [Array<Dendroid::Syntax::SymbolSeq>]
70
- def rhs
71
- [body]
72
- end
73
-
74
- private
75
-
76
- def productive=(val)
77
- @productive = val
78
- lhs.productive = val
79
- end
80
- end # class
81
- end # module
82
- end # module
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '..\..\spec_helper'
4
- require_relative '..\..\..\lib\dendroid\syntax\terminal'
5
- require_relative '..\..\..\lib\dendroid\syntax\non_terminal'
6
- require_relative '..\..\..\lib\dendroid\syntax\symbol_seq'
7
- require_relative '..\..\..\lib\dendroid\syntax\production'
8
- require_relative '..\..\..\lib\dendroid\grm_analysis\alternative_item'
9
-
10
- describe Dendroid::GrmAnalysis::DottedItem do
11
- # TODO
12
- end # describe
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../../spec_helper'
4
- require_relative '../../../lib/dendroid/syntax/terminal'
5
- require_relative '../../../lib/dendroid/syntax/non_terminal'
6
- require_relative '../../../lib/dendroid/syntax/symbol_seq'
7
- require_relative '../../../lib/dendroid/syntax/production'
8
- require_relative '../../../lib/dendroid/grm_analysis/production_items'
9
-
10
- describe Dendroid::GrmAnalysis::ProductionItems do
11
- let(:num_symb) { Dendroid::Syntax::Terminal.new('NUMBER') }
12
- let(:plus_symb) { Dendroid::Syntax::Terminal.new('PLUS') }
13
- let(:expr_symb) { Dendroid::Syntax::NonTerminal.new('expression') }
14
- let(:rhs) { Dendroid::Syntax::SymbolSeq.new([num_symb, plus_symb, num_symb]) }
15
- let(:empty_body) { Dendroid::Syntax::SymbolSeq.new([]) }
16
- let(:prod) { Dendroid::Syntax::Production.new(expr_symb, rhs) }
17
- let(:empty_prod) do
18
- e = Dendroid::Syntax::Production.new(expr_symb, empty_body)
19
- e.extend(Dendroid::GrmAnalysis::ProductionItems)
20
- e.build_items
21
- e
22
- end
23
-
24
- subject do
25
- prod.extend(Dendroid::GrmAnalysis::ProductionItems)
26
- prod.build_items
27
- prod
28
- end
29
-
30
- context 'Methods from mix-in' do
31
- it 'builds items for given non-empty production' do
32
- expect(subject.items.size).to eq(subject.body.size + 1)
33
- subject.items.each_with_index do |item, index|
34
- expect(item.rule).to eq(subject)
35
- expect(item.position).to eq(index)
36
- end
37
- end
38
-
39
- it 'builds the item for given empty production' do
40
- expect(empty_prod.items.size).to eq(1)
41
- expect(empty_prod.items[0].rule).to eq(empty_prod)
42
- expect(empty_prod.items[0].position).to eq(0)
43
- end
44
-
45
- it 'returns the first (predicted) item of the production' do
46
- expect(subject.predicted_items).to eq([subject.items.first])
47
- expect(empty_prod.predicted_items).to eq([empty_prod.items.first])
48
- end
49
-
50
- it 'returns the last (reduce) item of the production' do
51
- expect(subject.reduce_items).to eq([subject.items.last])
52
- expect(empty_prod.reduce_items).to eq([empty_prod.items.first])
53
- end
54
-
55
- # rubocop: disable Style/EachForSimpleLoop
56
- it 'returns the consecutive item to a given one' do
57
- (0..2).each do |pos|
58
- curr_item = subject.items[pos]
59
- next_one = subject.next_item(curr_item)
60
- expect(next_one).to eq(subject.items[pos + 1])
61
- end
62
- expect(subject.next_item(subject.items[-1])).to be_nil
63
-
64
- expect(empty_prod.next_item(empty_prod.items[-1])).to be_nil
65
- end
66
- # rubocop: enable Style/EachForSimpleLoop
67
- end # context
68
- end # describe
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '..\..\spec_helper'
4
- require_relative '..\..\..\lib\dendroid\syntax\terminal'
5
- require_relative '..\..\..\lib\dendroid\syntax\non_terminal'
6
- require_relative '..\..\..\lib\dendroid\syntax\symbol_seq'
7
- require_relative '..\..\..\lib\dendroid\syntax\choice'
8
-
9
- describe Dendroid::Syntax::Choice do
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') }
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([]) }
18
-
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]) }
24
-
25
- context 'Initialization:' do
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
28
- end
29
-
30
- it 'knows its alternatives' do
31
- expect(subject.alternatives).to eq([alt1, alt2, empty_body])
32
- end
33
-
34
- it 'renders a String representation of itself' do
35
- expectation = 'expression => NUMBER PLUS NUMBER | NUMBER MINUS NUMBER | '
36
- expect(subject.to_s).to eq(expectation)
37
- end
38
- end # context
39
-
40
- context 'Provided services:' do
41
- it 'knows its terminal members' do
42
- expect(subject.terminals).to eq([num_symb, plus_symb, minus_symb])
43
- end
44
-
45
- it 'knows its non-terminal members' do
46
- expect(subject.nonterminals).to be_empty
47
-
48
- my_alt1 = Dendroid::Syntax::SymbolSeq.new([expr_symb, plus_symb, expr_symb])
49
- my_alt2 = Dendroid::Syntax::SymbolSeq.new([foo_symb, minus_symb, expr_symb])
50
- instance = described_class.new(foo_symb, [my_alt1, my_alt2])
51
- expect(instance.nonterminals).to eq([expr_symb, foo_symb])
52
- end
53
- end # context
54
-
55
- context 'Errors:' do
56
- it 'fails when initialized with one alternative only' do
57
- err = StandardError
58
- err_msg = 'The choice for `expression` must have at least two alternatives.'
59
- expect { described_class.new(expr_symb, [alt1]) }.to raise_error(err, err_msg)
60
- end
61
-
62
- it 'fails in presence of duplicate rhs' do
63
- err = StandardError
64
- err_msg = 'Duplicate alternatives: expression => NUMBER PLUS NUMBER'
65
- expect { described_class.new(expr_symb, [alt1, alt2, alt1]) }.to raise_error(err, err_msg)
66
- end
67
- end # context
68
- end # describe
@@ -1,92 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '..\..\spec_helper'
4
- require_relative '..\..\..\lib\dendroid\syntax\terminal'
5
- require_relative '..\..\..\lib\dendroid\syntax\non_terminal'
6
- require_relative '..\..\..\lib\dendroid\syntax\symbol_seq'
7
- require_relative '..\..\..\lib\dendroid\syntax\production'
8
-
9
- describe Dendroid::Syntax::Production do
10
- let(:num_symb) { Dendroid::Syntax::Terminal.new('NUMBER') }
11
- let(:plus_symb) { Dendroid::Syntax::Terminal.new('PLUS') }
12
- let(:expr_symb) { Dendroid::Syntax::NonTerminal.new('expression') }
13
- let(:foo_symb) { Dendroid::Syntax::NonTerminal.new('foo') }
14
- let(:rhs) { Dendroid::Syntax::SymbolSeq.new([num_symb, plus_symb, num_symb]) }
15
- let(:cyclic_rhs) { Dendroid::Syntax::SymbolSeq.new([foo_symb]) }
16
- let(:empty_body) { Dendroid::Syntax::SymbolSeq.new([]) }
17
-
18
- # Implements a production rule: expression => NUMBER PLUS NUMBER
19
- subject { described_class.new(expr_symb, rhs) }
20
-
21
- context 'Initialization:' do
22
- it 'is initialized with a head and a body' do
23
- expect { described_class.new(expr_symb, rhs) }.not_to raise_error
24
- end
25
-
26
- it 'knows its body (aka rhs)' do
27
- expect(subject.body).to eq(rhs)
28
- end
29
-
30
- it 'renders a String representation of itself' do
31
- expect(subject.to_s).to eq('expression => NUMBER PLUS NUMBER')
32
- end
33
- end # context
34
-
35
- context 'Provided services:' do
36
- it 'knows whether its body empty is empty or not' do
37
- expect(described_class.new(expr_symb, empty_body).empty?).to be_truthy
38
- expect(subject.empty?).to be_falsey
39
- end
40
-
41
- it 'knows its non-terminal members' do
42
- foo_rhs = Dendroid::Syntax::SymbolSeq.new([expr_symb, plus_symb, expr_symb])
43
- instance = described_class.new(foo_symb, foo_rhs)
44
- expect(instance.nonterminals).to eq([expr_symb])
45
- end
46
-
47
- it 'knows its terminal members' do
48
- expect(subject.terminals).to eq([num_symb, plus_symb])
49
- end
50
-
51
- # rubocop: disable Lint/BinaryOperatorWithIdenticalOperands
52
- it 'can compare with another production' do
53
- expect(subject == subject).to be_truthy
54
-
55
- same = described_class.new(expr_symb, rhs)
56
- expect(subject == same).to be_truthy
57
-
58
- # Different lhs, same rhs
59
- different_lhs = described_class.new(foo_symb, rhs)
60
- expect(subject == different_lhs).to be_falsey
61
-
62
- # Same lhs, different rhs
63
- different_rhs = described_class.new(expr_symb, empty_body)
64
- expect(subject == different_rhs).to be_falsey
65
-
66
- # Two productions with same lhs and empty bodies
67
- empty = described_class.new(expr_symb, empty_body)
68
- void = described_class.new(expr_symb, Dendroid::Syntax::SymbolSeq.new([]))
69
- expect(empty == void).to be_truthy
70
-
71
- # Two productions with distinct lhs and empty bodies
72
- empty = described_class.new(expr_symb, empty_body)
73
- void = described_class.new(foo_symb, Dendroid::Syntax::SymbolSeq.new([]))
74
- expect(empty == void).to be_falsey
75
- end
76
- # rubocop: enable Lint/BinaryOperatorWithIdenticalOperands
77
- end # context
78
-
79
- context 'Errors:' do
80
- it "fails when rhs isn't initialized with a SymbolSeq" do
81
- err = StandardError
82
- err_msg = 'Expecting a SymbolSeq, found a String instead.'
83
- expect { described_class.new(foo_symb, 'bad') }.to raise_error err, err_msg
84
- end
85
-
86
- it 'fails when the production is cyclic' do
87
- err = StandardError
88
- err_msg = 'Cyclic rules of the kind foo => foo are not allowed.'
89
- expect { described_class.new(foo_symb, cyclic_rhs) }.to raise_error err, err_msg
90
- end
91
- end # context
92
- end # describe