rley 0.0.18 → 0.1.00
Sign up to get free protection for your applications and to get access to all the features.
- 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
|