sequitur 0.1.18 → 0.1.23
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.
- checksums.yaml +5 -5
- data/.rubocop.yml +430 -56
- data/.travis.yml +19 -13
- data/CHANGELOG.md +33 -0
- data/Gemfile +4 -3
- data/LICENSE.txt +1 -1
- data/README.md +1 -2
- data/Rakefile +2 -0
- data/appveyor.yml +20 -12
- data/examples/integer_sample.rb +8 -7
- data/examples/porridge.rb +6 -6
- data/examples/simple_case.rb +7 -6
- data/examples/symbol_sample.rb +8 -9
- data/examples/word_sample.rb +4 -3
- data/lib/sequitur/constants.rb +5 -3
- data/lib/sequitur/digram.rb +45 -43
- data/lib/sequitur/dynamic_grammar.rb +93 -95
- data/lib/sequitur/formatter/base_formatter.rb +3 -1
- data/lib/sequitur/formatter/base_text.rb +3 -1
- data/lib/sequitur/formatter/debug.rb +5 -3
- data/lib/sequitur/grammar_visitor.rb +99 -98
- data/lib/sequitur/production.rb +14 -19
- data/lib/sequitur/production_ref.rb +107 -106
- data/lib/sequitur/sequitur_grammar.rb +9 -6
- data/lib/sequitur/symbol_sequence.rb +10 -11
- data/lib/sequitur.rb +2 -0
- data/spec/sequitur/digram_spec.rb +10 -8
- data/spec/sequitur/dynamic_grammar_spec.rb +2 -0
- data/spec/sequitur/formatter/base_text_spec.rb +4 -2
- data/spec/sequitur/formatter/debug_spec.rb +4 -2
- data/spec/sequitur/grammar_visitor_spec.rb +2 -0
- data/spec/sequitur/production_ref_spec.rb +2 -0
- data/spec/sequitur/production_spec.rb +10 -8
- data/spec/sequitur/sequitur_grammar_spec.rb +13 -13
- data/spec/sequitur/symbol_sequence_spec.rb +6 -4
- data/spec/spec_helper.rb +2 -12
- metadata +18 -46
- data/.ruby-version +0 -1
- data/.simplecov +0 -7
@@ -1,101 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'production'
|
2
4
|
require_relative 'grammar_visitor'
|
3
5
|
|
4
6
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
#
|
96
|
-
def append_symbol_to(aProduction, aSymbol)
|
97
|
-
aProduction.append_symbol(aSymbol)
|
98
|
-
end
|
99
|
-
end # class
|
7
|
+
# A dynamic grammar is a context-free grammar that can be built incrementally.
|
8
|
+
# Formally, a grammar has:
|
9
|
+
# One start production
|
10
|
+
# Zero or more other productions
|
11
|
+
# Each production has a rhs that is a sequence of grammar symbols.
|
12
|
+
# Grammar symbols are categorized into
|
13
|
+
# -terminal symbols (i.e. String, Ruby Symbol,...)
|
14
|
+
# -non-terminal symbols (i.e. ProductionRef)
|
15
|
+
class DynamicGrammar
|
16
|
+
# Link to the start production.
|
17
|
+
attr_reader(:start)
|
18
|
+
|
19
|
+
# The set of production rules of the grammar
|
20
|
+
attr_reader(:productions)
|
21
|
+
|
22
|
+
# nodoc Trace the execution of the algorithm.
|
23
|
+
attr_accessor(:trace)
|
24
|
+
|
25
|
+
# Constructor.
|
26
|
+
# Build a grammar with one empty rule as start/start rule.
|
27
|
+
def initialize
|
28
|
+
@start = Production.new
|
29
|
+
@productions = [start]
|
30
|
+
@trace = false
|
31
|
+
end
|
32
|
+
|
33
|
+
# Emit a text representation of the grammar.
|
34
|
+
# Each production rule is emitted per line.
|
35
|
+
# @return [String]
|
36
|
+
def to_string
|
37
|
+
rule_text = productions.map(&:to_string).join("\n")
|
38
|
+
return rule_text
|
39
|
+
end
|
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
|
+
# Remove a production with given index from the grammar
|
51
|
+
# @param anIndex [Fixnum]
|
52
|
+
# @return [Production] the production removed from the grammar.
|
53
|
+
def remove_production(anIndex)
|
54
|
+
puts "Before production removal #{productions[anIndex].object_id}" if trace
|
55
|
+
puts to_string if trace
|
56
|
+
prod = productions.delete_at(anIndex)
|
57
|
+
# TODO: remove output
|
58
|
+
puts("Removed: #{prod.to_string}") if trace
|
59
|
+
prod.clear_rhs
|
60
|
+
|
61
|
+
return prod
|
62
|
+
end
|
63
|
+
|
64
|
+
# Add the given token to the grammar.
|
65
|
+
# Append the token to the rhs of the start/start rule.
|
66
|
+
# @param aToken [Object] input token to add
|
67
|
+
def add_token(aToken)
|
68
|
+
append_symbol_to(start, aToken)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Part of the 'visitee' role in the Visitor design pattern.
|
72
|
+
# A visitee is expected to accept the visit from a visitor object
|
73
|
+
# @param aVisitor [GrammarVisitor] the visitor object
|
74
|
+
def accept(aVisitor)
|
75
|
+
aVisitor.start_visit_grammar(self)
|
76
|
+
|
77
|
+
# Let's proceed with the visit of productions
|
78
|
+
productions.each { |prod| prod.accept(aVisitor) }
|
79
|
+
|
80
|
+
aVisitor.end_visit_grammar(self)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Factory method. Returns a visitor for this grammar.
|
84
|
+
# @return [GrammarVisitor]
|
85
|
+
def visitor
|
86
|
+
return GrammarVisitor.new(self)
|
87
|
+
end
|
88
|
+
|
89
|
+
protected
|
90
|
+
|
91
|
+
# Append a given symbol to the rhs of passed production.
|
92
|
+
# @param aProduction [Production]
|
93
|
+
# @param aSymbol [Object]
|
94
|
+
def append_symbol_to(aProduction, aSymbol)
|
95
|
+
aProduction.append_symbol(aSymbol)
|
96
|
+
end
|
97
|
+
end # class
|
100
98
|
end # module
|
101
99
|
# End of file
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Sequitur
|
2
4
|
# Namespace dedicated to grammar formatters.
|
3
5
|
module Formatter
|
4
6
|
# Superclass for grammar formatters.
|
5
7
|
class BaseFormatter
|
6
8
|
# The IO output stream in which the formatter's result will be sent.
|
7
|
-
|
9
|
+
attr_accessor(:output)
|
8
10
|
|
9
11
|
# Constructor.
|
10
12
|
# @param anIO [IO] an output IO where the formatter's result will
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'base_formatter'
|
2
4
|
|
3
5
|
module Sequitur
|
@@ -72,7 +74,7 @@ module Sequitur
|
|
72
74
|
private
|
73
75
|
|
74
76
|
# Read accessor of the production lookup
|
75
|
-
def prod_lookup
|
77
|
+
def prod_lookup
|
76
78
|
return @prod_lookup
|
77
79
|
end
|
78
80
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'base_formatter'
|
2
4
|
|
3
5
|
|
@@ -13,7 +15,7 @@ module Sequitur
|
|
13
15
|
# formatter.run(some_grammar.visitor)
|
14
16
|
class Debug < BaseFormatter
|
15
17
|
# Current indentation level
|
16
|
-
|
18
|
+
attr_accessor(:indentation)
|
17
19
|
|
18
20
|
# Constructor.
|
19
21
|
# @param anIO [IO] The output stream to which the rendered grammar
|
@@ -109,11 +111,11 @@ module Sequitur
|
|
109
111
|
|
110
112
|
private
|
111
113
|
|
112
|
-
def indent
|
114
|
+
def indent
|
113
115
|
@indentation += 1
|
114
116
|
end
|
115
117
|
|
116
|
-
def dedent
|
118
|
+
def dedent
|
117
119
|
@indentation -= 1
|
118
120
|
end
|
119
121
|
|
@@ -1,104 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Sequitur # Module for classes implementing the Sequitur algorithm
|
2
4
|
# A visitor class dedicated in the visit of Grammar.
|
3
|
-
class GrammarVisitor
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
5
|
+
class GrammarVisitor
|
6
|
+
# Link to the grammar to visit
|
7
|
+
attr_reader(:grammar)
|
8
|
+
|
9
|
+
# List of objects that subscribed to the visit event notification.
|
10
|
+
attr_reader(:subscribers)
|
11
|
+
|
12
|
+
# Build a visitor for the given grammar.
|
13
|
+
# @param aGrammar [DynamicGrammar-like] the grammar to visit.
|
14
|
+
def initialize(aGrammar)
|
15
|
+
@grammar = aGrammar
|
16
|
+
@subscribers = []
|
17
|
+
end
|
18
|
+
|
19
|
+
# Add a subscriber for the visit event notification.
|
20
|
+
# @param aSubscriber [Object]
|
21
|
+
def subscribe(aSubscriber)
|
22
|
+
subscribers << aSubscriber
|
23
|
+
end
|
24
|
+
|
25
|
+
# Remove the given object from the subscription list.
|
26
|
+
# The object won't be notified of visit events.
|
27
|
+
# @param aSubscriber [Object]
|
28
|
+
def unsubscribe(aSubscriber)
|
29
|
+
subscribers.delete_if { |entry| entry == aSubscriber }
|
30
|
+
end
|
31
|
+
|
32
|
+
# The signal to start the visit.
|
33
|
+
def start
|
34
|
+
grammar.accept(self)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Visit event. The visitor is about to visit the grammar.
|
38
|
+
# @param aGrammar [DynamicGrammar-like] the grammar to visit.
|
39
|
+
def start_visit_grammar(aGrammar)
|
40
|
+
broadcast(:before_grammar, aGrammar)
|
41
|
+
end
|
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
|
+
|
99
|
+
a_subscriber.send(msg, *args)
|
100
|
+
end
|
99
101
|
end
|
100
|
-
end
|
101
|
-
end # class
|
102
|
+
end # class
|
102
103
|
end # module
|
103
104
|
|
104
105
|
# End of file
|