1#
2#   irb/slex.rb - simple lex analyzer
3#   	$Release Version: 0.9.6$
4#   	$Revision: 38545 $
5#   	by Keiju ISHITSUKA(keiju@ruby-lang.org)
6#
7# --
8#
9#
10#
11
12require "e2mmap"
13require "irb/notifier"
14
15# :stopdoc:
16module IRB
17  class SLex
18    @RCS_ID='-$Id: slex.rb 38545 2012-12-21 17:36:14Z zzak $-'
19
20    extend Exception2MessageMapper
21    def_exception :ErrNodeNothing, "node nothing"
22    def_exception :ErrNodeAlreadyExists, "node already exists"
23
24    DOUT = Notifier::def_notifier("SLex::")
25    D_WARN = DOUT::def_notifier(1, "Warn: ")
26    D_DEBUG = DOUT::def_notifier(2, "Debug: ")
27    D_DETAIL = DOUT::def_notifier(4, "Detail: ")
28
29    DOUT.level = Notifier::D_NOMSG
30
31    def initialize
32      @head = Node.new("")
33    end
34
35    def def_rule(token, preproc = nil, postproc = nil, &block)
36      D_DETAIL.pp token
37
38      postproc = block if block_given?
39      create(token, preproc, postproc)
40    end
41
42    def def_rules(*tokens, &block)
43      if block_given?
44	p = block
45      end
46      for token in tokens
47	def_rule(token, nil, p)
48      end
49    end
50
51    def preproc(token, proc)
52      node = search(token)
53      node.preproc=proc
54    end
55
56    #$BMW%A%'%C%/(B?
57    def postproc(token)
58      node = search(token, proc)
59      node.postproc=proc
60    end
61
62    def search(token)
63      @head.search(token.split(//))
64    end
65
66    def create(token, preproc = nil, postproc = nil)
67      @head.create_subnode(token.split(//), preproc, postproc)
68    end
69
70    def match(token)
71      case token
72      when Array
73      when String
74	return match(token.split(//))
75      else
76	return @head.match_io(token)
77      end
78      ret = @head.match(token)
79      D_DETAIL.exec_if{D_DETAIL.printf "match end: %s:%s\n", ret, token.inspect}
80      ret
81    end
82
83    def inspect
84      format("<SLex: @head = %s>", @head.inspect)
85    end
86
87    #----------------------------------------------------------------------
88    #
89    #   class Node -
90    #
91    #----------------------------------------------------------------------
92    class Node
93      # if postproc is nil, this node is an abstract node.
94      # if postproc is non-nil, this node is a real node.
95      def initialize(preproc = nil, postproc = nil)
96	@Tree = {}
97	@preproc = preproc
98	@postproc = postproc
99      end
100
101      attr_accessor :preproc
102      attr_accessor :postproc
103
104      def search(chrs, opt = nil)
105	return self if chrs.empty?
106	ch = chrs.shift
107	if node = @Tree[ch]
108	  node.search(chrs, opt)
109	else
110	  if opt
111	    chrs.unshift ch
112	    self.create_subnode(chrs)
113	  else
114	    SLex.fail ErrNodeNothing
115	  end
116	end
117      end
118
119      def create_subnode(chrs, preproc = nil, postproc = nil)
120	if chrs.empty?
121	  if @postproc
122	    D_DETAIL.pp node
123	    SLex.fail ErrNodeAlreadyExists
124	  else
125	    D_DEBUG.puts "change abstract node to real node."
126	    @preproc = preproc
127	    @postproc = postproc
128	  end
129	  return self
130	end
131
132	ch = chrs.shift
133	if node = @Tree[ch]
134	  if chrs.empty?
135	    if node.postproc
136	      DebugLogger.pp node
137	      DebugLogger.pp self
138	      DebugLogger.pp ch
139	      DebugLogger.pp chrs
140	      SLex.fail ErrNodeAlreadyExists
141	    else
142	      D_WARN.puts "change abstract node to real node"
143	      node.preproc = preproc
144	      node.postproc = postproc
145	    end
146	  else
147	    node.create_subnode(chrs, preproc, postproc)
148	  end
149	else
150	  if chrs.empty?
151	    node = Node.new(preproc, postproc)
152	  else
153	    node = Node.new
154	    node.create_subnode(chrs, preproc, postproc)
155	  end
156	  @Tree[ch] = node
157	end
158	node
159      end
160
161      #
162      # chrs: String
163      #       character array
164      #       io must have getc()/ungetc(); and ungetc() must be
165      #       able to be called arbitrary number of times.
166      #
167      def match(chrs, op = "")
168	D_DETAIL.print "match>: ", chrs, "op:", op, "\n"
169	if chrs.empty?
170	  if @preproc.nil? || @preproc.call(op, chrs)
171	    DOUT.printf(D_DETAIL, "op1: %s\n", op)
172	    @postproc.call(op, chrs)
173	  else
174	    nil
175	  end
176	else
177	  ch = chrs.shift
178	  if node = @Tree[ch]
179	    if ret = node.match(chrs, op+ch)
180	      return ret
181	    else
182	      chrs.unshift ch
183	      if @postproc and @preproc.nil? || @preproc.call(op, chrs)
184		DOUT.printf(D_DETAIL, "op2: %s\n", op.inspect)
185		ret = @postproc.call(op, chrs)
186		return ret
187	      else
188		return nil
189	      end
190	    end
191	  else
192	    chrs.unshift ch
193	    if @postproc and @preproc.nil? || @preproc.call(op, chrs)
194	      DOUT.printf(D_DETAIL, "op3: %s\n", op)
195	      @postproc.call(op, chrs)
196	      return ""
197	    else
198	      return nil
199	    end
200	  end
201	end
202      end
203
204      def match_io(io, op = "")
205	if op == ""
206	  ch = io.getc
207	  if ch == nil
208	    return nil
209	  end
210	else
211	  ch = io.getc_of_rests
212	end
213	if ch.nil?
214	  if @preproc.nil? || @preproc.call(op, io)
215	    D_DETAIL.printf("op1: %s\n", op)
216	    @postproc.call(op, io)
217	  else
218	    nil
219	  end
220	else
221	  if node = @Tree[ch]
222	    if ret = node.match_io(io, op+ch)
223	      ret
224	    else
225	      io.ungetc ch
226	      if @postproc and @preproc.nil? || @preproc.call(op, io)
227		DOUT.exec_if{D_DETAIL.printf "op2: %s\n", op.inspect}
228		@postproc.call(op, io)
229	      else
230		nil
231	      end
232	    end
233	  else
234	    io.ungetc ch
235	    if @postproc and @preproc.nil? || @preproc.call(op, io)
236	      D_DETAIL.printf("op3: %s\n", op)
237	      @postproc.call(op, io)
238	    else
239	      nil
240	    end
241	  end
242	end
243      end
244    end
245  end
246end
247# :startdoc:
248
249if $0 == __FILE__
250  #    Tracer.on
251  case $1
252  when "1"
253    tr = SLex.new
254    print "0: ", tr.inspect, "\n"
255    tr.def_rule("=") {print "=\n"}
256    print "1: ", tr.inspect, "\n"
257    tr.def_rule("==") {print "==\n"}
258    print "2: ", tr.inspect, "\n"
259
260    print "case 1:\n"
261    print tr.match("="), "\n"
262    print "case 2:\n"
263    print tr.match("=="), "\n"
264    print "case 3:\n"
265    print tr.match("=>"), "\n"
266
267  when "2"
268    tr = SLex.new
269    print "0: ", tr.inspect, "\n"
270    tr.def_rule("=") {print "=\n"}
271    print "1: ", tr.inspect, "\n"
272    tr.def_rule("==", proc{false}) {print "==\n"}
273    print "2: ", tr.inspect, "\n"
274
275    print "case 1:\n"
276    print tr.match("="), "\n"
277    print "case 2:\n"
278    print tr.match("=="), "\n"
279    print "case 3:\n"
280    print tr.match("=>"), "\n"
281  end
282  exit
283end
284
285