ruby/lib/irb/slex.rb

283 строки
6.8 KiB
Ruby

# frozen_string_literal: false
#
# irb/slex.rb - simple lex analyzer
# $Release Version: 0.9.6$
# $Revision$
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
# --
#
#
#
require "e2mmap"
require "irb/notifier"
# :stopdoc:
module IRB
class SLex
extend Exception2MessageMapper
def_exception :ErrNodeNothing, "node nothing"
def_exception :ErrNodeAlreadyExists, "node already exists"
DOUT = Notifier::def_notifier("SLex::")
D_WARN = DOUT::def_notifier(1, "Warn: ")
D_DEBUG = DOUT::def_notifier(2, "Debug: ")
D_DETAIL = DOUT::def_notifier(4, "Detail: ")
DOUT.level = Notifier::D_NOMSG
def initialize
@head = Node.new("")
end
def def_rule(token, preproc = nil, postproc = nil, &block)
D_DETAIL.pp token
postproc = block if block_given?
create(token, preproc, postproc)
end
def def_rules(*tokens, &block)
if block_given?
p = block
end
for token in tokens
def_rule(token, nil, p)
end
end
def preproc(token, proc)
node = search(token)
node.preproc=proc
end
# need a check?
def postproc(token)
node = search(token, proc)
node.postproc=proc
end
def search(token)
@head.search(token.split(//))
end
def create(token, preproc = nil, postproc = nil)
@head.create_subnode(token.split(//), preproc, postproc)
end
def match(token)
case token
when Array
when String
return match(token.split(//))
else
return @head.match_io(token)
end
ret = @head.match(token)
D_DETAIL.exec_if{D_DETAIL.printf "match end: %s:%s\n", ret, token.inspect}
ret
end
def inspect
format("<SLex: @head = %s>", @head.inspect)
end
#----------------------------------------------------------------------
#
# class Node -
#
#----------------------------------------------------------------------
class Node
# if postproc is nil, this node is an abstract node.
# if postproc is non-nil, this node is a real node.
def initialize(preproc = nil, postproc = nil)
@Tree = {}
@preproc = preproc
@postproc = postproc
end
attr_accessor :preproc
attr_accessor :postproc
def search(chrs, opt = nil)
return self if chrs.empty?
ch = chrs.shift
if node = @Tree[ch]
node.search(chrs, opt)
else
if opt
chrs.unshift ch
self.create_subnode(chrs)
else
SLex.fail ErrNodeNothing
end
end
end
def create_subnode(chrs, preproc = nil, postproc = nil)
if chrs.empty?
if @postproc
D_DETAIL.pp node
SLex.fail ErrNodeAlreadyExists
else
D_DEBUG.puts "change abstract node to real node."
@preproc = preproc
@postproc = postproc
end
return self
end
ch = chrs.shift
if node = @Tree[ch]
if chrs.empty?
if node.postproc
DebugLogger.pp node
DebugLogger.pp self
DebugLogger.pp ch
DebugLogger.pp chrs
SLex.fail ErrNodeAlreadyExists
else
D_WARN.puts "change abstract node to real node"
node.preproc = preproc
node.postproc = postproc
end
else
node.create_subnode(chrs, preproc, postproc)
end
else
if chrs.empty?
node = Node.new(preproc, postproc)
else
node = Node.new
node.create_subnode(chrs, preproc, postproc)
end
@Tree[ch] = node
end
node
end
#
# chrs: String
# character array
# io must have getc()/ungetc(); and ungetc() must be
# able to be called arbitrary number of times.
#
def match(chrs, op = "")
D_DETAIL.print "match>: ", chrs, "op:", op, "\n"
if chrs.empty?
if @preproc.nil? || @preproc.call(op, chrs)
DOUT.printf(D_DETAIL, "op1: %s\n", op)
@postproc.call(op, chrs)
else
nil
end
else
ch = chrs.shift
if node = @Tree[ch]
if ret = node.match(chrs, op+ch)
return ret
else
chrs.unshift ch
if @postproc and @preproc.nil? || @preproc.call(op, chrs)
DOUT.printf(D_DETAIL, "op2: %s\n", op.inspect)
ret = @postproc.call(op, chrs)
return ret
else
return nil
end
end
else
chrs.unshift ch
if @postproc and @preproc.nil? || @preproc.call(op, chrs)
DOUT.printf(D_DETAIL, "op3: %s\n", op)
@postproc.call(op, chrs)
return ""
else
return nil
end
end
end
end
def match_io(io, op = "")
if op == ""
ch = io.getc
if ch == nil
return nil
end
else
ch = io.getc_of_rests
end
if ch.nil?
if @preproc.nil? || @preproc.call(op, io)
D_DETAIL.printf("op1: %s\n", op)
@postproc.call(op, io)
else
nil
end
else
if node = @Tree[ch]
if ret = node.match_io(io, op+ch)
ret
else
io.ungetc ch
if @postproc and @preproc.nil? || @preproc.call(op, io)
DOUT.exec_if{D_DETAIL.printf "op2: %s\n", op.inspect}
@postproc.call(op, io)
else
nil
end
end
else
io.ungetc ch
if @postproc and @preproc.nil? || @preproc.call(op, io)
D_DETAIL.printf("op3: %s\n", op)
@postproc.call(op, io)
else
nil
end
end
end
end
end
end
end
# :startdoc:
if $0 == __FILE__
case $1
when "1"
tr = SLex.new
print "0: ", tr.inspect, "\n"
tr.def_rule("=") {print "=\n"}
print "1: ", tr.inspect, "\n"
tr.def_rule("==") {print "==\n"}
print "2: ", tr.inspect, "\n"
print "case 1:\n"
print tr.match("="), "\n"
print "case 2:\n"
print tr.match("=="), "\n"
print "case 3:\n"
print tr.match("=>"), "\n"
when "2"
tr = SLex.new
print "0: ", tr.inspect, "\n"
tr.def_rule("=") {print "=\n"}
print "1: ", tr.inspect, "\n"
tr.def_rule("==", proc{false}) {print "==\n"}
print "2: ", tr.inspect, "\n"
print "case 1:\n"
print tr.match("="), "\n"
print "case 2:\n"
print tr.match("=="), "\n"
print "case 3:\n"
print tr.match("=>"), "\n"
end
exit
end