зеркало из 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
|
|
|
|
# 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
|