rley 0.0.18 → 0.1.00
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 +13 -0
- data/lib/rley/constants.rb +1 -1
- data/lib/rley/parser/dotted_item.rb +27 -1
- data/lib/rley/parser/parse_state.rb +25 -6
- data/lib/rley/parser/parsing.rb +68 -10
- data/lib/rley/parser/state_set.rb +21 -0
- data/lib/rley/ptree/non_terminal_node.rb +21 -0
- data/lib/rley/ptree/parse_tree.rb +95 -0
- data/lib/rley/ptree/parse_tree_node.rb +25 -0
- data/lib/rley/ptree/terminal_node.rb +16 -0
- data/lib/rley/ptree/token_range.rb +61 -0
- data/lib/rley/syntax/grammar.rb +16 -5
- data/spec/rley/parser/dotted_item_spec.rb +32 -4
- data/spec/rley/parser/parse_state_spec.rb +9 -0
- data/spec/rley/parser/parsing_spec.rb +39 -13
- data/spec/rley/parser/state_set_spec.rb +16 -0
- data/spec/rley/ptree/non_terminal_node_spec.rb +36 -0
- data/spec/rley/ptree/parse_tree_node_spec.rb +32 -0
- data/spec/rley/ptree/parse_tree_spec.rb +95 -0
- data/spec/rley/ptree/token_range_spec.rb +103 -0
- data/spec/rley/syntax/grammar_spec.rb +21 -8
- metadata +15 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MzdkNmMxNWFhMmZjYWY2MzUwM2JhZjUyYzQxYmJjMzdiZmFjMjRhOQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ODFlMzBlNjNjYjQyODIyNjQwYzY2OTUzMTAyNDI3NGIyMmFkOTcxMg==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
M2EyMDU5MDQ2YzFiYTMyNGU1OWYxYWQ2ODBjNjllZjNhZTUzZjU3NmY0YzE0
|
10
|
+
ODYzMTRiZjZmNzg2ZjZmMTIzZWI2MmNiNzY4M2ZiYjljZTcyN2E4MGNjZDNl
|
11
|
+
ZTQ5YTQ0OWRjMmFlNGIwNTFmMjU2Y2E4YzJkMjFiZGM4NjU4NDI=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZTRlMjdhNGM3MTIzZTUyZmQ5MDVjZGMxZjFlNzI0MTI3NTIyZjNlZDBiYmEz
|
14
|
+
ZTU1MDQ4NTBlNzJjYmRiMDRlZTdmODdhYTUwY2NiZDcxMTVlYTM5NjgwZDcz
|
15
|
+
MzM0YTBjZDJiMGI1YWMxMDI0MzYzOGQ4ZDU5ZjIwNWZkZDY2ZWY=
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
### 0.1.00 / 2014-12-05
|
2
|
+
* [CHANGE] Bumped version number: it is the first version able to generate a parse tree.
|
3
|
+
* [NEW] `Grammar#name2symbol` attribute and accessor. Retrieve a grammar symbol from its name.
|
4
|
+
* [NEW] Methods `DottedItem#prev_symbol`, `DottedItem#prev_position` to find symbol on left of dot.
|
5
|
+
* [NEW] Method `ParseState#precedes?`, predicate to check whether self is a predecessor of given parse state.
|
6
|
+
* [NEW] Method `Parsing#parse_tree` returns a ParseTree object that represents the result of a parse.
|
7
|
+
|
8
|
+
|
9
|
+
### 0.0.18 / 2014-11-23
|
10
|
+
* [CHANGE] `EarleyParser#parse`: Optimization prevent repeated prediction of same non-terminal for same state set.
|
11
|
+
* [CHANGE] File `earley_parser_spec.rb`: Added new test for nullable grammar.
|
12
|
+
* [CHANGE] Style refactoring in classes `EarleyParser`, `StateSet`, `Grammar`, `NonTerminal`
|
13
|
+
|
1
14
|
### 0.0.17 / 2014-11-23
|
2
15
|
* [CHANGE] File `earley_parser_spec.rb`: Added step-by-step test of ambiguous grammar parsing.
|
3
16
|
|
data/lib/rley/constants.rb
CHANGED
@@ -21,7 +21,7 @@ module Rley # This module is used as a namespace
|
|
21
21
|
# Index of the next symbol (from the rhs) after the 'dot'.
|
22
22
|
# If the dot is at the end of the rhs (i.e.) there is no next
|
23
23
|
# symbol, then the position takes the value -1.
|
24
|
-
# It the rhs is empty, then the
|
24
|
+
# It the rhs is empty, then the position is -2
|
25
25
|
attr_reader(:position)
|
26
26
|
|
27
27
|
# @param aProduction
|
@@ -64,11 +64,37 @@ module Rley # This module is used as a namespace
|
|
64
64
|
return production.lhs
|
65
65
|
end
|
66
66
|
|
67
|
+
# Return the symbol before the dot.
|
68
|
+
# nil is returned if the dot is at the start of the rhs
|
69
|
+
def prev_symbol()
|
70
|
+
before_position = prev_position
|
71
|
+
if before_position.nil?
|
72
|
+
result = nil
|
73
|
+
else
|
74
|
+
result = production.rhs[before_position]
|
75
|
+
end
|
76
|
+
|
77
|
+
return result
|
78
|
+
end
|
79
|
+
|
67
80
|
# Return the symbol after the dot.
|
68
81
|
# nil is returned if the dot is at the end
|
69
82
|
def next_symbol()
|
70
83
|
return (position < 0) ? nil : production.rhs[position]
|
71
84
|
end
|
85
|
+
|
86
|
+
# Calculate the position of the dot if were moved by
|
87
|
+
# one step on the left.
|
88
|
+
def prev_position()
|
89
|
+
case position
|
90
|
+
when -2, 0
|
91
|
+
result = nil
|
92
|
+
when -1
|
93
|
+
result = (production.rhs.size == 1) ? 0 : (production.rhs.size - 1)
|
94
|
+
else
|
95
|
+
result = position-1
|
96
|
+
end
|
97
|
+
end
|
72
98
|
|
73
99
|
# An item with the dot in front of a terminal is called a shift item
|
74
100
|
def shift_item?()
|
@@ -24,18 +24,36 @@ module Rley # This module is used as a namespace
|
|
24
24
|
|
25
25
|
return result
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
# Returns true if the dot is at the end of the rhs of the production.
|
29
29
|
# In other words, the complete rhs matches the input.
|
30
30
|
def complete?()
|
31
31
|
return dotted_rule.reduce_item?
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
# Next expected symbol in the production
|
35
35
|
def next_symbol()
|
36
36
|
return dotted_rule.next_symbol
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
|
+
# Does this parse state have the 'other' as successor?
|
40
|
+
def precedes?(other)
|
41
|
+
return false if self == other
|
42
|
+
|
43
|
+
return false unless origin == other.origin
|
44
|
+
other_production = other.dotted_rule.production
|
45
|
+
return false unless dotted_rule.production == other_production
|
46
|
+
|
47
|
+
prev_position = other.dotted_rule.prev_position
|
48
|
+
if prev_position.nil?
|
49
|
+
result = false
|
50
|
+
else
|
51
|
+
result = dotted_rule.position == prev_position
|
52
|
+
end
|
53
|
+
|
54
|
+
return result
|
55
|
+
end
|
56
|
+
|
39
57
|
# Give a String representation of itself.
|
40
58
|
# The format of the text representation is
|
41
59
|
# "format of dotted rule" + " | " + origin
|
@@ -43,13 +61,14 @@ module Rley # This module is used as a namespace
|
|
43
61
|
def to_s()
|
44
62
|
return dotted_rule.to_s + " | #{origin}"
|
45
63
|
end
|
46
|
-
|
64
|
+
|
65
|
+
|
47
66
|
private
|
48
|
-
|
67
|
+
|
49
68
|
# Return the validated dotted item(rule)
|
50
69
|
def valid_dotted_rule(aDottedRule)
|
51
70
|
fail StandardError, 'Dotted item cannot be nil' if aDottedRule.nil?
|
52
|
-
|
71
|
+
|
53
72
|
return aDottedRule
|
54
73
|
end
|
55
74
|
end # class
|
data/lib/rley/parser/parsing.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative 'chart'
|
2
|
+
require_relative '../ptree/parse_tree'
|
2
3
|
|
3
4
|
module Rley # This module is used as a namespace
|
4
5
|
module Parser # This module is used as a namespace
|
@@ -17,18 +18,61 @@ module Rley # This module is used as a namespace
|
|
17
18
|
# followed the syntax specified by the grammar)
|
18
19
|
def success?()
|
19
20
|
# Success can be detected as follows:
|
20
|
-
# The last chart entry has a parse state
|
21
|
-
#
|
22
|
-
|
23
|
-
|
24
|
-
start_dotted_rule = chart.start_dotted_rule
|
25
|
-
start_production = start_dotted_rule.production
|
26
|
-
last_chart_entry = chart.state_sets.last
|
27
|
-
candidate_states = last_chart_entry.states_for(start_production)
|
28
|
-
found = candidate_states.find(&:complete?)
|
29
|
-
|
21
|
+
# The last chart entry has a complete parse state
|
22
|
+
# with the start symbol as lhs
|
23
|
+
found = end_parse_state
|
30
24
|
return !found.nil?
|
31
25
|
end
|
26
|
+
|
27
|
+
# Factory method. Builds a ParseTree from the parse result.
|
28
|
+
# @return [ParseTree]
|
29
|
+
# Algorithm:
|
30
|
+
# set state_set_index = index of last state set in chart
|
31
|
+
# Search the completed parse state that corresponds to the full parse
|
32
|
+
def parse_tree()
|
33
|
+
state_set_index = chart.state_sets.size - 1
|
34
|
+
parse_state = end_parse_state
|
35
|
+
curr_dotted_item = parse_state.dotted_rule
|
36
|
+
full_range = { low: 0, high: state_set_index }
|
37
|
+
ptree = PTree::ParseTree.new(curr_dotted_item.production, full_range)
|
38
|
+
loop do
|
39
|
+
# Look at the symbol on left of the dot
|
40
|
+
curr_symbol = curr_dotted_item.prev_symbol
|
41
|
+
case curr_symbol
|
42
|
+
when Syntax::Terminal
|
43
|
+
state_set_index -= 1
|
44
|
+
ptree.step_back(state_set_index)
|
45
|
+
parse_state = chart[state_set_index].predecessor_state(parse_state)
|
46
|
+
curr_dotted_item = parse_state.dotted_rule
|
47
|
+
|
48
|
+
when Syntax::NonTerminal
|
49
|
+
# Retrieve complete states
|
50
|
+
new_states = chart[state_set_index].states_rewriting(curr_symbol)
|
51
|
+
# TODO: make this more robust
|
52
|
+
parse_state = new_states[0]
|
53
|
+
curr_dotted_item = parse_state.dotted_rule
|
54
|
+
ptree.current_node.range = { low: parse_state.origin }
|
55
|
+
node_range = ptree.current_node.range
|
56
|
+
ptree.add_children(curr_dotted_item.production, node_range)
|
57
|
+
|
58
|
+
when NilClass
|
59
|
+
lhs = curr_dotted_item.production.lhs
|
60
|
+
new_states = chart[state_set_index].states_expecting(lhs)
|
61
|
+
break if new_states.empty?
|
62
|
+
# TODO: make this more robust
|
63
|
+
parse_state = new_states[0]
|
64
|
+
curr_dotted_item = parse_state.dotted_rule
|
65
|
+
ptree.step_up(state_set_index)
|
66
|
+
ptree.current_node.range = { low: parse_state.origin }
|
67
|
+
break if ptree.root == ptree.current_node
|
68
|
+
else
|
69
|
+
msg = "Unexpected grammar symbol type #{curr_symbol.class}"
|
70
|
+
raise StandardError, msg
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
return ptree
|
75
|
+
end
|
32
76
|
|
33
77
|
|
34
78
|
# Push a parse state (dotted item + origin) to the
|
@@ -91,6 +135,20 @@ module Rley # This module is used as a namespace
|
|
91
135
|
def states_expecting(aTerminal, aPosition)
|
92
136
|
return chart[aPosition].states_expecting(aTerminal)
|
93
137
|
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
# Retrieve full parse state.
|
142
|
+
# After a successful parse, the last chart entry
|
143
|
+
# has a parse state that involves the start production and
|
144
|
+
# has a dot positioned at the end of its rhs.
|
145
|
+
def end_parse_state()
|
146
|
+
start_dotted_rule = chart.start_dotted_rule
|
147
|
+
start_production = start_dotted_rule.production
|
148
|
+
last_chart_entry = chart.state_sets[-1]
|
149
|
+
candidate_states = last_chart_entry.states_for(start_production)
|
150
|
+
return candidate_states.find(&:complete?)
|
151
|
+
end
|
94
152
|
end # class
|
95
153
|
end # module
|
96
154
|
end # module
|
@@ -26,10 +26,31 @@ module Rley # This module is used as a namespace
|
|
26
26
|
return states.select { |s| s.dotted_rule.next_symbol == aTerminal }
|
27
27
|
end
|
28
28
|
|
29
|
+
# The list of complete ParseState that have the symbol as the lhs of their
|
30
|
+
# production
|
31
|
+
def states_rewriting(aNonTerm)
|
32
|
+
return states.select do |s|
|
33
|
+
(s.dotted_rule.production.lhs == aNonTerm) && s.complete?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
29
37
|
# The list of ParseState that involve the given production
|
30
38
|
def states_for(aProduction)
|
31
39
|
return states.select { |s| s.dotted_rule.production == aProduction }
|
32
40
|
end
|
41
|
+
|
42
|
+
# Retrieve the parse state that is the predecessor of the given one.
|
43
|
+
def predecessor_state(aParseState)
|
44
|
+
if aParseState.dotted_rule.prev_position.nil?
|
45
|
+
raise StandardError, "#{aParseState}"
|
46
|
+
else
|
47
|
+
prod = aParseState.dotted_rule.production
|
48
|
+
candidate = states.find { |s| s.precedes?(aParseState) }
|
49
|
+
end
|
50
|
+
|
51
|
+
return candidate
|
52
|
+
end
|
53
|
+
|
33
54
|
|
34
55
|
private
|
35
56
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'parse_tree_node' # Load superclass
|
2
|
+
|
3
|
+
module Rley # This module is used as a namespace
|
4
|
+
module PTree # This module is used as a namespace
|
5
|
+
class NonTerminalNode < ParseTreeNode
|
6
|
+
# Link to the input token
|
7
|
+
attr_reader(:children)
|
8
|
+
|
9
|
+
def initialize(aSymbol, aRange)
|
10
|
+
super(aSymbol, aRange)
|
11
|
+
@children = []
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param aChildNode [ParseTreeNode-like] a child node.
|
15
|
+
def add_child(aChildNode)
|
16
|
+
children << aChildNode
|
17
|
+
end
|
18
|
+
end # class
|
19
|
+
end # module
|
20
|
+
end # module
|
21
|
+
# End of file
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require_relative 'terminal_node'
|
2
|
+
require_relative 'non_terminal_node'
|
3
|
+
|
4
|
+
module Rley # This module is used as a namespace
|
5
|
+
module PTree # This module is used as a namespace
|
6
|
+
class ParseTree
|
7
|
+
# The root node of the tree
|
8
|
+
attr_reader(:root)
|
9
|
+
|
10
|
+
# The path to current node
|
11
|
+
attr_reader(:current_path)
|
12
|
+
|
13
|
+
def initialize(aProduction, aRange)
|
14
|
+
@root = NonTerminalNode.new(aProduction.lhs, aRange)
|
15
|
+
@current_path = [ @root ]
|
16
|
+
add_children(aProduction, aRange)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Return the active node.
|
20
|
+
def current_node()
|
21
|
+
return current_path.last
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def add_children(aProduction, aRange)
|
26
|
+
aProduction.rhs.each do |symb|
|
27
|
+
case symb
|
28
|
+
when Syntax::Terminal
|
29
|
+
new_node = TerminalNode.new(symb,{})
|
30
|
+
when Syntax::NonTerminal
|
31
|
+
new_node = NonTerminalNode.new(symb,{})
|
32
|
+
else
|
33
|
+
fail Standard, "Unknown grammar symbol type #{symb.class}"
|
34
|
+
end
|
35
|
+
|
36
|
+
current_node.add_child(new_node)
|
37
|
+
end
|
38
|
+
|
39
|
+
children = current_node.children
|
40
|
+
children.first.range = low_bound(aRange)
|
41
|
+
children.last.range = high_bound(aRange)
|
42
|
+
unless children.empty?
|
43
|
+
path_increment = [children.size - 1, children.last]
|
44
|
+
@current_path.concat(path_increment)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Move the current node to the parent node.
|
49
|
+
# @param tokenPos [Fixnum] position of the matching input token
|
50
|
+
def step_up(tokenPos)
|
51
|
+
(pos, last_node) = current_path.pop(2)
|
52
|
+
#last_node.range = low_bound({low: tokenPos})
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
# Move the current node to the previous sibling node.
|
58
|
+
# @param tokenPos [Fixnum] position of the matching input token
|
59
|
+
def step_back(tokenPos)
|
60
|
+
(pos, last_node) = current_path[-2, 2]
|
61
|
+
last_node.range = low_bound({low: tokenPos})
|
62
|
+
|
63
|
+
if pos > 0
|
64
|
+
current_path.pop(2)
|
65
|
+
new_pos = pos - 1
|
66
|
+
new_curr_node = current_path.last.children[new_pos]
|
67
|
+
current_path << new_pos
|
68
|
+
current_path << new_curr_node
|
69
|
+
new_curr_node.range = high_bound({high: tokenPos})
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
def low_bound(aRange)
|
75
|
+
result = case aRange
|
76
|
+
when Hash then aRange[:low]
|
77
|
+
when TokenRange then aRange.low
|
78
|
+
end
|
79
|
+
|
80
|
+
return { low: result }
|
81
|
+
end
|
82
|
+
|
83
|
+
def high_bound(aRange)
|
84
|
+
result = case aRange
|
85
|
+
when Hash then aRange[:high]
|
86
|
+
when TokenRange then aRange.high
|
87
|
+
end
|
88
|
+
|
89
|
+
return { high: result }
|
90
|
+
end
|
91
|
+
|
92
|
+
end # class
|
93
|
+
end # module
|
94
|
+
end # module
|
95
|
+
# End of file
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'token_range'
|
2
|
+
|
3
|
+
module Rley # This module is used as a namespace
|
4
|
+
module PTree # This module is used as a namespace
|
5
|
+
class ParseTreeNode
|
6
|
+
# Link to the grammar symbol
|
7
|
+
attr_reader(:symbol)
|
8
|
+
|
9
|
+
# A range of indices for tokens corresponding to the node.
|
10
|
+
attr_reader(:range)
|
11
|
+
|
12
|
+
|
13
|
+
def initialize(aSymbol, aRange)
|
14
|
+
@symbol = aSymbol
|
15
|
+
@range = TokenRange.new(aRange)
|
16
|
+
end
|
17
|
+
|
18
|
+
def range=(aRange)
|
19
|
+
range.assign(aRange)
|
20
|
+
end
|
21
|
+
|
22
|
+
end # class
|
23
|
+
end # module
|
24
|
+
end # module
|
25
|
+
# End of file
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'parse_tree_node' # Load superclass
|
2
|
+
|
3
|
+
module Rley # This module is used as a namespace
|
4
|
+
module PTree # This module is used as a namespace
|
5
|
+
class TerminalNode < ParseTreeNode
|
6
|
+
# Link to the input token
|
7
|
+
attr_writer(:token)
|
8
|
+
|
9
|
+
def initialize(aTerminalSymbol, aRange)
|
10
|
+
super(aTerminalSymbol, aRange)
|
11
|
+
end
|
12
|
+
|
13
|
+
end # class
|
14
|
+
end # module
|
15
|
+
end # module
|
16
|
+
# End of file
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Rley # This module is used as a namespace
|
2
|
+
module PTree # This module is used as a namespace
|
3
|
+
class TokenRange
|
4
|
+
# The index of the lower bound of token range
|
5
|
+
attr_reader(:low)
|
6
|
+
|
7
|
+
# The index of the upper bound of token range
|
8
|
+
attr_reader(:high)
|
9
|
+
|
10
|
+
# @param aRangeRep [Hash]
|
11
|
+
def initialize(aRangeRep)
|
12
|
+
assign_low(aRangeRep)
|
13
|
+
assign_high(aRangeRep)
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def ==(other)
|
18
|
+
return true if object_id == other.object_id
|
19
|
+
|
20
|
+
case other
|
21
|
+
when Hash
|
22
|
+
result = low == other[:low] && high == other[:high]
|
23
|
+
when TokenRange
|
24
|
+
result = low == other.low && high == other.high
|
25
|
+
end
|
26
|
+
|
27
|
+
return result
|
28
|
+
end
|
29
|
+
|
30
|
+
# true when both bounds aren't nil.
|
31
|
+
def bounded?()
|
32
|
+
return !(low.nil? || high.nil?)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Conditional assign
|
36
|
+
def assign(aRange)
|
37
|
+
return if bounded?
|
38
|
+
|
39
|
+
assign_low(aRange) if low.nil?
|
40
|
+
assign_high(aRange) if high.nil?
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def assign_low(aRange)
|
45
|
+
case aRange
|
46
|
+
when Hash then @low = aRange.fetch(:low, nil)
|
47
|
+
when TokenRange then @low = aRange.low
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def assign_high(aRange)
|
52
|
+
case aRange
|
53
|
+
when Hash then @high = aRange.fetch(:high, nil)
|
54
|
+
when TokenRange then @high = aRange.high
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end # class
|
59
|
+
end # module
|
60
|
+
end # module
|
61
|
+
# End of file
|
data/lib/rley/syntax/grammar.rb
CHANGED
@@ -20,12 +20,16 @@ module Rley # This module is used as a namespace
|
|
20
20
|
|
21
21
|
# The list of grammar symbols in the language.
|
22
22
|
attr_reader(:symbols)
|
23
|
+
|
24
|
+
# A Hash with pairs of the kind: symbol name => grammar symbol
|
25
|
+
attr_reader(:name2symbol)
|
23
26
|
|
24
27
|
# @param theProduction [Array of Production] the list of productions
|
25
28
|
# of the grammar.
|
26
29
|
def initialize(theProductions)
|
27
30
|
@rules = []
|
28
31
|
@symbols = []
|
32
|
+
@name2symbol = {}
|
29
33
|
valid_productions = validate_productions(theProductions)
|
30
34
|
# TODO: use topological sorting
|
31
35
|
@start_symbol = valid_productions[0].lhs
|
@@ -50,13 +54,10 @@ module Rley # This module is used as a namespace
|
|
50
54
|
def add_production(aProduction)
|
51
55
|
@rules << aProduction
|
52
56
|
the_lhs = aProduction.lhs
|
53
|
-
|
57
|
+
add_symbol(the_lhs)
|
54
58
|
|
55
59
|
# TODO: remove quadratic execution time
|
56
|
-
aProduction.rhs.each
|
57
|
-
next if symbols.include? symb
|
58
|
-
@symbols << symb
|
59
|
-
end
|
60
|
+
aProduction.rhs.each { |symb| add_symbol(symb) }
|
60
61
|
end
|
61
62
|
|
62
63
|
|
@@ -104,6 +105,16 @@ module Rley # This module is used as a namespace
|
|
104
105
|
|
105
106
|
return nullable
|
106
107
|
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def add_symbol(aSymbol)
|
112
|
+
its_name = aSymbol.name
|
113
|
+
unless name2symbol.include? its_name
|
114
|
+
@symbols << aSymbol
|
115
|
+
@name2symbol[its_name] = aSymbol
|
116
|
+
end
|
117
|
+
end
|
107
118
|
end # class
|
108
119
|
end # module
|
109
120
|
end # module
|
@@ -16,7 +16,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
16
16
|
def build_prod(theLHS, *theRHSSymbols)
|
17
17
|
return Syntax::Production.new(theLHS, theRHSSymbols)
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
let(:t_a) { Syntax::Terminal.new('A') }
|
21
21
|
let(:t_b) { Syntax::Terminal.new('B') }
|
22
22
|
let(:t_c) { Syntax::Terminal.new('C') }
|
@@ -43,7 +43,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
43
43
|
it 'should know its production' do
|
44
44
|
expect(subject.production).to eq(sample_prod)
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
it 'should know the lhs of the production' do
|
48
48
|
expect(subject.lhs).to eq(sample_prod.lhs)
|
49
49
|
end
|
@@ -71,11 +71,11 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
71
71
|
context 'Provided service:' do
|
72
72
|
it 'should whether its dot is at start position' do
|
73
73
|
expect(subject).not_to be_at_start
|
74
|
-
|
74
|
+
|
75
75
|
# At start position
|
76
76
|
instance1 = DottedItem.new(sample_prod, 0)
|
77
77
|
expect(instance1).to be_at_start
|
78
|
-
|
78
|
+
|
79
79
|
# At start/end at the same time (production is empty)
|
80
80
|
instance2 = DottedItem.new(build_prod(nt_sentence), 0)
|
81
81
|
expect(instance2).to be_at_start
|
@@ -91,10 +91,38 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
91
91
|
expect(second_instance).to be_reduce_item
|
92
92
|
end
|
93
93
|
|
94
|
+
it 'should know the symbol before the dot' do
|
95
|
+
expect(subject.prev_symbol).to eq(t_a)
|
96
|
+
|
97
|
+
# Case of an empty production
|
98
|
+
instance = DottedItem.new(empty_prod, 0)
|
99
|
+
expect(instance.prev_symbol).to be_nil
|
100
|
+
|
101
|
+
# Case of a dot at start position
|
102
|
+
instance = DottedItem.new(sample_prod, 0)
|
103
|
+
expect(instance.prev_symbol).to be_nil
|
104
|
+
end
|
105
|
+
|
94
106
|
it 'should know the symbol after the dot' do
|
95
107
|
expect(subject.next_symbol).to eq(t_b)
|
96
108
|
end
|
97
109
|
|
110
|
+
it 'should calculate the previous position of the dot' do
|
111
|
+
expect(subject.prev_position).to eq(0)
|
112
|
+
|
113
|
+
# Case of an empty production
|
114
|
+
instance = DottedItem.new(empty_prod, 0)
|
115
|
+
expect(instance.prev_position).to be_nil
|
116
|
+
|
117
|
+
# Case of a dot at start position
|
118
|
+
instance = DottedItem.new(sample_prod, 0)
|
119
|
+
expect(instance.prev_position).to be_nil
|
120
|
+
|
121
|
+
# Case of single symbol production
|
122
|
+
instance = DottedItem.new(other_prod, 1)
|
123
|
+
expect(instance.prev_position).to eq(0)
|
124
|
+
end
|
125
|
+
|
98
126
|
it 'should give its text representation' do
|
99
127
|
expectation = 'sentence => A . B C'
|
100
128
|
expect(subject.to_s).to eq(expectation)
|
@@ -91,6 +91,15 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
91
91
|
expect(subject.next_symbol).to eq(t_c)
|
92
92
|
end
|
93
93
|
|
94
|
+
it 'should know whether another instance follows this one' do
|
95
|
+
expect(subject.precedes?(subject)).to eq(false)
|
96
|
+
state1 = ParseState.new(DottedItem.new(sample_prod, 1), origin_val)
|
97
|
+
expect(state1.precedes?(subject)).to eq(true)
|
98
|
+
state0 = ParseState.new(DottedItem.new(sample_prod, 0), origin_val)
|
99
|
+
expect(state0.precedes?(state1)).to eq(true)
|
100
|
+
expect(state0.precedes?(subject)).to eq(false)
|
101
|
+
end
|
102
|
+
|
94
103
|
it 'should know its text representation' do
|
95
104
|
expected = 'sentence => A B . C | 3'
|
96
105
|
expect(subject.to_s).to eq(expected)
|
@@ -3,8 +3,10 @@ require_relative '../../spec_helper'
|
|
3
3
|
require_relative '../../../lib/rley/syntax/non_terminal'
|
4
4
|
require_relative '../../../lib/rley/syntax/verbatim_symbol'
|
5
5
|
require_relative '../../../lib/rley/syntax/production'
|
6
|
+
require_relative '../../../lib/rley/syntax/grammar_builder'
|
6
7
|
require_relative '../../../lib/rley/parser/dotted_item'
|
7
8
|
require_relative '../../../lib/rley/parser/token'
|
9
|
+
require_relative '../../../lib/rley/parser/earley_parser'
|
8
10
|
# Load the class under test
|
9
11
|
require_relative '../../../lib/rley/parser/parsing'
|
10
12
|
|
@@ -48,7 +50,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
48
50
|
tokens = grm1_tokens
|
49
51
|
expect { Parsing.new(start_rule, tokens) }.not_to raise_error
|
50
52
|
end
|
51
|
-
|
53
|
+
|
52
54
|
it 'should know the input tokens' do
|
53
55
|
expect(subject.tokens).to eq(grm1_tokens)
|
54
56
|
end
|
@@ -58,28 +60,28 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
58
60
|
end
|
59
61
|
|
60
62
|
end # context
|
61
|
-
|
63
|
+
|
62
64
|
context 'Parsing:' do
|
63
65
|
it 'should push a state to a given chart entry' do
|
64
66
|
expect(subject.chart[1]).to be_empty
|
65
67
|
item = DottedItem.new(prod_A1, 1)
|
66
|
-
|
68
|
+
|
67
69
|
subject.push_state(item, 1, 1)
|
68
70
|
expect(subject.chart[1]).not_to be_empty
|
69
71
|
expect(subject.chart[1].first.dotted_rule).to eq(item)
|
70
|
-
|
72
|
+
|
71
73
|
# Pushing twice the same state must be no-op
|
72
74
|
subject.push_state(item, 1, 1)
|
73
75
|
expect(subject.chart[1].size).to eq(1)
|
74
76
|
end
|
75
|
-
|
77
|
+
|
76
78
|
it 'should complain when trying to push a nil dotted item' do
|
77
79
|
err = StandardError
|
78
80
|
msg = 'Dotted item may not be nil'
|
79
81
|
expect { subject.push_state(nil, 1, 1) }.to raise_error(err, msg)
|
80
82
|
end
|
81
|
-
|
82
|
-
|
83
|
+
|
84
|
+
|
83
85
|
it 'should retrieve the parse states that expect a given terminal' do
|
84
86
|
item1 = DottedItem.new(prod_A1, 2)
|
85
87
|
item2 = DottedItem.new(prod_A1, 1)
|
@@ -89,27 +91,51 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
89
91
|
expect(states.size).to eq(1)
|
90
92
|
expect(states[0].dotted_rule).to eq(item1)
|
91
93
|
end
|
92
|
-
|
94
|
+
|
93
95
|
it 'should update the states upon token match' do
|
94
96
|
# When a input token matches an expected terminal symbol
|
95
97
|
# then new parse states must be pushed to the following chart slot
|
96
98
|
expect(subject.chart[1]).to be_empty
|
97
|
-
|
99
|
+
|
98
100
|
item1 = DottedItem.new(prod_A1, 0)
|
99
101
|
item2 = DottedItem.new(prod_A2, 0)
|
100
102
|
subject.push_state(item1, 0, 0)
|
101
103
|
subject.push_state(item2, 0, 0)
|
102
104
|
subject.scanning(a_, 0) { |i| i } # Code block is mock
|
103
|
-
|
105
|
+
|
104
106
|
# Expected side effect: a new state at chart[1]
|
105
107
|
expect(subject.chart[1].size).to eq(1)
|
106
108
|
new_state = subject.chart[1].states[0]
|
107
109
|
expect(new_state.dotted_rule).to eq(item1)
|
108
110
|
expect(new_state.origin).to eq(0)
|
109
111
|
end
|
110
|
-
|
111
|
-
end
|
112
|
-
|
112
|
+
|
113
|
+
end # context
|
114
|
+
|
115
|
+
context 'Parse tree building:' do
|
116
|
+
let(:sample_grammar1) do
|
117
|
+
builder = Syntax::GrammarBuilder.new
|
118
|
+
builder.add_terminals('a', 'b', 'c')
|
119
|
+
builder.add_production('S' => ['A'])
|
120
|
+
builder.add_production('A' => %w(a A c))
|
121
|
+
builder.add_production('A' => ['b'])
|
122
|
+
builder.grammar
|
123
|
+
end
|
124
|
+
|
125
|
+
let(:token_seq1) do
|
126
|
+
%w(a a b c c).map do |letter|
|
127
|
+
Token.new(letter, sample_grammar1.name2symbol[letter])
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
it 'should build the parse tree for a non-ambiguous grammar' do
|
133
|
+
parser = EarleyParser.new(sample_grammar1)
|
134
|
+
instance = parser.parse(token_seq1)
|
135
|
+
ptree = instance.parse_tree
|
136
|
+
expect(ptree).to be_kind_of(PTree::ParseTree)
|
137
|
+
end
|
138
|
+
end # context
|
113
139
|
end # describe
|
114
140
|
end # module
|
115
141
|
end # module
|
@@ -56,6 +56,22 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
56
56
|
allow(dotted_rule2).to receive(:production).and_return(a_prod)
|
57
57
|
expect(subject.states_for(a_prod)).to eq([state2])
|
58
58
|
end
|
59
|
+
|
60
|
+
it 'should list the states that rewrite a given non-terminal' do
|
61
|
+
non_term = double('fake-non-terminal')
|
62
|
+
prod1 = double('fake-production1')
|
63
|
+
prod2 = double('fake-production2')
|
64
|
+
|
65
|
+
# Adding states
|
66
|
+
subject.push_state(state1)
|
67
|
+
subject.push_state(state2)
|
68
|
+
allow(dotted_rule1).to receive(:production).and_return(prod1)
|
69
|
+
allow(prod1).to receive(:lhs).and_return(:dummy)
|
70
|
+
allow(dotted_rule2).to receive(:production).and_return(prod2)
|
71
|
+
allow(dotted_rule2).to receive(:reduce_item?).and_return(true)
|
72
|
+
allow(prod2).to receive(:lhs).and_return(non_term)
|
73
|
+
expect(subject.states_rewriting(non_term)).to eq([state2])
|
74
|
+
end
|
59
75
|
|
60
76
|
end # context
|
61
77
|
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
# Load the class under test
|
4
|
+
require_relative '../../../lib/rley/ptree/non_terminal_node'
|
5
|
+
|
6
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
7
|
+
module PTree # Open this namespace to avoid module qualifier prefixes
|
8
|
+
describe NonTerminalNode do
|
9
|
+
let(:sample_symbol) { double('fake-symbol') }
|
10
|
+
let(:sample_range) { double('fake-range') }
|
11
|
+
|
12
|
+
subject { NonTerminalNode.new(sample_symbol, sample_range) }
|
13
|
+
|
14
|
+
context 'Initialization:' do
|
15
|
+
it "shouldn't have children yet" do
|
16
|
+
expect(subject.children).to be_empty
|
17
|
+
end
|
18
|
+
end # context
|
19
|
+
|
20
|
+
context 'Provided services:' do
|
21
|
+
it 'should accept children' do
|
22
|
+
child1 = double('first_child')
|
23
|
+
child2 = double('second_child')
|
24
|
+
child3 = double('third_child')
|
25
|
+
expect { subject.add_child(child1) }.not_to raise_error
|
26
|
+
subject.add_child(child2)
|
27
|
+
subject.add_child(child3)
|
28
|
+
expect(subject.children).to eq([child1, child2, child3])
|
29
|
+
end
|
30
|
+
end # context
|
31
|
+
|
32
|
+
end # describe
|
33
|
+
end # module
|
34
|
+
end # module
|
35
|
+
|
36
|
+
# End of file
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
# Load the class under test
|
4
|
+
require_relative '../../../lib/rley/ptree/parse_tree_node'
|
5
|
+
|
6
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
7
|
+
module PTree # Open this namespace to avoid module qualifier prefixes
|
8
|
+
describe ParseTreeNode do
|
9
|
+
let(:sample_symbol) { double('fake-symbol') }
|
10
|
+
let(:sample_range) { {low: 0, high: 5} }
|
11
|
+
|
12
|
+
subject { ParseTreeNode.new(sample_symbol, sample_range) }
|
13
|
+
|
14
|
+
context 'Initialization:' do
|
15
|
+
it 'should be created with a symbol and a range' do
|
16
|
+
expect { ParseTreeNode.new(sample_symbol, sample_range) }.not_to raise_error
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should know its symbol' do
|
20
|
+
expect(subject.symbol).to eq(sample_symbol)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should know its range" do
|
24
|
+
expect(subject.range).to eq(sample_range)
|
25
|
+
end
|
26
|
+
end # context
|
27
|
+
|
28
|
+
end # describe
|
29
|
+
end # module
|
30
|
+
end # module
|
31
|
+
|
32
|
+
# End of file
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
require_relative '../../../lib/rley/syntax/grammar_builder'
|
4
|
+
# Load the class under test
|
5
|
+
require_relative '../../../lib/rley/ptree/parse_tree'
|
6
|
+
|
7
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
8
|
+
module PTree # Open this namespace to avoid module qualifier prefixes
|
9
|
+
describe ParseTree do
|
10
|
+
let(:sample_grammar) do
|
11
|
+
builder = Syntax::GrammarBuilder.new
|
12
|
+
builder.add_terminals('a', 'b', 'c')
|
13
|
+
builder.add_production('S' => ['A'])
|
14
|
+
builder.add_production('A' => %w(a A c))
|
15
|
+
builder.add_production('A' => ['b'])
|
16
|
+
builder.grammar
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:sample_prod) { sample_grammar.rules[0] }
|
20
|
+
let(:sample_range) { {low:0, high:5} }
|
21
|
+
subject { ParseTree.new(sample_prod, sample_range) }
|
22
|
+
|
23
|
+
context 'Initialization:' do
|
24
|
+
it 'should be created with a production and a range' do
|
25
|
+
construction = -> { ParseTreeNode.new(sample_prod, sample_range) }
|
26
|
+
expect(construction).not_to raise_error
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should know its root node' do
|
30
|
+
its_root = subject.root
|
31
|
+
expect(its_root.symbol.name).to eq('S')
|
32
|
+
expect(its_root.range).to eq(sample_range)
|
33
|
+
expect(its_root.children.size).to eq(1)
|
34
|
+
expect(its_root.children[0].symbol.name).to eq('A')
|
35
|
+
expect(its_root.children[0].range).to eq(sample_range)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should know its current path' do
|
39
|
+
path = subject.current_path
|
40
|
+
|
41
|
+
# Given the tree:
|
42
|
+
# S[0,5]
|
43
|
+
# +- A[0,5] <- current node
|
44
|
+
# Expected path: [S[0,5], 0, A[0,5]]
|
45
|
+
expect(path.size).to eq(3)
|
46
|
+
expect(path[0]).to eq(subject.root)
|
47
|
+
expect(path[1]).to eq(0)
|
48
|
+
expect(path[2]).to eq(subject.root.children[0])
|
49
|
+
expect(path[2].range).to eq(sample_range)
|
50
|
+
end
|
51
|
+
end # context
|
52
|
+
|
53
|
+
context 'Provided service:' do
|
54
|
+
it 'should add children to current node' do
|
55
|
+
subject.add_children(sample_grammar.rules[1], sample_range)
|
56
|
+
|
57
|
+
# Given the tree:
|
58
|
+
# S[0,5]
|
59
|
+
# +- A[0,5]
|
60
|
+
# +-a[0,nil]
|
61
|
+
# +-A[nil, nil]
|
62
|
+
# +-c[nil, 5] <- current node
|
63
|
+
# Expected path: [S[0,5], 0, A[0,5], 2, c[nil, 5]]
|
64
|
+
path = subject.current_path
|
65
|
+
expect(path.size).to eq(5)
|
66
|
+
expect(path[3]).to eq(2)
|
67
|
+
expect(path[4].symbol.name).to eq('c')
|
68
|
+
expect(path[4].range.low).to be_nil
|
69
|
+
expect(path[4].range.high).to eq(5)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should step back to a previous sibling node' do
|
73
|
+
subject.add_children(sample_grammar.rules[1], sample_range)
|
74
|
+
subject.step_back(4)
|
75
|
+
# Expected tree:
|
76
|
+
# S[0,5]
|
77
|
+
# +- A[0,5]
|
78
|
+
# +-a[0,nil]
|
79
|
+
# +-A[nil, 4] <- current node
|
80
|
+
# +-c[4, 5]
|
81
|
+
# Expected path: [S[0,5], 0, A[0,5], 1, A[nil, 4]]
|
82
|
+
path = subject.current_path
|
83
|
+
expect(path.size).to eq(5)
|
84
|
+
expect(path[3]).to eq(1)
|
85
|
+
expect(path[4].symbol.name).to eq('A')
|
86
|
+
expect(path[4].range.low).to be_nil
|
87
|
+
expect(path[4].range.high).to eq(4)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end # describe
|
92
|
+
end # module
|
93
|
+
end # module
|
94
|
+
|
95
|
+
# End of file
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
# Load the class under test
|
4
|
+
require_relative '../../../lib/rley/ptree/token_range'
|
5
|
+
|
6
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
7
|
+
module PTree # Open this namespace to avoid module qualifier prefixes
|
8
|
+
describe TokenRange do
|
9
|
+
|
10
|
+
let(:sample_range) { {low: 0, high: 5} }
|
11
|
+
|
12
|
+
# Default instantiation rule
|
13
|
+
subject { TokenRange.new(sample_range) }
|
14
|
+
|
15
|
+
context 'Initialization:' do
|
16
|
+
it 'should be created with a range Hash' do
|
17
|
+
# No bounds provided
|
18
|
+
expect { TokenRange.new({}) }.not_to raise_error
|
19
|
+
|
20
|
+
# Low bound provided
|
21
|
+
expect { TokenRange.new({low: 0}) }.not_to raise_error
|
22
|
+
|
23
|
+
# High bound provided
|
24
|
+
expect { TokenRange.new({high: 5}) }.not_to raise_error
|
25
|
+
|
26
|
+
# Both bounds provided
|
27
|
+
expect { TokenRange.new({low: 0, high: 5}) }.not_to raise_error
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should know its low bound' do
|
31
|
+
expect(subject.low).to eq(0)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should know its low bound' do
|
35
|
+
expect(subject.high).to eq(5)
|
36
|
+
end
|
37
|
+
end # context
|
38
|
+
|
39
|
+
context 'Provided services:' do
|
40
|
+
it 'should know whether it is bounded or not' do
|
41
|
+
expect(subject).to be_bounded
|
42
|
+
|
43
|
+
# Case: only low bound is set
|
44
|
+
instance = TokenRange.new({low: 0})
|
45
|
+
expect(instance).not_to be_bounded
|
46
|
+
|
47
|
+
# Case: only upper bound is set
|
48
|
+
instance = TokenRange.new({high: 5})
|
49
|
+
expect(instance).not_to be_bounded
|
50
|
+
|
51
|
+
# No bound is set
|
52
|
+
instance = TokenRange.new({})
|
53
|
+
expect(instance).not_to be_bounded
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should assign it open bounds' do
|
57
|
+
some_range = {low: 1, high: 4}
|
58
|
+
|
59
|
+
###########
|
60
|
+
# Case of bounded token range...
|
61
|
+
subject.assign(some_range)
|
62
|
+
|
63
|
+
# ... should be unchanged
|
64
|
+
expect(subject.low).to eq(sample_range[:low])
|
65
|
+
expect(subject.high).to eq(sample_range[:high])
|
66
|
+
|
67
|
+
###########
|
68
|
+
# Case: only low bound is set
|
69
|
+
instance = TokenRange.new({low: 0})
|
70
|
+
instance.assign(some_range)
|
71
|
+
|
72
|
+
# Expectation: high is assigned the new value
|
73
|
+
expect(instance).to be_bounded
|
74
|
+
expect(instance.low).to eq(0)
|
75
|
+
expect(instance.high).to eq(4)
|
76
|
+
|
77
|
+
###########
|
78
|
+
# Case: only high bound is set
|
79
|
+
instance = TokenRange.new({high: 5})
|
80
|
+
instance.assign(some_range)
|
81
|
+
|
82
|
+
# Expectation: low is assigned the new value
|
83
|
+
expect(instance).to be_bounded
|
84
|
+
expect(instance.low).to eq(1)
|
85
|
+
expect(instance.high).to eq(5)
|
86
|
+
|
87
|
+
###########
|
88
|
+
# Case: no bound is set
|
89
|
+
instance = TokenRange.new({})
|
90
|
+
instance.assign(some_range)
|
91
|
+
|
92
|
+
# Expectation: low is assigned the new value
|
93
|
+
expect(instance).to be_bounded
|
94
|
+
expect(instance.low).to eq(1)
|
95
|
+
expect(instance.high).to eq(4)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end # describe
|
100
|
+
end # module
|
101
|
+
end # module
|
102
|
+
|
103
|
+
# End of file
|
@@ -117,11 +117,12 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
117
117
|
let(:nominal_prods) { Production}
|
118
118
|
=end
|
119
119
|
|
120
|
+
subject do
|
121
|
+
productions = [prod_S, prod_A1, prod_A2]
|
122
|
+
Grammar.new(productions)
|
123
|
+
end
|
124
|
+
|
120
125
|
context 'Initialization:' do
|
121
|
-
subject do
|
122
|
-
productions = [prod_S, prod_A1, prod_A2]
|
123
|
-
Grammar.new(productions)
|
124
|
-
end
|
125
126
|
|
126
127
|
it 'should be created with a list of productions' do
|
127
128
|
expect { Grammar.new([prod_S, prod_A1, prod_A2]) }.not_to raise_error
|
@@ -144,11 +145,23 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
144
145
|
end
|
145
146
|
end # context
|
146
147
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
148
|
+
# let(:nt_S) { NonTerminal.new('S') }
|
149
|
+
# let(:nt_A) { NonTerminal.new('A') }
|
150
|
+
# let(:a_) { VerbatimSymbol.new('a') }
|
151
|
+
# let(:b_) { VerbatimSymbol.new('b') }
|
152
|
+
# let(:c_) { VerbatimSymbol.new('c') }
|
153
|
+
|
154
|
+
context 'Provided services:' do
|
155
|
+
it 'should retrieve its symbols from their name' do
|
156
|
+
expect(subject.name2symbol['S']).to eq(nt_S)
|
157
|
+
expect(subject.name2symbol['A']).to eq(nt_A)
|
158
|
+
expect(subject.name2symbol['a']).to eq(a_)
|
159
|
+
expect(subject.name2symbol['b']).to eq(b_)
|
160
|
+
expect(subject.name2symbol['c']).to eq(c_)
|
151
161
|
end
|
162
|
+
end # context
|
163
|
+
|
164
|
+
context 'Non-nullable grammar:' do
|
152
165
|
|
153
166
|
it 'should mark all its nonterminals as non-nullable' do
|
154
167
|
nonterms = subject.non_terminals
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rley
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.00
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -96,6 +96,11 @@ files:
|
|
96
96
|
- lib/rley/parser/parsing.rb
|
97
97
|
- lib/rley/parser/state_set.rb
|
98
98
|
- lib/rley/parser/token.rb
|
99
|
+
- lib/rley/ptree/non_terminal_node.rb
|
100
|
+
- lib/rley/ptree/parse_tree.rb
|
101
|
+
- lib/rley/ptree/parse_tree_node.rb
|
102
|
+
- lib/rley/ptree/terminal_node.rb
|
103
|
+
- lib/rley/ptree/token_range.rb
|
99
104
|
- lib/rley/syntax/grammar.rb
|
100
105
|
- lib/rley/syntax/grammar_builder.rb
|
101
106
|
- lib/rley/syntax/grm_symbol.rb
|
@@ -112,6 +117,10 @@ files:
|
|
112
117
|
- spec/rley/parser/parsing_spec.rb
|
113
118
|
- spec/rley/parser/state_set_spec.rb
|
114
119
|
- spec/rley/parser/token_spec.rb
|
120
|
+
- spec/rley/ptree/non_terminal_node_spec.rb
|
121
|
+
- spec/rley/ptree/parse_tree_node_spec.rb
|
122
|
+
- spec/rley/ptree/parse_tree_spec.rb
|
123
|
+
- spec/rley/ptree/token_range_spec.rb
|
115
124
|
- spec/rley/syntax/grammar_builder_spec.rb
|
116
125
|
- spec/rley/syntax/grammar_spec.rb
|
117
126
|
- spec/rley/syntax/grm_symbol_spec.rb
|
@@ -161,6 +170,10 @@ test_files:
|
|
161
170
|
- spec/rley/parser/parsing_spec.rb
|
162
171
|
- spec/rley/parser/state_set_spec.rb
|
163
172
|
- spec/rley/parser/token_spec.rb
|
173
|
+
- spec/rley/ptree/non_terminal_node_spec.rb
|
174
|
+
- spec/rley/ptree/parse_tree_node_spec.rb
|
175
|
+
- spec/rley/ptree/parse_tree_spec.rb
|
176
|
+
- spec/rley/ptree/token_range_spec.rb
|
164
177
|
- spec/rley/syntax/grammar_builder_spec.rb
|
165
178
|
- spec/rley/syntax/grammar_spec.rb
|
166
179
|
- spec/rley/syntax/grm_symbol_spec.rb
|