rley 0.5.02 → 0.5.03
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 +4 -4
- data/.rubocop.yml +4 -0
- data/CHANGELOG.md +5 -0
- data/examples/data_formats/JSON/json_ast_builder.rb +18 -19
- data/examples/data_formats/JSON/json_ast_nodes.rb +9 -12
- data/examples/data_formats/JSON/json_demo.rb +1 -1
- data/examples/general/calc_iter1/calc_ast_builder.rb +27 -30
- data/examples/general/calc_iter1/calc_ast_nodes.rb +8 -17
- data/examples/general/calc_iter1/calc_lexer.rb +4 -5
- data/examples/general/calc_iter1/spec/calculator_spec.rb +0 -2
- data/lib/rley/constants.rb +1 -1
- data/lib/rley/gfg/grm_flow_graph.rb +1 -1
- data/lib/rley/parser/cst_builder.rb +2 -3
- data/lib/rley/parser/parse_forest_factory.rb +1 -2
- data/lib/rley/parser/parse_tree_builder.rb +16 -19
- data/lib/rley/parser/parse_tree_factory.rb +0 -1
- data/lib/rley/parser/parse_walker_factory.rb +1 -1
- data/lib/rley/syntax/grammar.rb +17 -15
- data/spec/rley/gfg/grm_flow_graph_spec.rb +1 -1
- data/spec/rley/parser/ast_builder_spec.rb +29 -29
- data/spec/rley/parser/cst_builder_spec.rb +3 -3
- data/spec/rley/parser/error_reason_spec.rb +4 -4
- data/spec/rley/support/grammar_arr_int_helper.rb +3 -3
- data/spec/spec_helper.rb +6 -5
- metadata +2 -8
- data/examples/general/calc_iter2/calc_ast_builder.rb +0 -186
- data/examples/general/calc_iter2/calc_ast_nodes.rb +0 -151
- data/examples/general/calc_iter2/calc_demo.rb +0 -40
- data/examples/general/calc_iter2/calc_grammar.rb +0 -28
- data/examples/general/calc_iter2/calc_lexer.rb +0 -81
- data/examples/general/calc_iter2/calc_parser.rb +0 -24
data/spec/spec_helper.rb
CHANGED
@@ -6,10 +6,12 @@ require 'coveralls'
|
|
6
6
|
|
7
7
|
Coveralls.wear!
|
8
8
|
|
9
|
-
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
|
10
|
+
[
|
11
|
+
SimpleCov::Formatter::HTMLFormatter,
|
12
|
+
Coveralls::SimpleCov::Formatter
|
13
|
+
]
|
14
|
+
)
|
13
15
|
|
14
16
|
require 'pp' # Use pretty-print for debugging purposes
|
15
17
|
require 'rspec' # Use the RSpec framework
|
@@ -25,5 +27,4 @@ RSpec.configure do |config|
|
|
25
27
|
config.full_backtrace = true
|
26
28
|
end
|
27
29
|
|
28
|
-
|
29
30
|
# End of file
|
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.5.
|
4
|
+
version: 0.5.03
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-10-
|
11
|
+
date: 2017-10-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: coveralls
|
@@ -146,12 +146,6 @@ files:
|
|
146
146
|
- examples/general/calc_iter1/calc_lexer.rb
|
147
147
|
- examples/general/calc_iter1/calc_parser.rb
|
148
148
|
- examples/general/calc_iter1/spec/calculator_spec.rb
|
149
|
-
- examples/general/calc_iter2/calc_ast_builder.rb
|
150
|
-
- examples/general/calc_iter2/calc_ast_nodes.rb
|
151
|
-
- examples/general/calc_iter2/calc_demo.rb
|
152
|
-
- examples/general/calc_iter2/calc_grammar.rb
|
153
|
-
- examples/general/calc_iter2/calc_lexer.rb
|
154
|
-
- examples/general/calc_iter2/calc_parser.rb
|
155
149
|
- lib/rley.rb
|
156
150
|
- lib/rley/constants.rb
|
157
151
|
- lib/rley/formatter/asciitree.rb
|
@@ -1,186 +0,0 @@
|
|
1
|
-
require_relative 'calc_ast_nodes'
|
2
|
-
|
3
|
-
# The purpose of a CalcASTBuilder is to build piece by piece an AST
|
4
|
-
# (Abstract Syntax Tree) from a sequence of input tokens and
|
5
|
-
# visit events produced by walking over a GFGParsing object.
|
6
|
-
# Uses the Builder GoF pattern.
|
7
|
-
# The Builder pattern creates a complex object
|
8
|
-
# (say, a parse tree) from simpler objects (terminal and non-terminal
|
9
|
-
# nodes) and using a step by step approach.
|
10
|
-
class CalcASTBuilder < Rley::Parser::ParseTreeBuilder
|
11
|
-
Terminal2NodeClass = {
|
12
|
-
# Plus sign character is ambiguous. It can represent an operator
|
13
|
-
# or a positive value
|
14
|
-
'+' => { 'add_operator[0]' => CalcAddNode, 'sign[0]' => PTree::TerminalNode },
|
15
|
-
# Minus sign character is ambiguous. It can represent an operator
|
16
|
-
# or a negative value
|
17
|
-
'-' => { 'add_operator[1]' => CalcSubtractNode, 'sign[1]' => CalcNegateNode },
|
18
|
-
'*' => CalcMultiplyNode,
|
19
|
-
'/' => CalcDivideNode,
|
20
|
-
'number' => CalcNumberNode
|
21
|
-
}
|
22
|
-
|
23
|
-
protected
|
24
|
-
|
25
|
-
def return_first_child(_range, _tokens, theChildren)
|
26
|
-
return theChildren[0]
|
27
|
-
end
|
28
|
-
|
29
|
-
def return_second_child(_range, _tokens, theChildren)
|
30
|
-
return theChildren[1]
|
31
|
-
end
|
32
|
-
|
33
|
-
def return_last_child(_range, _tokens, theChildren)
|
34
|
-
return theChildren[-1]
|
35
|
-
end
|
36
|
-
|
37
|
-
# Overriding method.
|
38
|
-
# Create a parse tree object with given
|
39
|
-
# node as root node.
|
40
|
-
def create_tree(aRootNode)
|
41
|
-
return Rley::PTree::ParseTree.new(aRootNode)
|
42
|
-
end
|
43
|
-
|
44
|
-
# Overriding method.
|
45
|
-
# Factory method for creating a node object for the given
|
46
|
-
# input token.
|
47
|
-
# @param aTerminal [Terminal] Terminal symbol associated with the token
|
48
|
-
# @param aTokenPosition [Integer] Position of token in the input stream
|
49
|
-
# @param aToken [Token] The input token
|
50
|
-
def new_leaf_node(aProduction, aTerminal, aTokenPosition, aToken)
|
51
|
-
klass = Terminal2NodeClass.fetch(aTerminal.name, CalcTerminalNode)
|
52
|
-
klass = klass[aProduction.name] if klass.is_a?(Hash) # Lexical ambiguity
|
53
|
-
return klass.new(aToken, aTokenPosition)
|
54
|
-
end
|
55
|
-
|
56
|
-
|
57
|
-
# Method to override.
|
58
|
-
# Factory method for creating a parent node object.
|
59
|
-
# @param aProduction [Production] Production rule
|
60
|
-
# @param aRange [Range] Range of tokens matched by the rule
|
61
|
-
# @param theTokens [Array] The input tokens
|
62
|
-
# @param theChildren [Array] Children nodes (one per rhs symbol)
|
63
|
-
def new_parent_node(aProduction, aRange, theTokens, theChildren)
|
64
|
-
node = case aProduction.name
|
65
|
-
when 'expression[0]' # rule 'expression' => %w[sign simple_expression]
|
66
|
-
reduce_expression_0(aProduction, aRange, theTokens, theChildren)
|
67
|
-
|
68
|
-
# when /value\[\d\]/
|
69
|
-
# return_first_child(aRange, theTokens, theChildren)
|
70
|
-
|
71
|
-
# when 'object[0]'
|
72
|
-
# reduce_object_0(aProduction, aRange, theTokens, theChildren)
|
73
|
-
|
74
|
-
# when 'object[1]'
|
75
|
-
# reduce_object_1(aRange, theTokens, theChildren)
|
76
|
-
|
77
|
-
# when 'member-list[0]'
|
78
|
-
# reduce_member_list_0(aRange, theTokens, theChildren)
|
79
|
-
|
80
|
-
# when 'member-list[1]'
|
81
|
-
# reduce_member_list_1(aProduction, aRange, theTokens, theChildren)
|
82
|
-
|
83
|
-
# when 'member[0]'
|
84
|
-
# reduce_member_0(aProduction, aRange, theTokens, theChildren)
|
85
|
-
|
86
|
-
# when 'array[0]'
|
87
|
-
# reduce_array_0(aProduction, aRange, theTokens, theChildren)
|
88
|
-
|
89
|
-
# when 'array[1]'
|
90
|
-
# reduce_array_1(aRange, theTokens, theChildren)
|
91
|
-
|
92
|
-
# when 'array-items[0]'
|
93
|
-
# reduce_array_items_0(aRange, theTokens, theChildren)
|
94
|
-
|
95
|
-
when 'sign[0]' # rule 'sign' => 'PLUS'
|
96
|
-
return_first_child(aRange, theTokens, theChildren)
|
97
|
-
|
98
|
-
when 'sign[1]' # rule 'sign' => 'MINUS'
|
99
|
-
return_first_child(aRange, theTokens, theChildren)
|
100
|
-
|
101
|
-
when 'sign[2]' #rule 'sign' => []
|
102
|
-
reduce_sign_2(aProduction, aRange, theTokens, theChildren)
|
103
|
-
else
|
104
|
-
raise StandardError, "Don't know production #{aProduction.name}"
|
105
|
-
end
|
106
|
-
|
107
|
-
return node
|
108
|
-
end
|
109
|
-
|
110
|
-
# rule 'expression' => %w[sign simple_expression]
|
111
|
-
def reduce_expression_0(aProduction, aRange, theTokens, theChildren)
|
112
|
-
sign = theChildren[0]
|
113
|
-
# Check type of sign
|
114
|
-
node = if sign && sign.kind_of?(CalcNegateNode)
|
115
|
-
sign.members << theChildren.last
|
116
|
-
else
|
117
|
-
theChildren.last
|
118
|
-
end
|
119
|
-
|
120
|
-
return node
|
121
|
-
end
|
122
|
-
|
123
|
-
# rule 'sign' => []
|
124
|
-
def reduce_sign_2(aProduction, aRange, theTokens, theChildren)
|
125
|
-
return nil # TODO; check whether this make sense
|
126
|
-
end
|
127
|
-
=begin
|
128
|
-
second_child = theChildren[1]
|
129
|
-
second_child.symbol = aProduction.lhs
|
130
|
-
return second_child
|
131
|
-
end
|
132
|
-
|
133
|
-
# rule 'object' => %w[begin-object end-object]
|
134
|
-
def reduce_object_1(aRange, theTokens, theChildren)
|
135
|
-
return CalcObjectNode.new(aProduction.lhs)
|
136
|
-
end
|
137
|
-
|
138
|
-
# rule 'member-list' => %w[member-list value-separator member]
|
139
|
-
def reduce_member_list_0(aRange, theTokens, theChildren)
|
140
|
-
node = theChildren[0]
|
141
|
-
node.members << theChildren.last
|
142
|
-
return node
|
143
|
-
end
|
144
|
-
|
145
|
-
# rule 'member-list' => 'member'
|
146
|
-
def reduce_member_list_1(aProduction, aRange, theTokens, theChildren)
|
147
|
-
node = CalcObjectNode.new(aProduction.lhs)
|
148
|
-
node.members << theChildren[0]
|
149
|
-
return node
|
150
|
-
end
|
151
|
-
|
152
|
-
# rule 'member' => %w[string name-separator value]
|
153
|
-
def reduce_member_0(aProduction, aRange, theTokens, theChildren)
|
154
|
-
return CalcPair.new(theChildren[0], theChildren[2], aProduction.lhs)
|
155
|
-
end
|
156
|
-
|
157
|
-
# rule 'object' => %w[begin-object member-list end-object]
|
158
|
-
def reduce_array_0(aProduction, aRange, theTokens, theChildren)
|
159
|
-
second_child = theChildren[1]
|
160
|
-
second_child.symbol = aProduction.lhs
|
161
|
-
return second_child
|
162
|
-
end
|
163
|
-
|
164
|
-
|
165
|
-
# rule 'array' => %w[begin-array end-array]
|
166
|
-
def reduce_array_1(aRange, theTokens, theChildren)
|
167
|
-
return CalcArrayNode.new
|
168
|
-
end
|
169
|
-
|
170
|
-
# rule 'array-items' => %w[array-items value-separator value]
|
171
|
-
def reduce_array_items_0(aRange, theTokens, theChildren)
|
172
|
-
node = theChildren[0]
|
173
|
-
node.children << theChildren[2]
|
174
|
-
return node
|
175
|
-
end
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
# rule 'array-items' => %w[value]
|
180
|
-
def reduce_array_items_1(aProduction, aRange, theTokens, theChildren)
|
181
|
-
node = CalcArrayNode.new(aProduction.lhs)
|
182
|
-
node.children << theChildren[0]
|
183
|
-
return node
|
184
|
-
end
|
185
|
-
=end
|
186
|
-
end # class
|
@@ -1,151 +0,0 @@
|
|
1
|
-
# Classes that implement nodes of Abstract Syntax Trees (AST) representing
|
2
|
-
# calculator parse results.
|
3
|
-
|
4
|
-
|
5
|
-
CalcTerminalNode = Struct.new(:token, :value, :position) do
|
6
|
-
def initialize(aToken, aPosition)
|
7
|
-
self.token = aToken
|
8
|
-
self.position = aPosition
|
9
|
-
init_value(aToken.lexeme)
|
10
|
-
end
|
11
|
-
|
12
|
-
# This method can be overriden
|
13
|
-
def init_value(aLiteral)
|
14
|
-
self.value = aLiteral.dup
|
15
|
-
end
|
16
|
-
|
17
|
-
def symbol()
|
18
|
-
self.token.terminal
|
19
|
-
end
|
20
|
-
|
21
|
-
def to_ruby()
|
22
|
-
return value
|
23
|
-
end
|
24
|
-
|
25
|
-
# Part of the 'visitee' role in Visitor design pattern.
|
26
|
-
# @param aVisitor[ParseTreeVisitor] the visitor
|
27
|
-
def accept(aVisitor)
|
28
|
-
aVisitor.visit_terminal(self)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
class CalcNumberNode < CalcTerminalNode
|
33
|
-
def init_value(aLiteral)
|
34
|
-
case aLiteral
|
35
|
-
when /^[+-]?\d+$/
|
36
|
-
self.value = aLiteral.to_i
|
37
|
-
|
38
|
-
when /^[+-]?\d+(\.\d+)?([eE][+-]?\d+)?$/
|
39
|
-
self.value = aLiteral.to_f
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
class CalcCompositeNode
|
45
|
-
attr_accessor(:children)
|
46
|
-
attr_accessor(:symbol)
|
47
|
-
|
48
|
-
def initialize(aSymbol)
|
49
|
-
@symbol = aSymbol
|
50
|
-
@children = []
|
51
|
-
end
|
52
|
-
|
53
|
-
# Part of the 'visitee' role in Visitor design pattern.
|
54
|
-
# @param aVisitor[ParseTreeVisitor] the visitor
|
55
|
-
def accept(aVisitor)
|
56
|
-
aVisitor.visit_nonterminal(self)
|
57
|
-
end
|
58
|
-
|
59
|
-
alias subnodes children
|
60
|
-
|
61
|
-
end # class
|
62
|
-
|
63
|
-
class CalcUnaryOpNode < CalcCompositeNode
|
64
|
-
def initialize(aSymbol)
|
65
|
-
super(aSymbol)
|
66
|
-
end
|
67
|
-
|
68
|
-
# Convert this tree node in a simpler Ruby representation.
|
69
|
-
# Basically a Calc object corresponds to a Ruhy Hash
|
70
|
-
def to_ruby()
|
71
|
-
rep = {}
|
72
|
-
members.each do |pair|
|
73
|
-
rep[pair.name.to_ruby] = pair.value.to_ruby
|
74
|
-
end
|
75
|
-
|
76
|
-
return rep
|
77
|
-
end
|
78
|
-
|
79
|
-
alias members children
|
80
|
-
end # class
|
81
|
-
|
82
|
-
class CalcNegateNode CalcUnaryOpNode
|
83
|
-
end # class
|
84
|
-
|
85
|
-
class CalcBinaryOpNode < CalcCompositeNode
|
86
|
-
def initialize(aSymbol)
|
87
|
-
super(aSymbol)
|
88
|
-
end
|
89
|
-
|
90
|
-
end # class
|
91
|
-
|
92
|
-
class CalcAddNode < CalcBinaryOpNode
|
93
|
-
|
94
|
-
# Convert this tree node in a simpler Ruby representation.
|
95
|
-
# Basically a Calc object corresponds to a Ruhy Hash
|
96
|
-
def to_ruby()
|
97
|
-
rep = []
|
98
|
-
children.each do |child|
|
99
|
-
rep << child.to_ruby
|
100
|
-
end
|
101
|
-
|
102
|
-
return rep
|
103
|
-
end
|
104
|
-
end # class
|
105
|
-
|
106
|
-
|
107
|
-
class CalcSubtractNode < CalcBinaryOpNode
|
108
|
-
|
109
|
-
# Convert this tree node in a simpler Ruby representation.
|
110
|
-
# Basically a Calc object corresponds to a Ruhy Hash
|
111
|
-
def to_ruby()
|
112
|
-
rep = []
|
113
|
-
children.each do |child|
|
114
|
-
rep << child.to_ruby
|
115
|
-
end
|
116
|
-
|
117
|
-
return rep
|
118
|
-
end
|
119
|
-
end # class
|
120
|
-
|
121
|
-
class CalcMultiplyNode < CalcBinaryOpNode
|
122
|
-
|
123
|
-
# Convert this tree node in a simpler Ruby representation.
|
124
|
-
# Basically a Calc object corresponds to a Ruhy Hash
|
125
|
-
def to_ruby()
|
126
|
-
rep = []
|
127
|
-
children.each do |child|
|
128
|
-
rep << child.to_ruby
|
129
|
-
end
|
130
|
-
|
131
|
-
return rep
|
132
|
-
end
|
133
|
-
end # class
|
134
|
-
|
135
|
-
class CalcDivideNode < CalcBinaryOpNode
|
136
|
-
|
137
|
-
# Convert this tree node in a simpler Ruby representation.
|
138
|
-
# Basically a Calc object corresponds to a Ruhy Hash
|
139
|
-
def to_ruby()
|
140
|
-
rep = []
|
141
|
-
children.each do |child|
|
142
|
-
rep << child.to_ruby
|
143
|
-
end
|
144
|
-
|
145
|
-
return rep
|
146
|
-
end
|
147
|
-
end # class
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
@@ -1,40 +0,0 @@
|
|
1
|
-
require_relative 'calc_parser'
|
2
|
-
|
3
|
-
# Create a calculator parser object
|
4
|
-
parser = CalcParser.new
|
5
|
-
|
6
|
-
# Parse the input expression in command-line
|
7
|
-
if ARGV.empty?
|
8
|
-
my_name = File.basename(__FILE__)
|
9
|
-
msg = <<-END_MSG
|
10
|
-
Command-line symtax:
|
11
|
-
ruby #{my_name} "arithmetic expression"
|
12
|
-
where:
|
13
|
-
the arithmetic expression is enclosed between double quotes (")
|
14
|
-
|
15
|
-
Example:
|
16
|
-
ruby #{my_name} "2 * 3 + (4 - 1)"
|
17
|
-
END_MSG
|
18
|
-
puts msg
|
19
|
-
exit(1)
|
20
|
-
end
|
21
|
-
puts ARGV[0]
|
22
|
-
result = parser.parse_expression(ARGV[0])
|
23
|
-
|
24
|
-
unless result.success?
|
25
|
-
# Stop if the parse failed...
|
26
|
-
puts "Parsing of '#{ARGV[0]}' failed"
|
27
|
-
puts "Reason: #{result.failure_reason.message}"
|
28
|
-
exit(1)
|
29
|
-
end
|
30
|
-
|
31
|
-
# Generate a parse tree from the parse result
|
32
|
-
ptree = result.parse_tree
|
33
|
-
|
34
|
-
# Let's create a parse tree visitor
|
35
|
-
visitor = Rley::ParseTreeVisitor.new(ptree)
|
36
|
-
|
37
|
-
# Now output formatted parse tree
|
38
|
-
renderer = Rley::Formatter::Asciitree.new($stdout)
|
39
|
-
renderer.render(visitor)
|
40
|
-
# End of file
|
@@ -1,28 +0,0 @@
|
|
1
|
-
# Grammar for simple arithmetical expressions
|
2
|
-
require 'rley' # Load the gem
|
3
|
-
|
4
|
-
########################################
|
5
|
-
# Define a grammar for basic arithmetical expressions
|
6
|
-
builder = Rley::Syntax::GrammarBuilder.new do
|
7
|
-
add_terminals('NUMBER')
|
8
|
-
add_terminals('LPAREN', 'RPAREN') # For '(', ')' delimiters
|
9
|
-
add_terminals('PLUS', 'MINUS') # For '+', '-' operators or sign
|
10
|
-
add_terminals('STAR', 'DIVIDE') # For '*', '/' operators
|
11
|
-
rule 'expression' => %w[sign simple_expression]
|
12
|
-
rule 'simple_expression' => 'term'
|
13
|
-
rule 'simple_expression' => %w[simple_expression add_operator term]
|
14
|
-
rule 'term' => 'factor'
|
15
|
-
rule 'term' => %w[term mul_operator factor]
|
16
|
-
rule 'factor' => 'NUMBER'
|
17
|
-
rule 'factor' => %w[LPAREN expression RPAREN]
|
18
|
-
rule 'sign' => 'PLUS'
|
19
|
-
rule 'sign' => 'MINUS'
|
20
|
-
rule 'sign' => []
|
21
|
-
rule 'add_operator' => 'PLUS'
|
22
|
-
rule 'add_operator' => 'MINUS'
|
23
|
-
rule 'mul_operator' => 'STAR'
|
24
|
-
rule 'mul_operator' => 'DIVIDE'
|
25
|
-
end
|
26
|
-
|
27
|
-
# And now build the grammar...
|
28
|
-
CalcGrammar = builder.grammar
|