Skip to content

Instantly share code, notes, and snippets.

@lnznt
Last active August 29, 2015 14:13
Show Gist options
  • Save lnznt/29762008fa958bf8fb75 to your computer and use it in GitHub Desktop.
Save lnznt/29762008fa958bf8fb75 to your computer and use it in GitHub Desktop.
Ruby: Racc を利用した Prolog ミニパーサ ref: http://qiita.com/lnznt/items/3ab55937136b06cf44f8
require 'pp'
require 'socket'
host, port = 'localhost', 3333
insert = -> clause {
TCPSocket.open(host, port) do |s|
s.puts "assert(#{clause})."
s.gets
end
}
pp insert.("person(socrates)")
pp insert.("mortal(X) :- person(X)")
require 'pp'
require 'socket'
require 'prolog_parser'
host, port = 'localhost', 3333
inquire = -> query {
TCPSocket.open(host, port) do |s|
s.puts "#{query}."
s.gets
end
}
res = inquire.("mortal(Who)")
pp Prolog::Parser.new.parse(res)
$ racc -g -o prolog_parser.rb prolog_parser.ry
[assert(person(socrates))] # クライアントその1 で表示
[assert((mortal(_G61):-person(_G61)))] # 〃
[mortal(socrates)] # クライアントその2 で表示
(mortal(_G61):-person(_G61))
to_prolog = -> x, w='[]' {
x.kind_of?(Hash) ? "#{x.first[0]}#{to_prolog.(x.first[1],'()')}" :
x.kind_of?(Array) ? "#{w[0]}#{(x.map(&to_prolog) * ',')}#{w[-1]}" :
x.kind_of?(String) ? %("#{x}") :
"#{x}"
}
puts to_prolog.(pred: [:a,:b,10]) # pred(a,b,10)
puts to_prolog.([:a,:b,10]) # [a,b,10]
puts to_prolog.([]) # []
puts to_prolog.("abc") # "abc"
puts to_prolog.(1) # 1
puts to_prolog.(-10) # -10
puts to_prolog.(1.1) # 1.1
puts to_prolog.(-10.5) # -10.5
puts to_prolog.(:a) # a
puts to_prolog.(:"'ABC'") # 'ABC'
puts to_prolog.(:X) # X
puts to_prolog.(:_abc) # _abc
puts to_prolog.(:_) # _
s_to_l = -> s { s.unpack("C*") }
l_to_s = -> l { l.pack("C*") }
p s_to_l.("abc") #=> [97, 98, 99]
p l_to_s.([97, 98, 99]) #=> "abc"
is_atom = -> x { !!(x.kind_of?(Symbol) && x.to_s =~ /\A[[:lower:]']/) }
is_var = -> x { !!(x.kind_of?(Symbol) && x.to_s =~ /\A[[:upper:]_]/) }
p is_atom.(:a) #=> true
p is_atom.(:"'ABC'") #=> true
p is_var.(:X) #=> true
p is_var.(:_abc) #=> true
p is_var.(:_) #=> true
puts "---"
p is_var.(:a) #=> false
p is_var.(:"'ABC'") #=> false
p is_atom.(:X) #=> false
p is_atom.(:_abc) #=> false
p is_atom.(:_) #=> false
$ sudo apt-get install swi-prolog
$ swipl -l server.pro -g 'create_server(3333).' # 起動したままになる
person(socrates). %% ファクト
mortal(X) :- person(X). %% ルール
?- mortal(Who). %% 問い合わせ
Who = socrates ->;
no
$ ruby client1.rb
"[assert(person(socrates))]"
"[assert((mortal(_G61):-person(_G61)))]"
$ ruby client2.rb
[{:mortal=>[:socrates]}]
class Prolog::Parser
rule
response :
| term
term : pred
| list
| number
| atom
| var
terms : term { result = [val[0]] }
| terms ',' term { result << val[2] }
pred : atom '(' terms ')' { result = { val[0] => val[2] } }
list : '[' ']' { result = [] }
| '[' terms ']' { result = val[1] }
| string
string : DQWORD { result = val[0][1..-2] }
number : INTEGER { result = val[0].to_i }
| FLOAT { result = val[0].to_f }
atom : LWORD { result = val[0].to_sym }
| SQWORD { result = val[0].to_sym }
var : UWORD { result = val[0].to_sym }
| USWORD { result = val[0].to_sym }
end
---- header
require 'pp'
require 'strscan'
---- inner
attr_accessor :yydebug
attr_accessor :verbose
def parse(str)
s = StringScanner.new str
@q = []
until s.eos?
s.scan(/[-+]?(0|[1-9]\d*)\.\d+/) ? (@q << [:FLOAT, s.matched]) :
s.scan(/[-+]?(0|[1-9]\d*)/) ? (@q << [:INTEGER, s.matched]) :
s.scan(/[[:lower:]]\w*/) ? (@q << [:LWORD, s.matched]) :
s.scan(/[[:upper:]]\w*/) ? (@q << [:UWORD, s.matched]) :
s.scan(/'[^']+'/) ? (@q << [:SQWORD, s.matched]) :
s.scan(/"[^"]+"/) ? (@q << [:DQWORD, s.matched]) :
s.scan(/_\w*/) ? (@q << [:USWORD, s.matched]) :
s.scan(/./) ? (@q << [s.matched, s.matched]) :
(raise "scanner error")
end
pp @q if verbose
do_parse
end
def next_token
@q.shift
end
---- footer
if __FILE__ == $0
require 'pp'
require 'optparse'
require 'ostruct'
opt = OpenStruct.new ARGV.getopts 'vd'
str = ARGV.shift or (raise "no arguments")
parser = Prolog::Parser.new.tap {|p| p.yydebug = opt.d; p.verbose = opt.v }
begin
pp parser.parse str
rescue Racc::ParseError => e
$stderr.puts e
end
end
create_server(Port) :-
tcp_socket(Socket),
tcp_bind(Socket, Port),
tcp_listen(Socket, 5),
tcp_open_socket(Socket, AcceptFd, _),
dispatch(AcceptFd).
dispatch(AcceptFd) :-
tcp_accept(AcceptFd, Socket, Peer),
thread_create(process_client(Socket, Peer), _,
[ detached(true)
]),
dispatch(AcceptFd).
process_client(Socket, _) :-
setup_call_cleanup(tcp_open_socket(Socket, In, Out),
handle_service(In, Out),
close_connection(In, Out)).
close_connection(In, Out) :-
close(In, [force(true)]),
close(Out, [force(true)]).
handle_service(In, Out) :-
read(In, Chars),
findall(Chars, Chars, L),
write(Out, L),
writeln(L).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment