module TMZ
module Commander
unless ::Object.new.respond_to?(:yield_self) # Ruby 2.5 or after
class ::Object
def yield_self
yield self
end
end
end
module_function
begin
require 'reline' # Ruby 2.7 or after
def input(prompt)
Reline.readline(prompt, true)
end
rescue ::LoadError
begin
require 'readline' # Ruby 2.6 or before
def input(prompt)
Readline.readline(prompt, true)
end
rescue ::LoadError
def input(prompt)
STDERR.print prompt; STDERR.flush
STDIN.gets
end
end
end
STDIN_FILE_NAME = '<stdin>'
PARSER = SC::Parser.new
def main(args)
ASSERT.kind_of args, ::Array
exit_code = begin
prelude_env = Commander.process_file(
PRELUDE,
'<prelude>',
E.setup(E::INITIAL_PREFERENCE)
)
pref, file_names = Commander.parse_option args, prelude_env.pref
init_env = prelude_env.update_preference pref
if pref.interactive_mode?
env = unless file_names.empty?
Commander.process_files file_names, init_env
else
init_env
end
Commander.interact env
else
if file_names.empty?
raise X::CommandError.new('No input files')
end
Commander.process_files file_names, init_env
end
0
rescue X::Abstraction::RuntimeError => e
e.print_backtrace
STDERR.puts
STDERR.puts e.to_s
1
rescue X::Abstraction::Expected, ::SystemCallError => e
STDERR.puts
STDERR.puts e.to_s
1
rescue ::Interrupt
1
end
ASSERT.kind_of exit_code, ::Integer
end
def interact(init_env)
ASSERT.kind_of init_env, E::Entry
init_tokens = []
init_lexer = L::Lexer.make_initial_lexer STDIN_FILE_NAME, 1
final_env = loop.inject(
[1, init_tokens, init_lexer, init_env]
) { |(line_num, tokens, lexer, env ), _|
ASSERT.kind_of line_num, ::Integer
ASSERT.kind_of tokens, ::Array
ASSERT.kind_of lexer, L::Lexer::Abstract
ASSERT.kind_of env, E::Entry
prompt = format(
"%04d%s%s%s ",
line_num,
if lexer.in_braket?
format(":%s:%d",
lexer.braket_stack.reverse.map { |bb|
format("%-2s", bb)
}.join('.'),
lexer.braket_stack.length
)
else
''
end,
if lexer.in_comment?
format(":%s:%d",
'(*' * lexer.comment_depth,
lexer.comment_depth
)
else
''
end,
if lexer.in_comment?
'*'
elsif lexer.in_braket?
'|'
else
'>'
end
)
opt_line = Commander.input prompt
break env if opt_line.nil?
line = opt_line
next_tokens, next_lexer, next_env = begin
if lexer.between_braket? && /^:/ =~ line
[
[],
init_lexer,
Commander.subcommand(line, line_num, env)
]
else
Commander.process_line(
line + "\n",
line_num,
tokens,
lexer,
env.update_line(STDIN_FILE_NAME, line_num, line)
)
end
rescue X::Abstraction::RuntimeError => e
e.print_backtrace
STDERR.puts
STDERR.puts e.to_s
[[], init_lexer, env]
rescue X::Abstraction::Expected, ::SystemCallError => e
STDERR.puts
STDERR.puts e.to_s
[[], init_lexer, env]
rescue ::Interrupt
STDERR.puts
STDERR.puts 'Interrupt!!'
[[], init_lexer, env]
end
[line_num + 1, next_tokens, next_lexer, next_env]
}
ASSERT.kind_of final_env, E::Entry
end
def process_line(line, line_num, tokens, lexer, env)
ASSERT.kind_of line, ::String
ASSERT.kind_of line_num, ::Integer
ASSERT.kind_of tokens, ::Array
ASSERT.kind_of lexer, L::Lexer::Abstract
ASSERT.kind_of env, E::Entry
pref = env.pref
file_name = STDIN_FILE_NAME
if pref.trace_mode?
STDERR.puts
STDERR.printf "________ Source: '%s' ________\n", file_name
STDERR.printf "%04d: %s", line_num, line
end
if pref.trace_mode?
STDERR.puts
STDERR.printf "________ Tokens: '%s' ________", file_name
end
scanner = ::StringScanner.new line
next_tokens, next_lexer = L::Lexer.lex(
tokens, lexer, 0, scanner, pref
) do |token, before_line_num|
if pref.trace_mode?
line_num = token.pos.line_num
if line_num != before_line_num
STDERR.printf "\n%04d: ", line_num
end
unless token && token.separator?
STDERR.printf "%s ", token.to_s
STDERR.flush
end
end
end
if pref.trace_mode?
STDERR.puts
end
if next_lexer.between_braket?
[
[],
next_lexer,
Commander.execute(next_tokens, env)
]
else
[
next_tokens,
next_lexer,
env
]
end
end
def process_files(file_names, init_env)
ASSERT.kind_of file_names, ::Array
ASSERT.kind_of init_env, E::Entry
final_env = file_names.inject(init_env) { |env, file_name|
ASSERT.kind_of env, E::Entry
ASSERT.kind_of file_name, ::String
source = ::File.open(file_name) { |io| io.read }
Commander.process_file(
source,
file_name,
env.update_source(file_name, source)
)
}
ASSERT.kind_of final_env, E::Entry
end
def process_file(source, file_name, env)
ASSERT.kind_of source, ::String
ASSERT.kind_of file_name, ::String
ASSERT.kind_of env, E::Entry
pref = env.pref
if pref.trace_mode?
STDERR.puts
STDERR.printf "________ Source: '%s' ________\n", file_name
source.each_line.with_index do |line, index|
STDERR.printf "%04d: %s", index + 1, line
end
end
if pref.trace_mode?
STDERR.puts
STDERR.printf "________ Tokens: '%s' ________", file_name
end
init_tokens = []
init_lexer = L::Lexer.make_initial_lexer file_name, 1
scanner = ::StringScanner.new source
tokens, _lexer = L::Lexer.lex(
init_tokens, init_lexer, 0, scanner, pref
) do |token, before_line_num|
if pref.trace_mode?
line_num = token.pos.line_num
if line_num != before_line_num
STDERR.printf "\n%04d: ", line_num
end
unless token && token.separator?
STDERR.printf "%s ", token.to_s
STDERR.flush
end
end
end
if pref.trace_mode?
STDERR.puts
end
Commander.execute(tokens, env)
end
def execute(tokens, init_env)
ASSERT.kind_of tokens, ::Array
ASSERT.kind_of init_env, E::Entry
con_syntaxes = PARSER.parse tokens
final_env = con_syntaxes.inject(init_env) { |env, con_syntax|
ASSERT.kind_of env, E::Entry
ASSERT.kind_of con_syntax, SC::Abstract
pref = env.pref
con_syntax.tap { |csyn|
ASSERT.kind_of csyn, SC::Abstract
if pref.trace_mode?
STDERR.puts
STDERR.printf(
"________ Concrete Syntax: #%d in '%s' ________\n",
csyn.pos.line_num,
csyn.pos.file_name
)
STDERR.puts csyn.to_s
end
}.desugar.tap { |asyn|
ASSERT.kind_of asyn, SA::Abstract
if pref.trace_mode?
STDERR.puts
STDERR.printf(
"________ Abstract Syntax: #%d in '%s' ________\n",
asyn.pos.line_num,
asyn.pos.file_name
)
STDERR.puts asyn.to_s
end
if pref.trace_mode?
STDERR.puts
STDERR.puts "________ Evaluator Trace ________"
end
}.evaluate(env).yield_self { |result|
ASSERT.kind_of result, SAR::Abstract
case result
when SAR::Value
value = result.value
STDERR.puts
STDERR.printf("-> %s : %s\n",
value.to_s,
value.type_sym.to_s
)
env
when SAR::Environment
result.env
else
ASSERT.abort result.inspect
end
}
}
ASSERT.kind_of final_env, E::Entry
end
end # TMZ::Commander
end # TMZ
bW9kdWxlIFRNWgoKbW9kdWxlIENvbW1hbmRlcgoKdW5sZXNzIDo6T2JqZWN0Lm5ldy5yZXNwb25kX3RvPyg6eWllbGRfc2VsZikJIyBSdWJ5IDIuNSBvciBhZnRlcgoJY2xhc3MgOjpPYmplY3QKCQlkZWYgeWllbGRfc2VsZgoJCQl5aWVsZCBzZWxmCgkJZW5kCgllbmQKZW5kCgoKbW9kdWxlX2Z1bmN0aW9uCgpiZWdpbgoJcmVxdWlyZSAncmVsaW5lJwkJIyBSdWJ5IDIuNyBvciBhZnRlcgoKCWRlZiBpbnB1dChwcm9tcHQpCgkJUmVsaW5lLnJlYWRsaW5lKHByb21wdCwgdHJ1ZSkKCWVuZAoKcmVzY3VlIDo6TG9hZEVycm9yCgliZWdpbgoJCXJlcXVpcmUgJ3JlYWRsaW5lJwkjIFJ1YnkgMi42IG9yIGJlZm9yZQoKCQlkZWYgaW5wdXQocHJvbXB0KQoJCQlSZWFkbGluZS5yZWFkbGluZShwcm9tcHQsIHRydWUpCgkJZW5kCgoJcmVzY3VlIDo6TG9hZEVycm9yCgkJZGVmIGlucHV0KHByb21wdCkKCQkJU1RERVJSLnByaW50IHByb21wdDsgU1RERVJSLmZsdXNoCgoJCQlTVERJTi5nZXRzCgkJZW5kCgllbmQKZW5kCgoKCVNURElOX0ZJTEVfTkFNRSA9ICc8c3RkaW4+JwoKCVBBUlNFUiA9IFNDOjpQYXJzZXIubmV3CgoKCWRlZiBtYWluKGFyZ3MpCgkJQVNTRVJULmtpbmRfb2YgYXJncywgOjpBcnJheQoKCQlleGl0X2NvZGUgPSBiZWdpbgoJCQlwcmVsdWRlX2VudiA9IENvbW1hbmRlci5wcm9jZXNzX2ZpbGUoCgkJCQkJCQlQUkVMVURFLAoJCQkJCQkJJzxwcmVsdWRlPicsCgkJCQkJCQlFLnNldHVwKEU6OklOSVRJQUxfUFJFRkVSRU5DRSkKCQkJCQkJKQoJCQlwcmVmLCBmaWxlX25hbWVzID0gQ29tbWFuZGVyLnBhcnNlX29wdGlvbiBhcmdzLCBwcmVsdWRlX2Vudi5wcmVmCgkJCWluaXRfZW52ID0gcHJlbHVkZV9lbnYudXBkYXRlX3ByZWZlcmVuY2UgcHJlZgoKCQkJaWYgcHJlZi5pbnRlcmFjdGl2ZV9tb2RlPwoJCQkJZW52ID0gdW5sZXNzIGZpbGVfbmFtZXMuZW1wdHk/CgkJCQkJCQlDb21tYW5kZXIucHJvY2Vzc19maWxlcyBmaWxlX25hbWVzLCBpbml0X2VudgoJCQkJCQllbHNlCgkJCQkJCQlpbml0X2VudgoJCQkJCQllbmQKCgkJCQlDb21tYW5kZXIuaW50ZXJhY3QgZW52CgkJCWVsc2UKCQkJCWlmIGZpbGVfbmFtZXMuZW1wdHk/CgkJCQkJcmFpc2UgWDo6Q29tbWFuZEVycm9yLm5ldygnTm8gaW5wdXQgZmlsZXMnKQoJCQkJZW5kCgoJCQkJQ29tbWFuZGVyLnByb2Nlc3NfZmlsZXMgZmlsZV9uYW1lcywgaW5pdF9lbnYKCQkJZW5kCgoJCQkwCgkJcmVzY3VlIFg6OkFic3RyYWN0aW9uOjpSdW50aW1lRXJyb3IgPT4gZQoJCQllLnByaW50X2JhY2t0cmFjZQoJCQlTVERFUlIucHV0cwoJCQlTVERFUlIucHV0cyBlLnRvX3MKCgkJCTEKCQlyZXNjdWUgWDo6QWJzdHJhY3Rpb246OkV4cGVjdGVkLCA6OlN5c3RlbUNhbGxFcnJvciA9PiBlCgkJCVNUREVSUi5wdXRzCgkJCVNUREVSUi5wdXRzIGUudG9fcwoKCQkJMQoJCXJlc2N1ZSA6OkludGVycnVwdAoJCQkxCgkJZW5kCgoJCUFTU0VSVC5raW5kX29mIGV4aXRfY29kZSwgOjpJbnRlZ2VyCgllbmQKCgoJZGVmIGludGVyYWN0KGluaXRfZW52KQoJCUFTU0VSVC5raW5kX29mIGluaXRfZW52LCBFOjpFbnRyeQoKCQlpbml0X3Rva2Vucwk9IFtdCgkJaW5pdF9sZXhlcgk9IEw6OkxleGVyLm1ha2VfaW5pdGlhbF9sZXhlciBTVERJTl9GSUxFX05BTUUsIDEKCQlmaW5hbF9lbnYgPSBsb29wLmluamVjdCgKCQkJIFsxLCAgICAgICAgaW5pdF90b2tlbnMsIGluaXRfbGV4ZXIsIGluaXRfZW52XQoJCSkgeyB8KGxpbmVfbnVtLCB0b2tlbnMsICAgICAgbGV4ZXIsICAgICAgZW52ICAgICApLCBffAoJCQlBU1NFUlQua2luZF9vZiBsaW5lX251bSwJOjpJbnRlZ2VyCgkJCUFTU0VSVC5raW5kX29mIHRva2VucywJCTo6QXJyYXkKCQkJQVNTRVJULmtpbmRfb2YgbGV4ZXIsCQlMOjpMZXhlcjo6QWJzdHJhY3QKCQkJQVNTRVJULmtpbmRfb2YgZW52LAkJCUU6OkVudHJ5CgoJCQlwcm9tcHQgPSBmb3JtYXQoCgkJCQkiJTA0ZCVzJXMlcyAiLAoKCQkJCWxpbmVfbnVtLAoKCQkJCWlmIGxleGVyLmluX2JyYWtldD8KCQkJCQlmb3JtYXQoIjolczolZCIsCgkJCQkJCQlsZXhlci5icmFrZXRfc3RhY2sucmV2ZXJzZS5tYXAgeyB8YmJ8CgkJCQkJCQkJZm9ybWF0KCIlLTJzIiwgYmIpCgkJCQkJCQl9LmpvaW4oJy4nKSwKCQkJCQkJCWxleGVyLmJyYWtldF9zdGFjay5sZW5ndGgKCQkJCQkpCgkJCQllbHNlCgkJCQkJJycKCQkJCWVuZCwKCgkJCQlpZiBsZXhlci5pbl9jb21tZW50PwoJCQkJCWZvcm1hdCgiOiVzOiVkIiwKCQkJCQkJCScoKicgKiBsZXhlci5jb21tZW50X2RlcHRoLAoJCQkJCQkJbGV4ZXIuY29tbWVudF9kZXB0aAoJCQkJCSkKCQkJCWVsc2UKCQkJCQknJwoJCQkJZW5kLAoKCQkJCWlmIGxleGVyLmluX2NvbW1lbnQ/CgkJCQkJJyonCgkJCQllbHNpZiBsZXhlci5pbl9icmFrZXQ/CgkJCQkJJ3wnCgkJCQllbHNlCgkJCQkJJz4nCgkJCQllbmQKCQkJKQoJCQlvcHRfbGluZSA9IENvbW1hbmRlci5pbnB1dCBwcm9tcHQKCgkJCWJyZWFrIGVudiBpZiBvcHRfbGluZS5uaWw/CgkJCWxpbmUgPSBvcHRfbGluZQoKCQkJbmV4dF90b2tlbnMsIG5leHRfbGV4ZXIsIG5leHRfZW52ID0gYmVnaW4KCQkJCWlmIGxleGVyLmJldHdlZW5fYnJha2V0PyAmJiAvXjovID1+IGxpbmUKCQkJCQlbCgkJCQkJCVtdLAoJCQkJCQlpbml0X2xleGVyLAoJCQkJCQlDb21tYW5kZXIuc3ViY29tbWFuZChsaW5lLCBsaW5lX251bSwgZW52KQoJCQkJCV0KCQkJCWVsc2UKCQkJCQlDb21tYW5kZXIucHJvY2Vzc19saW5lKAoJCQkJCQlsaW5lICsgIlxuIiwKCQkJCQkJbGluZV9udW0sCgkJCQkJCXRva2VucywKCQkJCQkJbGV4ZXIsCgkJCQkJCWVudi51cGRhdGVfbGluZShTVERJTl9GSUxFX05BTUUsIGxpbmVfbnVtLCBsaW5lKQoJCQkJCSkKCQkJCWVuZAoJCQlyZXNjdWUgWDo6QWJzdHJhY3Rpb246OlJ1bnRpbWVFcnJvciA9PiBlCgkJCQllLnByaW50X2JhY2t0cmFjZQoJCQkJU1RERVJSLnB1dHMKCQkJCVNUREVSUi5wdXRzIGUudG9fcwoKCQkJCVtbXSwgaW5pdF9sZXhlciwgZW52XQoJCQlyZXNjdWUgWDo6QWJzdHJhY3Rpb246OkV4cGVjdGVkLCA6OlN5c3RlbUNhbGxFcnJvciA9PiBlCgkJCQlTVERFUlIucHV0cwoJCQkJU1RERVJSLnB1dHMgZS50b19zCgoJCQkJW1tdLCBpbml0X2xleGVyLCBlbnZdCgkJCXJlc2N1ZSA6OkludGVycnVwdAoJCQkJU1RERVJSLnB1dHMKCQkJCVNUREVSUi5wdXRzICdJbnRlcnJ1cHQhIScKCgkJCQlbW10sIGluaXRfbGV4ZXIsIGVudl0KCQkJZW5kCgoJCQlbbGluZV9udW0gKyAxLCBuZXh0X3Rva2VucywgbmV4dF9sZXhlciwgbmV4dF9lbnZdCgkJfQoKCQlBU1NFUlQua2luZF9vZiBmaW5hbF9lbnYsIEU6OkVudHJ5CgllbmQKCgoJZGVmIHByb2Nlc3NfbGluZShsaW5lLCBsaW5lX251bSwgdG9rZW5zLCBsZXhlciwgZW52KQoJCUFTU0VSVC5raW5kX29mIGxpbmUsCQk6OlN0cmluZwoJCUFTU0VSVC5raW5kX29mIGxpbmVfbnVtLAk6OkludGVnZXIKCQlBU1NFUlQua2luZF9vZiB0b2tlbnMsCQk6OkFycmF5CgkJQVNTRVJULmtpbmRfb2YgbGV4ZXIsCQlMOjpMZXhlcjo6QWJzdHJhY3QKCQlBU1NFUlQua2luZF9vZiBlbnYsCQkJRTo6RW50cnkKCgkJcHJlZgkJPSBlbnYucHJlZgoJCWZpbGVfbmFtZQk9IFNURElOX0ZJTEVfTkFNRQoKCQlpZiBwcmVmLnRyYWNlX21vZGU/CgkJCVNUREVSUi5wdXRzCgkJCVNUREVSUi5wcmludGYgIl9fX19fX19fIFNvdXJjZTogJyVzJyBfX19fX19fX1xuIiwgZmlsZV9uYW1lCgkJCVNUREVSUi5wcmludGYgIiUwNGQ6ICVzIiwgbGluZV9udW0sIGxpbmUKCQllbmQKCgkJaWYgcHJlZi50cmFjZV9tb2RlPwoJCQlTVERFUlIucHV0cwoJCQlTVERFUlIucHJpbnRmICJfX19fX19fXyBUb2tlbnM6ICclcycgX19fX19fX18iLCBmaWxlX25hbWUKCQllbmQKCgkJc2Nhbm5lciA9IDo6U3RyaW5nU2Nhbm5lci5uZXcgbGluZQoJCW5leHRfdG9rZW5zLCBuZXh0X2xleGVyID0gTDo6TGV4ZXIubGV4KAoJCQl0b2tlbnMsIGxleGVyLCAwLCBzY2FubmVyLCBwcmVmCgkJKSBkbyB8dG9rZW4sIGJlZm9yZV9saW5lX251bXwKCgkJCWlmIHByZWYudHJhY2VfbW9kZT8KCQkJCWxpbmVfbnVtID0gdG9rZW4ucG9zLmxpbmVfbnVtCgoJCQkJaWYgbGluZV9udW0gIT0gYmVmb3JlX2xpbmVfbnVtCgkJCQkJU1RERVJSLnByaW50ZiAiXG4lMDRkOiAiLCBsaW5lX251bQoJCQkJZW5kCgoJCQkJdW5sZXNzIHRva2VuICYmIHRva2VuLnNlcGFyYXRvcj8KCQkJCQlTVERFUlIucHJpbnRmICIlcyAiLCB0b2tlbi50b19zCgkJCQkJU1RERVJSLmZsdXNoCgkJCQllbmQKCQkJZW5kCgkJZW5kCgoJCWlmIHByZWYudHJhY2VfbW9kZT8KCQkJU1RERVJSLnB1dHMKCQllbmQKCgkJaWYgbmV4dF9sZXhlci5iZXR3ZWVuX2JyYWtldD8KCQkJWwoJCQkJW10sCgkJCQluZXh0X2xleGVyLAoJCQkJQ29tbWFuZGVyLmV4ZWN1dGUobmV4dF90b2tlbnMsIGVudikKCQkJXQoJCWVsc2UKCQkJWwoJCQkJbmV4dF90b2tlbnMsCgkJCQluZXh0X2xleGVyLAoJCQkJZW52CgkJCV0KCQllbmQKCWVuZAoKCglkZWYgcHJvY2Vzc19maWxlcyhmaWxlX25hbWVzLCBpbml0X2VudikKCQlBU1NFUlQua2luZF9vZiBmaWxlX25hbWVzLAk6OkFycmF5CgkJQVNTRVJULmtpbmRfb2YgaW5pdF9lbnYsCUU6OkVudHJ5CgoJCWZpbmFsX2VudiA9IGZpbGVfbmFtZXMuaW5qZWN0KGluaXRfZW52KSB7IHxlbnYsIGZpbGVfbmFtZXwKCQkJQVNTRVJULmtpbmRfb2YgZW52LAkJCUU6OkVudHJ5CgkJCUFTU0VSVC5raW5kX29mIGZpbGVfbmFtZSwJOjpTdHJpbmcKCgkJCXNvdXJjZSA9IDo6RmlsZS5vcGVuKGZpbGVfbmFtZSkgeyB8aW98IGlvLnJlYWQgfQoKCQkJQ29tbWFuZGVyLnByb2Nlc3NfZmlsZSgKCQkJCXNvdXJjZSwKCQkJCWZpbGVfbmFtZSwKCQkJCWVudi51cGRhdGVfc291cmNlKGZpbGVfbmFtZSwgc291cmNlKQoJCQkpCgkJfQoKCQlBU1NFUlQua2luZF9vZiBmaW5hbF9lbnYsIEU6OkVudHJ5CgllbmQKCgoJZGVmIHByb2Nlc3NfZmlsZShzb3VyY2UsIGZpbGVfbmFtZSwgZW52KQoJCUFTU0VSVC5raW5kX29mIHNvdXJjZSwJCTo6U3RyaW5nCgkJQVNTRVJULmtpbmRfb2YgZmlsZV9uYW1lLAk6OlN0cmluZwoJCUFTU0VSVC5raW5kX29mIGVudiwJCQlFOjpFbnRyeQoKCQlwcmVmID0gZW52LnByZWYKCgkJaWYgcHJlZi50cmFjZV9tb2RlPwoJCQlTVERFUlIucHV0cwoJCQlTVERFUlIucHJpbnRmICJfX19fX19fXyBTb3VyY2U6ICclcycgX19fX19fX19cbiIsIGZpbGVfbmFtZQoJCQlzb3VyY2UuZWFjaF9saW5lLndpdGhfaW5kZXggZG8gfGxpbmUsIGluZGV4fAoJCQkJU1RERVJSLnByaW50ZiAiJTA0ZDogJXMiLCBpbmRleCArIDEsIGxpbmUKCQkJZW5kCgkJZW5kCgoJCWlmIHByZWYudHJhY2VfbW9kZT8KCQkJU1RERVJSLnB1dHMKCQkJU1RERVJSLnByaW50ZiAiX19fX19fX18gVG9rZW5zOiAnJXMnIF9fX19fX19fIiwgZmlsZV9uYW1lCgkJZW5kCgoJCWluaXRfdG9rZW5zCT0gW10KCQlpbml0X2xleGVyCT0gTDo6TGV4ZXIubWFrZV9pbml0aWFsX2xleGVyIGZpbGVfbmFtZSwgMQoJCXNjYW5uZXIJCT0gOjpTdHJpbmdTY2FubmVyLm5ldyBzb3VyY2UKCQl0b2tlbnMsIF9sZXhlciA9IEw6OkxleGVyLmxleCgKCQkJaW5pdF90b2tlbnMsIGluaXRfbGV4ZXIsIDAsIHNjYW5uZXIsIHByZWYKCQkpIGRvIHx0b2tlbiwgYmVmb3JlX2xpbmVfbnVtfAoKCQkJaWYgcHJlZi50cmFjZV9tb2RlPwoJCQkJbGluZV9udW0gPSB0b2tlbi5wb3MubGluZV9udW0KCgkJCQlpZiBsaW5lX251bSAhPSBiZWZvcmVfbGluZV9udW0KCQkJCQlTVERFUlIucHJpbnRmICJcbiUwNGQ6ICIsIGxpbmVfbnVtCgkJCQllbmQKCgkJCQl1bmxlc3MgdG9rZW4gJiYgdG9rZW4uc2VwYXJhdG9yPwoJCQkJCVNUREVSUi5wcmludGYgIiVzICIsIHRva2VuLnRvX3MKCQkJCQlTVERFUlIuZmx1c2gKCQkJCWVuZAoJCQllbmQKCQllbmQKCgkJaWYgcHJlZi50cmFjZV9tb2RlPwoJCQlTVERFUlIucHV0cwoJCWVuZAoKCQlDb21tYW5kZXIuZXhlY3V0ZSh0b2tlbnMsIGVudikKCWVuZAoKCglkZWYgZXhlY3V0ZSh0b2tlbnMsIGluaXRfZW52KQoJCUFTU0VSVC5raW5kX29mIHRva2VucywJCTo6QXJyYXkKCQlBU1NFUlQua2luZF9vZiBpbml0X2VudiwJRTo6RW50cnkKCgkJY29uX3N5bnRheGVzID0gUEFSU0VSLnBhcnNlIHRva2VucwoKCQlmaW5hbF9lbnYgPSBjb25fc3ludGF4ZXMuaW5qZWN0KGluaXRfZW52KSB7IHxlbnYsIGNvbl9zeW50YXh8CgkJCUFTU0VSVC5raW5kX29mIGVudiwJCQlFOjpFbnRyeQoJCQlBU1NFUlQua2luZF9vZiBjb25fc3ludGF4LAlTQzo6QWJzdHJhY3QKCgkJCXByZWYgPSBlbnYucHJlZgoKCQkJY29uX3N5bnRheC50YXAgeyB8Y3N5bnwKCQkJCUFTU0VSVC5raW5kX29mIGNzeW4sIFNDOjpBYnN0cmFjdAoKCQkJCWlmIHByZWYudHJhY2VfbW9kZT8KCQkJCQlTVERFUlIucHV0cwoJCQkJCVNUREVSUi5wcmludGYoCgkJCQkJCSJfX19fX19fXyBDb25jcmV0ZSBTeW50YXg6ICMlZCBpbiAnJXMnIF9fX19fX19fXG4iLAoJCQkJCQljc3luLnBvcy5saW5lX251bSwKCQkJCQkJY3N5bi5wb3MuZmlsZV9uYW1lCgkJCQkJKQoJCQkJCVNUREVSUi5wdXRzIGNzeW4udG9fcwoJCQkJZW5kCgkJCX0uZGVzdWdhci50YXAgeyB8YXN5bnwKCQkJCUFTU0VSVC5raW5kX29mIGFzeW4sIFNBOjpBYnN0cmFjdAoKCQkJCWlmIHByZWYudHJhY2VfbW9kZT8KCQkJCQlTVERFUlIucHV0cwoJCQkJCVNUREVSUi5wcmludGYoCgkJCQkJCSJfX19fX19fXyBBYnN0cmFjdCBTeW50YXg6ICMlZCBpbiAnJXMnIF9fX19fX19fXG4iLAoJCQkJCQlhc3luLnBvcy5saW5lX251bSwKCQkJCQkJYXN5bi5wb3MuZmlsZV9uYW1lCgkJCQkJKQoJCQkJCVNUREVSUi5wdXRzIGFzeW4udG9fcwoJCQkJZW5kCgoJCQkJaWYgcHJlZi50cmFjZV9tb2RlPwoJCQkJCVNUREVSUi5wdXRzCgkJCQkJU1RERVJSLnB1dHMgIl9fX19fX19fIEV2YWx1YXRvciBUcmFjZSBfX19fX19fXyIKCQkJCWVuZAoJCQl9LmV2YWx1YXRlKGVudikueWllbGRfc2VsZiB7IHxyZXN1bHR8CgkJCQlBU1NFUlQua2luZF9vZiByZXN1bHQsIFNBUjo6QWJzdHJhY3QKCgkJCQljYXNlIHJlc3VsdAoJCQkJd2hlbiBTQVI6OlZhbHVlCgkJCQkJdmFsdWUgPSByZXN1bHQudmFsdWUKCgkJCQkJU1RERVJSLnB1dHMKCQkJCQlTVERFUlIucHJpbnRmKCItPiAlcyA6ICVzXG4iLAoJCQkJCQkJCQl2YWx1ZS50b19zLAoJCQkJCQkJCQl2YWx1ZS50eXBlX3N5bS50b19zCgkJCQkJKQoKCQkJCQllbnYKCQkJCXdoZW4gU0FSOjpFbnZpcm9ubWVudAoJCQkJCXJlc3VsdC5lbnYKCQkJCWVsc2UKCQkJCQlBU1NFUlQuYWJvcnQgcmVzdWx0Lmluc3BlY3QKCQkJCWVuZAoJCQl9CgkJfQoKCQlBU1NFUlQua2luZF9vZiBmaW5hbF9lbnYsIEU6OkVudHJ5CgllbmQKCmVuZCAjIFRNWjo6Q29tbWFuZGVyCgplbmQgIyBUTVoK