rley 0.5.01 → 0.5.02
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/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
|