rley 0.5.01 → 0.5.02
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/examples/data_formats/JSON/cli_options.rb +25 -9
- data/examples/data_formats/JSON/json_ast_builder.rb +152 -0
- data/examples/data_formats/JSON/json_ast_nodes.rb +141 -0
- data/examples/data_formats/JSON/json_demo.rb +24 -8
- data/examples/general/calc_iter1/calc_ast_builder.rb +142 -0
- data/examples/general/calc_iter1/calc_ast_nodes.rb +151 -0
- data/examples/general/calc_iter1/calc_demo.rb +38 -0
- data/examples/general/calc_iter1/calc_grammar.rb +25 -0
- data/examples/general/calc_iter1/calc_lexer.rb +81 -0
- data/examples/general/{calc → calc_iter1}/calc_parser.rb +0 -0
- data/examples/general/calc_iter1/spec/calculator_spec.rb +73 -0
- data/examples/general/calc_iter2/calc_ast_builder.rb +186 -0
- data/examples/general/calc_iter2/calc_ast_nodes.rb +151 -0
- data/examples/general/{calc → calc_iter2}/calc_demo.rb +3 -2
- data/examples/general/{calc → calc_iter2}/calc_grammar.rb +0 -0
- data/examples/general/calc_iter2/calc_lexer.rb +81 -0
- data/examples/general/calc_iter2/calc_parser.rb +24 -0
- data/lib/rley.rb +1 -0
- data/lib/rley/constants.rb +1 -1
- data/lib/rley/parser/cst_builder.rb +5 -225
- data/lib/rley/parser/gfg_parsing.rb +2 -2
- data/lib/rley/parser/parse_forest_factory.rb +1 -1
- data/lib/rley/parser/parse_rep_creator.rb +2 -2
- data/lib/rley/parser/parse_tree_builder.rb +161 -104
- data/lib/rley/parser/parse_tree_factory.rb +6 -2
- data/spec/rley/parser/ast_builder_spec.rb +395 -0
- data/spec/rley/support/grammar_arr_int_helper.rb +21 -11
- metadata +20 -9
- data/examples/general/calc/calc_lexer.rb +0 -90
- data/spec/rley/parser/parse_tree_builder_spec.rb +0 -249
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 638178eefcf2e673a801018aeee17ff0ef7ed16b
|
4
|
+
data.tar.gz: 894a767a871ba958b61047369e989783ae5fe88d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23571e5b06645c427ee3ac06e662d93a3aca87d64d1cb7b57ea133ee14fd9f6a0ac0d4bebde44dd8d70cefcbb8e4614d3e49752c76e4545ed8e0923c2cbdb1dc
|
7
|
+
data.tar.gz: 47bdc2e63a242463221143098758c0cfc2ff65b64789c9b46ca6e5073d5dc9a8b07317b29a6edac79056cea7c3c41d334d9a5ad7b697780e4ba17258624a7526
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
### 0.5.02 / 2017-10-08
|
2
|
+
* [NEW] Addedsupport for ASTs (Abstract Syntax Tree)
|
3
|
+
* [CHANGE] File `examples/data_formats/JSON/JSON_demo.rb` Added New command-line switches for details use --help option
|
4
|
+
* [CHANGE] File `examples/general/calc_iter1/calc_demo.rb` Calculator now computes the value of a parsed expression
|
5
|
+
|
1
6
|
### 0.5.01 / 2017-08-20
|
2
7
|
* [CHANGE] File `appveyor.yml`: Added Ruby 2.4 build environment in Appveyor CI
|
3
8
|
|
@@ -7,12 +7,13 @@ class CLIOptions < Hash
|
|
7
7
|
# parse trees from LBN output.
|
8
8
|
def initialize(progName, progVersion, args)
|
9
9
|
super()
|
10
|
-
|
10
|
+
|
11
11
|
# Default values
|
12
12
|
self[:prog_name] = progName
|
13
13
|
self[:prog_version] = progVersion
|
14
14
|
self[:format] = :ascii_tree
|
15
|
-
|
15
|
+
self[:rep] = :cst
|
16
|
+
|
16
17
|
options = build_option_parser
|
17
18
|
options.parse!(args)
|
18
19
|
end
|
@@ -23,29 +24,44 @@ class CLIOptions < Hash
|
|
23
24
|
OptionParser.new do |opts|
|
24
25
|
opts.banner = <<-END_BANNER
|
25
26
|
#{self[:prog_name]}: a demo utility that parses a JSON file
|
26
|
-
and renders its parse tree to the standard output
|
27
|
+
and renders its parse tree to the standard output
|
27
28
|
in the format specified in the command-line.
|
28
29
|
|
29
30
|
Usage: json_demo.rb [options] FILE
|
30
31
|
|
31
32
|
Examples:
|
32
33
|
json_demo --format ascii_tree sample01.json
|
34
|
+
json_demo --rep ast --format ruby sample01.json
|
33
35
|
END_BANNER
|
34
36
|
|
35
37
|
opts.separator ''
|
36
38
|
|
37
39
|
format_help = <<-END_TEXT
|
38
40
|
Select the output format (default: ascii_tree). Available formats:
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
ascii_tree [cst, ast] Simple text representation of parse trees
|
42
|
+
minify [cst] Strip all unnecessary whitespace in input json file
|
43
|
+
labelled [cst, ast] Labelled square notation (LBN)
|
44
|
+
Use online tools (e.g. http://yohasebe.com/rsyntaxtree/)
|
45
|
+
to visualize parse trees from LBN output.
|
46
|
+
ruby [ast] A Ruby representation of the JSON input.
|
44
47
|
END_TEXT
|
45
|
-
formats = %i[ascii_tree labelled minify]
|
48
|
+
formats = %i[ascii_tree labelled minify ruby]
|
46
49
|
opts.on('-f', '--format FORMAT', formats, format_help) do |frm|
|
47
50
|
self[:format] = frm
|
48
51
|
end
|
52
|
+
opts.separator ''
|
53
|
+
|
54
|
+
rep_help = <<-END_TEXT
|
55
|
+
Set the parse tree representation (default: cst)
|
56
|
+
cst Concrete Syntax Tree. The out-of-the-box parse tree
|
57
|
+
representation.
|
58
|
+
ast Abstract Syntaxt Tree. A customized parse tree for JSON.
|
59
|
+
It is a more compact and practical representation.
|
60
|
+
END_TEXT
|
61
|
+
representations = %i[cst ast]
|
62
|
+
opts.on('-r', '--rep REP', representations, rep_help) do |rep|
|
63
|
+
self[:rep] = rep
|
64
|
+
end
|
49
65
|
|
50
66
|
opts.separator ''
|
51
67
|
opts.separator ' **** Utility ****'
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require_relative 'json_ast_nodes'
|
2
|
+
|
3
|
+
# The purpose of a JSONASTBuilder 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 JSONASTBuilder < Rley::Parser::ParseTreeBuilder
|
11
|
+
Terminal2NodeClass = {
|
12
|
+
'false' => JSONBooleanNode,
|
13
|
+
'true' => JSONBooleanNode,
|
14
|
+
'null' => JSONNullNode,
|
15
|
+
'string' => JSONStringNode,
|
16
|
+
'number' => JSONNumberNode,
|
17
|
+
}
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def return_first_child(_range, _tokens, theChildren)
|
22
|
+
return theChildren[0]
|
23
|
+
end
|
24
|
+
|
25
|
+
def return_second_child(_range, _tokens, theChildren)
|
26
|
+
return theChildren[1]
|
27
|
+
end
|
28
|
+
|
29
|
+
# Overriding method.
|
30
|
+
# Create a parse tree object with given
|
31
|
+
# node as root node.
|
32
|
+
def create_tree(aRootNode)
|
33
|
+
return Rley::PTree::ParseTree.new(aRootNode)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Overriding method.
|
37
|
+
# Factory method for creating a node object for the given
|
38
|
+
# input token.
|
39
|
+
# @param terminal [Terminal] Terminal symbol associated with the token
|
40
|
+
# @param aTokenPosition [Integer] Position of token in the input stream
|
41
|
+
# @param aToken [Token] The input token
|
42
|
+
def new_leaf_node(terminal, aTokenPosition, aToken)
|
43
|
+
klass = Terminal2NodeClass.fetch(terminal.name, JSONTerminalNode)
|
44
|
+
return klass.new(aToken, aTokenPosition)
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
# Method to override.
|
49
|
+
# Factory method for creating a parent node object.
|
50
|
+
# @param aProduction [Production] Production rule
|
51
|
+
# @param aRange [Range] Range of tokens matched by the rule
|
52
|
+
# @param theTokens [Array] The input tokens
|
53
|
+
# @param theChildren [Array] Children nodes (one per rhs symbol)
|
54
|
+
def new_parent_node(aProduction, aRange, theTokens, theChildren)
|
55
|
+
node = case aProduction.name
|
56
|
+
when 'JSON-text[0]' # rule 'JSON-text' => 'value'
|
57
|
+
return_first_child(aRange, theTokens, theChildren)
|
58
|
+
|
59
|
+
when /value\[\d\]/
|
60
|
+
return_first_child(aRange, theTokens, theChildren)
|
61
|
+
|
62
|
+
when 'object[0]'
|
63
|
+
reduce_object_0(aProduction, aRange, theTokens, theChildren)
|
64
|
+
|
65
|
+
when 'object[1]'
|
66
|
+
reduce_object_1(aRange, theTokens, theChildren)
|
67
|
+
|
68
|
+
when 'member-list[0]'
|
69
|
+
reduce_member_list_0(aRange, theTokens, theChildren)
|
70
|
+
|
71
|
+
when 'member-list[1]'
|
72
|
+
reduce_member_list_1(aProduction, aRange, theTokens, theChildren)
|
73
|
+
|
74
|
+
when 'member[0]'
|
75
|
+
reduce_member_0(aProduction, aRange, theTokens, theChildren)
|
76
|
+
|
77
|
+
when 'array[0]'
|
78
|
+
reduce_array_0(aProduction, aRange, theTokens, theChildren)
|
79
|
+
|
80
|
+
when 'array[1]'
|
81
|
+
reduce_array_1(aRange, theTokens, theChildren)
|
82
|
+
|
83
|
+
when 'array-items[0]'
|
84
|
+
reduce_array_items_0(aRange, theTokens, theChildren)
|
85
|
+
|
86
|
+
when 'array-items[1]'
|
87
|
+
reduce_array_items_1(aProduction, aRange, theTokens, theChildren)
|
88
|
+
else
|
89
|
+
raise StandardError, "Don't know production #{aProduction.name}"
|
90
|
+
end
|
91
|
+
|
92
|
+
return node
|
93
|
+
end
|
94
|
+
|
95
|
+
# rule 'object' => %w[begin-object member-list end-object]
|
96
|
+
def reduce_object_0(aProduction, aRange, theTokens, theChildren)
|
97
|
+
second_child = theChildren[1]
|
98
|
+
second_child.symbol = aProduction.lhs
|
99
|
+
return second_child
|
100
|
+
end
|
101
|
+
|
102
|
+
# rule 'object' => %w[begin-object end-object]
|
103
|
+
def reduce_object_1(aRange, theTokens, theChildren)
|
104
|
+
return JSONObjectNode.new(aProduction.lhs)
|
105
|
+
end
|
106
|
+
|
107
|
+
# rule 'member-list' => %w[member-list value-separator member]
|
108
|
+
def reduce_member_list_0(aRange, theTokens, theChildren)
|
109
|
+
node = theChildren[0]
|
110
|
+
node.members << theChildren.last
|
111
|
+
return node
|
112
|
+
end
|
113
|
+
|
114
|
+
# rule 'member-list' => 'member'
|
115
|
+
def reduce_member_list_1(aProduction, aRange, theTokens, theChildren)
|
116
|
+
node = JSONObjectNode.new(aProduction.lhs)
|
117
|
+
node.members << theChildren[0]
|
118
|
+
return node
|
119
|
+
end
|
120
|
+
|
121
|
+
# rule 'member' => %w[string name-separator value]
|
122
|
+
def reduce_member_0(aProduction, aRange, theTokens, theChildren)
|
123
|
+
return JSONPair.new(theChildren[0], theChildren[2], aProduction.lhs)
|
124
|
+
end
|
125
|
+
|
126
|
+
# rule 'object' => %w[begin-object member-list end-object]
|
127
|
+
def reduce_array_0(aProduction, aRange, theTokens, theChildren)
|
128
|
+
second_child = theChildren[1]
|
129
|
+
second_child.symbol = aProduction.lhs
|
130
|
+
return second_child
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
# rule 'array' => %w[begin-array end-array]
|
135
|
+
def reduce_array_1(aRange, theTokens, theChildren)
|
136
|
+
return JSONArrayNode.new
|
137
|
+
end
|
138
|
+
|
139
|
+
# rule 'array-items' => %w[array-items value-separator value]
|
140
|
+
def reduce_array_items_0(aRange, theTokens, theChildren)
|
141
|
+
node = theChildren[0]
|
142
|
+
node.children << theChildren[2]
|
143
|
+
return node
|
144
|
+
end
|
145
|
+
|
146
|
+
# rule 'array-items' => %w[value]
|
147
|
+
def reduce_array_items_1(aProduction, aRange, theTokens, theChildren)
|
148
|
+
node = JSONArrayNode.new(aProduction.lhs)
|
149
|
+
node.children << theChildren[0]
|
150
|
+
return node
|
151
|
+
end
|
152
|
+
end # class
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# Classes that implement nodes of Abstract Syntax Trees (AST) representing
|
2
|
+
# JSON parse results.
|
3
|
+
|
4
|
+
|
5
|
+
JSONTerminalNode = 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
|
+
|
33
|
+
class JSONNullNode < JSONTerminalNode
|
34
|
+
def init_value(_aLiteral)
|
35
|
+
self.value = nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class JSONBooleanNode < JSONTerminalNode
|
40
|
+
def init_value(aLiteral)
|
41
|
+
self.value = aLiteral == 'true'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class JSONStringNode < JSONTerminalNode
|
46
|
+
end
|
47
|
+
|
48
|
+
class JSONNumberNode < JSONTerminalNode
|
49
|
+
def init_value(aLiteral)
|
50
|
+
case aLiteral
|
51
|
+
when /^[+-]?\d+$/
|
52
|
+
self.value = aLiteral.to_i
|
53
|
+
|
54
|
+
when /^[+-]?\d+(\.\d+)?([eE][+-]?\d+)?$/
|
55
|
+
self.value = aLiteral.to_f
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class JSONCompositeNode
|
61
|
+
attr_accessor(:children)
|
62
|
+
attr_accessor(:symbol)
|
63
|
+
|
64
|
+
def initialize(aSymbol)
|
65
|
+
@symbol = aSymbol
|
66
|
+
@children = []
|
67
|
+
end
|
68
|
+
|
69
|
+
# Part of the 'visitee' role in Visitor design pattern.
|
70
|
+
# @param aVisitor[ParseTreeVisitor] the visitor
|
71
|
+
def accept(aVisitor)
|
72
|
+
aVisitor.visit_nonterminal(self)
|
73
|
+
end
|
74
|
+
|
75
|
+
alias subnodes children
|
76
|
+
|
77
|
+
end # class
|
78
|
+
|
79
|
+
|
80
|
+
class JSONArrayNode < JSONCompositeNode
|
81
|
+
def initialize(aSymbol)
|
82
|
+
super(aSymbol)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Convert this tree node in a simpler Ruby representation.
|
86
|
+
# Basically a JSON object corresponds to a Ruhy Hash
|
87
|
+
def to_ruby()
|
88
|
+
rep = []
|
89
|
+
children.each do |child|
|
90
|
+
rep << child.to_ruby
|
91
|
+
end
|
92
|
+
|
93
|
+
return rep
|
94
|
+
end
|
95
|
+
end # class
|
96
|
+
|
97
|
+
class JSONPair
|
98
|
+
attr_reader(:name)
|
99
|
+
attr_reader(:value)
|
100
|
+
attr_reader(:symbol)
|
101
|
+
|
102
|
+
def initialize(aName, aValue, aSymbol)
|
103
|
+
@name = aName
|
104
|
+
@value = aValue
|
105
|
+
@symbol = aSymbol
|
106
|
+
end
|
107
|
+
|
108
|
+
def children()
|
109
|
+
return [name, value]
|
110
|
+
end
|
111
|
+
|
112
|
+
alias subnodes children
|
113
|
+
|
114
|
+
# Part of the 'visitee' role in Visitor design pattern.
|
115
|
+
# @param aVisitor[ParseTreeVisitor] the visitor
|
116
|
+
def accept(aVisitor)
|
117
|
+
aVisitor.visit_nonterminal(self)
|
118
|
+
end
|
119
|
+
|
120
|
+
end # class
|
121
|
+
|
122
|
+
class JSONObjectNode < JSONCompositeNode
|
123
|
+
def initialize(aSymbol)
|
124
|
+
super(aSymbol)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Convert this tree node in a simpler Ruby representation.
|
128
|
+
# Basically a JSON object corresponds to a Ruhy Hash
|
129
|
+
def to_ruby()
|
130
|
+
rep = {}
|
131
|
+
members.each do |pair|
|
132
|
+
rep[pair.name.to_ruby] = pair.value.to_ruby
|
133
|
+
end
|
134
|
+
|
135
|
+
return rep
|
136
|
+
end
|
137
|
+
|
138
|
+
alias members children
|
139
|
+
end # class
|
140
|
+
|
141
|
+
|
@@ -1,9 +1,10 @@
|
|
1
1
|
require_relative 'cli_options'
|
2
2
|
require_relative 'json_parser'
|
3
3
|
require_relative 'json_minifier'
|
4
|
+
require_relative 'json_ast_builder'
|
4
5
|
|
5
6
|
prog_name = 'json_demo'
|
6
|
-
prog_version = '0.
|
7
|
+
prog_version = '0.3.0'
|
7
8
|
|
8
9
|
cli_options = CLIOptions.new(prog_name, prog_version, ARGV)
|
9
10
|
if ARGV.empty?
|
@@ -24,8 +25,8 @@ unless result.success?
|
|
24
25
|
exit(1)
|
25
26
|
end
|
26
27
|
|
27
|
-
|
28
|
-
|
28
|
+
tree_rep = cli_options[:rep]
|
29
|
+
renderer = nil
|
29
30
|
|
30
31
|
# Select the output format
|
31
32
|
case cli_options[:format]
|
@@ -34,12 +35,27 @@ case cli_options[:format]
|
|
34
35
|
when :labelled
|
35
36
|
renderer = Rley::Formatter::BracketNotation.new($stdout)
|
36
37
|
when :minify
|
37
|
-
|
38
|
+
msg = "minify format works for 'cst' representation only"
|
39
|
+
raise StandardError, msg if tree_rep == :ast
|
40
|
+
renderer = JSONMinifier.new($stdout)
|
41
|
+
when :ruby
|
42
|
+
msg = "ruby format works for 'ast' representation only"
|
43
|
+
raise StandardError, msg if tree_rep == :cst
|
38
44
|
end
|
39
45
|
|
40
|
-
|
41
|
-
|
46
|
+
tree_builder = (tree_rep == :ast)? JSONASTBuilder : nil
|
47
|
+
|
48
|
+
# Generate a parse tree from the parse result
|
49
|
+
ptree = result.parse_tree(tree_builder)
|
42
50
|
|
43
|
-
|
44
|
-
|
51
|
+
if renderer
|
52
|
+
# Let's create a parse tree visitor
|
53
|
+
visitor = Rley::ParseTreeVisitor.new(ptree)
|
54
|
+
|
55
|
+
# Now output formatted parse tree
|
56
|
+
renderer.render(visitor)
|
57
|
+
else
|
58
|
+
root = ptree.root
|
59
|
+
p(root.to_ruby) # Output the Ruby representation of the JSON input
|
60
|
+
end
|
45
61
|
# End of file
|
@@ -0,0 +1,142 @@
|
|
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
|
+
'NUMBER' => CalcNumberNode
|
13
|
+
}
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def return_first_child(_range, _tokens, theChildren)
|
18
|
+
return theChildren[0]
|
19
|
+
end
|
20
|
+
|
21
|
+
def return_second_child(_range, _tokens, theChildren)
|
22
|
+
return theChildren[1]
|
23
|
+
end
|
24
|
+
|
25
|
+
def return_last_child(_range, _tokens, theChildren)
|
26
|
+
return theChildren[-1]
|
27
|
+
end
|
28
|
+
|
29
|
+
# Overriding method.
|
30
|
+
# Create a parse tree object with given
|
31
|
+
# node as root node.
|
32
|
+
def create_tree(aRootNode)
|
33
|
+
return Rley::PTree::ParseTree.new(aRootNode)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Overriding method.
|
37
|
+
# Factory method for creating a node object for the given
|
38
|
+
# input token.
|
39
|
+
# @param aTerminal [Terminal] Terminal symbol associated with the token
|
40
|
+
# @param aTokenPosition [Integer] Position of token in the input stream
|
41
|
+
# @param aToken [Token] The input token
|
42
|
+
def new_leaf_node(aProduction, aTerminal, aTokenPosition, aToken)
|
43
|
+
klass = Terminal2NodeClass.fetch(aTerminal.name, CalcTerminalNode)
|
44
|
+
if klass
|
45
|
+
node = klass.new(aToken, aTokenPosition)
|
46
|
+
else
|
47
|
+
node = PTree::TerminalNode.new(aToken, aTokenPosition)
|
48
|
+
end
|
49
|
+
|
50
|
+
return node
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
# Method to override.
|
55
|
+
# Factory method for creating a parent node object.
|
56
|
+
# @param aProduction [Production] Production rule
|
57
|
+
# @param aRange [Range] Range of tokens matched by the rule
|
58
|
+
# @param theTokens [Array] The input tokens
|
59
|
+
# @param theChildren [Array] Children nodes (one per rhs symbol)
|
60
|
+
def new_parent_node(aProduction, aRange, theTokens, theChildren)
|
61
|
+
node = case aProduction.name
|
62
|
+
when 'expression[0]' # rule 'expression' => 'simple_expression'
|
63
|
+
return_first_child(aRange, theTokens, theChildren)
|
64
|
+
|
65
|
+
when 'simple_expression[0]' # rule 'simple_expression' => 'term'
|
66
|
+
return_first_child(aRange, theTokens, theChildren)
|
67
|
+
|
68
|
+
when 'simple_expression[1]'
|
69
|
+
# rule 'simple_expression' => %w[simple_expression add_operator term]
|
70
|
+
reduce_simple_expression_1(aProduction, aRange, theTokens, theChildren)
|
71
|
+
|
72
|
+
when 'term[0]' # rule 'term' => 'factor'
|
73
|
+
return_first_child(aRange, theTokens, theChildren)
|
74
|
+
|
75
|
+
when 'term[1]' # rule 'term' => %w[term mul_operator factor]
|
76
|
+
reduce_term_1(aProduction, aRange, theTokens, theChildren)
|
77
|
+
|
78
|
+
when 'factor[0]' # rule 'factor' => 'NUMBER'
|
79
|
+
return_first_child(aRange, theTokens, theChildren)
|
80
|
+
|
81
|
+
when 'factor[1]' # rule 'factor' => %w[LPAREN expression RPAREN]
|
82
|
+
return_second_child(aRange, theTokens, theChildren)
|
83
|
+
|
84
|
+
when 'add_operator[0]' # rule 'add_operator' => 'PLUS'
|
85
|
+
reduce_add_operator_0(aProduction, aRange, theTokens, theChildren)
|
86
|
+
|
87
|
+
when 'add_operator[1]' # rule 'add_operator' => 'MINUS'
|
88
|
+
reduce_add_operator_1(aProduction, aRange, theTokens, theChildren)
|
89
|
+
|
90
|
+
when 'mul_operator[0]' # rule 'mul_operator' => 'STAR'
|
91
|
+
reduce_mul_operator_0(aProduction, aRange, theTokens, theChildren)
|
92
|
+
|
93
|
+
when 'mul_operator[1]' # rule 'mul_operator' => 'DIVIDE'
|
94
|
+
reduce_mul_operator_1(aProduction, aRange, theTokens, theChildren)
|
95
|
+
|
96
|
+
else
|
97
|
+
raise StandardError, "Don't know production #{aProduction.name}"
|
98
|
+
end
|
99
|
+
|
100
|
+
return node
|
101
|
+
end
|
102
|
+
|
103
|
+
def reduce_binary_operator(theChildren)
|
104
|
+
operator_node = theChildren[1]
|
105
|
+
operator_node.children << theChildren[0]
|
106
|
+
operator_node.children << theChildren[2]
|
107
|
+
return operator_node
|
108
|
+
end
|
109
|
+
|
110
|
+
# rule 'simple_expression' => %w[simple_expression add_operator term]
|
111
|
+
def reduce_simple_expression_1(aProduction, aRange, theTokens, theChildren)
|
112
|
+
reduce_binary_operator(theChildren)
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
# rule 'term' => %w[term mul_operator factor]
|
117
|
+
def reduce_term_1(aProduction, aRange, theTokens, theChildren)
|
118
|
+
reduce_binary_operator(theChildren)
|
119
|
+
end
|
120
|
+
|
121
|
+
# rule 'add_operator' => 'PLUS'
|
122
|
+
def reduce_add_operator_0(aProduction, aRange, theTokens, theChildren)
|
123
|
+
return CalcAddNode.new(theChildren[0].symbol)
|
124
|
+
end
|
125
|
+
|
126
|
+
# rule 'add_operator' => 'MINUS'
|
127
|
+
def reduce_add_operator_1(aProduction, aRange, theTokens, theChildren)
|
128
|
+
return CalcSubtractNode.new(theChildren[0].symbol)
|
129
|
+
end
|
130
|
+
|
131
|
+
# rule 'mul_operator' => 'STAR'
|
132
|
+
def reduce_mul_operator_0(aProduction, aRange, theTokens, theChildren)
|
133
|
+
return CalcMultiplyNode.new(theChildren[0].symbol)
|
134
|
+
end
|
135
|
+
|
136
|
+
# rule 'mul_operator' => 'DIVIDE'
|
137
|
+
def reduce_mul_operator_1(aProduction, aRange, theTokens, theChildren)
|
138
|
+
return CalcDivideNode.new(theChildren[0].symbol)
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
end # class
|