mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	[#15206] [Fix GH-1976] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@65505 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
		
			
				
	
	
		
			282 lines
		
	
	
	
		
			6.8 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			282 lines
		
	
	
	
		
			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_relative "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
 |