rley 0.5.04 → 0.5.05

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b5c84e4d5e47e769c76da705abef632e4748311c
4
- data.tar.gz: 876569a22741aa76f5d9b7bec8404c0a518e02b3
3
+ metadata.gz: 5fdf5f08878706a50fe9b024c7ffed95b3cb08fd
4
+ data.tar.gz: 92265adccb7cb660b56ac2b060aee4a5facdd777
5
5
  SHA512:
6
- metadata.gz: da13f9f5924752adc98be8822f079e078ff4e5345a1cbf63dcd3f6d0c28c695397abf4f80db6fa38e549615cb1db264baadcb4b3fdc1428ef45ac3aa490459cf
7
- data.tar.gz: 84b73e987ffb98f5ce5cf1a57f62a743507bff497c8c8c3294e887310036ba9893796ec5f3b9d1d8f2335b7c33bb47cda3e6455e7f66ce0dfcb3c85b90e629a4
6
+ metadata.gz: 936959f76b1b2dc9fe6db68e4a0372f6aa421ea143876421c0ee90ab95852f71dad9fc166d80269af070fe84dfcbdef3848cb29be321e474afd40dae9e3bce59
7
+ data.tar.gz: 4e2f8a16366d4771e6d8506d3333adc40329c01a21952ee2002d432a5d0205c6ab6658b96d245bd4688227c6cf2c053fa66a57a9bdbeaeca4ef4969425b21457
@@ -1,3 +1,13 @@
1
+ ### 0.5.05 / 2017-11-04
2
+ * [FIX] Method `GFGParsing#call_rule` didn't handle properly the case of nullable symbols appearing in more than one production rule.
3
+ * [New] New calculator example. In addition to the basic arithmetic operators (+, -, *, /) it accepts the unary minus
4
+ and the exponentiation operator. As a convenience it displays both CST and AST parse representations.
5
+ * [CHANGE] Method `ParseWalkerFactory#build_walker` added one argument that controls the way the visit when reaching anew an end vertex.
6
+ * [CHANGE] Method `ParseWalkerFactory#visit_entry` when re-visit an end vertex, the jump to related start vertex is now conditional.
7
+ * [CHANGE] File `parse_walker_factory_spec.rb` Added test to validate the different ways to walk over the parse entries.
8
+ * [CHANGE] Class `GrmFlowGraph`: Added more documentation.
9
+ * [CHANGE] Class `Vertex`: Documentation refined.
10
+
1
11
  ### 0.5.04 / 2017-10-26
2
12
  * [Fix] Method GrmFlowGraph#traverse_df code terminated prematurely with nested call edges.
3
13
  * [CHANGE] Method Grammar#name_production: suffix in default production name is changed (e.g. 'expression[3]' changed to expression_3)
