Skip to content

Instantly share code, notes, and snippets.

@RPGP1
Created December 27, 2014 03:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RPGP1/95171c06ac40a1bc8911 to your computer and use it in GitHub Desktop.
Save RPGP1/95171c06ac40a1bc8911 to your computer and use it in GitHub Desktop.
Rubyでコマンドシステム作ってみたら遅かった。(本体はcommand_test.rbです)
# coding: utf-8
require_relative './command_block'
require_relative './command_error'
require_relative './command_parts'
module Command
def self.included(mod)
mod.instance_eval do
include CommandSetter
extend CommandReceiver
end
end
module CommandSetter
def send_command(command, **k)
@line_index = 0
@interpret_self = [self]
@interpret_skip = [false]
@briefly_skip = true
@data = {}
result = []
command = command.dup.gsub("\\\n", "").split("\n") #行毎に取り出し
begin
while @line_index <= command.size - 1
@briefly_skip = true
k[:self] = @interpret_self[-1]
k[:command_self] = self
result << interpret_command(command[@line_index], **k)
@line_index += 1
end
rescue => e
last_num = (@line_index + 1) % 10
line_index = (@line_index + 1).to_s + (case last_num
when 1
"st"
when 2
"nd"
when 3
"rd"
else
"th"
end
)
if CommandError === e
raise e.class, "(#{line_index} line) " + e.message.gsub(/\G\(\d*(?:st|nd|rd|th)\sline\)\s/, ""), caller(2)
else
raise e.class, "(#{line_index} line) " + e.message.gsub(/\G\(\d*(?:st|nd|rd|th)\sline\)\s/, ""), e.backtrace
end
end
result
end
def interpret_command(command, **k)
line = ' ' + command
pull_brace = Proc.new{|text, start, close, same=nil|
ary = [(
arr = []
pos = 0
regexp = Regexp.new('[^\\\\](?:\\\\\\\\)*(' + start + ')')
while i = line.index(regexp, pos)
arr << (pos = i + $&.length - Regexp.last_match(-1).length)
end
arr
),(
same.nil? ? start == close : same
)]
ary.insert(1,
ary[1] ? ary[0].dup : (
arr = []
pos = 0
regexp = Regexp.new('[^\\\\](?:\\\\\\\\)*(' + close + ')')
while i = line.index(regexp, pos)
arr << (pos = i + $&.length - Regexp.last_match(-1).length)
end
arr
))
}
brace = []
brace_proc = {}
self.class.class_variable_get(:@@braces).each do |ary|
result = pull_brace.call(line, *ary[0..-2])
brace << result
result[0].each do |i|
brace_proc[i] = ary[-1]
end
end
division = []
until brace.delete_if{|x| x[0..1].any?(&:empty?)}.empty?
#取り出した括弧が無くなるまで
brace.sort_by!{|ary| ary[0][0]}
b_0 = brace[0]
f = nil
l = nil
unless b_0[2]
l = f = (b_0_0 = b_0[0])[0]
b_0_1 = b_0[1]
count = 0
(b_0[0..1].map(&:max).max - l + 1).times do
count += 1 if b_0_0.include?(l)
count -= 1 if b_0_1.include?(l)
break if count == 0
l += 1
end
range = f..l
else
range = (f = b_0[0][0])..(l = b_0[0][1])
end
brace[1..-1].each do |ary|
ary[0..1].each do |ary2|
ary2.shift until ary2.empty? || l <= ary2[0]
end
end
b_0[0..1].each do |ary2|
ary2.shift until ary2.empty? || l < ary2[0]
end
division.push (f - 1), l
end
if division.empty?
#文字列内文字列が無いならそのまま空白で分割
word = line.split(/\s/).map do |s|
self.class.interpret_literal s
end
string_pos = []
else
is_str = (division[0] == -1) #次に処理するのは文字列か
division.shift if is_str
unless division[-1] >= (ls_1 = line.size - 1)
division << ls_1
end
word = []
string_pos = []
pos = 0
division.each do |i|
if is_str
string_pos << (word << brace_proc[pos].call(line[pos..i])).size - 1 #括弧の中は対応するプロックで処理
else
#その他は空白で分割
word.push *(line[pos..i].split(/\s/).map! do |s|
self.class.interpret_literal s
end)
end
is_str = !is_str #次は今の逆の処理
pos = i + 1 #次は今の次の所を処理
end
end
#不必要なデータを消し、関数を呼び出す
i = -1
word.map! do |obj|
i += 1
next CommandPart.new(obj) if CommandArgument === obj
next obj if obj.class != String || string_pos.include?(i) || CommandOperator === obj || CommandRuby === obj
next CommandNull if obj == "" || obj == CommandNull
if i < word.size - 1
j = i
latter_word = word[(i+1)..-1]
following_index = latter_word.index{|x|
j += 1
x != "" || string_pos.include?(j)
}
if following_index
following_index = j
following = word[following_index]
else
following_index = nil
following = CommandNull
end
else
following_index = nil
following = CommandNull
end
if CommandArgument === following
word[following_index] = CommandNull
load_function(obj.to_sym, following, **k)
else
load_function(obj.to_sym, nil, **k)
end
end
word.delete_if{|x| CommandNull == x}
#演算子を呼び出す
self.class.class_variable_get(:@@operators).sort_by{|x| x[0]}.each do |ope|
while i = word.index{|item| CommandOperator === item && ope[1].include?(String.new(item))}
obj = word[i]
if i > 0 && !(CommandOperator === word[i-1])
previous = word[i-1]
word[i-1] = CommandNull
else
previous = CommandNull
end
if i < word.size - 1 && !(CommandOperator === word[i+1])
following = word[i+1]
word[i+1] = CommandNull
else
following = CommandNull
end
word[i] = obj.call(previous, following, **k)
word.delete_if{|x| CommandNull == x}
end
end
word.map! do |x|
next x.interpret_command(**k) if CommandPart === x
x
end
word.delete_if{|x| CommandNull == x}
end
def load_function(type, arg, **k)
unless self.class.class_variable_get(:@@no_skip_commands).include?(type)
return CommandNull if skip?
else
@briefly_skip = false
end
if arg
execute_function(type, *(arg.interpret_command(**k)), **k)
else
execute_function(type, **k)
end
end
def execute_function(type, *args, **k)
commands = self.class.class_variable_get(:@@commands)
unless commands.has_key?(type)
args = [type] + args
type = :command_missing
end
pr = commands[type]
unless pr.accept_arguments?(args)
raise CommandArgumentError, [type, pr.min_arguments, pr.max_arguments, args.size]
end
if pr.accept_keywords?(**k)
k[:self].instance_exec(*args, **k, &commands[type])
else
k[:self].instance_exec(*args, &commands[type])
end
end
def interpreting_line
@line_index
end
def interpreting_line=(v)
@line_index = Integer(v)
end
def interpret_self
@interpret_self
end
def skip?
@interpret_skip[-1] && @briefly_skip
end
def interpret_skip
@interpret_skip
end
def data_in_command
@data
end
def on_enter_new_block(**k)
bool = k[:command_self].interpret_skip[-1]
k[:command_self].interpret_skip << bool
return yield
end
def on_close_block(**k)
raise CommandSyntaxError, "unexpected keyword_end, expecting end-of-command"
end
end
module CommandReceiver
private
def self.extended(klass)
#=begin
klass.class_variable_set(:@@commands, {:command_missing => Proc.new{|type, *args, **k|
raise NoCommandError, type
}, :Array => Proc.new{|*args, **k|
if args.any?{|x| CommandRuby === x}
'[' + args.join(',') + ']'
else
args
end
}, :Regexp => Proc.new{|str, **k|
if CommandRuby === str
CommandRuby.new("Rexeup.new(#{str})")
else
Regexp.new(str)
end
}, :Data => Proc.new{|**k|
CommandRuby.new('key[:data]')
}, :p => klass.block_command_proc{|obj, **k|
p obj
}, :if => klass.block_command_proc{|bool, **k|
CommandIf.new(bool, **k)
}, :unless => klass.block_command_proc{|bool, **k|
CommandIf.new(bool, **k)
}, :case => klass.block_command_proc{|obj = CommandNull, **k|
CommandCase.new(obj, **k)
}, :loop => klass.block_command_proc{|**k|
CommandLoop.new(true, **k)
}, :while => klass.block_command_proc{|obj, **k|
CommandLoop.new(obj, **k)
}, :until => klass.block_command_proc{|obj, **k|
CommandLoop.new(!obj, **k)
}, :define => klass.block_command_proc{|name, **k|
CommandDefineFunction.new(name.to_sym, **k)
}, :end => Proc.new{|**k|
obj = k[:command_self].interpret_self[-1]
if obj.respond_to?(:on_close_block)
obj.on_close_block(**k)
else
k[:command_self].interpret_self.pop
k[:command_self].interpret_skip.pop
obj = k[:command_self].interpret_self[-1]
obj.on_close_new_block(**k) if obj.respond_to?(:on_close_new_block)
CommandNull
end
}})
#=end
=begin
klass.class_variable_set(:@@commands, {:command_missing => Proc.new{|type, *args, **k|
raise NoCommandError, type
}, :Array => Proc.new{|*args, **k|
args
}, :Regexp => Proc.new{|str, **k|
Regexp.new(str)
}, :Data => Proc.new{|**k|
k[:command_self].data_in_command
}, :if => klass.block_command_proc{|bool, **k|
CommandIf.new(bool, **k)
}, :unless => klass.block_command_proc{|bool, **k|
CommandIf.new(!bool, **k)
}, :case => klass.block_command_proc{|obj = CommandNull, **k|
CommandCase.new(obj, **k)
}, :loop => klass.block_command_proc{|**k|
CommandLoop.new(true, **k)
}, :while => klass.block_command_proc{|obj, **k|
CommandLoop.new(obj, **k)
}, :until => klass.block_command_proc{|obj, **k|
CommandLoop.new(!obj, **k)
}, :define => klass.block_command_proc{|name, **k|
CommandDefineFunction.new(name.to_sym, **k)
}, :end => Proc.new{|**k|
obj = k[:command_self].interpret_self[-1]
if obj.respond_to?(:on_close_block)
obj.on_close_block(**k)
else
k[:command_self].interpret_self.pop
k[:command_self].interpret_skip.pop
obj = k[:command_self].interpret_self[-1]
obj.on_close_new_block(**k) if obj.respond_to?(:on_close_new_block)
CommandNull
end
}})
=end
klass.class_variable_set(:@@no_skip_commands, [:if, :unless, :case, :loop, :while, :until, :end])
klass.class_variable_set(:@@literals, [Proc.new{|x|
if x == "true"
next true
else
next x
end
},Proc.new{|x|
if x == "false"
next false
else
next x
end
},Proc.new{|x|
if x == "nil"
next nil
else
next x
end
},Proc.new{|x|
next Integer(x) rescue ArgumentError
next x
},Proc.new{|x|
next Float(x) rescue ArgumentError
next x
},Proc.new{|x|
begin
if x[0] == ':'
next eval(x)
end
rescue SyntaxError
end
next x
}])
klass.class_variable_set(:@@braces, [['\\"', '\\"', true, Proc.new{|x| eval(x)}],
["\\'", "\\'", true, Proc.new{|x| eval(x)}],
['\\(', '\\)', false, Proc.new{|x| CommandArgument.new(x[1..-2])}],
['\\-', '\\-', false, Proc.new{|x| CommandOperator.new("-")}],
['\\![^\\~]', '(\\![^\\~])', false, Proc.new{|x| CommandOperator.new("!")}],
['\\!\\~', '\\!(\\~)', false, Proc.new{|x| CommandOperator.new("!~")}],
['\\!\\=', '\\!(\\=)', false, Proc.new{|x| CommandOperator.new("!=")}],
['\\%', '\\%', false, Proc.new{|x| CommandOperator.new("%")}],
['[^\\&](\\&[^\\&])', '[^\\&](\\&[^\\&])', false, Proc.new{|x| CommandOperator.new("&")}],
['\\&\\&', '\\&(\\&)', false, Proc.new{|x| CommandOperator.new("&&")}],
['[^\\*](\\*[^\\*])', '[^\\*](\\*[^\\*])', false, Proc.new{|x| CommandOperator.new("*")}],
['\\*\\*', '\\*(\\*)', false, Proc.new{|x| CommandOperator.new("**")}],
['[^\\.](\\.\\.[^\\.])', '[^\\.]\\.(\\.[^\\.])', false, Proc.new{|x| CommandOperator.new(".."){|previous, following, **k|
if k[:ruby] || CommandRuby === previous || CommandRuby === following
"#{previous}..#{following}"
else
previous..following
end
}}],
['\\.\\.\\.', '\\.\\.(\\.)', false, Proc.new{|x| CommandOperator.new("..."){|previous, following, **k|
if k[:ruby] || CommandRuby === previous || CommandRuby === following
"#{previous}...#{following}"
else
previous...following
end
}}],
['\\/', '\\/', false, Proc.new{|x| CommandOperator.new("/")}],
['\\^', '\\^', false, Proc.new{|x| CommandOperator.new("^")}],
['[^\\|](\\|[^\\|])', '[^\\|](\\|[^\\|])', false, Proc.new{|x| CommandOperator.new("|")}],
['\\|\\|', '\\|(\\|)', false, Proc.new{|x| CommandOperator.new("||")}],
['[^\\=\\!](\\~)', '[^\\=\\!](\\~)', false, Proc.new{|x| CommandOperator.new("~")}],
['\\+', '\\+', false, Proc.new{|x| CommandOperator.new("+")}],
['[^\\<](\\<[^\\<])', '[^\\<](\\<[^\\<])', false, Proc.new{|x| CommandOperator.new("<")}],
['\\<\\<', '\\<(\\<)', false, Proc.new{|x| CommandOperator.new("<<")}],
['\\<\\=[^\\>]', '\\<(\\=[^\\>])', false, Proc.new{|x| CommandOperator.new("<=")}],
['\\<\\=\\>', '\\<\\=(\\>)', false, Proc.new{|x| CommandOperator.new("<=>")}],
['\\=\\~', '\\=(\\~)', false, Proc.new{|x| CommandOperator.new("=~")}],
['[^\\=](\\=\\=[^\\=])', '[^\\=]\\=(\\=[^\\=])', false, Proc.new{|x| CommandOperator.new("==")}],
['\\=\\=\\=', '\\=\\=(\\=)', false, Proc.new{|x| CommandOperator.new("===")}],
['[^\\>](\\>[^\\>])', '[^\\>](\\>[^\\>])', false, Proc.new{|x| CommandOperator.new(">")}],
['\\>\\=', '\\>(\\=)', false, Proc.new{|x| CommandOperator.new(">=")}],
['\\>\\>', '\\>(\\>)', false, Proc.new{|x| CommandOperator.new(">>")}],
['\\[', '(\\])(?:(?!\\s*(?:[\\+\\-\\*\\/\\%\\%&\\|\\^]|[\\*\\<\\>\\&\\|]{2})?\\=[\\=\\~])(?:\\s*(?:[\\+\\-\\*\\/\\%\\%&\\|\\^]|[\\*\\<\\>\\&\\|]{2})?\\=))?', false, Proc.new{|x|
if x =~ /\]\s*([^\s]*)\=$/ #代入
x = x[1..(- $&.length - 1)]
x = CommandArgument.new(x).interpret_command(**k)
op = $1.to_sym
case $1
when '+', '-', '*', '/', '%', '**', '&', '|', '^', '<<', '>>'
CommandOperator.new("[]="){|previous, following, **k|
if k[:ruby] || CommandRuby === previous || CommandRuby === following || x.any?()
else
previous[*CommandArgument.new(x).interpret_command(**k)] = previous[*CommandArgument.new(x).interpret_command(**k)].__send__(op, following)
end
}
when '&&'
CommandOperator.new("[]="){|previous, following, **k|
if k[:ruby] || CommandRuby === previous || CommandRuby === following || CommandRuby === x
else
previous[*CommandArgument.new(x).interpret_command(**k)] && (previous[*CommandArgument.new(x).interpret_command(**k)] = following)
end
}
when '||'
CommandOperator.new("[]="){|previous, following, **k|
if k[:ruby] || CommandRuby === previous || CommandRuby === following || CommandRuby === x
else
previous[*CommandArgument.new(x).interpret_command(**k)] || (previous[*CommandArgument.new(x).interpret_command(**k)] = following)
end
}
when ''
CommandOperator.new("[]="){|previous, following, **k|
if k[:ruby] || CommandRuby === previous || CommandRuby === following || CommandRuby === x
else
previous[*CommandArgument.new(x).interpret_command(**k)] = following
end
}
end
else #呼び出し
CommandOperator.new("[]"){|previous, following, **k|
if k[:ruby] || CommandRuby === previous
else
previous[*CommandArgument.new(x[1..-2]).interpret_command(**k)]
end
}
end
}]
])
h = {2 => ["[]"],
3 => ["!", "~"],
4 => ["**"],
5 => ["%"],
6 => ["*", "/"],
7 => ["+", "-"],
8 => ["<<", ">>"],
9 => ["&"],
10 => ["^", "|"],
11 => ["<", "<=", ">", ">="],
12 => ["!~", "!=", "<=>", "=~", "==", "==="],
13 => ["&&"],
14 => ["||"],
15 => ["..", "..."],
17 => ["[]="]}
h.default_proc = Proc.new{Array.new}
klass.class_variable_set(:@@operators, h)
super
end
public
def self.copy_commands(new, original)
new.class_variable_set(:@@commands, original.class_variable_get(:@@commands))
nil
end
def self.copy_braces(new, original)
new.class_variable_set(:@@braces, original.class_variable_get(:@@braces))
nil
end
def self.copy_literals(new, original)
new.class_variable_set(:@@literals, original.class_variable_get(:@@literals))
nil
end
def add_command(type, no_skip=false, &b)
return nil unless b
self.class_variable_get(:@@commands)[type.to_sym] = b
self.class_variable_get(:@@no_skip_commands) << type.to_sym if no_skip
nil
end
def add_block_command(type, no_skip=false, &b)
add_command(type, true, &block_command_proc(&b))
end
def copy_command(new, original)
self.class_variable_get(:@@commands)[new] = self.class_variable_get(:@@commands)[original]
nil
end
def remove_command(type)
self.class_variable_get(:@@commands).delete(type)
self.class_variable_get(:@@no_skip_commands).delete(type)
nil
end
def block_command_proc(&b)
Proc.new{|*args, **k|
if self.respond_to?(:on_enter_new_block)
obj = self.on_enter_new_block(**k){
unless b.accept_arguments?(args)
raise CommandArgumentError, [type, b.min_arguments, b.max_arguments, args.size]
end
if b.accept_keywords?(**k)
k[:self].instance_exec(*args, **k, &b)
else
k[:self].instance_exec(*args, &b)
end
}
else
bool = k[:command_self].interpret_skip[-1]
k[:command_self].interpret_skip << bool
unless bool
unless b.accept_arguments?(args)
raise CommandArgumentError, [type, b.min_arguments, b.max_arguments, args.size]
end
if b.accept_keywords?(**k)
obj = k[:self].instance_exec(*args, **k, &b)
else
obj = k[:self].instance_exec(*args, &b)
end
else
obj = CommandNull
end
end
k[:command_self].interpret_self << obj
if obj.respond_to?(:on_enter_text)
obj.on_enter_text(**k)
else
obj
end
}
end
def add_brace(start, close = start, same = nil, &b)
args = [start, close]
args << same unless same.nil?
args << b
self.class_variable_get(:@@braces) << args if b
nil
end
def add_literals(&b)
self.class_variable_get(:@@literals) << b if b
nil
end
def interpret_literal(x)
old_x = x
self.class_variable_get(:@@literals).each do |pr|
x = pr.call(x)
break if x != old_x
end
return x
end
end
end
# coding: utf-8
class CommandBlockManager
def self.add_command(type, no_skip=false, &b)
return nil unless b
self.class_variable_get(:@@commands)[type.to_sym] = b
self.class_variable_get(:@@no_skip_commands) << type.to_sym
nil
end
def self.add_brace_command(type, no_skip=false, &b)
add_command(type, no_skip, block_command_proc(&b))
end
def self.block_command_proc(&b)
Proc.new{|*args, **k|
if self.respond_to?(:on_enter_new_block)
obj = self.on_enter_new_block(**k) do
unless b.accept_arguments?(*args)
raise CommandArgumentError, [type, b.min_arguments, b.max_arguments, args.size]
end
if b.accept_keywords?(**k)
k[:self].instance_exec(*args, **k, &b)
else
k[:self].instance_exec(*args, &b)
end
end
else
bool = k[:command_self].interpret_skip[-1]
k[:command_self].interpret_skip << bool
unless bool
unless b.accept_arguments?(*args)
raise CommandArgumentError, [type, b.min_arguments, b.max_arguments, args.size]
end
if b.accept_keywords?(**k)
obj = k[:self].instance_exec(*args, **k, &b)
else
obj = k[:self].instance_exec(*args, &b)
end
else
obj = CommandNull
end
end
k[:command_self].interpret_self << obj
if obj.respond_to?(:on_enter_text)
obj.on_enter_text(**k)
else
obj
end
}
end
def self.inherited(klass)
klass.class_variable_set(:@@commands, {})
klass.class_variable_set(:@@no_skip_commands, [])
end
def initialize(**k)
@parent = k[:self]
@default_ruby = k[:ruby]
set_added_commands(**k)
end
def on_close_new_block(**k)
set_added_commands(**k)
end
def set_added_commands(**k)
@removed_commands = {}
@added_commands = []
commands = self.class.class_variable_get(:@@commands)
no_skip = self.class.class_variable_get(:@@no_skip_commands)
self_class = k[:command_self].class
self_commands = self_class.class_variable_get(:@@commands)
self_no_skip = self_class.class_variable_get(:@@no_skip_commands)
commands.each_pair do |type, proc| #コマンドを登録していく
if self_commands.has_key?(type) #既に登録されている場合
@removed_commands[type] = [self_no_skip.include?(type), self_commands[type]] #元のコマンドを保存
end
self_class.add_command(type, no_skip.include?(type), &proc) #登録!
@added_commands << type #保存
end
end
def method_missing(name, *args, **k)
m = @parent.method(name)
if m.to_proc.accept_keywords?(**k)
m.call(*args, **k)
else
m.call(*args)
end
end
def on_enter_new_block(**k)
clear_added_commands(**k)
bool = k[:command_self].interpret_skip[-1]
k[:command_self].interpret_skip << bool
return yield unless bool
CommandNull
end
def clear_added_commands(**k)
@added_commands.each do |type|
k[:command_self].class.remove_command(type)
end
@removed_commands.each_pair do |type, ary|
k[:command_self].class.add_command(type, *ary)
end
nil
end
def on_close_block(**k)
if @default_ruby
k[:ruby] = @default_ruby
else
k.delete(:ruby)
end
clear_added_commands(**k)
k[:command_self].interpret_skip.pop
k[:command_self].interpret_self.pop
obj = k[:command_self].interpret_self[-1]
obj.on_close_new_block(**k) if obj.respond_to?(:on_close_new_block)
CommandNull
end
def on_enter_text(**k)
CommandNull
end
end
class CommandIf < CommandBlockManager
add_command(:elsif, true) do |bool, **k|
self.elsif(bool, **k)
end
add_command(:else, true) do |**k|
self.else(**k)
end
def initialize(bool, **k)
super(**k)
@ruby = k[:ruby] || CommandRuby === bool
@bool_obj = bool
@bool = !@ruby && (bool ? true : false)
@else = false
k[:command_self].interpret_skip[-1] = !@ruby && !@bool
k[:ruby] ||= @ruby if @ruby
end
def elsif(bool, **k)
raise CommandError, 'else must be only one and the last' if @else
if @bool #ifで実行した
k[:command_self].interpret_skip[-1] = true
return CommandNull
elsif !@ruby #ifでfalseだった
@ruby = CommandRuby === bool
@bool = !@ruby && (bool ? true : false)
k[:command_self].interpret_skip[-1] = !@ruby && !@bool
k[:ruby] ||= @ruby if @ruby
if @ruby
return "if #{bool}\n"
else
return CommandNull
end
else
#ifで@rubyだった
k[:command_self].interpret_skip[-1] = false
return "elsif #{bool}\n"
end
end
def else(**k)
raise CommandError, 'else must be only one and the last' if @else
@else = true
if @bool #実行した
k[:command_self].interpret_skip[-1] = true
return CommandNull
elsif !@ruby #実行しなかった
k[:command_self].interpret_skip[-1] = false
return CommandNull
else #@rubyだった
k[:command_self].interpret_skip[-1] = false
return "else\n"
end
end
def on_enter_text(**k)
if @ruby
"if #{@bool_obj}\n"
else
CommandNull
end
end
def on_close_block(**k)
super(**k)
if @ruby
"end\n"
else
CommandNull
end
end
end
class CommandCase < CommandBlockManager
add_command(:when) do |arg, *args, **k|
self.when(arg, *args, **k)
end
add_command(:else) do |**k|
self.else(**k)
end
def initialize(obj, **k)
super(**k)
@bool = false
@obj = obj
p @obj_text = (@obj != CommandNull ? "#{@obj}" : "")
@else = false
@loaded = false
@first = true
@line = k[:command_self].interpreting_line
@when_args = []
k[:command_self].interpret_skip[-1] = true
end
def when(*args, **k)
unless @loaded
@when_args << args
return CommandNull
end
raise CommandError, 'else must be only one and the last' if @else
if !@ruby
bool = (@obj != CommandNull ? args.any?{|obj| obj === @obj} : args.any?)
k[:command_self].interpret_skip[-1] = !bool || @bool
@bool = @bool || bool
else
k[:command_self].interpret_skip[-1] = false
text = ''
if @first
test += "case " + @obj_text + "\n"
@first = false
end
test += 'when ' + args.map{|x| x.to_s}.join(", ")
end
end
def else(**k)
return CommandNull unless @loaded
raise CommandError, 'else must be only one and the last' if @else
if !@ruby
k[:command_self].interpret_skip[-1] = @bool
@else = true
else
k[:command_self].interpret_skip[-1] = false
text = ''
if @first
test += "case " + @obj_text + "\n"
@first = false
end
text += "else\n"
end
end
def on_close_block(**k)
unless @loaded
k[:command_self].interpreting_line = @line + 1
@loaded = true
ary = @when_args << [@obj]
@ruby = !ary.empty? && ary.any?{|x| x.any?{|x2| CommandRuby === x2}}
return CommandNull
else
super
text = ''
if @first
test += "case " + @obj_text + "\n"
@first = false
end
return text + "end\n" if @ruby
return CommandNull
end
end
end
class CommandLoop < CommandBlockManager
add_command(:break) do |obj = CommandNull, **k|
self.break(obj, **k)
end
add_command(:redo) do |**k|
self.redo(**k)
end
add_command(:next) do |**k|
self.redo(**k)
end
def initialize(bool, **k)
super(**k)
@line_index = k[:command_self].interpreting_line - 1
@obj = CommandNull
@redo = @next = false
k[:command_self].interpret_skip[-1] = !bool
end
def break(obj, **k)
k[:command_self].interpret_skip[-1] = true
@obj = obj
end
def redo(**k)
k[:command_self].interpret_skip[-1] = true
@redo = true
CommandNull
end
def next(**k)
k[:command_self].interpret_skip[-1] = true
@next = true
CommandNull
end
def on_close_block(**k)
if k[:command_self].interpret_skip[-1] && !(@redo || @next)
super
return @obj
elsif @redo
k[:command_self].interpreting_line = @line_index + 1
k[:command_self].interpret_skip[-1] = false
@redo = false
else
super
k[:command_self].interpreting_line = @line_index
end
CommandNull
end
end
class CommandDefineFunction < CommandBlockManager
def initialize(name, **k)
super(**k)
k[:command_self].interpret_skip[-1] = true
line = k[:command_self].interpreting_line
k[:command_self].class.add_block_command(name){|*args, **key|
line_index = key[:command_self].interpreting_line
key[:command_self].interpreting_line = line
CommandCallFunction.new(line_index, *args, **key)
}
end
end
class CommandCallFunction < CommandBlockManager
add_command(:args) do
self.args
end
def initialize(line, *args, **k)
super(**k)
@line = line
@args = args
end
def args
@args.dup
end
def on_close_block(**k)
super
k[:command_self].interpreting_line = @line
CommandNull
end
end
# coding: utf-8
class CommandError < StandardError
end
class NoCommandError < CommandError
def initialize(command_name)
if Symbol === command_name
super("undefined command `#{command_name.to_s}'")
else
super
end
end
end
class CommandArgumentError < CommandError
def initialize(command_name)
if Array === command_name
min = command_name[1]
max = command_name[2]
sent = command_name[3]
command_name = command_name[0]
if min == max
num = min.to_s
elsif max.nil?
num = min.to_s + "+"
else
num = min.to_s + ".." + max.to_s
end
super("wrong number of arguments (#{sent} for #{num}) in `#{command_name.to_s}'")
else
super
end
end
end
class CommandSyntaxError < CommandError
end
class Proc
def accept_arguments?(args, **k)
if args.size < min_arguments #必要な数の引数が無い
return false
elsif max_arguments.nil? #大丈夫
return true
elsif args.size > max_arguments #引数送りすぎ
return false
else #大丈夫
return true
end
end
def min_arguments
arity >= 0 ? arity : - 1 - arity
end
def max_arguments
arity < 0 ? nil : parameters.count{|x| x[0] == :opt}
end
def accept_keywords?(**k)
!k.empty? && (k.keys.all?{|keyword| b = parameters.any?{|ary| ary[0] == :key && ary[1] == keyword}} || parameters.any?{|ary| ary[0] == :keyrest})
end
end
# coding: utf-8
class CommandArgument < String
def interpret_command(**k)
k[:command_self].interpret_command(String.new(self), **k)
end
end
class CommandPart < String
def interpret_command(**k)
k[:command_self].interpret_command(String.new(self), **k)[-1]
end
end
class CommandRuby < String
def self.===(other)
if Array === other
other.any?{|x| self === x}
elsif Hash === other
h = {}
h.values
end
end
end
CommandNull = Object.new
class CommandOperator < String
def initialize(string = "", &b)
super(string)
if b
@proc = b
else
@proc = Proc.new{|previous, following, **k|
if previous == CommandNull
name = string + "@"
if k[:ruby] || CommandRuby === following
"#{string}#{following}"
else
following.__send__(name.to_sym)
end
elsif following == CommandNull
if k[:ruby] || CommandRuby === previous
"#{following} #{string}"
else
previous.__send__(name.to_sym)
end
else
if k[:ruby] || CommandRuby === previous || CommandRuby === following
"#{previous} #{string} #{following}"
else
previous.__send__(string.to_sym, following)
end
end
}
end
end
def call(previous, following, **k)
return CommandNull if k[:command_self].skip?
previous = previous.interpret_command(**k) if CommandPart === previous
following = following.interpret_command(**k) if CommandPart === following
k[:command_self].instance_exec(previous, following, **k, &@proc)
end
end
# coding: utf-8
require "dxruby" unless defined? DXRuby
require_relative './command'
class Test < Sprite
include Command
def initialize(*args)
super
@vector = [0,0]
end
def update
self.x += @vector[0]
self.y += @vector[1]
end
attr_accessor :vector
add_command(:left) do |size = 1, **k|
k[:command_self].vector[0] = - size.to_f
end
add_command(:right) do |size = 1, **k|
k[:command_self].vector[0] = size.to_f
end
add_command(:up) do |size = 1, **k|
k[:command_self].vector[1] = - size.to_f
end
add_command(:down) do |size = 1, **k|
k[:command_self].vector[1] = size.to_f
end
add_command(:stop) do |**k|
k[:command_self].vector = [0, 0]
end
add_command(:exit) do
exit
end
add_brace('\\#', '$') do
CommandNull
end
end
t = Test.new(100,100,Image.new(50,50,C_WHITE))
th = Thread.new{
loop do
t.send_command(gets.chomp)
end
}
Window.loop do
t.update
t.draw
break unless th.alive?
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment