class Racc::GrammarFileScanner
def atom_symbol(token)
def atom_symbol(token) if token == 'end' symbol = :END @in_conv_blk = false @in_rule_blk = false else if @line_head and not @in_conv_blk and not @in_rule_blk symbol = ReservedWord[token] || :SYMBOL else symbol = :SYMBOL end case symbol when :RULE then @in_rule_blk = true when :CONV then @in_conv_blk = true end end @line_head = false symbol end
def get_quoted_re(left)
def get_quoted_re(left) term = Regexp.quote(LEFT_TO_RIGHT[left] || left) CACHE[left] ||= /\A[^#{term}\\]*(?:\\.[^\\#{term}]*)*#{term}/ end
def initialize(str, filename = '-')
def initialize(str, filename = '-') @lines = str.b.split(/\n|\r\n|\r/) @filename = filename @lineno = -1 @line_head = true @in_rule_blk = false @in_conv_blk = false @in_block = nil @epilogue = '' @debug = false next_line end
def lineno
def lineno @lineno + 1 end
def literal_head?(pre, post)
def literal_head?(pre, post) (!pre || /[a-zA-Z_0-9]/n !~ pre[-1,1]) && !post.empty? && /\A[\s\=]/n !~ post end
def next_line
def next_line @lineno += 1 @line = @lines[@lineno] if not @line or /\A----/ =~ @line @epilogue = @lines.join("\n") @lines.clear @line = nil if @in_block @lineno -= 1 scan_error! sprintf('unterminated %s', @in_block) end false else @line.sub!(/(?:\n|\r\n|\r)\z/, '') @line_head = true true end end
def read(len)
def read(len) s = @line[0, len] @line = @line[len .. -1] s end
def reads(re)
def reads(re) m = re.match(@line) or return nil @line = m.post_match m[0] end
def scan_action
def scan_action buf = String.new nest = 1 pre = nil @in_block = 'action' begin pre = nil if s = reads(/\A\s+/) # does not set 'pre' buf << s end until @line.empty? if s = reads(/\A[^'"`{}%#\/\$]+/) buf << (pre = s) next end case ch = read(1) when '{' nest += 1 buf << (pre = ch) when '}' nest -= 1 if nest == 0 @in_block = nil buf.sub!(/[ \t\f]+\z/, '') return buf end buf << (pre = ch) when '#' # comment buf << ch << @line break when "'", '"', '`' buf << (pre = scan_quoted(ch)) when '%' if literal_head? pre, @line # % string, regexp, array buf << ch case ch = read(1) when /[qQx]/n buf << ch << (pre = scan_quoted(read(1), '%string')) when /wW/n buf << ch << (pre = scan_quoted(read(1), '%array')) when /s/n buf << ch << (pre = scan_quoted(read(1), '%symbol')) when /r/n buf << ch << (pre = scan_quoted(read(1), '%regexp')) when /[a-zA-Z0-9= ]/n # does not include "_" scan_error! "unknown type of % literal '%#{ch}'" else buf << (pre = scan_quoted(ch, '%string')) end else # operator buf << '||op->' if $raccs_print_type buf << (pre = ch) end when '/' if literal_head? pre, @line # regexp buf << (pre = scan_quoted(ch, 'regexp')) else # operator buf << '||op->' if $raccs_print_type buf << (pre = ch) end when '$' # gvar buf << ch << (pre = read(1)) else raise 'racc: fatal: must not happen' end end buf << "\n" end while next_line() raise 'racc: fatal: scan finished before parser finished' end
def scan_error!(msg)
def scan_error!(msg) raise CompileError, "#{lineno()}: #{msg}" end
def scan_quoted(left, tag = 'string')
def scan_quoted(left, tag = 'string') buf = left.dup buf = "||#{tag}->" + buf if $raccs_print_type re = get_quoted_re(left) sv, @in_block = @in_block, tag begin if s = reads(re) buf << s break else buf << @line end end while next_line() @in_block = sv buf << "<-#{tag}||" if $raccs_print_type buf end
def skip_comment
def skip_comment @in_block = 'comment' until m = /\*\//.match(@line) next_line end @line = m.post_match @in_block = nil end
def yylex(&block)
def yylex(&block) unless @debug yylex0(&block) else yylex0 do |sym, tok| $stderr.printf "%7d %-10s %s\n", lineno(), sym.inspect, tok.inspect yield [sym, tok] end end end
def yylex0
def yylex0 begin until @line.empty? @line.sub!(/\A\s+/, '') if /\A\#/ =~ @line break elsif /\A\/\*/ =~ @line skip_comment elsif s = reads(/\A[a-zA-Z_]\w*/) yield [atom_symbol(s), s.intern] elsif s = reads(/\A\d+/) yield [:DIGIT, s.to_i] elsif ch = reads(/\A./) case ch when '"', "'" yield [:STRING, eval(scan_quoted(ch))] when '{' lineno = lineno() yield [:ACTION, SourceText.new(scan_action(), @filename, lineno)] else if ch == '|' @line_head = false end yield [ch, ch] end else end end end while next_line() yield nil end