зеркало из https://github.com/github/ruby.git
283 строки
6.8 KiB
Ruby
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
|
||
|
||
#$BMW%A%'%C%/(B?
|
||
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
|