=begin copyright rubylexer - a ruby lexer written in ruby Copyright (C) 2004,2005 Caleb Clausen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =end class RubyLexer #------------------------- class Token attr_accessor :ident alias to_s ident attr_accessor :offset #file offset of start of this token def initialize(ident,offset=nil) @ident=ident @offset=offset end def error; end def has_no_block?; false end end #------------------------- class WToken< Token def ===(pattern) assert @ident pattern===@ident end end #------------------------- class KeywordToken < WToken #also some operators #----------------------------------- def set_callsite! #not needed @callsite=true end #----------------------------------- def callsite? #not used @callsite ||= nil end #----------------------------------- def set_infix! @infix=true end #----------------------------------- def infix? @infix ||= nil end def prefix?; !infix? end #----------------------------------- def has_end! assert self===RubyLexer::BEGINWORDS @has_end=true end #----------------------------------- def has_end? self===RubyLexer::BEGINWORDS and @has_end||=nil end end #------------------------- class OperatorToken < WToken attr_accessor :unary alias prefix? unary def infix?; !prefix? end end #------------------------- module TokenPat @@TokenPats={} def token_pat #used in various case statements... result=self.dup @@TokenPats[self] ||= (class <"%w[", '{'=>'%W{' } SUFFIXERS={ '['=>"]", '{'=>'}' } def to_s(transname=:transform) assert @char[/[\[{"`\/]/] #" #on output, all single-quoted strings become double-quoted assert(@elems.length==1) if @char=='[' result=(PREFIXERS[@char] or @char).dup starter=result[-1,1] ender=(SUFFIXERS[@char] or @char).dup 0.step(@elems.length-1,2) { |i| strfrag=@elems[i].dup result << send(transname,strfrag,starter,ender) if e=@elems[i+1] assert(e.kind_of?(RubyCode)) result << '#' + e.to_s end } result << ender modifiers and result << modifiers #regex only return result end def to_term result=[] 0.step(@elems.length-1,2) { |i| result << ConstTerm.new(@elems[i].dup) if e=@elems[i+1] assert(e.kind_of?(RubyCode)) result << (RubyTerm.new e) end } return result end def append(glob) assert @elems.last.kind_of?(String) case glob when String,Integer then append_str! glob when RubyCode then append_code! glob else raise "bad string contents: #{glob}, a #{glob.class}" end assert @elems.last.kind_of?(String) end def append_token(strtok) assert @elems.last.kind_of?(String) assert strtok.elems.last.kind_of?(String) assert strtok.elems.first.kind_of?(String) @elems.last << strtok.elems.shift first=strtok.elems.first assert( first.nil? || first.kind_of?(RubyCode) ) @elems += strtok.elems @ident << strtok.ident assert((!@modifiers or !strtok.modifiers)) @modifiers||=strtok.modifiers assert @elems.last.kind_of?(String) return self end private #simpler transform, preserves original exactly def simple_transform(strfrag,starter,ender) #assert('[{/'[@char]) #strfrag.gsub!(/#([{$@])/,'\\#\\1') unless @char=='[' strfrag.gsub!(Regexp.new("[\\"+starter+"\\"+ender+"]"), '\\\\\&') return strfrag end def transform(strfrag,starter,ender) strfrag.gsub!("\\",'\\'*4) strfrag.gsub!(/#([{$@])/,'\\#\\1') strfrag.gsub!(Regexp.new("[\\"+starter+"\\"+ender+"]"),'\\\\\\&') unless @char=='?' DQUOTE_ESCAPE_TABLE.each {|pair| strfrag.gsub!(*pair) } unless @char=='/' strfrag.gsub!(/[^ -~]/){|np| #nonprintables "\\x"+sprintf('%02X',np[0]) } #break up long lines (best done later?) strfrag.gsub!(/(\\x[0-9A-F]{2}|\\?.){40}/i, "\\&\\\n") return strfrag end def append_str!(str) assert @elems.last.kind_of?(String) @elems.last << str @ident << str assert @elems.last.kind_of?(String) end def append_code!(code) assert @elems.last.kind_of?(String) @elems.concat [code, ''] @ident << "\#{#{code}}" assert @elems.last.kind_of?(String) end end #------------------------- class RenderExactlyStringToken < StringToken alias transform simple_transform end #------------------------- class HerePlaceholderToken < WToken attr_reader :termex, :quote, :ender, :dash attr_accessor :unsafe_to_use, :string attr_accessor :bodyclass def initialize(dash,quote,ender) @dash,@quote,@ender=dash,quote,ender @unsafe_to_use=true @string=StringToken.new #@termex=/^#{'[\s\v]*' if dash}#{Regexp.escape ender}$/ @termex=Regexp.new \ ["^", ('[\s\v]*' if dash), Regexp.escape(ender), "$"].to_s @bodyclass=HereBodyToken end def ===(bogus); false end def to_s if @bodyclass==OutlinedHereBodyToken result=if/[^a-z_0-9]/i===@ender %["#{@ender.gsub(/[\\"]/, '\\\\'+'\\&')}"] else @ender end ["<<",@quote,@ender,@quote].to_s else assert !unsafe_to_use @string.to_s end end def append s; @string.append s end def append_token tok; @string.append_token tok end #def with_line(line) @string.line=line; self end def line; @string.line end def line=line; @string.line=line end end #------------------------- module StillIgnoreToken end #------------------------- class IgnoreToken < Token include StillIgnoreToken end #------------------------- class WsToken < IgnoreToken end #------------------------- class ZwToken < IgnoreToken def initialize(offset) super('',offset) end def explicit_form abstract end def explicit_form_all; explicit_form end end class NoWsToken < ZwToken def explicit_form_all "#nows#" end def explicit_form nil end end class ImplicitParamListStartToken < KeywordToken include StillIgnoreToken def initialize(offset) super("(",offset) end def to_s; '' end end class ImplicitParamListEndToken < KeywordToken include StillIgnoreToken def initialize(offset) super(")",offset) end def to_s; '' end end class AssignmentRhsListStartToken < ZwToken def explicit_form '*[' end end class AssignmentRhsListEndToken < ZwToken def explicit_form ']' end end class KwParamListStartToken < ZwToken def explicit_form_all "#((#" end def explicit_form nil end end class KwParamListEndToken < ZwToken def explicit_form_all "#))#" end def explicit_form nil end end #------------------------- class EscNlToken < IgnoreToken def initialize(filename,linenum,ident="\\\n",offset=nil) super(ident,offset) #@char='\\' @filename=filename @linenum=linenum end end #------------------------- class EoiToken < IgnoreToken attr :file alias :pos :offset def initialize(cause,file, offset=nil) super(cause,offset) @file=file end end #------------------------- class HereBodyToken < IgnoreToken #attr_accessor :ender def initialize(headtok) assert HerePlaceholderToken===headtok super(headtok.string,headtok.string.offset) @headtok=headtok end attr :headtok end #------------------------- class FileAndLineToken < IgnoreToken attr :line def initialize(ident,line,offset=nil) super ident,offset #@char='#' @line=line end #def char; '#' end def to_s() ['#', @ident, ':', @line].to_s end def file() @ident end def subitem() @line end #needed? end #------------------------- class OutlinedHereBodyToken < HereBodyToken def to_s assert HerePlaceholderToken===@headtok result=@headtok.string result=result.to_s(:simple_transform).match(/^"(.*)"$/m)[1] return result + @headtok.ender + "\n" end end #------------------------- module ErrorToken attr_accessor :error end #------------------------- class SubitemToken < Token attr :char2 attr :subitem def initialize(ident,subitem) super ident @subitem=subitem end def to_s() super+@char2+@subitem.to_s end end #------------------------- class DecoratorToken < SubitemToken def initialize(ident,subitem) super '^'+ident,subitem @subitem=@subitem.to_s #why to_s? #@char='^' @char2='=' end #alias to_s ident #parent has right implementation of to_s... i think def needs_value?() @subitem.nil? end def value=(v) @subitem=v end def value() @subitem end end end require "rubylexer/rubycode"