sequitur 0.1.18 → 0.1.19

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.
@@ -1,19 +1,17 @@
1
- require 'sequitur' # Load the Sequitur library
1
+ require 'sequitur' # Load the Sequitur library
2
2
 
3
3
  #
4
4
  # Purpose: show how to apply Sequitur on a stream of Symbol values
5
5
  #
6
- input_sequence = [
7
- :aa, :bb, :aa, :bb,
8
- :cc, :aa, :bb, :cc,
9
- :dd, :aa, :bb, :cc,
10
- :dd, :ee
6
+ input_sequence = %i[
7
+ aa bb aa bb cc
8
+ aa bb cc dd aa
9
+ bb cc dd ee
11
10
  ]
12
11
 
13
12
  # Generate the grammar from the sequence
14
13
  grammar = Sequitur.build_from(input_sequence)
15
14
 
16
-
17
15
  # Use a formatter to display the grammar rules on the console output
18
16
  formatter = Sequitur::Formatter::BaseText.new(STDOUT)
19
17
 
@@ -25,4 +23,3 @@ formatter.render(grammar.visitor)
25
23
  # P1 : aa bb.
26
24
  # P2 : P1 cc.
27
25
  # P3 : P2 dd.
28
-
@@ -1,4 +1,4 @@
1
- require 'sequitur' # Load the Sequitur library
1
+ require 'sequitur' # Load the Sequitur library
2
2
 
3
3
  #
4
4
  # Purpose: show how to apply Sequitur on a stream of text words
@@ -27,4 +27,3 @@ formatter.render(grammar.visitor)
27
27
  # start : P2 6 Error illegal P1 20 P2 9.
28
28
  # P1 : character at position.
29
29
  # P2 : Error unknown P1.
30
-
@@ -3,7 +3,7 @@
3
3
 
4
4
  module Sequitur # Module used as a namespace
5
5
  # The version number of the gem.
6
- Version = '0.1.18'.freeze
6
+ Version = '0.1.19'.freeze
7
7
 
8
8
  # Brief description of the gem.
9
9
  Description = 'Ruby implementation of the Sequitur algorithm'.freeze
@@ -1,49 +1,49 @@
1
1
  # File: digram.rb
2
2
 
3
3
  module Sequitur # Module for classes implementing the Sequitur algorithm
4
- # In linguistics, a digram is a sequence of two letters.
5
- # In Sequitur, a digram is a sequence of two consecutive symbols that
6
- # appear in a production rule. Each symbol in a digram
7
- # can be a terminal or not.
8
- class Digram
9
- # The sequence of two consecutive grammar symbols.
10
- # The two symbols should respond to the :hash message.
11
- attr_reader(:symbols)
4
+ # In linguistics, a digram is a sequence of two letters.
5
+ # In Sequitur, a digram is a sequence of two consecutive symbols that
6
+ # appear in a production rule. Each symbol in a digram
7
+ # can be a terminal or not.
8
+ class Digram
9
+ # The sequence of two consecutive grammar symbols.
10
+ # The two symbols should respond to the :hash message.
11
+ attr_reader(:symbols)
12
12
 
13
- # An unique hash key of the digram
14
- attr_reader(:key)
13
+ # An unique hash key of the digram
14
+ attr_reader(:key)
15
15
 
16
- # The production in which the digram occurs
17
- attr_reader(:production)
16
+ # The production in which the digram occurs
17
+ attr_reader(:production)
18
18
 
19
- # Constructor.
20
- # A digram represents a sequence of two symbols
21
- # (that appears in a rhs of a production).
22
- # Terminal symbols must respond to the :hash message.
23
- # @param symbol1 [StringOrSymbol] First element of the digram
24
- # @param symbol2 [StringOrSymbol] Second element of the digram
25
- # @param aProduction [Production] Production in which the RHS
26
- # the sequence symbol1 symbol2 appears.
27
- def initialize(symbol1, symbol2, aProduction)
28
- @symbols = [symbol1, symbol2]
29
- @key = symbol1.hash.to_s(16) + ':' + symbol2.hash.to_s(16)
30
- @production = aProduction
31
- end
19
+ # Constructor.
20
+ # A digram represents a sequence of two symbols
21
+ # (that appears in a rhs of a production).
22
+ # Terminal symbols must respond to the :hash message.
23
+ # @param symbol1 [StringOrSymbol] First element of the digram
24
+ # @param symbol2 [StringOrSymbol] Second element of the digram
25
+ # @param aProduction [Production] Production in which the RHS
26
+ # the sequence symbol1 symbol2 appears.
27
+ def initialize(symbol1, symbol2, aProduction)
28
+ @symbols = [symbol1, symbol2]
29
+ @key = symbol1.hash.to_s(16) + ':' + symbol2.hash.to_s(16)
30
+ @production = aProduction
31
+ end
32
32
 
33
- # Equality testing.
34
- # true iff keys of both digrams are equal, false otherwise
35
- # @param other [Digram] another to compare with
36
- # @return [true/false]
37
- def ==(other)
38
- return key == other.key
39
- end
33
+ # Equality testing.
34
+ # true iff keys of both digrams are equal, false otherwise
35
+ # @param other [Digram] another to compare with
36
+ # @return [true/false]
37
+ def ==(other)
38
+ return key == other.key
39
+ end
40
40
 
41
- # Does the digram consists of twice the same symbols?
42
- # @return [true/false] true when symbols.first == symbols.last
43
- def repeating?()
44
- return symbols[0] == symbols[1]
45
- end
46
- end # class
41
+ # Does the digram consists of twice the same symbols?
42
+ # @return [true/false] true when symbols.first == symbols.last
43
+ def repeating?
44
+ return symbols[0] == symbols[1]
45
+ end
46
+ end # class
47
47
  end # module
48
48
 
49
49
  # End of file
@@ -2,100 +2,96 @@ require_relative 'production'
2
2
  require_relative 'grammar_visitor'
3
3
 
4
4
  module Sequitur # Module for classes implementing the Sequitur algorithm
5
- # A dynamic grammar is a context-free grammar that can be built incrementally.
6
- # Formally, a grammar has:
7
- # One start production
8
- # Zero or more other productions
9
- # Each production has a rhs that is a sequence of grammar symbols.
10
- # Grammar symbols are categorized into
11
- # -terminal symbols (i.e. String, Ruby Symbol,...)
12
- # -non-terminal symbols (i.e. ProductionRef)
13
- class DynamicGrammar
14
- # Link to the start production.
15
- attr_reader(:start)
16
-
17
- # The set of production rules of the grammar
18
- attr_reader(:productions)
19
-
20
- # nodoc Trace the execution of the algorithm.
21
- attr(:trace)
22
-
23
-
24
- # Constructor.
25
- # Build a grammar with one empty rule as start/start rule.
26
- def initialize()
27
- @start = Production.new
28
- @productions = [ start ]
29
- @trace = false
30
- end
31
-
32
- # Emit a text representation of the grammar.
33
- # Each production rule is emitted per line.
34
- # @return [String]
35
- def to_string()
36
- rule_text = productions.map(&:to_string).join("\n")
37
- return rule_text
38
- end
39
-
40
-
41
- # Add a given production to the grammar.
42
- # @param aProduction [Production]
43
- def add_production(aProduction)
44
- # TODO: remove output
45
- puts "Adding #{aProduction.object_id}" if trace
46
- puts aProduction.to_string if trace
47
- productions << aProduction
48
- end
49
-
50
-
51
- # Remove a production with given index from the grammar
52
- # @param anIndex [Fixnum]
53
- # @return [Production] the production removed from the grammar.
54
- def remove_production(anIndex)
55
- puts "Before production removal #{productions[anIndex].object_id}" if trace
56
- puts to_string if trace
57
- prod = productions.delete_at(anIndex)
58
- # TODO: remove output
59
- puts('Removed: ' + prod.to_string) if trace
60
- prod.clear_rhs
61
-
62
- return prod
63
- end
64
-
65
-
66
- # Add the given token to the grammar.
67
- # Append the token to the rhs of the start/start rule.
68
- # @param aToken [Object] input token to add
69
- def add_token(aToken)
70
- append_symbol_to(start, aToken)
71
- end
72
-
73
- # Part of the 'visitee' role in the Visitor design pattern.
74
- # A visitee is expected to accept the visit from a visitor object
75
- # @param aVisitor [GrammarVisitor] the visitor object
76
- def accept(aVisitor)
77
- aVisitor.start_visit_grammar(self)
78
-
79
- # Let's proceed with the visit of productions
80
- productions.each { |prod| prod.accept(aVisitor) }
81
-
82
- aVisitor.end_visit_grammar(self)
83
- end
84
-
85
- # Factory method. Returns a visitor for this grammar.
86
- # @return [GrammarVisitor]
87
- def visitor()
88
- return GrammarVisitor.new(self)
89
- end
90
-
91
- protected
92
-
93
- # Append a given symbol to the rhs of passed production.
94
- # @param aProduction [Production]
95
- # @param aSymbol [Object]
96
- def append_symbol_to(aProduction, aSymbol)
97
- aProduction.append_symbol(aSymbol)
98
- end
99
- end # class
5
+ # A dynamic grammar is a context-free grammar that can be built incrementally.
6
+ # Formally, a grammar has:
7
+ # One start production
8
+ # Zero or more other productions
9
+ # Each production has a rhs that is a sequence of grammar symbols.
10
+ # Grammar symbols are categorized into
11
+ # -terminal symbols (i.e. String, Ruby Symbol,...)
12
+ # -non-terminal symbols (i.e. ProductionRef)
13
+ class DynamicGrammar
14
+ # Link to the start production.
15
+ attr_reader(:start)
16
+
17
+ # The set of production rules of the grammar
18
+ attr_reader(:productions)
19
+
20
+ # nodoc Trace the execution of the algorithm.
21
+ attr(:trace)
22
+
23
+ # Constructor.
24
+ # Build a grammar with one empty rule as start/start rule.
25
+ def initialize
26
+ @start = Production.new
27
+ @productions = [start]
28
+ @trace = false
29
+ end
30
+
31
+ # Emit a text representation of the grammar.
32
+ # Each production rule is emitted per line.
33
+ # @return [String]
34
+ def to_string
35
+ rule_text = productions.map(&:to_string).join("\n")
36
+ return rule_text
37
+ end
38
+
39
+ # Add a given production to the grammar.
40
+ # @param aProduction [Production]
41
+ def add_production(aProduction)
42
+ # TODO: remove output
43
+ puts "Adding #{aProduction.object_id}" if trace
44
+ puts aProduction.to_string if trace
45
+ productions << aProduction
46
+ end
47
+
48
+ # Remove a production with given index from the grammar
49
+ # @param anIndex [Fixnum]
50
+ # @return [Production] the production removed from the grammar.
51
+ def remove_production(anIndex)
52
+ puts "Before production removal #{productions[anIndex].object_id}" if trace
53
+ puts to_string if trace
54
+ prod = productions.delete_at(anIndex)
55
+ # TODO: remove output
56
+ puts('Removed: ' + prod.to_string) if trace
57
+ prod.clear_rhs
58
+
59
+ return prod
60
+ end
61
+
62
+ # Add the given token to the grammar.
63
+ # Append the token to the rhs of the start/start rule.
64
+ # @param aToken [Object] input token to add
65
+ def add_token(aToken)
66
+ append_symbol_to(start, aToken)
67
+ end
68
+
69
+ # Part of the 'visitee' role in the Visitor design pattern.
70
+ # A visitee is expected to accept the visit from a visitor object
71
+ # @param aVisitor [GrammarVisitor] the visitor object
72
+ def accept(aVisitor)
73
+ aVisitor.start_visit_grammar(self)
74
+
75
+ # Let's proceed with the visit of productions
76
+ productions.each { |prod| prod.accept(aVisitor) }
77
+
78
+ aVisitor.end_visit_grammar(self)
79
+ end
80
+
81
+ # Factory method. Returns a visitor for this grammar.
82
+ # @return [GrammarVisitor]
83
+ def visitor
84
+ return GrammarVisitor.new(self)
85
+ end
86
+
87
+ protected
88
+
89
+ # Append a given symbol to the rhs of passed production.
90
+ # @param aProduction [Production]
91
+ # @param aSymbol [Object]
92
+ def append_symbol_to(aProduction, aSymbol)
93
+ aProduction.append_symbol(aSymbol)
94
+ end
95
+ end # class
100
96
  end # module
101
97
  # End of file
@@ -72,7 +72,7 @@ module Sequitur
72
72
  private
73
73
 
74
74
  # Read accessor of the production lookup
75
- def prod_lookup()
75
+ def prod_lookup
76
76
  return @prod_lookup
77
77
  end
78
78
 
@@ -109,11 +109,11 @@ module Sequitur
109
109
 
110
110
  private
111
111
 
112
- def indent()
112
+ def indent
113
113
  @indentation += 1
114
114
  end
115
115
 
116
- def dedent()
116
+ def dedent
117
117
  @indentation -= 1
118
118
  end
119
119
 
@@ -1,104 +1,102 @@
1
1
  module Sequitur # Module for classes implementing the Sequitur algorithm
2
2
  # A visitor class dedicated in the visit of Grammar.
3
- class GrammarVisitor
4
- # Link to the grammar to visit
5
- attr_reader(:grammar)
6
-
7
- # List of objects that subscribed to the visit event notification.
8
- attr_reader(:subscribers)
9
-
10
- # Build a visitor for the given grammar.
11
- # @param aGrammar [DynamicGrammar-like] the grammar to visit.
12
- def initialize(aGrammar)
13
- @grammar = aGrammar
14
- @subscribers = []
15
- end
16
-
17
- # Add a subscriber for the visit event notification.
18
- # @param aSubscriber [Object]
19
- def subscribe(aSubscriber)
20
- subscribers << aSubscriber
21
- end
22
-
23
- # Remove the given object from the subscription list.
24
- # The object won't be notified of visit events.
25
- # @param aSubscriber [Object]
26
- def unsubscribe(aSubscriber)
27
- subscribers.delete_if { |entry| entry == aSubscriber }
28
- end
29
-
30
- # The signal to start the visit.
31
- def start()
32
- grammar.accept(self)
33
- end
34
-
35
-
36
- # Visit event. The visitor is about to visit the grammar.
37
- # @param aGrammar [DynamicGrammar-like] the grammar to visit.
38
- def start_visit_grammar(aGrammar)
39
- broadcast(:before_grammar, aGrammar)
40
- end
41
-
42
-
43
- # Visit event. The visitor is about to visit the given production.
44
- # @param aProduction [Production] the production to visit.
45
- def start_visit_production(aProduction)
46
- broadcast(:before_production, aProduction)
47
- end
48
-
49
- # Visit event. The visitor is about to visit the given rhs of production.
50
- # @param rhs [SymbolSequence] the rhs of a production to visit.
51
- def start_visit_rhs(rhs)
52
- broadcast(:before_rhs, rhs)
53
- end
54
-
55
- # Visit event. The visitor is visiting the
56
- # given reference production (= non-terminal symbol).
57
- # @param aProdRef [ProductionRef] the production reference to visit.
58
- def visit_prod_ref(aProdRef)
59
- production = aProdRef.production
60
- broadcast(:before_non_terminal, production)
61
- broadcast(:after_non_terminal, production)
62
- end
63
-
64
- # Visit event. The visitor is visiting the
65
- # given terminal symbol.
66
- # @param aTerminal [Object] the terminal to visit.
67
- def visit_terminal(aTerminal)
68
- broadcast(:before_terminal, aTerminal)
69
- broadcast(:after_terminal, aTerminal)
70
- end
71
-
72
- # Visit event. The visitor has completed its visit of the given rhs.
73
- # @param rhs [SymbolSequence] the rhs of a production to visit.
74
- def end_visit_rhs(rhs)
75
- broadcast(:after_rhs, rhs)
76
- end
77
-
78
- # Visit event. The visitor has completed its visit of the given production.
79
- # @param aProduction [Production] the production to visit.
80
- def end_visit_production(aProduction)
81
- broadcast(:after_production, aProduction)
82
- end
83
-
84
- # Visit event. The visitor has completed the visit of the grammar.
85
- # @param aGrammar [DynamicGrammar-like] the grammar to visit.
86
- def end_visit_grammar(aGrammar)
87
- broadcast(:after_grammar, aGrammar)
88
- end
89
-
90
- private
91
-
92
- # Send a notification to all subscribers.
93
- # @param msg [Symbol] event to notify
94
- # @param args [Array] arguments of the notification.
95
- def broadcast(msg, *args)
96
- subscribers.each do |a_subscriber|
97
- next unless a_subscriber.respond_to?(msg)
98
- a_subscriber.send(msg, *args)
3
+ class GrammarVisitor
4
+ # Link to the grammar to visit
5
+ attr_reader(:grammar)
6
+
7
+ # List of objects that subscribed to the visit event notification.
8
+ attr_reader(:subscribers)
9
+
10
+ # Build a visitor for the given grammar.
11
+ # @param aGrammar [DynamicGrammar-like] the grammar to visit.
12
+ def initialize(aGrammar)
13
+ @grammar = aGrammar
14
+ @subscribers = []
99
15
  end
100
- end
101
- end # class
16
+
17
+ # Add a subscriber for the visit event notification.
18
+ # @param aSubscriber [Object]
19
+ def subscribe(aSubscriber)
20
+ subscribers << aSubscriber
21
+ end
22
+
23
+ # Remove the given object from the subscription list.
24
+ # The object won't be notified of visit events.
25
+ # @param aSubscriber [Object]
26
+ def unsubscribe(aSubscriber)
27
+ subscribers.delete_if { |entry| entry == aSubscriber }
28
+ end
29
+
30
+ # The signal to start the visit.
31
+ def start
32
+ grammar.accept(self)
33
+ end
34
+
35
+ # Visit event. The visitor is about to visit the grammar.
36
+ # @param aGrammar [DynamicGrammar-like] the grammar to visit.
37
+ def start_visit_grammar(aGrammar)
38
+ broadcast(:before_grammar, aGrammar)
39
+ end
40
+
41
+ # Visit event. The visitor is about to visit the given production.
42
+ # @param aProduction [Production] the production to visit.
43
+ def start_visit_production(aProduction)
44
+ broadcast(:before_production, aProduction)
45
+ end
46
+
47
+ # Visit event. The visitor is about to visit the given rhs of production.
48
+ # @param rhs [SymbolSequence] the rhs of a production to visit.
49
+ def start_visit_rhs(rhs)
50
+ broadcast(:before_rhs, rhs)
51
+ end
52
+
53
+ # Visit event. The visitor is visiting the
54
+ # given reference production (= non-terminal symbol).
55
+ # @param aProdRef [ProductionRef] the production reference to visit.
56
+ def visit_prod_ref(aProdRef)
57
+ production = aProdRef.production
58
+ broadcast(:before_non_terminal, production)
59
+ broadcast(:after_non_terminal, production)
60
+ end
61
+
62
+ # Visit event. The visitor is visiting the
63
+ # given terminal symbol.
64
+ # @param aTerminal [Object] the terminal to visit.
65
+ def visit_terminal(aTerminal)
66
+ broadcast(:before_terminal, aTerminal)
67
+ broadcast(:after_terminal, aTerminal)
68
+ end
69
+
70
+ # Visit event. The visitor has completed its visit of the given rhs.
71
+ # @param rhs [SymbolSequence] the rhs of a production to visit.
72
+ def end_visit_rhs(rhs)
73
+ broadcast(:after_rhs, rhs)
74
+ end
75
+
76
+ # Visit event. The visitor has completed its visit of the given production.
77
+ # @param aProduction [Production] the production to visit.
78
+ def end_visit_production(aProduction)
79
+ broadcast(:after_production, aProduction)
80
+ end
81
+
82
+ # Visit event. The visitor has completed the visit of the grammar.
83
+ # @param aGrammar [DynamicGrammar-like] the grammar to visit.
84
+ def end_visit_grammar(aGrammar)
85
+ broadcast(:after_grammar, aGrammar)
86
+ end
87
+
88
+ private
89
+
90
+ # Send a notification to all subscribers.
91
+ # @param msg [Symbol] event to notify
92
+ # @param args [Array] arguments of the notification.
93
+ def broadcast(msg, *args)
94
+ subscribers.each do |a_subscriber|
95
+ next unless a_subscriber.respond_to?(msg)
96
+ a_subscriber.send(msg, *args)
97
+ end
98
+ end
99
+ end # class
102
100
  end # module
103
101
 
104
102
  # End of file