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