Created
January 23, 2015 23:02
-
-
Save lnznt/a2e0616e3a59c6ddb865 to your computer and use it in GitHub Desktop.
Prolog: Prolog からの Ruby 呼び出し ref: http://qiita.com/lnznt/items/4043f4b75b00d525156c
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
$ ruby prolog_server.rb # デフォルトでポート 53340/tcp を LISTEN | |
# 起動したままになる。停止するには Ctrl-C | |
# --log ログファイル名 を付けて起動するとログ出力します |
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
$ swipl -l ruby_client.pro | |
: | |
(Prolog が起動する) |
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
: ターミナル#1 : Ruby サーバ起動 | |
$ ruby prolog_server.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
: ターミナル#2 : Prolog サーバ起動 | |
$ swipl -l server.pro -g 'create_server(53330).' | |
# 起動したままになる |
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
: ターミナル#3 : Prolog サーバに ruby_client をロード | |
$ ruby -r prolog_proxy -e 'Prolog::Proxy.load ["ruby-client.pro"]' |
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
: ターミナル#4 : Proxy デーモン(drbサービス)起動 | |
$ ruby prolog_proxy.rb --drb --daemon # バックグランドで起動 |
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
: ターミナル#5 (リモート) : pry で操作 | |
$ pry -r drb # drb を require | |
: | |
[1] pry(main)> px = DRbObject.new_with_uri 'druby://:53331' | |
=> ... | |
[2] pry(main)> px.q 'ruby(foo(x))' | |
=> "[ruby(foo(x))]" |
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
: | |
call method: foo(x), args = 1 |
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
require 'prolog_server' | |
class MyClass # レシーバにするオブジェクトのクラス定義 | |
# 適当にメソッド定義する | |
# def hoge(...) ... とすると Prolog側から `?- ruby(hoge(...)).` で呼び出せる | |
end | |
my_receiver = MyClass.new |
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
th = Prolog::Server.start(my_receiver) | |
th.join # 戻り値はサーバの Thread オブジェクトなので join できる |
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
Prolog::Server.daemon(my_receiver) # この場合、デーモンになる |
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
server = Prolog::Server.new # インスタンスを作って指定する方法 | |
th = server.start(my_receiver) | |
th.join |
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
ruby(Pred) % ruby/1 : Host = 'localhost', Port = 53340 | |
ruby(Pred, Port) % ruby/2 : Host = 'localhost' | |
ruby(Pred, Port, Host) % ruby/3 |
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
server = Prolog::Server.new | |
server.daemon(my_receiver) # この場合、デーモンになる |
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
server = Prolog::Server.new | |
# on_error でハンドラを指定する。引数はキャプチャする例外タイプ | |
server.on_error(NoMethodError) {|e| puts "Exception: #{e.class}" } | |
th = server.start(my_receiver) | |
th.join |
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
[63] pry(Prolog::Proxy):1> q 'ruby(foo("ABC"))' | |
=> "[ruby(foo([65,66,67]))]" |
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
# 変換の例 | |
p [65,66,67].pack "C*" #=> "ABC" | |
p "ABC".unpack "C*" #=> [65, 66, 67] |
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
# バイトの配列か判定する例 | |
arg = [65,66,67] | |
p arg.kind_of?(Array) && arg.all? {|n| (0..255).include? n } #=> true |
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
require 'kind_of_test' | |
class Object ; include KindOfTest ; end | |
p [65,66,67].array? &:byte? #=> true | |
p [65,66,67,256].array? &:byte? #=> false |
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
?- ruby(foo). % アトムの場合、引数なしでメソッドを呼び出す | |
true. | |
?- ruby(bar(a,b,c)). % 述語の場合、述語の引数を渡してメソッドを呼び出す | |
true. | |
?- ruby(baz(a,f(x,y),[1,2,3],"ABC")). | |
true. | |
?- ruby(1). % 1 はアトム/述語以外なので無視される | |
true. |
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
$ ruby prolog_server.rb | |
call method: foo(), args = 0 | |
call method: bar(a,b,c), args = 3 | |
call method: baz(a,{:f=>[:x, :y]},1,2,3,65,66,67), args = 4 # puts なのでこう見えますが、引数は 4 個です。 |
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
$ ruby prolog_server.rb # デフォルトでポート 53340/tcp を LISTEN | |
# 起動したままになる。停止するには Ctrl-C |
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
$ swipl -l server.pro -g 'create_sserver(53330).' # 起動したままになる |
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
$ pry -r prolog_proxy # prolog_proxy.rb を require します。 | |
: |
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
[1] pry(main)> cd Prolog::Proxy | |
[2] pry(Prolog::Proxy):1> load ['ruby-client.pro'] | |
=> ["./ruby-client.pro"] | |
[3] pry(Prolog::Proxy):1> q 'ruby(foo(a,b))' | |
=> "[ruby(foo(a,b))]" |
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
(Ruby サーバが起動しているターミナル) | |
call method: foo(a,b), args = 2 |
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
require 'socket' | |
require 'resolv-replace' | |
require 'logger' | |
require 'prolog_parser' | |
module Prolog | |
class Server | |
HOST, PORT = '0.0.0.0', 53340 | |
def self.daemon(*args, &block) | |
new(*args, &block).daemon | |
end | |
def self.start(*args, &block) | |
new(*args, &block).start | |
end | |
class ServerLogger < Logger | |
def level=(x) | |
x = Logger.const_get(x) if %i(DEBUG INFO WARN ERROR FATAL).include?(x) | |
super(x) | |
end | |
end | |
attr_accessor :log | |
attr_writer :logger | |
def logger; @logger ||= (log ? ServerLogger.new(log) : null) ; end | |
attr_accessor :host, :port, :receiver | |
def initialize(receiver=nil, port:nil,host:nil, logger:nil,log:nil, &block) | |
tap {|my| my.receiver, my.port, my.host, my.logger, my.log = | |
receiver, port, host, logger, log } | |
instance_eval(&block) if block | |
end | |
attr_reader :thread | |
def daemon(*args, &block) | |
stop | |
logger.info "start daemon" | |
Process.daemon | |
service_loop *args, &block | |
end | |
def start(*args, &block) | |
stop | |
logger.info "start server: pid = #{$$}" | |
@thread = Thread.new { service_loop *args, &block } | |
end | |
def stop | |
return unless @thread | |
logger.info "stop server: pid = #{$$}" | |
@thread.kill rescue nil | |
@thread = nil | |
end | |
private | |
def service_loop(receiver=nil, port:nil, host:nil) | |
port ||= (self.port || PORT) | |
host ||= (self.host || HOST) | |
receiver ||= (self.receiver || null) | |
logger.info "accept loop: host = #{host}, port = #{port}" | |
Socket.tcp_server_loop(host, port) do |conn, *| | |
Thread.new do | |
begin | |
serivce(receiver, conn) | |
ensure | |
conn.close | |
end | |
end | |
end | |
end | |
def serivce(receiver, conn) | |
msg = conn.read ; logger.debug "request(raw) : #{msg}" | |
req = Prolog.to_ruby(msg) ; logger.debug "request(ruby): #{req}" | |
pred?(req) ? receiver.send(req.keys.first, *req.values.first) : | |
atom?(req) ? receiver.send(req) : nil | |
rescue Exception => e | |
logger.error "exception: #{e}" | |
error_notify(e) | |
end | |
def pred?(x) | |
x.kind_of?(Hash) && | |
x.size == 1 && | |
x.keys.first.kind_of?(Symbol) && | |
x.values.first.kind_of?(Array) | |
end | |
def atom?(x) | |
x.kind_of?(Symbol) | |
end | |
def null | |
@null ||= Class.new{ def method_missing(*) ; end }.new | |
end | |
public | |
def on_error(exception_type=Exception, &block) | |
handlers[exception_type] << block if block | |
end | |
private | |
def handlers | |
@handlers ||= Hash.new {|h, k| h[k] = [] } | |
end | |
def error_notify(exception) | |
handlers.select{|type, *| exception.kind_of? type }.values.reduce(:+) | |
.each {|handler| handler.(exception) } | |
end | |
end | |
end | |
if __FILE__ == $0 | |
require 'optparse' | |
require 'ostruct' | |
class DummyReceiver | |
def foo(*args) | |
puts "call method: foo(#{args * ','}), args = #{args.count}" | |
end | |
def bar(*args) | |
puts "call method: bar(#{args * ','}), args = #{args.count}" | |
end | |
def baz(*args) | |
puts "call method: baz(#{args * ','}), args = #{args.count}" | |
end | |
end | |
dummy_receiver = DummyReceiver.new | |
opt = OpenStruct.new ARGV.getopts '', 'port:','host:','log:','daemon' | |
if opt.daemon | |
# (注意) デーモンにすると標準出力などが切り離されます | |
Prolog::Server.daemon(dummy_receiver, | |
port:opt.port, host:opt.host, log:opt.log) | |
else | |
server = Prolog::Server.new(dummy_receiver, | |
port:opt.port, host:opt.host, log:opt.log) | |
server.on_error {|e| puts e } | |
server.logger.level = :DEBUG | |
server.start.join | |
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
ruby(Req) :- ruby(Req, 53340). | |
ruby(Req, Port) :- ruby(Req, 'localhost', Port). | |
ruby(Req, Host, Port) :- create_client(Req, Host, Port). | |
create_client(Req, Host, Port) :- | |
setup_call_catcher_cleanup(tcp_socket(Socket), | |
tcp_connect(Socket, Host:Port), | |
exception(_), | |
tcp_close_socket(Socket)), | |
setup_call_cleanup(tcp_open_socket(Socket, In, Out), | |
request_to_server(Req, In, Out), | |
close_client_connection(In, Out)). | |
close_client_connection(In, Out) :- | |
close(In, [force(true)]), | |
close(Out, [force(true)]). | |
request_to_server(Req, _, Out) :- | |
write(Out, Req). |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment