choria-mcorpc-support 2.23.0 → 2.24.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/mcollective.rb +3 -3
- data/lib/mcollective/agent/bolt_tasks.ddl +18 -0
- data/lib/mcollective/agent/bolt_tasks.json +18 -0
- data/lib/mcollective/agent/bolt_tasks.rb +4 -2
- data/lib/mcollective/agent/rpcutil.ddl +2 -2
- data/lib/mcollective/agent/rpcutil.json +2 -2
- data/lib/mcollective/application/choria.rb +3 -63
- data/lib/mcollective/application/facts.rb +2 -67
- data/lib/mcollective/application/federation.rb +1 -3
- data/lib/mcollective/application/find.rb +1 -1
- data/lib/mcollective/application/ping.rb +31 -3
- data/lib/mcollective/application/plugin.rb +2 -15
- data/lib/mcollective/application/tasks.rb +9 -0
- data/lib/mcollective/client.rb +1 -1
- data/lib/mcollective/config.rb +135 -103
- data/lib/mcollective/ddl.rb +0 -1
- data/lib/mcollective/discovery.rb +12 -65
- data/lib/mcollective/discovery/broadcast.ddl +11 -0
- data/lib/mcollective/discovery/choria.ddl +6 -4
- data/lib/mcollective/discovery/delegate.ddl +13 -0
- data/lib/mcollective/discovery/delegate.rb +73 -0
- data/lib/mcollective/discovery/external.ddl +13 -0
- data/lib/mcollective/discovery/file.ddl +13 -0
- data/lib/mcollective/discovery/flatfile.ddl +7 -5
- data/lib/mcollective/discovery/inventory.ddl +13 -0
- data/lib/mcollective/discovery/mc.ddl +3 -3
- data/lib/mcollective/generators.rb +0 -1
- data/lib/mcollective/message.rb +0 -24
- data/lib/mcollective/optionparser.rb +2 -2
- data/lib/mcollective/pluginpackager/forge_packager.rb +1 -1
- data/lib/mcollective/rpc/client.rb +6 -4
- data/lib/mcollective/security/base.rb +1 -37
- data/lib/mcollective/util.rb +23 -31
- data/lib/mcollective/util/choria.rb +0 -157
- data/lib/mcollective/util/tasks_support.rb +22 -3
- metadata +9 -27
- data/lib/mcollective/application/describe_filter.rb +0 -87
- data/lib/mcollective/data.rb +0 -96
- data/lib/mcollective/data/agent_data.ddl +0 -22
- data/lib/mcollective/data/agent_data.rb +0 -17
- data/lib/mcollective/data/base.rb +0 -68
- data/lib/mcollective/data/bolt_task_data.ddl +0 -90
- data/lib/mcollective/data/bolt_task_data.rb +0 -32
- data/lib/mcollective/data/collective_data.ddl +0 -20
- data/lib/mcollective/data/collective_data.rb +0 -9
- data/lib/mcollective/data/fact_data.ddl +0 -28
- data/lib/mcollective/data/fact_data.rb +0 -55
- data/lib/mcollective/data/fstat_data.ddl +0 -89
- data/lib/mcollective/data/fstat_data.rb +0 -54
- data/lib/mcollective/data/result.rb +0 -50
- data/lib/mcollective/ddl/dataddl.rb +0 -56
- data/lib/mcollective/discovery/choria.rb +0 -223
- data/lib/mcollective/discovery/flatfile.rb +0 -47
- data/lib/mcollective/discovery/stdin.ddl +0 -11
- data/lib/mcollective/discovery/stdin.rb +0 -67
- data/lib/mcollective/generators/data_generator.rb +0 -50
- data/lib/mcollective/generators/templates/data_input_snippet.erb +0 -7
- data/lib/mcollective/matcher.rb +0 -220
- data/lib/mcollective/matcher/parser.rb +0 -118
- data/lib/mcollective/matcher/scanner.rb +0 -236
@@ -1,47 +0,0 @@
|
|
1
|
-
# discovers against a flatfile instead of the traditional network discovery
|
2
|
-
# the flat file must have a node name per line which should match identities
|
3
|
-
# as configured
|
4
|
-
module MCollective
|
5
|
-
class Discovery
|
6
|
-
class Flatfile
|
7
|
-
def self.discover(filter, timeout, limit=0, client=nil)
|
8
|
-
if client.options[:discovery_options].empty?
|
9
|
-
raise "The flatfile discovery method needs a path to a text file"
|
10
|
-
else
|
11
|
-
file = client.options[:discovery_options].first
|
12
|
-
end
|
13
|
-
|
14
|
-
raise "Cannot read the file %s specified as discovery source" % file unless File.readable?(file)
|
15
|
-
|
16
|
-
discovered = []
|
17
|
-
hosts = []
|
18
|
-
|
19
|
-
File.readlines(file).each do |host|
|
20
|
-
host = host.chomp.strip
|
21
|
-
next if host.empty? || host.match(/^#/)
|
22
|
-
raise 'Identities can only match /^[\w\.\-]+$/' unless host.match(/^[\w.\-]+$/)
|
23
|
-
|
24
|
-
hosts << host
|
25
|
-
end
|
26
|
-
|
27
|
-
# this plugin only supports identity filters, do regex matches etc against
|
28
|
-
# the list found in the flatfile
|
29
|
-
if !filter["identity"].empty?
|
30
|
-
filter["identity"].each do |identity|
|
31
|
-
identity = Regexp.new(identity.gsub("\/", "")) if identity.match("^/")
|
32
|
-
|
33
|
-
if identity.is_a?(Regexp)
|
34
|
-
discovered = hosts.grep(identity)
|
35
|
-
elsif hosts.include?(identity)
|
36
|
-
discovered << identity
|
37
|
-
end
|
38
|
-
end
|
39
|
-
else
|
40
|
-
discovered = hosts
|
41
|
-
end
|
42
|
-
|
43
|
-
discovered
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
@@ -1,11 +0,0 @@
|
|
1
|
-
metadata :name => "stdin",
|
2
|
-
:description => "STDIN based discovery for node identities",
|
3
|
-
:author => "Tomas Doran <bobtfish@bobtfish.net.net>",
|
4
|
-
:license => "ASL 2.0",
|
5
|
-
:version => "0.1",
|
6
|
-
:url => "https://docs.puppetlabs.com/mcollective/",
|
7
|
-
:timeout => 0
|
8
|
-
|
9
|
-
discovery do
|
10
|
-
capabilities :identity
|
11
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
# discovers against stdin instead of the traditional network discovery
|
2
|
-
# the input must be a flat file with a node name per line which should match identities as configured,
|
3
|
-
# or it should be a json string as output by the -j option of mco rpc
|
4
|
-
require "mcollective/rpc/helpers"
|
5
|
-
|
6
|
-
module MCollective
|
7
|
-
class Discovery
|
8
|
-
class Stdin
|
9
|
-
def self.discover(filter, timeout, limit=0, client=nil)
|
10
|
-
if client.options[:discovery_options].empty?
|
11
|
-
type = "auto"
|
12
|
-
else
|
13
|
-
type = client.options[:discovery_options].first.downcase
|
14
|
-
end
|
15
|
-
|
16
|
-
discovered = []
|
17
|
-
|
18
|
-
file = $stdin.read
|
19
|
-
|
20
|
-
raise("data piped on STDIN contained only whitespace - could not discover hosts from it.") if file =~ /^\s*$/
|
21
|
-
|
22
|
-
if type == "auto"
|
23
|
-
if file =~ /^\s*\[/
|
24
|
-
type = "json"
|
25
|
-
else
|
26
|
-
type = "text"
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
Log.debug("Parsing STDIN input as type %s" % type)
|
31
|
-
|
32
|
-
case type
|
33
|
-
when "json"
|
34
|
-
hosts = RPC::Helpers.extract_hosts_from_json(file)
|
35
|
-
when "text"
|
36
|
-
hosts = file.split("\n")
|
37
|
-
else
|
38
|
-
raise("stdin discovery plugin only knows the types auto/text/json, not \"#{type}\"")
|
39
|
-
end
|
40
|
-
|
41
|
-
hosts.map do |host|
|
42
|
-
raise 'Identities can only match /\w\.\-/' unless host.match(/^[\w.\-]+$/)
|
43
|
-
|
44
|
-
host
|
45
|
-
end
|
46
|
-
|
47
|
-
# this plugin only supports identity filters, do regex matches etc against
|
48
|
-
# the list found in the flatfile
|
49
|
-
if filter["identity"].empty?
|
50
|
-
discovered = hosts
|
51
|
-
else
|
52
|
-
filter["identity"].each do |identity|
|
53
|
-
identity = Regexp.new(identity.gsub("\/", "")) if identity.match("^/")
|
54
|
-
|
55
|
-
if identity.is_a?(Regexp)
|
56
|
-
discovered = hosts.grep(identity)
|
57
|
-
elsif hosts.include?(identity)
|
58
|
-
discovered << identity
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
discovered
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
module MCollective
|
2
|
-
module Generators
|
3
|
-
class DataGenerator < Base
|
4
|
-
attr_accessor :ddl, :content
|
5
|
-
|
6
|
-
def initialize(plugin_name, outputs=[], name=nil, description=nil, author=nil,
|
7
|
-
license=nil, version=nil, url=nil, timeout=nil)
|
8
|
-
|
9
|
-
super(name, description, author, license, version, url, timeout)
|
10
|
-
@mod_name = "Data"
|
11
|
-
@pclass = "Base"
|
12
|
-
@plugin_name = plugin_name
|
13
|
-
@outputs = outputs
|
14
|
-
@ddl = create_ddl
|
15
|
-
@content = create_plugin_content
|
16
|
-
@plugin = create_plugin_string
|
17
|
-
write_plugins
|
18
|
-
end
|
19
|
-
|
20
|
-
def create_ddl
|
21
|
-
query_text = "dataquery :description => \"Query information\" do\n"
|
22
|
-
query_text += ERB.new(File.read(File.join(File.dirname(__FILE__), "templates", "data_input_snippet.erb"))).result
|
23
|
-
|
24
|
-
@outputs.each_with_index do |output, i|
|
25
|
-
query_text += "%2s%s" % [" ", "output :#{output},\n"]
|
26
|
-
query_text += "%9s%s" % [" ", ":description => \"%DESCRIPTION%\",\n"]
|
27
|
-
query_text += "%9s%s" % [" ", ":display_as => \"%DESCRIPTION%\"\n"]
|
28
|
-
query_text += "\n" unless @outputs.size == (i + 1)
|
29
|
-
end
|
30
|
-
|
31
|
-
query_text += "end"
|
32
|
-
|
33
|
-
# Use inherited method to create metadata part of the ddl
|
34
|
-
create_metadata_string + query_text
|
35
|
-
end
|
36
|
-
|
37
|
-
def create_plugin_content
|
38
|
-
content_text = "%6s%s" % [" ", "query do |what|\n"]
|
39
|
-
|
40
|
-
@outputs.each do |output|
|
41
|
-
content_text += "%8s%s" % [" ", "result[:#{output}] = nil\n"]
|
42
|
-
end
|
43
|
-
content_text += "%6s%s" % [" ", "end\n"]
|
44
|
-
|
45
|
-
# Add actions to agent file
|
46
|
-
content_text
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
data/lib/mcollective/matcher.rb
DELETED
@@ -1,220 +0,0 @@
|
|
1
|
-
module MCollective
|
2
|
-
# A parser and scanner that creates a stack machine for a simple
|
3
|
-
# fact and class matching language used on the CLI to facilitate
|
4
|
-
# a rich discovery language
|
5
|
-
#
|
6
|
-
# Language EBNF
|
7
|
-
#
|
8
|
-
# compound = ["("] expression [")"] {["("] expression [")"]}
|
9
|
-
# expression = [!|not]statement ["and"|"or"] [!|not] statement
|
10
|
-
# char = A-Z | a-z | < | > | => | =< | _ | - |* | / { A-Z | a-z | < | > | => | =< | _ | - | * | / | }
|
11
|
-
# int = 0|1|2|3|4|5|6|7|8|9{|0|1|2|3|4|5|6|7|8|9|0}
|
12
|
-
module Matcher
|
13
|
-
require "mcollective/matcher/parser"
|
14
|
-
require "mcollective/matcher/scanner"
|
15
|
-
|
16
|
-
# Helper creates a hash from a function call string
|
17
|
-
def self.create_function_hash(function_call)
|
18
|
-
func_hash = {}
|
19
|
-
f = ""
|
20
|
-
func_parts = function_call.split(/(!=|>=|<=|<|>|=)/)
|
21
|
-
func_hash["r_compare"] = func_parts.pop
|
22
|
-
func_hash["operator"] = func_parts.pop
|
23
|
-
func = func_parts.join
|
24
|
-
|
25
|
-
# Deal with dots in function parameters and functions without dot values
|
26
|
-
if func =~ /^.+\(.*\)$/
|
27
|
-
f = func
|
28
|
-
else
|
29
|
-
func_parts = func.split(".")
|
30
|
-
func_hash["value"] = func_parts.pop
|
31
|
-
f = func_parts.join(".")
|
32
|
-
end
|
33
|
-
|
34
|
-
# Deal with regular expression matches
|
35
|
-
if func_hash["r_compare"] =~ /^\/.*\/$/
|
36
|
-
func_hash["operator"] = "=~" if func_hash["operator"] == "="
|
37
|
-
func_hash["operator"] = "!=~" if func_hash["operator"] == "!="
|
38
|
-
func_hash["r_compare"] = Regexp.new(func_hash["r_compare"].gsub(/^\/|\/$/, ""))
|
39
|
-
# Convert = operators to == so they can be propperly evaluated
|
40
|
-
elsif func_hash["operator"] == "="
|
41
|
-
func_hash["operator"] = "=="
|
42
|
-
end
|
43
|
-
|
44
|
-
# Grab function name and parameters from left compare string
|
45
|
-
func_hash["name"], func_hash["params"] = f.split("(")
|
46
|
-
if func_hash["params"] == ")"
|
47
|
-
func_hash["params"] = nil
|
48
|
-
else
|
49
|
-
|
50
|
-
# Walk the function parameters from the front and from the
|
51
|
-
# back removing the first and last instances of single of
|
52
|
-
# double qoutes. We do this to handle the case where params
|
53
|
-
# contain escaped qoutes.
|
54
|
-
func_hash["params"] = func_hash["params"].gsub(")", "")
|
55
|
-
func_quotes = func_hash["params"].split(/('|")/)
|
56
|
-
|
57
|
-
func_quotes.each_with_index do |item, i|
|
58
|
-
if item =~ /'|"/
|
59
|
-
func_quotes.delete_at(i)
|
60
|
-
break
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
func_quotes.reverse.each_with_index do |item, i|
|
65
|
-
if item =~ /'|"/
|
66
|
-
func_quotes.delete_at(func_quotes.size - i - 1)
|
67
|
-
break
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
func_hash["params"] = func_quotes.join
|
72
|
-
end
|
73
|
-
|
74
|
-
func_hash
|
75
|
-
end
|
76
|
-
|
77
|
-
# Returns the result of an executed function
|
78
|
-
def self.execute_function(function_hash)
|
79
|
-
# In the case where a data plugin isn't present there are two ways we can handle
|
80
|
-
# the raised exception. The function result can either be false or the entire
|
81
|
-
# expression can fail.
|
82
|
-
#
|
83
|
-
# In the case where we return the result as false it opens us op to unexpected
|
84
|
-
# negation behavior.
|
85
|
-
#
|
86
|
-
# !foo('bar').name = bar
|
87
|
-
#
|
88
|
-
# In this case the user would expect discovery to match on all machines where
|
89
|
-
# the name value of the foo function does not equal bar. If a non existent function
|
90
|
-
# returns false then it is posible to match machines where the name value of the
|
91
|
-
# foo function is bar.
|
92
|
-
#
|
93
|
-
# Instead we raise a DDLValidationError to prevent this unexpected behavior from
|
94
|
-
# happening.
|
95
|
-
|
96
|
-
result = Data.send(function_hash["name"], function_hash["params"])
|
97
|
-
|
98
|
-
if function_hash["value"]
|
99
|
-
begin
|
100
|
-
eval_result = result.send(function_hash["value"])
|
101
|
-
rescue
|
102
|
-
# If data field has not been set we set the comparison result to nil
|
103
|
-
eval_result = nil
|
104
|
-
end
|
105
|
-
eval_result
|
106
|
-
else
|
107
|
-
result
|
108
|
-
end
|
109
|
-
rescue NoMethodError
|
110
|
-
Log.debug("cannot execute discovery function '#{function_hash['name']}'. data plugin not found")
|
111
|
-
raise DDLValidationError
|
112
|
-
end
|
113
|
-
|
114
|
-
# Evaluates a compound statement
|
115
|
-
def self.eval_compound_statement(expression)
|
116
|
-
case expression.values.first
|
117
|
-
when /^\//
|
118
|
-
Util.has_cf_class?(expression.values.first)
|
119
|
-
when />=|<=|=|<|>/
|
120
|
-
optype = expression.values.first.match(/>=|<=|=|<|>/)
|
121
|
-
name, value = expression.values.first.split(optype[0])
|
122
|
-
if value.split("")[0] == "/"
|
123
|
-
optype = "=~"
|
124
|
-
else
|
125
|
-
optype[0] == "=" ? optype = "==" : optype = optype[0]
|
126
|
-
end
|
127
|
-
|
128
|
-
Util.has_fact?(name, value, optype).to_s
|
129
|
-
else
|
130
|
-
Util.has_cf_class?(expression.values.first)
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
# Returns the result of an evaluated compound statement that
|
135
|
-
# includes a function
|
136
|
-
def self.eval_compound_fstatement(function_hash) # rubocop:disable Metrics/MethodLength
|
137
|
-
l_compare = execute_function(function_hash)
|
138
|
-
r_compare = function_hash["r_compare"]
|
139
|
-
operator = function_hash["operator"]
|
140
|
-
|
141
|
-
# Break out early and return false if the function returns nil
|
142
|
-
return false if l_compare.nil?
|
143
|
-
|
144
|
-
# Prevent unwanted discovery by limiting comparison operators
|
145
|
-
# on Strings and Booleans
|
146
|
-
if (l_compare.is_a?(String) || l_compare.is_a?(TrueClass) || l_compare.is_a?(FalseClass)) && function_hash["operator"].match(/<|>/)
|
147
|
-
Log.debug("Cannot do > and < comparison on Booleans and Strings '#{l_compare} #{function_hash['operator']} #{function_hash['r_compare']}'")
|
148
|
-
return false
|
149
|
-
end
|
150
|
-
|
151
|
-
# Prevent backticks in function parameters
|
152
|
-
if function_hash["params"] =~ /`/
|
153
|
-
Log.debug("Cannot use backticks in function parameters")
|
154
|
-
return false
|
155
|
-
end
|
156
|
-
|
157
|
-
# Do a regex comparison if right compare string is a regex
|
158
|
-
if operator =~ /(=~|!=~)/
|
159
|
-
# Fail if left compare value isn't a string
|
160
|
-
if l_compare.is_a?(String)
|
161
|
-
result = l_compare.match(r_compare)
|
162
|
-
# Flip return value for != operator
|
163
|
-
if function_hash["operator"] == "!=~"
|
164
|
-
!result
|
165
|
-
else
|
166
|
-
!!result
|
167
|
-
end
|
168
|
-
else
|
169
|
-
Log.debug("Cannot do a regex check on a non string value.")
|
170
|
-
false
|
171
|
-
end
|
172
|
-
# Otherwise do a normal comparison while taking the type into account
|
173
|
-
else
|
174
|
-
if l_compare.is_a? String
|
175
|
-
r_compare = r_compare.to_s
|
176
|
-
elsif r_compare.is_a? String
|
177
|
-
case l_compare
|
178
|
-
when Numeric
|
179
|
-
r_compare = r_compare.strip
|
180
|
-
begin
|
181
|
-
r_compare = Integer(r_compare)
|
182
|
-
rescue ArgumentError # rubocop:disable Metrics/BlockNesting
|
183
|
-
begin
|
184
|
-
r_compare = Float(r_compare)
|
185
|
-
rescue ArgumentError # rubocop:disable Metrics/BlockNesting
|
186
|
-
raise(ArgumentError, "invalid numeric value: #{r_compare}")
|
187
|
-
end
|
188
|
-
end
|
189
|
-
when TrueClass, FalseClass
|
190
|
-
r_compare = r_compare.strip
|
191
|
-
if r_compare == true.to_s # rubocop:disable Metrics/BlockNesting
|
192
|
-
r_compare = true
|
193
|
-
elsif r_compare == false.to_s # rubocop:disable Metrics/BlockNesting
|
194
|
-
r_compare = false
|
195
|
-
else
|
196
|
-
raise(ArgumentError, "invalid boolean value: #{r_compare}")
|
197
|
-
end
|
198
|
-
end
|
199
|
-
end
|
200
|
-
operator = operator.strip
|
201
|
-
if operator =~ /(?:(!=|<=|>=|<|>)|==?)/
|
202
|
-
operator = $1 ? $1.to_sym : :==
|
203
|
-
else
|
204
|
-
raise(ArgumentError, "invalid operator: #{operator}")
|
205
|
-
end
|
206
|
-
l_compare.send(operator, r_compare)
|
207
|
-
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
# Creates a callstack to be evaluated from a compound evaluation string
|
212
|
-
def self.create_compound_callstack(call_string)
|
213
|
-
callstack = Matcher::Parser.new(call_string).execution_stack
|
214
|
-
callstack.each_with_index do |statement, i|
|
215
|
-
callstack[i]["fstatement"] = create_function_hash(statement.values.first) if statement.keys.first == "fstatement"
|
216
|
-
end
|
217
|
-
callstack
|
218
|
-
end
|
219
|
-
end
|
220
|
-
end
|
@@ -1,118 +0,0 @@
|
|
1
|
-
# rubocop:disable Metrics/BlockNesting
|
2
|
-
module MCollective
|
3
|
-
module Matcher
|
4
|
-
class Parser
|
5
|
-
attr_reader :scanner, :execution_stack
|
6
|
-
|
7
|
-
def initialize(args)
|
8
|
-
@scanner = Scanner.new(args)
|
9
|
-
@execution_stack = []
|
10
|
-
@parse_errors = []
|
11
|
-
@token_errors = []
|
12
|
-
@paren_errors = []
|
13
|
-
parse
|
14
|
-
exit_with_token_errors unless @token_errors.empty?
|
15
|
-
exit_with_parse_errors unless @parse_errors.empty?
|
16
|
-
exit_with_paren_errors unless @paren_errors.empty?
|
17
|
-
end
|
18
|
-
|
19
|
-
# Exit and highlight any malformed tokens
|
20
|
-
def exit_with_token_errors
|
21
|
-
@token_errors.each do |error_range|
|
22
|
-
(error_range[0]..error_range[1]).each do |i|
|
23
|
-
@scanner.arguments[i] = Util.colorize(:red, @scanner.arguments[i])
|
24
|
-
end
|
25
|
-
end
|
26
|
-
raise "Malformed token(s) found while parsing -S input #{@scanner.arguments.join}"
|
27
|
-
end
|
28
|
-
|
29
|
-
def exit_with_parse_errors
|
30
|
-
@parse_errors.each do |error_range|
|
31
|
-
(error_range[0]..error_range[1]).each do |i|
|
32
|
-
@scanner.arguments[i] = Util.colorize(:red, @scanner.arguments[i])
|
33
|
-
end
|
34
|
-
end
|
35
|
-
raise "Parse errors found while parsing -S input #{@scanner.arguments.join}"
|
36
|
-
end
|
37
|
-
|
38
|
-
def exit_with_paren_errors
|
39
|
-
@paren_errors.each do |i|
|
40
|
-
@scanner.arguments[i] = Util.colorize(:red, @scanner.arguments[i])
|
41
|
-
end
|
42
|
-
raise "Missing parenthesis found while parsing -S input #{@scanner.arguments.join}"
|
43
|
-
end
|
44
|
-
|
45
|
-
# Parse the input string, one token at a time a contruct the call stack
|
46
|
-
def parse # rubocop:disable Metrics/MethodLength
|
47
|
-
pre_index = @scanner.token_index
|
48
|
-
p_token = nil
|
49
|
-
c_token, c_token_value = @scanner.get_token
|
50
|
-
|
51
|
-
until c_token.nil?
|
52
|
-
@scanner.token_index += 1
|
53
|
-
n_token, n_token_value = @scanner.get_token
|
54
|
-
|
55
|
-
unless n_token == " "
|
56
|
-
case c_token
|
57
|
-
when "bad_token"
|
58
|
-
@token_errors << c_token_value
|
59
|
-
|
60
|
-
when "and"
|
61
|
-
unless (n_token =~ /not|fstatement|statement|\(/) || (scanner.token_index == scanner.arguments.size) && !n_token.nil?
|
62
|
-
@parse_errors << [pre_index, scanner.token_index]
|
63
|
-
end
|
64
|
-
|
65
|
-
case p_token
|
66
|
-
when nil
|
67
|
-
@parse_errors << [pre_index - c_token.size, scanner.token_index]
|
68
|
-
when "and", "or"
|
69
|
-
@parse_errors << [pre_index - 1 - p_token.size, pre_index - 1]
|
70
|
-
end
|
71
|
-
|
72
|
-
when "or"
|
73
|
-
unless (n_token =~ /not|fstatement|statement|\(/) || (scanner.token_index == scanner.arguments.size) && !n_token.nil?
|
74
|
-
@parse_errors << [pre_index, scanner.token_index]
|
75
|
-
end
|
76
|
-
|
77
|
-
case p_token
|
78
|
-
when nil
|
79
|
-
@parse_errors << [pre_index - c_token.size, scanner.token_index]
|
80
|
-
when "and", "or"
|
81
|
-
@parse_errors << [pre_index - 1 - p_token.size, pre_index - 1]
|
82
|
-
end
|
83
|
-
|
84
|
-
when "not"
|
85
|
-
@parse_errors << [pre_index, scanner.token_index] unless n_token =~ /fstatement|statement|\(|not/ && !n_token.nil?
|
86
|
-
|
87
|
-
when "statement", "fstatement"
|
88
|
-
@parse_errors << [pre_index, scanner.token_index] if n_token !~ /and|or|\)/ && scanner.token_index != scanner.arguments.size
|
89
|
-
|
90
|
-
when ")"
|
91
|
-
@parse_errors << [pre_index, scanner.token_index] if n_token !~ /|and|or|not|\(/ && scanner.token_index != scanner.arguments.size
|
92
|
-
if @paren_errors.empty?
|
93
|
-
@paren_errors.push(n_token.nil? ? scanner.token_index - 1 : scanner.token_index - n_token_value.size)
|
94
|
-
else
|
95
|
-
@paren_errors.pop
|
96
|
-
end
|
97
|
-
|
98
|
-
when "("
|
99
|
-
@parse_errors << [pre_index, scanner.token_index] unless n_token =~ /fstatement|statement|not|\(/
|
100
|
-
@paren_errors.push(n_token.nil? ? scanner.token_index - 1 : scanner.token_index - n_token_value.size)
|
101
|
-
|
102
|
-
else
|
103
|
-
@parse_errors << [pre_index, scanner.token_index]
|
104
|
-
end
|
105
|
-
|
106
|
-
@execution_stack << {c_token => c_token_value} unless n_token == " " || c_token == "bad_token"
|
107
|
-
|
108
|
-
p_token = c_token
|
109
|
-
c_token = n_token
|
110
|
-
c_token_value = n_token_value
|
111
|
-
end
|
112
|
-
pre_index = @scanner.token_index
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
# rubocop:enable Metrics/BlockNesting
|