class RedParse::ListInNode::StringNode

Constants

CHAROPT2NUM
CHARSETFLAG2NUM
DOWNSHIFT_STRING_TYPE
DQ_ESC
DQ_EVEN
DQ_ODD
ESCAPABLES
EVEN_BSS
EVEN_NUM_BSLASHES
LETTER2ENCODING
SQ_ESC
SQ_EVEN
SQ_ODD

Attributes

char[R]
modifiers[R]
type[R]

Public Class Methods

new(token) click to toggle source
Calls superclass method RedParse::ListInNode::Node::new
# File lib/redparse/node.rb, line 3531
      def initialize(token)
        if HerePlaceholderToken===token 
          str=token.string
          @char=token.quote
        else
          str=token
          @char=str.char
        end
        @modifiers=str.modifiers #if str.modifiers
        super( *with_string_data(str) )

        @open=token.open
        @close=token.close
        @offset=token.offset
        @bs_handler=str.bs_handler

        if /[\[{]/===@char
          @parses_like=split_into_words(str)
        end

        return

=begin
#this should have been taken care of by with_string_data        
        first=shift
        delete_if{|x| ''==x }
        unshift(first)

#escape translation now done later on
        map!{|strfrag|
          if String===strfrag
            str.translate_escapes strfrag
          else
            strfrag
          end
        }
=end
      end

Public Instance Methods

Regexp_new(src,opts,lang) click to toggle source
# File lib/redparse/node.rb, line 4015
def Regexp_new(src,opts,lang)
  src.encode!(LETTER2ENCODING[lang])
  Regexp.new(src,opts)
end
depthwalk(*args,&callback) click to toggle source
Calls superclass method RedParse::ListInNode::Node#depthwalk
# File lib/redparse/node.rb, line 3673
def depthwalk(*args,&callback)
  return @parses_like.depthwalk(*args,&callback) if defined? @parses_like
  super
end
endline=(endline) click to toggle source
Calls superclass method
# File lib/redparse/node.rb, line 3740
def endline= endline
  each{|frag| 
    frag.endline||=endline if frag.respond_to? :endline
  }

  super
end
escapable(open=@open,close=@close) click to toggle source
# File lib/redparse/node.rb, line 3632
def escapable open=@open,close=@close
  unless escapable=ESCAPABLES[open]
    maybe_crunch='\\#' if %r{\A["`/\{]\Z} === @char and open[1] != ?q and open != "'" #"
    #crunch (#) might need to be escaped too, depending on what @char is
    quotes=open[-1,1]
    quotes+=close unless quotes==close
    escapable=ESCAPABLES[open]=
      /[#{Regexp.quote(quotes)}#{maybe_crunch}]/
  end
  escapable             
end
image() click to toggle source
# File lib/redparse/node.rb, line 3666
def image; '(#@char)' end
initialize_ivars() click to toggle source
# File lib/redparse/node.rb, line 3570
def initialize_ivars
  @char||='"' 
  @open||='"' 
  @close||='"' 
  @bs_handler||=:dquote_esc_seq
  @modifiers||=nil
  if /[\[{]/===@char
    @parses_like||=split_into_words(str)
  end
end
old_cat_initialize(*tokens) click to toggle source
# File lib/redparse/node.rb, line 3598
      def old_cat_initialize(*tokens) #not needed anymore?
        token=tokens.shift
  
        tokens.size==1 or fail "string node must be made from a single string token"

        newdata=with_string_data(*tokens)

        case token
        when HereDocNode
          token.list_to_append=newdata
        when StringNode #do nothing
        else fail "non-string token class used to construct string node"
        end
        replace token.data

#        size%2==1 and last<<newdata.shift
        if size==1 and String===first and String===newdata.first
          first << newdata.shift
        end
        concat newdata
        
        @implicit_match=false
      end
parsetree(o) click to toggle source
# File lib/redparse/node.rb, line 3859
      def parsetree(o)
        if size==1
          val=translate_escapes first
          type=case @char
               when '"',"'"; :str
               when '/'
                 numopts=0
                 charset=0
                 RubyLexer::CharHandler.each_char(@modifiers){|ch| 
                   if ch==?o
                     type=:dregx_once
                   elsif numopt=CHAROPT2NUM[ch].nonzero?
                     numopts|=numopt
                   elsif set=CHARSETFLAG2NUM[ch].nonzero?
                     charset=set
                   else fail
                   end
                 }
                 val=Regexp.new val,numopts|charset
                 :lit
               when '[','{'
                 return @parses_like.parsetree(o)
=begin
                 double_chunks=val.split(/([^\\]|\A)(?:\s|\v)/,-1)
                 chunks=[]
                 (0..double_chunks.size).step(2){|i| 
                   chunks << double_chunks[i,2].to_s.gsub(/\\(\s|\v)/){$1}
                 }
#                 last=chunks
#                 last.last.empty? and last.pop if last and !last.empty?

                 words=chunks#.flatten
                 words.shift if words.first.empty? unless words.empty?
                 words.pop if words.last.empty? unless words.empty?
                 return [:zarray] if words.empty? 
                 return words.map{|word| [:str,word]}.unshift(:array)
=end
               when '`'; :xstr
               else raise "dunno what to do with #@char<StringToken"
               end
          result=[type,val]
        else
          saw_string=false
          vals=[]
          each{|elem| 
            case elem
            when String
              was_esc_nl= (elem=="\\\n") #ick
              elem=translate_escapes elem
              if saw_string
                vals.push [:str, elem] if !elem.empty? or was_esc_nl
              else
                saw_string=true
                vals.push elem
              end
            when NopNode
              vals.push [:evstr]
            when Node #,VarNameToken
              res=elem.parsetree(o)
              if res.first==:str and @char != '{'
                vals.push res
              elsif res.first==:dstr and @char != '{'
                vals.push [:str, res[1]], *res[2..-1]
              else
                vals.push [:evstr, res]
              end
            else fail "#{elem.class} not expected here"
            end
          }
          while vals.size>1 and vals[1].first==:str
            vals[0]+=vals.delete_at(1).last
          end
          #vals.pop if vals.last==[:str, ""]

          type=case @char
               when '"'; :dstr
               when '/'
                 type=:dregx
                 numopts=charset=0
                 RubyLexer::CharHandler.each_char(@modifiers){|ch| 
                   if ch==?o
                     type=:dregx_once
                   elsif numopt=CHAROPT2NUM[ch].nonzero?
                     numopts|=numopt
                   elsif set=CHARSETFLAG2NUM[ch].nonzero?
                     charset=set
                   end
                 }
                 regex_options= numopts|charset unless numopts|charset==0
                 val=/#{val}/
                 type
               when '{'
                 return @parses_like.parsetree(o)
=begin
                 vals[0]=vals[0].sub(/\A(\s|\v)+/,'') if /\A(\s|\v)/===vals.first
                 merged=Array.new(vals)
                 result=[]
                 merged.each{|i|
                   if String===i
                     next if /\A(?:\s|\v)+\Z/===i 
                     double_chunks=i.split(/([^\\]|\A)(?:\s|\v)/,-1)
                     chunks=[]
                     (0..double_chunks.size).step(2){|ii| 
                       chunks << double_chunks[ii,2].to_s.gsub(/\\(\s|\v)/){$1}
                     }
                     words=chunks.map{|word| [:str,word]}
                     if !result.empty? and frag=words.shift and !frag.last.empty?
                       result[-1]+=frag
                     end
                     result.push( *words )
                   else
                     result.push [:str,""] if result.empty?
                     if i.first==:evstr and i.size>1 and i.last.first==:str
                       if String===result.last[-1]
                         result.last[-1]+=i.last.last
                       else
                         result.last[0]=:dstr
                         result.last.push(i.last)
                       end
                     else
                       result.last[0]=:dstr
                       result.last.push(i)
                     end
                   end
                 }
                 return result.unshift(:array)
=end
               when '`'; :dxstr
               else raise "dunno what to do with #@char<StringToken"
               end

          if vals.size==1
            if :dregx==type or :dregx_once==type
              lang=@modifiers.tr_s("^nesuNESU","")
              lang=lang[-1,1] unless lang.empty?
              lang.downcase!
              regex_options=nil
              vals=[Regexp_new( vals.first,numopts,lang )]
            end
            type=DOWNSHIFT_STRING_TYPE[type]
          end
          result= vals.unshift(type)
          result.push regex_options if regex_options
        end
        result=[:match, result] if defined? @implicit_match and @implicit_match
        return result
      end
special_conditions!() click to toggle source
# File lib/redparse/node.rb, line 3678
def special_conditions!
  @implicit_match= @char=="/"
end
split_into_words(strtok) click to toggle source
# File lib/redparse/node.rb, line 3772
      def split_into_words strtok
        @offset=strtok.offset
        return unless /[{\[]/===@char
        result=ArrayLiteralNode[]
        result << StringNode['',{:@char=>'"',:@open=>@open,:@close=>@close,:@bs_handler=>@bs_handler}]
        proxy=dup
        proxy[0]=proxy[0][/\A(?:\s|\v)+(.*)\Z/m,1] if /\A(?:\s|\v)/===proxy[0]
#        first[/\A(?:\s|\v)+/]='' if /\A(?:\s|\v)/===first #uh-oh, changes first
        proxy.each{|x|
          if String===x
#            x=x[/\A(?:\s|\v)+(.*)\Z/,1] if /\A[\s\v]/===x
if false
            #split on ws preceded by an even # of backslashes or a non-backslash, non-ws char
            #this ignores backslashed ws
            #save the thing that preceded the ws, it goes back on the token preceding split
            double_chunks=x.split(/( #{EVEN_BSS} | (?:[^\\\s\v]|\A|#{EVEN_BSS}\\[\s\v]) )(?:\s|\v)+/xo,-1)
            chunks=[]
            (0..double_chunks.size).step(2){|i| 
              chunks << #strtok.translate_escapes \
                double_chunks[i,2].to_s #.gsub(/\\([\s\v\\])/){$1}
            }
else
            #split on ws, then ignore ws preceded by an odd number of esc's
            #esc is \ in squote word array, \ or \c or \C- or \M- in dquote
            chunks_and_ws=x.split(/([\s\v]+)/,-1)
            start=chunks_and_ws.size; start-=1 if start&1==1
            chunks=[]
            i=start+2; 
            while (i-=2)>=0 
              ch=chunks_and_ws[i]||""
              if i<chunks_and_ws.size and ch.match(@char=="[" ? /#{SQ_ODD}\Z/omx : /#{DQ_ODD}\Z/omx)
                ch<< chunks_and_ws[i+1][0,1]
                if chunks_and_ws[i+1].size==1
                  ch<< chunks.shift
                end
              end
              chunks.unshift ch
            end
end

            chunk1= chunks.shift          
            if chunk1.empty?
              #do nothing more
            elsif String===result.last.last
              result.last.last << chunk1
            else
              result.last.push chunk1
            end
#            result.last.last.empty? and result.last.pop
            result.concat chunks.map{|chunk| 
              StringNode[chunk,{:@char=>'"',:@open=>@open,:@close=>@close,:@bs_handler=>@bs_handler}]
            }
          else
            #result.last << x
            unless String===result.last.last
              result.push StringNode["",{:@char=>'"',:@open=>@open,:@close=>@close,:@bs_handler=>@bs_handler}]
            end
            result.last.push x
#            result.push StringNode["",x,{:@char=>'"',:@open=>@open,:@close=>@close,:@bs_handler=>@bs_handler}]
          end
        } 
        result.shift if StringNode&-{:size=>1, :first=>''}===result.first
        result.pop if StringNode&-{:size=>1, :first=>''}===result.last

        return result
      end
to_lisp() click to toggle source
# File lib/redparse/node.rb, line 3748
def to_lisp
  return %{"#{first}"} if size<=1 and @char=='"'
  huh
end
translate_escapes(str) click to toggle source
# File lib/redparse/node.rb, line 3581
def translate_escapes(str)
  rl=RubyLexer.new("(string escape translation hack...)",'')
  result=str.dup
  seq=result.to_sequence
  rl.instance_eval{@file=seq}
  i=0
  #ugly ugly ugly... all so I can call @bs_handler
  while i<result.size and bs_at=result.index(/\\./m,i)
    seq.pos=$~.end(0)-1
    ch=rl.send(@bs_handler,"\\",@open[-1,1],@close)
    result[bs_at...seq.pos]=ch
    i=bs_at+ch.size
  end

  return  result
end
unparse(o=default_unparse_options) click to toggle source
# File lib/redparse/node.rb, line 3624
def unparse o=default_unparse_options
  o[:linenum]+=@open.count("\n")
  result=[@open,unparse_interior(o),@close,@modifiers].join
  o[:linenum]+=@close.count("\n")
  result<<" " if /\r\z/===result
  return result
end
unparse_interior(o,open=@open,close=@close,escape=nil) click to toggle source
# File lib/redparse/node.rb, line 3644
def unparse_interior o,open=@open,close=@close,escape=nil
  escapable=escapable(open,close)
  result=map{|substr|
    case substr
    when String

      #hack: this is needed for here documents only, because their
      #delimiter is changing.
      substr.gsub!(escape){|ch| ch[0...-1]+"\\"+ch[-1,1]} if escape

      o[:linenum]+=substr.count("\n") if o[:linenum]

      substr
    when NopNode
      '#{}'
    else
      ['#{',substr.unparse(o),'}']
    end
  }
  result
end
walk(*args,&callback) click to toggle source
Calls superclass method RedParse::ListInNode::Node#walk
# File lib/redparse/node.rb, line 3668
def walk(*args,&callback)
  @parses_like.walk(*args,&callback) if defined? @parses_like
  super
end
with_string_data(token) click to toggle source
# File lib/redparse/node.rb, line 3685
      def with_string_data(token)
#        token=tokens.first

#        data=tokens.inject([]){|sum,token|
#          data=elems=token.string.elems
          data= #elems=
            case token
            when StringToken; token.elems
            when HerePlaceholderToken; token.string.elems
            else raise "unknown string token type: #{token}:#{token.class}"
            end
#          sum.size%2==1 and sum.last<<elems.shift
#          sum+elems
#        }
#        endline=@endline
        1.step(data.length-1,2){|i|
          tokens=data[i].ident.dup

          #replace trailing } with EoiToken
          (tokens.size-1).downto(0){|j| 
             tok=tokens[j]
             break(tokens[j..-1]=[EoiToken.new('',nil,tokens[j].offset)]) if tok.ident=='}' 
          }
          #remove leading {
          tokens.each_with_index{|tok,j| break(tokens.delete_at j) if tok.ident=='{' }

          if tokens.size==1 and VarNameToken===tokens.first
            data[i]=VarNode.new tokens.first
            data[i].startline=data[i].endline=token.endline
            data[i].offset=tokens.first.offset
          else
            #parse the token list in the string inclusion
            parser=Thread.current[:$RedParse_parser]
            klass=parser.class
            data[i]=klass.new(tokens, "(string inclusion)",1,[],:rubyversion=>parser.rubyversion,:cache_mode=>:none).parse
          end
        } #if data
#        was_nul_header= (String===data.first and data.first.empty?) #and o[:quirks]
        last=data.size-1

        #remove (most) empty string fragments
        last.downto(1){|frag_i| 
          frag=data[frag_i]
          String===frag or next
          next unless frag.empty? 
          next if frag_i==last #and o[:quirks]
          next if data[frag_i-1].endline != data[frag_i+1].endline #and o[:quirks]
                  #prev and next inclusions on different lines
          data.slice!(frag_i)
        }
#        data.unshift '' if was_nul_header

        return data
      end