sequitur 0.1.10 → 0.1.11
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 +8 -8
- data/CHANGELOG.md +4 -0
- data/README.md +58 -1
- data/Rakefile +31 -31
- data/examples/integer_sample.rb +33 -0
- data/examples/porridge.rb +41 -0
- data/examples/simple_case.rb +27 -0
- data/examples/symbol_sample.rb +28 -0
- data/examples/word_sample.rb +30 -0
- data/lib/sequitur.rb +1 -1
- data/lib/sequitur/constants.rb +1 -1
- data/lib/sequitur/digram.rb +52 -52
- data/lib/sequitur/dynamic_grammar.rb +106 -106
- data/lib/sequitur/formatter/base_formatter.rb +39 -39
- data/lib/sequitur/formatter/base_text.rb +95 -95
- data/lib/sequitur/formatter/debug.rb +131 -131
- data/lib/sequitur/grammar_visitor.rb +110 -110
- data/lib/sequitur/production.rb +243 -243
- data/lib/sequitur/production_ref.rb +119 -119
- data/lib/sequitur/sequitur_grammar.rb +158 -158
- data/lib/sequitur/symbol_sequence.rb +182 -182
- data/spec/sequitur/sequitur_grammar_spec.rb +75 -3
- metadata +7 -2
@@ -1,106 +1,106 @@
|
|
1
|
-
require_relative 'production'
|
2
|
-
require_relative 'grammar_visitor'
|
3
|
-
|
4
|
-
module Sequitur # Module for classes implementing the Sequitur algorithm
|
5
|
-
|
6
|
-
# A dynamic grammar is a context-free grammar that can be built incrementally.
|
7
|
-
# Formally, a grammar has:
|
8
|
-
# One start production
|
9
|
-
# Zero or more other productions
|
10
|
-
# Each production has a rhs that is a sequence of grammar symbols.
|
11
|
-
# Grammar symbols are categorized into
|
12
|
-
# -terminal symbols (i.e. String, Ruby Symbol,...)
|
13
|
-
# -non-terminal symbols (i.e. ProductionRef)
|
14
|
-
class DynamicGrammar
|
15
|
-
# Link to the start production.
|
16
|
-
attr_reader(:start)
|
17
|
-
|
18
|
-
# The set of production rules of the grammar
|
19
|
-
attr_reader(:productions)
|
20
|
-
|
21
|
-
# nodoc Trace the execution of the algorithm.
|
22
|
-
attr(:trace)
|
23
|
-
|
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
|
-
public
|
34
|
-
|
35
|
-
# Emit a text representation of the grammar.
|
36
|
-
# Each production rule is emitted per line.
|
37
|
-
# @return [String]
|
38
|
-
def to_string()
|
39
|
-
rule_text = productions.map(&:to_string).join("\n")
|
40
|
-
return rule_text
|
41
|
-
end
|
42
|
-
|
43
|
-
|
44
|
-
# Add a given production to the grammar.
|
45
|
-
# @param aProduction [Production]
|
46
|
-
def add_production(aProduction)
|
47
|
-
# TODO: remove output
|
48
|
-
puts "Adding #{aProduction.object_id}" if trace
|
49
|
-
puts aProduction.to_string if trace
|
50
|
-
productions << aProduction
|
51
|
-
end
|
52
|
-
|
53
|
-
|
54
|
-
# Remove a production with given index from the grammar
|
55
|
-
# @param anIndex [Fixnum]
|
56
|
-
# @return [Production] the production removed from the grammar.
|
57
|
-
def remove_production(anIndex)
|
58
|
-
puts "Before production removal #{productions[anIndex].object_id}" if trace
|
59
|
-
puts to_string if trace
|
60
|
-
prod = productions.delete_at(anIndex)
|
61
|
-
# TODO: remove output
|
62
|
-
puts('Removed: ' + prod.to_string) if trace
|
63
|
-
prod.clear_rhs
|
64
|
-
|
65
|
-
return prod
|
66
|
-
end
|
67
|
-
|
68
|
-
|
69
|
-
# Add the given token to the grammar.
|
70
|
-
# Append the token to the rhs of the start/start rule.
|
71
|
-
# @param aToken [Object] input token to add
|
72
|
-
def add_token(aToken)
|
73
|
-
append_symbol_to(start, aToken)
|
74
|
-
end
|
75
|
-
|
76
|
-
# Part of the 'visitee' role in the Visitor design pattern.
|
77
|
-
# A visitee is expected to accept the visit from a visitor object
|
78
|
-
# @param aVisitor [GrammarVisitor] the visitor object
|
79
|
-
def accept(aVisitor)
|
80
|
-
aVisitor.start_visit_grammar(self)
|
81
|
-
|
82
|
-
# Let's proceed with the visit of productions
|
83
|
-
productions.each { |prod| prod.accept(aVisitor) }
|
84
|
-
|
85
|
-
aVisitor.end_visit_grammar(self)
|
86
|
-
end
|
87
|
-
|
88
|
-
# Factory method. Returns a visitor for this grammar.
|
89
|
-
# @return [GrammarVisitor]
|
90
|
-
def visitor()
|
91
|
-
return GrammarVisitor.new(self)
|
92
|
-
end
|
93
|
-
|
94
|
-
protected
|
95
|
-
|
96
|
-
# Append a given symbol to the rhs of passed production.
|
97
|
-
# @param aProduction [Production]
|
98
|
-
# @param aSymbol [Object]
|
99
|
-
def append_symbol_to(aProduction, aSymbol)
|
100
|
-
aProduction.append_symbol(aSymbol)
|
101
|
-
end
|
102
|
-
end # class
|
103
|
-
|
104
|
-
end # module
|
105
|
-
|
106
|
-
# End of file
|
1
|
+
require_relative 'production'
|
2
|
+
require_relative 'grammar_visitor'
|
3
|
+
|
4
|
+
module Sequitur # Module for classes implementing the Sequitur algorithm
|
5
|
+
|
6
|
+
# A dynamic grammar is a context-free grammar that can be built incrementally.
|
7
|
+
# Formally, a grammar has:
|
8
|
+
# One start production
|
9
|
+
# Zero or more other productions
|
10
|
+
# Each production has a rhs that is a sequence of grammar symbols.
|
11
|
+
# Grammar symbols are categorized into
|
12
|
+
# -terminal symbols (i.e. String, Ruby Symbol,...)
|
13
|
+
# -non-terminal symbols (i.e. ProductionRef)
|
14
|
+
class DynamicGrammar
|
15
|
+
# Link to the start production.
|
16
|
+
attr_reader(:start)
|
17
|
+
|
18
|
+
# The set of production rules of the grammar
|
19
|
+
attr_reader(:productions)
|
20
|
+
|
21
|
+
# nodoc Trace the execution of the algorithm.
|
22
|
+
attr(:trace)
|
23
|
+
|
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
|
+
public
|
34
|
+
|
35
|
+
# Emit a text representation of the grammar.
|
36
|
+
# Each production rule is emitted per line.
|
37
|
+
# @return [String]
|
38
|
+
def to_string()
|
39
|
+
rule_text = productions.map(&:to_string).join("\n")
|
40
|
+
return rule_text
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
# Add a given production to the grammar.
|
45
|
+
# @param aProduction [Production]
|
46
|
+
def add_production(aProduction)
|
47
|
+
# TODO: remove output
|
48
|
+
puts "Adding #{aProduction.object_id}" if trace
|
49
|
+
puts aProduction.to_string if trace
|
50
|
+
productions << aProduction
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
# Remove a production with given index from the grammar
|
55
|
+
# @param anIndex [Fixnum]
|
56
|
+
# @return [Production] the production removed from the grammar.
|
57
|
+
def remove_production(anIndex)
|
58
|
+
puts "Before production removal #{productions[anIndex].object_id}" if trace
|
59
|
+
puts to_string if trace
|
60
|
+
prod = productions.delete_at(anIndex)
|
61
|
+
# TODO: remove output
|
62
|
+
puts('Removed: ' + prod.to_string) if trace
|
63
|
+
prod.clear_rhs
|
64
|
+
|
65
|
+
return prod
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
# Add the given token to the grammar.
|
70
|
+
# Append the token to the rhs of the start/start rule.
|
71
|
+
# @param aToken [Object] input token to add
|
72
|
+
def add_token(aToken)
|
73
|
+
append_symbol_to(start, aToken)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Part of the 'visitee' role in the Visitor design pattern.
|
77
|
+
# A visitee is expected to accept the visit from a visitor object
|
78
|
+
# @param aVisitor [GrammarVisitor] the visitor object
|
79
|
+
def accept(aVisitor)
|
80
|
+
aVisitor.start_visit_grammar(self)
|
81
|
+
|
82
|
+
# Let's proceed with the visit of productions
|
83
|
+
productions.each { |prod| prod.accept(aVisitor) }
|
84
|
+
|
85
|
+
aVisitor.end_visit_grammar(self)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Factory method. Returns a visitor for this grammar.
|
89
|
+
# @return [GrammarVisitor]
|
90
|
+
def visitor()
|
91
|
+
return GrammarVisitor.new(self)
|
92
|
+
end
|
93
|
+
|
94
|
+
protected
|
95
|
+
|
96
|
+
# Append a given symbol to the rhs of passed production.
|
97
|
+
# @param aProduction [Production]
|
98
|
+
# @param aSymbol [Object]
|
99
|
+
def append_symbol_to(aProduction, aSymbol)
|
100
|
+
aProduction.append_symbol(aSymbol)
|
101
|
+
end
|
102
|
+
end # class
|
103
|
+
|
104
|
+
end # module
|
105
|
+
|
106
|
+
# End of file
|
@@ -1,39 +1,39 @@
|
|
1
|
-
module Sequitur
|
2
|
-
|
3
|
-
# Namespace dedicated to grammar formatters.
|
4
|
-
module Formatter
|
5
|
-
|
6
|
-
# Superclass for grammar formatters.
|
7
|
-
class BaseFormatter
|
8
|
-
# The IO output stream in which the formatter's result will be sent.
|
9
|
-
attr(:output)
|
10
|
-
|
11
|
-
# Constructor.
|
12
|
-
# @param anIO [IO] an output IO where the formatter's result will
|
13
|
-
# be placed.
|
14
|
-
def initialize(anIO)
|
15
|
-
@output = anIO
|
16
|
-
end
|
17
|
-
|
18
|
-
public
|
19
|
-
|
20
|
-
# Given a grammar or a grammar visitor, perform the visit
|
21
|
-
# and render the visit events in the output stream.
|
22
|
-
# @param aGrmOrVisitor [DynamicGrammar or GrammarVisitor]
|
23
|
-
def render(aGrmOrVisitor)
|
24
|
-
if aGrmOrVisitor.kind_of?(GrammarVisitor)
|
25
|
-
a_visitor = aGrmOrVisitor
|
26
|
-
else
|
27
|
-
a_visitor = aGrmOrVisitor.visitor
|
28
|
-
end
|
29
|
-
|
30
|
-
a_visitor.subscribe(self)
|
31
|
-
a_visitor.start
|
32
|
-
a_visitor.unsubscribe(self)
|
33
|
-
end
|
34
|
-
|
35
|
-
end # class
|
36
|
-
end # module
|
37
|
-
end # module
|
38
|
-
|
39
|
-
# End of file
|
1
|
+
module Sequitur
|
2
|
+
|
3
|
+
# Namespace dedicated to grammar formatters.
|
4
|
+
module Formatter
|
5
|
+
|
6
|
+
# Superclass for grammar formatters.
|
7
|
+
class BaseFormatter
|
8
|
+
# The IO output stream in which the formatter's result will be sent.
|
9
|
+
attr(:output)
|
10
|
+
|
11
|
+
# Constructor.
|
12
|
+
# @param anIO [IO] an output IO where the formatter's result will
|
13
|
+
# be placed.
|
14
|
+
def initialize(anIO)
|
15
|
+
@output = anIO
|
16
|
+
end
|
17
|
+
|
18
|
+
public
|
19
|
+
|
20
|
+
# Given a grammar or a grammar visitor, perform the visit
|
21
|
+
# and render the visit events in the output stream.
|
22
|
+
# @param aGrmOrVisitor [DynamicGrammar or GrammarVisitor]
|
23
|
+
def render(aGrmOrVisitor)
|
24
|
+
if aGrmOrVisitor.kind_of?(GrammarVisitor)
|
25
|
+
a_visitor = aGrmOrVisitor
|
26
|
+
else
|
27
|
+
a_visitor = aGrmOrVisitor.visitor
|
28
|
+
end
|
29
|
+
|
30
|
+
a_visitor.subscribe(self)
|
31
|
+
a_visitor.start
|
32
|
+
a_visitor.unsubscribe(self)
|
33
|
+
end
|
34
|
+
|
35
|
+
end # class
|
36
|
+
end # module
|
37
|
+
end # module
|
38
|
+
|
39
|
+
# End of file
|
@@ -1,95 +1,95 @@
|
|
1
|
-
require_relative 'base_formatter'
|
2
|
-
|
3
|
-
module Sequitur
|
4
|
-
module Formatter
|
5
|
-
|
6
|
-
# A formatter class that can render a dynamic grammar in plain text.
|
7
|
-
# @example
|
8
|
-
# some_grammar = ... # Points to a DynamicGrammar-like object
|
9
|
-
# # Output the result to the standard console output
|
10
|
-
# formatter = Sequitur::Formatter::BaseText.new(STDOUT)
|
11
|
-
# # Render the grammar (through a visitor)
|
12
|
-
# formatter.run(some_grammar.visitor)
|
13
|
-
class BaseText < BaseFormatter
|
14
|
-
|
15
|
-
# Constructor.
|
16
|
-
# @param anIO [IO] The output stream to which the rendered grammar
|
17
|
-
# is written.
|
18
|
-
def initialize(anIO)
|
19
|
-
super(anIO)
|
20
|
-
@prod_lookup = {}
|
21
|
-
end
|
22
|
-
|
23
|
-
public
|
24
|
-
|
25
|
-
# Method called by a GrammarVisitor to which the formatter subscribed.
|
26
|
-
# Notification of a visit event: the visitor is about to visit a grammar
|
27
|
-
# @param aGrammar [DynamicGrammar-like object]
|
28
|
-
def before_grammar(aGrammar)
|
29
|
-
aGrammar.productions.each_with_index do |a_prod, index|
|
30
|
-
prod_lookup[a_prod] = index
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
# Method called by a GrammarVisitor to which the formatter subscribed.
|
35
|
-
# Notification of a visit event: the visitor is about to visit
|
36
|
-
# a production
|
37
|
-
# @param aProduction [aProduction]
|
38
|
-
def before_production(aProduction)
|
39
|
-
p_name = prod_name(aProduction)
|
40
|
-
output.print p_name
|
41
|
-
end
|
42
|
-
|
43
|
-
# Method called by a GrammarVisitor to which the formatter subscribed.
|
44
|
-
# Notification of a visit event: the visitor is about to visit
|
45
|
-
# the rhs of a production
|
46
|
-
# @param _ [Array]
|
47
|
-
def before_rhs(_)
|
48
|
-
output.print ' :'
|
49
|
-
end
|
50
|
-
|
51
|
-
# Method called by a GrammarVisitor to which the formatter subscribed.
|
52
|
-
# Notification of a visit event: the visitor is about to visit
|
53
|
-
# a terminal symbol from the rhs of a production
|
54
|
-
# @param aSymbol [Object]
|
55
|
-
def before_terminal(aSymbol)
|
56
|
-
output.print " #{aSymbol}"
|
57
|
-
end
|
58
|
-
|
59
|
-
# Method called by a GrammarVisitor to which the formatter subscribed.
|
60
|
-
# Notification of a visit event: the visitor is about to visit
|
61
|
-
# a non-terminal (= an allusion to a production) in the rhs of a
|
62
|
-
# production
|
63
|
-
# @param aProduction [Production] a production occurring in the rhs
|
64
|
-
def before_non_terminal(aProduction)
|
65
|
-
p_name = prod_name(aProduction)
|
66
|
-
output.print " #{p_name}"
|
67
|
-
end
|
68
|
-
|
69
|
-
# Method called by a GrammarVisitor to which the formatter subscribed.
|
70
|
-
# Notification of a visit event: the visitor complete the visit
|
71
|
-
# of a production
|
72
|
-
def after_production(_)
|
73
|
-
output.print ".\n"
|
74
|
-
end
|
75
|
-
|
76
|
-
private
|
77
|
-
|
78
|
-
# Read accessor of the production lookup
|
79
|
-
def prod_lookup()
|
80
|
-
return @prod_lookup
|
81
|
-
end
|
82
|
-
|
83
|
-
# Generate a name of a given production.
|
84
|
-
# @param aProduction [Production]
|
85
|
-
def prod_name(aProduction)
|
86
|
-
prod_index = prod_lookup[aProduction]
|
87
|
-
name = (prod_index == 0) ? 'start' : "P#{prod_index}"
|
88
|
-
return name
|
89
|
-
end
|
90
|
-
|
91
|
-
end # class
|
92
|
-
end # module
|
93
|
-
end # module
|
94
|
-
|
95
|
-
# End of file
|
1
|
+
require_relative 'base_formatter'
|
2
|
+
|
3
|
+
module Sequitur
|
4
|
+
module Formatter
|
5
|
+
|
6
|
+
# A formatter class that can render a dynamic grammar in plain text.
|
7
|
+
# @example
|
8
|
+
# some_grammar = ... # Points to a DynamicGrammar-like object
|
9
|
+
# # Output the result to the standard console output
|
10
|
+
# formatter = Sequitur::Formatter::BaseText.new(STDOUT)
|
11
|
+
# # Render the grammar (through a visitor)
|
12
|
+
# formatter.run(some_grammar.visitor)
|
13
|
+
class BaseText < BaseFormatter
|
14
|
+
|
15
|
+
# Constructor.
|
16
|
+
# @param anIO [IO] The output stream to which the rendered grammar
|
17
|
+
# is written.
|
18
|
+
def initialize(anIO)
|
19
|
+
super(anIO)
|
20
|
+
@prod_lookup = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
public
|
24
|
+
|
25
|
+
# Method called by a GrammarVisitor to which the formatter subscribed.
|
26
|
+
# Notification of a visit event: the visitor is about to visit a grammar
|
27
|
+
# @param aGrammar [DynamicGrammar-like object]
|
28
|
+
def before_grammar(aGrammar)
|
29
|
+
aGrammar.productions.each_with_index do |a_prod, index|
|
30
|
+
prod_lookup[a_prod] = index
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Method called by a GrammarVisitor to which the formatter subscribed.
|
35
|
+
# Notification of a visit event: the visitor is about to visit
|
36
|
+
# a production
|
37
|
+
# @param aProduction [aProduction]
|
38
|
+
def before_production(aProduction)
|
39
|
+
p_name = prod_name(aProduction)
|
40
|
+
output.print p_name
|
41
|
+
end
|
42
|
+
|
43
|
+
# Method called by a GrammarVisitor to which the formatter subscribed.
|
44
|
+
# Notification of a visit event: the visitor is about to visit
|
45
|
+
# the rhs of a production
|
46
|
+
# @param _ [Array]
|
47
|
+
def before_rhs(_)
|
48
|
+
output.print ' :'
|
49
|
+
end
|
50
|
+
|
51
|
+
# Method called by a GrammarVisitor to which the formatter subscribed.
|
52
|
+
# Notification of a visit event: the visitor is about to visit
|
53
|
+
# a terminal symbol from the rhs of a production
|
54
|
+
# @param aSymbol [Object]
|
55
|
+
def before_terminal(aSymbol)
|
56
|
+
output.print " #{aSymbol}"
|
57
|
+
end
|
58
|
+
|
59
|
+
# Method called by a GrammarVisitor to which the formatter subscribed.
|
60
|
+
# Notification of a visit event: the visitor is about to visit
|
61
|
+
# a non-terminal (= an allusion to a production) in the rhs of a
|
62
|
+
# production
|
63
|
+
# @param aProduction [Production] a production occurring in the rhs
|
64
|
+
def before_non_terminal(aProduction)
|
65
|
+
p_name = prod_name(aProduction)
|
66
|
+
output.print " #{p_name}"
|
67
|
+
end
|
68
|
+
|
69
|
+
# Method called by a GrammarVisitor to which the formatter subscribed.
|
70
|
+
# Notification of a visit event: the visitor complete the visit
|
71
|
+
# of a production
|
72
|
+
def after_production(_)
|
73
|
+
output.print ".\n"
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# Read accessor of the production lookup
|
79
|
+
def prod_lookup()
|
80
|
+
return @prod_lookup
|
81
|
+
end
|
82
|
+
|
83
|
+
# Generate a name of a given production.
|
84
|
+
# @param aProduction [Production]
|
85
|
+
def prod_name(aProduction)
|
86
|
+
prod_index = prod_lookup[aProduction]
|
87
|
+
name = (prod_index == 0) ? 'start' : "P#{prod_index}"
|
88
|
+
return name
|
89
|
+
end
|
90
|
+
|
91
|
+
end # class
|
92
|
+
end # module
|
93
|
+
end # module
|
94
|
+
|
95
|
+
# End of file
|