Created
December 27, 2014 03:25
-
-
Save RPGP1/95171c06ac40a1bc8911 to your computer and use it in GitHub Desktop.
Rubyでコマンドシステム作ってみたら遅かった。(本体はcommand_test.rbです)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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