@@ -11,7 +11,7 @@ Command-line symtax:
11
11
  the arithmetic expression is enclosed between double quotes (")
12
12
 
13
13
  Example:
14
- ruby #{my_name} "2 * 3 + (4 - 1)"
14
+ ruby #{my_name} "2 * 3 + (1 + 3 ** 2)"
15
15
  END_MSG
16
16
  puts msg
17
17
  exit(1)
@@ -0,0 +1,200 @@
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
+ # Lexical ambiguity: minus sign represents two very concepts:
13
+ # The unary negation operator on one hand, the binary substraction operator
14
+ 'MINUS' => { 'add_operator_1' => Rley::PTree::TerminalNode,
15
+ 'factor_2' => CalcNegateNode,
16
+ 'sign_1' => CalcNegateNode
17
+ },
18
+ 'NUMBER' => CalcNumberNode
19
+ }.freeze
20
+
21
+ protected
22
+
23
+ def return_first_child(_range, _tokens, theChildren)
24
+ return theChildren[0]
25
+ end
26
+
27
+ def return_second_child(_range, _tokens, theChildren)
28
+ return theChildren[1]
29
+ end
30
+
31
+ def return_last_child(_range, _tokens, theChildren)
32
+ return theChildren[-1]
33
+ end
34
+
35
+ def return_epsilon(_range, _tokens, _children)
36
+ return nil
37
+ end
38
+
39
+ # Overriding method.
40
+ # Create a parse tree object with given
41
+ # node as root node.
42
+ def create_tree(aRootNode)
43
+ return Rley::PTree::ParseTree.new(aRootNode)
44
+ end
45
+
46
+ # Overriding method.
47
+ # Factory method for creating a node object for the given
48
+ # input token.
49
+ # @param aTerminal [Terminal] Terminal symbol associated with the token
50
+ # @param aTokenPosition [Integer] Position of token in the input stream
51
+ # @param aToken [Token] The input token
52
+ def new_leaf_node(aProduction, aTerminal, aTokenPosition, aToken)
53
+ klass = Terminal2NodeClass.fetch(aTerminal.name, CalcTerminalNode)
54
+ node = if klass
55
+ if klass.is_a?(Hash)
56
+ # Lexical ambiguity...
57
+ klass = klass.fetch(aProduction.name)
58
+ end
59
+ klass.new(aToken, aTokenPosition)
60
+ else
61
+ PTree::TerminalNode.new(aToken, aTokenPosition)
62
+ end
63
+
64
+ return node
65
+ end
66
+
67
+ # Method to override.
68
+ # Factory method for creating a parent node object.
69
+ # @param aProduction [Production] Production rule
70
+ # @param aRange [Range] Range of tokens matched by the rule
71
+ # @param theTokens [Array] The input tokens
72
+ # @param theChildren [Array] Children nodes (one per rhs symbol)
73
+ def new_parent_node(aProduction, aRange, theTokens, theChildren)
74
+ node = case aProduction.name
75
+ when 'expression_0' # rule 'expression' => %w[simple_expression]
76
+ return_first_child(aRange, theTokens, theChildren)
77
+
78
+ when 'simple_expression_0' # rule 'simple_expression' => 'term'
79
+ return_first_child(aRange, theTokens, theChildren)
80
+
81
+ when 'simple_expression_1'
82
+ # rule 'simple_expression' => %w[simple_expression add_operator term]
83
+ reduce_simple_expression_1(aProduction, aRange, theTokens, theChildren)
84
+
85
+ when 'term_0' # rule 'term' => 'factor'
86
+ return_first_child(aRange, theTokens, theChildren)
87
+
88
+ when 'term_1' # rule 'term' => %w[term mul_operator factor]
89
+ reduce_term_1(aProduction, aRange, theTokens, theChildren)
90
+
91
+ when 'factor_0' # rule 'factor' => 'simple_factor'
92
+ return_first_child(aRange, theTokens, theChildren)
93
+
94
+ when 'factor_1' # rule 'factor' => %w[simple_factor POWER simple_factor]
95
+ reduce_factor_1(aProduction, aRange, theTokens, theChildren)
96
+
97
+ when 'simple_factor_0' # rule 'simple_factor' => %[sign NUMBER]
98
+ reduce_simple_factor_0(aProduction, aRange, theTokens, theChildren)
99
+
100
+ when 'simple_factor_1' # rule 'simple_factor' => %w[LPAREN expression RPAREN]
101
+ return_second_child(aRange, theTokens, theChildren)
102
+
103
+ when 'simple_factor_2' # rule 'simple_factor' => %w[MINUS LPAREN expression RPAREN]
104
+ reduce_simple_factor_2(aProduction, aRange, theTokens, theChildren)
105
+
106
+
107
+ when 'sign_0' # rule 'sign' => 'PLUS'
108
+ return_first_child(aRange, theTokens, theChildren)
109
+
110
+ when 'sign_1' # rule 'sign' => 'MINUS'
111
+ return_first_child(aRange, theTokens, theChildren)
112
+
113
+ when 'sign_2'
114
+ return_epsilon(aRange, theTokens, theChildren)
115
+
116
+ when 'add_operator_0' # rule 'add_operator' => 'PLUS'
117
+ reduce_add_operator_0(aProduction, aRange, theTokens, theChildren)
118
+
119
+ when 'add_operator_1' # rule 'add_operator' => 'MINUS'
120
+ reduce_add_operator_1(aProduction, aRange, theTokens, theChildren)
121
+
122
+ when 'mul_operator_0' # rule 'mul_operator' => 'STAR'
123
+ reduce_mul_operator_0(aProduction, aRange, theTokens, theChildren)
124
+
125
+ when 'mul_operator_1' # rule 'mul_operator' => 'DIVIDE'
126
+ reduce_mul_operator_1(aProduction, aRange, theTokens, theChildren)
127
+
128
+ else
129
+ raise StandardError, "Don't know production #{aProduction.name}"
130
+ end
131
+
132
+ return node
133
+ end
134
+
135
+ def reduce_binary_operator(theChildren)
136
+ operator_node = theChildren[1]
137
+ operator_node.children << theChildren[0]
138
+ operator_node.children << theChildren[2]
139
+ return operator_node
140
+ end
141
+
142
+ # rule 'simple_expression' => %w[simple_expression add_operator term]
143
+ def reduce_simple_expression_1(_production, _range, _tokens, theChildren)
144
+ reduce_binary_operator(theChildren)
145
+ end
146
+
147
+ # rule 'term' => %w[term mul_operator factor]
148
+ def reduce_term_1(_production, _range, _tokens, theChildren)
149
+ reduce_binary_operator(theChildren)
150
+ end
151
+
152
+ # rule 'factor' => %w[simple_factor POWER simple_factor]]
153
+ def reduce_factor_1(aProduction, aRange, theTokens, theChildren)
154
+ result = PowerNode.new(theChildren[1].symbol, aRange)
155
+ result.children << theChildren[0]
156
+ result.children << theChildren[2]
157
+
158
+ return result
159
+ end
160
+
161
+ # rule 'simple_factor' => %[sign NUMBER]
162
+ def reduce_simple_factor_0(aProduction, aRange, theTokens, theChildren)
163
+ first_child = theChildren[0]
164
+ result = if first_child.kind_of?(CalcNegateNode)
165
+ -theChildren[1]
166
+ else
167
+ theChildren[1]
168
+ end
169
+
170
+ return result
171
+ end
172
+
173
+ # rule 'simple_factor' => %w[MINUS LPAREN expression RPAREN]
174
+ def reduce_simple_factor_2(aProduction, aRange, theTokens, theChildren)
175
+ negation = CalcNegateNode.new(theChildren[0].symbol, aRange.low)
176
+ negation.children << theChildren[2]
177
+ return negation
178
+ end
179
+
180
+ # rule 'add_operator' => 'PLUS'
181
+ def reduce_add_operator_0(_production, aRange, _tokens, theChildren)
182
+ return CalcAddNode.new(theChildren[0].symbol, aRange)
183
+ end
184
+
185
+ # rule 'add_operator' => 'MINUS'
186
+ def reduce_add_operator_1(_production, aRange, _tokens, theChildren)
187
+ return CalcSubtractNode.new(theChildren[0].symbol, aRange)
188
+ end
189
+
190
+ # rule 'mul_operator' => 'STAR'
191
+ def reduce_mul_operator_0(_production, aRange, _tokens, theChildren)
192
+ return CalcMultiplyNode.new(theChildren[0].symbol, aRange)
193
+ end
194
+
195
+ # rule 'mul_operator' => 'DIVIDE'
196
+ def reduce_mul_operator_1(_production, aRange, _tokens, theChildren)
197
+ return CalcDivideNode.new(theChildren[0].symbol, aRange)
198
+ end
199
+ end # class
200
+ # End of file
@@ -0,0 +1,156 @@
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
+ token.terminal
19
+ end
20
+
21
+ def interpret()
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
+
43
+ # Overriding the unary minus operator
44
+ def -@()
45
+ self.value = - self.value
46
+ return self
47
+ end
48
+ end
49
+
50
+ class CalcCompositeNode
51
+ attr_accessor(:children)
52
+ attr_accessor(:symbol)
53
+ attr_accessor(:aPosition)
54
+
55
+ def initialize(aSymbol, aPosition)
56
+ @symbol = aSymbol
57
+ @children = []
58
+ @position = aPosition
59
+ end
60
+
61
+ # Part of the 'visitee' role in Visitor design pattern.
62
+ # @param aVisitor[ParseTreeVisitor] the visitor
63
+ def accept(aVisitor)
64
+ aVisitor.visit_nonterminal(self)
65
+ end
66
+
67
+ alias subnodes children
68
+ end # class
69
+
70
+ class CalcUnaryOpNode < CalcCompositeNode
71
+ def initialize(aSymbol, aPosition)
72
+ super(aSymbol,aPosition)
73
+ end
74
+
75
+ alias members children
76
+ end # class
77
+
78
+ class CalcNegateNode < CalcUnaryOpNode
79
+ def initialize(aSymbol, aPosition)
80
+ super(aSymbol,aPosition)
81
+ end
82
+
83
+ def interpret()
84
+ return -(children[0].interpret)
85
+ end
86
+ end # class
87
+
88
+ class CalcBinaryOpNode < CalcCompositeNode
89
+ def initialize(aSymbol, aRange)
90
+ super(aSymbol, aRange)
91
+ end
92
+
93
+ protected
94
+
95
+ def retrieve_operands()
96
+ operands = []
97
+ children.each do |child|
98
+ oper = child.respond_to?(:interpret) ? child.interpret : child
99
+ operands << oper
100
+ end
101
+
102
+ return operands
103
+ end
104
+ end # class
105
+
106
+ class CalcAddNode < CalcBinaryOpNode
107
+ # TODO
108
+ def interpret()
109
+ operands = retrieve_operands
110
+
111
+ sum = operands[0] + operands[1]
112
+ return sum
113
+ end
114
+ end # class
115
+
116
+
117
+ class CalcSubtractNode < CalcBinaryOpNode
118
+ # TODO
119
+ def interpret()
120
+ operands = retrieve_operands
121
+
122
+ substraction = operands[0] - operands[1]
123
+ return substraction
124
+ end
125
+ end # class
126
+
127
+ class CalcMultiplyNode < CalcBinaryOpNode
128
+ # TODO
129
+ def interpret()
130
+ operands = retrieve_operands
131
+ multiplication = operands[0] * operands[1]
132
+ return multiplication
133
+ end
134
+ end # class
135
+
136
+ class CalcDivideNode < CalcBinaryOpNode
137
+ # TODO
138
+ def interpret()
139
+ operands = retrieve_operands
140
+ numerator = operands[0].to_f
141
+ denominator = operands[1]
142
+ division = numerator / denominator
143
+ return division
144
+ end
145
+ end # class
146
+
147
+
148
+ class PowerNode < CalcBinaryOpNode
149
+ # TODO
150
+ def interpret()
151
+ operands = retrieve_operands
152
+ exponentiation = operands[0] ** operands[1]
153
+ return exponentiation
154
+ end
155
+ end # class
156
+ # End of file
@@ -0,0 +1,66 @@
1
+ require_relative 'calc_parser'
2
+ require_relative 'calc_ast_builder'
3
+
4
+ def print_title(aTitle)
5
+ puts aTitle
6
+ puts '=' * aTitle.size
7
+ end
8
+
9
+ def print_tree(aTitle, aParseTree)
10
+ # Let's create a parse tree visitor
11
+ visitor = Rley::ParseTreeVisitor.new(aParseTree)
12
+
13
+ # Now output formatted parse tree
14
+ print_title(aTitle)
15
+ renderer = Rley::Formatter::Asciitree.new($stdout)
16
+ renderer.render(visitor)
17
+ puts ''
18
+ end
19
+
20
+ # Create a calculator parser object
21
+ parser = CalcParser.new
22
+
23
+ # Parse the input expression in command-line
24
+ if ARGV.empty?
25
+ my_name = File.basename(__FILE__)
26
+ msg = <<-END_MSG
27
+ Demo calculator that prints:
28
+ - The Concrete and Abstract Syntax Trees of the math expression.
29
+ - The result of the math expression.
30
+
31
+ Command-line symtax:
32
+ ruby #{my_name} "arithmetic expression"
33
+ where:
34
+ the arithmetic expression is enclosed between double quotes (")
35
+
36
+ Example:
37
+ ruby #{my_name} "2 * 3 + (1 + 3 ** 2)"
38
+ END_MSG
39
+ puts msg
40
+ exit(1)
41
+ end
42
+ puts ARGV[0]
43
+ result = parser.parse_expression(ARGV[0])
44
+
45
+ unless result.success?
46
+ # Stop if the parse failed...
47
+ puts "Parsing of '#{ARGV[0]}' failed"
48
+ puts "Reason: #{result.failure_reason.message}"
49
+ exit(1)
50
+ end
51
+
52
+
53
+ # Generate a concrete syntax parse tree from the parse result
54
+ cst_ptree = result.parse_tree
55
+ print_tree('Concrete Syntax Tree (CST)', cst_ptree)
56
+
57
+ # Generate an abstract syntax parse tree from the parse result
58
+ tree_builder = CalcASTBuilder
59
+ ast_ptree = result.parse_tree(tree_builder)
60
+ print_tree('Abstract Syntax Tree (AST)', ast_ptree)
61
+
62
+ # Now perform the computation of math expression
63
+ root = ast_ptree.root
64
+ print_title('Result:')
65
+ puts root.interpret.to_s # Output the expression result
66
+ # End of file