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