rubylexer 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. data/COPYING +0 -0
  2. data/History.txt +16 -1
  3. data/Manifest.txt +0 -0
  4. data/README.txt +0 -0
  5. data/Rakefile +1 -1
  6. data/howtouse.txt +0 -0
  7. data/lib/assert.rb +0 -0
  8. data/lib/rubylexer.rb +156 -45
  9. data/lib/rubylexer/0.6.2.rb +0 -0
  10. data/lib/rubylexer/0.6.rb +0 -0
  11. data/lib/rubylexer/0.7.0.rb +0 -0
  12. data/lib/rubylexer/0.7.1.rb +0 -0
  13. data/lib/rubylexer/charhandler.rb +0 -0
  14. data/lib/rubylexer/charset.rb +0 -0
  15. data/lib/rubylexer/context.rb +6 -0
  16. data/lib/rubylexer/rubycode.rb +0 -0
  17. data/lib/rubylexer/rulexer.rb +3 -2
  18. data/lib/rubylexer/symboltable.rb +0 -0
  19. data/lib/rubylexer/token.rb +3 -3
  20. data/lib/rubylexer/tokenprinter.rb +0 -0
  21. data/lib/rubylexer/version.rb +1 -1
  22. data/rubylexer.vpj +39 -29
  23. data/test/code/all_the_gems.rb +0 -0
  24. data/test/code/all_the_raas.rb +0 -0
  25. data/test/code/all_the_rubies.rb +0 -0
  26. data/test/code/deletewarns.rb +0 -0
  27. data/test/code/dumptokens.rb +6 -2
  28. data/test/code/errscan +0 -0
  29. data/test/code/isolate_error.rb +0 -0
  30. data/test/code/lexloop +0 -0
  31. data/test/code/locatetest +0 -0
  32. data/test/code/locatetest.rb +0 -0
  33. data/test/code/regression.rb +0 -2
  34. data/test/code/rubylexervsruby.rb +0 -0
  35. data/test/code/strgen.rb +0 -0
  36. data/test/code/tarball.rb +0 -0
  37. data/test/code/testcases.rb +0 -0
  38. data/test/code/tokentest.rb +0 -0
  39. data/test/code/torment +0 -0
  40. data/test/data/1.rb.broken +0 -0
  41. data/test/data/23.rb +0 -0
  42. data/test/data/__end__.rb +0 -0
  43. data/test/data/__end__2.rb +0 -0
  44. data/test/data/__eof2.rb +0 -0
  45. data/test/data/__eof5.rb +0 -0
  46. data/test/data/__eof6.rb +0 -0
  47. data/test/data/and.rb +0 -0
  48. data/test/data/blockassigntest.rb +0 -0
  49. data/test/data/chunky.plain.rb +0 -0
  50. data/test/data/chunky_bacon.rb +0 -0
  51. data/test/data/chunky_bacon2.rb +0 -0
  52. data/test/data/chunky_bacon3.rb +0 -0
  53. data/test/data/chunky_bacon4.rb +0 -0
  54. data/test/data/cvtesc.rb +0 -0
  55. data/test/data/for.rb +0 -0
  56. data/test/data/format.rb +0 -0
  57. data/test/data/g.rb +0 -0
  58. data/test/data/hd0.rb +0 -0
  59. data/test/data/hdateof.rb +0 -0
  60. data/test/data/hdempty.rb +0 -0
  61. data/test/data/hdr.rb +0 -0
  62. data/test/data/hdr_dos.rb +0 -0
  63. data/test/data/hdr_dos2.rb +0 -0
  64. data/test/data/heart.rb +0 -0
  65. data/test/data/here_escnl.rb +0 -0
  66. data/test/data/here_escnl_dos.rb +0 -0
  67. data/test/data/here_squote.rb +0 -0
  68. data/test/data/heremonsters.rb +0 -0
  69. data/test/data/heremonsters.rb.broken +0 -0
  70. data/test/data/heremonsters.rb.broken.save +0 -0
  71. data/test/data/heremonsters_dos.rb +0 -0
  72. data/test/data/heremonsters_dos.rb.broken +0 -0
  73. data/test/data/if.rb +0 -0
  74. data/test/data/jarh.rb +0 -0
  75. data/test/data/lbrace.rb +0 -0
  76. data/test/data/lbrack.rb +0 -0
  77. data/test/data/make_ws_strdelim.rb +0 -0
  78. data/test/data/maven2_builer_test.rb +0 -0
  79. data/test/data/migration.rb +0 -0
  80. data/test/data/modl.rb +0 -0
  81. data/test/data/modl_dos.rb +0 -0
  82. data/test/data/modl_fails.rb +0 -0
  83. data/test/data/multilinestring.rb +0 -0
  84. data/test/data/newsyntax.rb +0 -0
  85. data/test/data/noeolatend.rb +0 -0
  86. data/test/data/oneliners.rb +1 -1
  87. data/test/data/p-op.rb +0 -0
  88. data/test/data/p.rb +0 -0
  89. data/test/data/pleac.rb.broken +0 -0
  90. data/test/data/pre.rb +0 -0
  91. data/test/data/pre.unix.rb +0 -0
  92. data/test/data/regtest.rb +0 -0
  93. data/test/data/rescue.rb +0 -0
  94. data/test/data/s.rb +0 -0
  95. data/test/data/simple.rb +0 -0
  96. data/test/data/simple_dos.rb +0 -0
  97. data/test/data/stanzas.rb +0 -0
  98. data/test/data/strdelim_crlf.rb +0 -0
  99. data/test/data/strinc.rb +0 -0
  100. data/test/data/stuff.rb +0 -0
  101. data/test/data/stuff2.rb +0 -0
  102. data/test/data/stuff3.rb +0 -0
  103. data/test/data/tkweird.rb +0 -0
  104. data/test/data/tokentest.assert.rb.can +0 -0
  105. data/test/data/unending_stuff.rb +0 -0
  106. data/test/data/untermed_here.rb.broken +0 -0
  107. data/test/data/untermed_string.rb.broken +0 -0
  108. data/test/data/untitled1.rb +0 -0
  109. data/test/data/w.rb +0 -0
  110. data/test/data/whatnot.rb +0 -0
  111. data/test/data/ws_strdelim.rb +0 -0
  112. data/test/data/wsdlDriver.rb +0 -0
  113. data/test/test.sh +0 -0
  114. data/testing.txt +0 -0
  115. metadata +64 -149
data/COPYING CHANGED
File without changes
@@ -1,4 +1,19 @@
1
- === 0.7.1/10-29-2008
1
+ === 0.7.2/10-12-2008
2
+ * 12 Minor Enhancements:
3
+ * a new context for then kw expected
4
+ * disable all backtracking when scanning string interiors
5
+ * ternary flag distinguishes ternary : from other uses
6
+ * EndDefHeaderToken renamed to EndHeaderToken
7
+ * ^ now gets its own scanning method
8
+ * correct # of parens emitted after of kw used as (or like) method
9
+ * more special casing for break return and next
10
+ * abort_noparens! now better if When context on the stack
11
+ * semicolon may now be omitted after module header
12
+ * { and } in BEGIN/END expr masquerade as do and end
13
+ * trying to make 'rake test' work right
14
+ * certain other changes of no importance whatsoever
15
+
16
+ === 0.7.1/8-29-2008
2
17
  * 6 Major Enhancements:
3
18
  * handling of empty string fragments now more closely mirrors ruby; this resolves many warnings
4
19
  * yet more hacks in aid of string inclusions
File without changes
data/README.txt CHANGED
File without changes
data/Rakefile CHANGED
@@ -15,7 +15,7 @@ require 'lib/rubylexer/version.rb'
15
15
  _.email = "rubylexer-owner @at@ inforadical .dot. net"
16
16
  _.url = ["http://rubylexer.rubyforge.org/", "http://rubyforge.org/projects/rubylexer/"]
17
17
  _.extra_deps << ['sequence', '>= 0.2.0']
18
- _.test_globs=["test/{code/*,data/*rb*,results/}"]
18
+ _.test_globs=["test/code/regression.rb"]
19
19
  _.description=desc
20
20
  _.summary=desc[/\A[^.]+\./]
21
21
  _.spec_extras={:bindir=>''}
File without changes
File without changes
@@ -83,7 +83,7 @@ class RubyLexer
83
83
  ?, => :comma,
84
84
  ?; => :semicolon,
85
85
 
86
- ?^ => :biop,
86
+ ?^ => :caret,
87
87
  ?~ => :tilde,
88
88
  ?= => :equals,
89
89
  ?! => :exclam,
@@ -130,6 +130,9 @@ class RubyLexer
130
130
  @in_def_name=false
131
131
  @last_operative_token=nil
132
132
  @last_token_maybe_implicit=nil
133
+ @enable_macro=nil
134
+ @base_file=nil
135
+ @progress_thread=nil
133
136
 
134
137
  @toptable=CharHandler.new(self, :illegal_char, CHARMAPPINGS)
135
138
 
@@ -442,12 +445,12 @@ private
442
445
  end
443
446
 
444
447
  #-----------------------------------
445
- def in_lvar_define_state
448
+ def in_lvar_define_state lasttok=@last_operative_token
446
449
  #@defining_lvar is a hack
447
450
  @defining_lvar or case ctx=@parsestack.last
448
451
  #when ForSMContext; ctx.state==:for
449
452
  when RescueSMContext
450
- @last_operative_token.ident=="=>" and @file.match? /\A[\s\v]*([:;#\n]|then[^a-zA-Z0-9_])/m
453
+ lasttok.ident=="=>" and @file.match? /\A[\s\v]*([:;#\n]|then[^a-zA-Z0-9_])/m
451
454
  #when BlockParamListLhsContext; true
452
455
  end
453
456
  end
@@ -472,7 +475,7 @@ private
472
475
 
473
476
  assert String===name
474
477
 
475
- was_in_lvar_define_state=in_lvar_define_state
478
+ was_in_lvar_define_state=in_lvar_define_state(lasttok)
476
479
  #maybe_local really means 'maybe local or constant'
477
480
  maybe_local=case name
478
481
  when /[^a-z_0-9]$/i #do nothing
@@ -500,8 +503,6 @@ private
500
503
  if /^[a-z_][a-zA-Z_0-9]*$/===name
501
504
  assert !(lasttok===/^(\.|::)$/)
502
505
  localvars[name]=true
503
- else
504
- lexerror tok,"not a valid variable name: #{name}"
505
506
  end
506
507
  return result.unshift(tok)
507
508
  elsif maybe_local
@@ -534,7 +535,6 @@ private
534
535
  if (assignment_coming && !(lasttok===/^(\.|::)$/) or was_in_lvar_define_state)
535
536
  tok=assign_lvar_type! VarNameToken.new(name,pos)
536
537
  if /[^a-z_0-9]$/i===name
537
- lexerror tok,"not a valid variable name: #{name}"
538
538
  elsif /^[a-z_]/===name and !(lasttok===/^(\.|::)$/)
539
539
  localvars[name]=true
540
540
  end
@@ -573,7 +573,7 @@ private
573
573
  !(KeywordToken===oldlast and oldlast===/\A(\.|::)\Z/)
574
574
  x
575
575
  =end
576
- when ?(;
576
+ when ?(
577
577
  maybe_local=false
578
578
  lastid=lasttok&&lasttok.ident
579
579
  case lastid
@@ -595,10 +595,12 @@ private
595
595
  # ignored_tokens(true)
596
596
  # want_parens=false if nextchar==?)
597
597
  # @file.pos=afterparen
598
-
598
+ want_parens=true if /^(return|break|next)$/===@last_operative_token.ident and not(
599
+ KeywordToken===lasttok and /^(.|::)$/===lasttok.ident
600
+ )
599
601
  want_parens ? 1 : 0
600
- when ?},?],?),?;,?^, ?|, ?>, ?,, ?., ?=; 2
601
- when ?+, ?-, ?%, ?/
602
+ when ?},?],?),?;,(?^ unless @enable_macro), ?|, ?>, ?,, ?., ?=; 2
603
+ when ?+, ?-, ?%, ?/, (?^ if @enable_macro)
602
604
  if /^(return|break|next)$/===@last_operative_token.ident and not(
603
605
  KeywordToken===lasttok and /^(.|::)$/===lasttok.ident
604
606
  )
@@ -607,7 +609,7 @@ private
607
609
  (ws_toks.empty? || readahead(2)[/^.[#{WHSPLF}]/o]) ? 2 : 3
608
610
  end
609
611
  when ?*, ?&
610
- lasttok=@last_operative_token
612
+ # lasttok=@last_operative_token
611
613
  if /^(return|break|next)$/===@last_operative_token.ident and not(
612
614
  KeywordToken===lasttok and /^(.|::)$/===lasttok.ident
613
615
  )
@@ -626,7 +628,12 @@ private
626
628
  /^\?([#{WHSPLF}]|[a-z_][a-z_0-9])/io===next3 ? 2 : 3
627
629
  # when ?:,??; (readahead(2)[/^.[#{WHSPLF}]/o]) ? 2 : 3
628
630
  when ?<; (!ws_toks.empty? && readahead(4)[/^<<-?["'`a-zA-Z_0-9]/]) ? 3 : 2
629
- when ?[; ws_toks.empty?&&!(KeywordToken===oldlast and /^(return|break|next)$/===oldlast.ident) ? 2 : 3
631
+ when ?[;
632
+ if ws_toks.empty?
633
+ (KeywordToken===oldlast and /^(return|break|next)$/===oldlast.ident) ? 3 : 2
634
+ else
635
+ 3
636
+ end
630
637
  when ?\\, ?\s, ?\t, ?\n, ?\r, ?\v, ?#; raise 'failure'
631
638
  else raise "unknown char after ident: #{nc=nextchar ? nc.chr : "<<EOF>>"}"
632
639
  end
@@ -704,8 +711,8 @@ private
704
711
  CONTEXT2ENDTOK={
705
712
  AssignmentRhsContext=>AssignmentRhsListEndToken,
706
713
  ParamListContextNoParen=>ImplicitParamListEndToken,
707
- KWParamListContextNoParen=>ImplicitParamListEndToken,
708
- WhenParamListContext=>KwParamListEndToken,
714
+ KWParamListContextNoParen=>ImplicitParamListEndToken, #break,next,return
715
+ WhenParamListContext=>KwParamListEndToken,
709
716
  RescueSMContext=>KwParamListEndToken
710
717
  }
711
718
  def abort_noparens!(str='')
@@ -713,7 +720,8 @@ private
713
720
  result=[]
714
721
  while klass=CONTEXT2ENDTOK[@parsestack.last.class]
715
722
  result << klass.new(input_position-str.length)
716
- break if RescueSMContext===@parsestack.last
723
+ break if RescueSMContext===@parsestack.last #and str==':'
724
+ break if WhenParamListContext===@parsestack.last and str==':'
717
725
  @parsestack.pop
718
726
  end
719
727
  return result
@@ -724,7 +732,7 @@ private
724
732
  AssignmentRhsContext=>AssignmentRhsListEndToken,
725
733
  ParamListContextNoParen=>ImplicitParamListEndToken,
726
734
  KWParamListContextNoParen=>ImplicitParamListEndToken,
727
- WhenParamListContext=>KwParamListEndToken,
735
+ WhenParamListContext=>KwParamListEndToken, #I think this isn't needed...
728
736
  RescueSMContext=>KwParamListEndToken
729
737
  }
730
738
  def abort_noparens_for_rescue!(str='')
@@ -791,6 +799,39 @@ private
791
799
  return result
792
800
  end
793
801
 
802
+ #-----------------------------------
803
+ def enable_macros!
804
+ @enable_macro="macro"
805
+ end
806
+ public :enable_macros!
807
+
808
+
809
+ #-----------------------------------
810
+ @@SPACES=/[\ \t\v\f\v]/
811
+ @@WSTOK=/\r?\n|\r*#@@SPACES+(?:#@@SPACES|\r(?!\n))*|\#[^\n]*\n|\\\r?\n|
812
+ ^=begin[\s\n](?:(?!=end).*\n)*=end[\s\n].*\n/x
813
+ @@WSTOKS=/(?!=begin)#@@WSTOK+/o
814
+ def divide_ws(ws,offset)
815
+ result=[]
816
+ ws.scan(/\G#@@WSTOK/o){|ws|
817
+ incr= $~.begin(0)
818
+ klass=case ws
819
+ when /\A[\#=]/: CommentToken
820
+ when /\n\Z/: EscNlToken
821
+ else WsToken
822
+ end
823
+ result << klass.new(ws,offset+incr)
824
+ }
825
+ result.each_with_index{|ws,i|
826
+ if WsToken===ws
827
+ ws.ident << result.delete(i+1).ident while WsToken===result[i+1]
828
+ end
829
+ }
830
+ return result
831
+ end
832
+
833
+
834
+
794
835
  #-----------------------------------
795
836
  #parse keywords now, to prevent confusion over bare symbols
796
837
  #and match end with corresponding preceding def or class or whatever.
@@ -823,7 +864,33 @@ private
823
864
  result.first.has_end!
824
865
  @parsestack.push WantsEndContext.new(str,@linenum)
825
866
  @localvars_stack.push SymbolTable.new
826
-
867
+ offset=input_position
868
+ @file.scan(/\A(#@@WSTOKS)?(::)?/o)
869
+ md=@file.last_match
870
+ all,ws,dc=*md
871
+ fail if all.empty?
872
+ @moretokens.concat divide_ws(ws,offset) if ws
873
+ @moretokens.push KeywordToken.new('::',offset+md.end(0)-2) if dc
874
+ loop do
875
+ offset=input_position
876
+ @file.scan(/\A(#@@WSTOKS)?([A-Z][a-zA-Z_0-9]*)(::)?/o)
877
+ #this regexp---^ will need to change in order to support utf8 properly.
878
+ md=@file.last_match
879
+ all,ws,name,dc=*md
880
+ if ws
881
+ @moretokens.concat divide_ws(ws,offset)
882
+ incr=ws.size
883
+ else
884
+ incr=0
885
+ end
886
+ @moretokens.push VarNameToken.new(name,offset+incr)
887
+ break unless dc
888
+ @moretokens.push KeywordToken.new('::',offset+md.end(0)-2)
889
+ end
890
+ @moretokens.push EndHeaderToken.new(input_position)
891
+
892
+
893
+
827
894
  when "class"
828
895
  result.first.has_end!
829
896
  @parsestack.push ClassContext.new(str,@linenum)
@@ -832,11 +899,12 @@ private
832
899
  if after_nonid_op?{false} #prefix form
833
900
  result.first.has_end!
834
901
  @parsestack.push WantsEndContext.new(str,@linenum)
835
-
836
-
902
+ @parsestack.push ExpectThenOrNlContext.new(str,@linenum)
837
903
  else #infix form
838
904
  result.unshift(*abort_noparens!(str))
839
905
  end
906
+ when "elsif"
907
+ @parsestack.push ExpectThenOrNlContext.new(str,@linenum)
840
908
  when "begin","case"
841
909
  result.first.has_end!
842
910
  @parsestack.push WantsEndContext.new(str,@linenum)
@@ -868,12 +936,12 @@ private
868
936
  localvars.start_block
869
937
  block_param_list_lookahead
870
938
  end
871
- when "def"
939
+ when "def",@enable_macro
872
940
  result.first.has_end!
873
941
  @parsestack.push ctx=DefContext.new(@linenum)
874
942
  ctx.state=:saw_def
875
943
  safe_recurse { |aa|
876
- set_last_token KeywordToken.new "def" #hack
944
+ set_last_token KeywordToken.new str #hack
877
945
  result.concat ignored_tokens
878
946
 
879
947
  #read an expr like a.b.c or a::b::c
@@ -957,7 +1025,7 @@ private
957
1025
  else
958
1026
  ofs+=listend.to_s.size
959
1027
  end
960
- result.insert end_index+1,EndDefHeaderToken.new(ofs)
1028
+ result.insert end_index+1,EndHeaderToken.new(ofs)
961
1029
  break
962
1030
  end
963
1031
 
@@ -983,7 +1051,7 @@ private
983
1051
  if endofs
984
1052
  result.insert -2,ImplicitParamListEndToken.new(tok.offset)
985
1053
  end
986
- result.insert -2, EndDefHeaderToken.new(tok.offset)
1054
+ result.insert -2, EndHeaderToken.new(tok.offset)
987
1055
  break
988
1056
  else
989
1057
  lexerror(tok, "bizarre token in def name: " +
@@ -1060,6 +1128,11 @@ private
1060
1128
  when "then"
1061
1129
  result.unshift(*abort_noparens!(str))
1062
1130
  @parsestack.last.see self,:then
1131
+
1132
+ if ExpectThenOrNlContext===@parsestack.last
1133
+ @parsestack.pop
1134
+ else #error... does anyone care?
1135
+ end
1063
1136
 
1064
1137
  when "in"
1065
1138
  result.unshift KwParamListEndToken.new( offset)
@@ -1083,7 +1156,7 @@ private
1083
1156
  #END blocks are visible to subsequent code. (Why??)
1084
1157
  #That difference forces a custom parsing.
1085
1158
  if @last_operative_token===/^(\.|::)$/
1086
- result=yield nil #should pass a keyword token here
1159
+ result=yield MethNameToken.new(str) #should pass a methname token here
1087
1160
  else
1088
1161
  safe_recurse{
1089
1162
  old=result.first
@@ -1096,17 +1169,18 @@ private
1096
1169
  getchar=='{' or lexerror(result.first,"expected { after #{str}")
1097
1170
  result.push KeywordToken.new('{',input_position-1)
1098
1171
  result.last.set_infix!
1172
+ result.last.as="do"
1099
1173
  @parsestack.push BeginEndContext.new(str,offset)
1100
1174
  }
1101
1175
  end
1102
1176
 
1103
1177
  when FUNCLIKE_KEYWORDS
1104
- result=yield nil #should be a keyword token
1178
+ result=yield MethNameToken.new(str) #should be a keyword token?
1105
1179
 
1106
1180
  when RUBYKEYWORDS
1107
1181
  #do nothing
1108
1182
 
1109
- else result=yield nil
1183
+ else result=yield MethNameToken.new(str)
1110
1184
 
1111
1185
  end
1112
1186
 
@@ -1307,7 +1381,7 @@ end
1307
1381
  # if !eof? and nextchar.chr[/[iuw\/<|>+\-*&%?:({]/] and
1308
1382
  # !(NewlineToken===@last_operative_token) and
1309
1383
  # !(/^(end|;)$/===@last_operative_token)
1310
- #result<<EndDefHeaderToken.new(result.last.offset+result.last.to_s.size)
1384
+ #result<<EndHeaderToken.new(result.last.offset+result.last.to_s.size)
1311
1385
  set_last_token KeywordToken.new ';'
1312
1386
  result<< get1token
1313
1387
  # end
@@ -1426,10 +1500,18 @@ end
1426
1500
  #-----------------------------------
1427
1501
  def symbol_or_op(ch)
1428
1502
  startpos= input_position
1503
+
1504
+
1429
1505
  qe= colon_quote_expected?(ch)
1430
1506
  lastchar=prevchar
1431
1507
  eat_next_if(ch[0]) or raise "needed: "+ch
1432
1508
 
1509
+ if nextchar==?( and @enable_macro
1510
+ result= OperatorToken.new(':', startpos)
1511
+ result.unary=true
1512
+ return result
1513
+ end
1514
+
1433
1515
  #handle quoted symbols like :"foobar", :"[]"
1434
1516
  qe and return symbol(':')
1435
1517
 
@@ -1437,17 +1519,23 @@ end
1437
1519
  unless eat_next_if(?:)
1438
1520
  #cancel implicit contexts...
1439
1521
  @moretokens.push(*abort_noparens!(':'))
1440
- @moretokens.push KeywordToken.new(':',startpos)
1522
+ @moretokens.push tok=KeywordToken.new(':',startpos)
1441
1523
 
1442
1524
  case @parsestack.last
1443
- when TernaryContext: @parsestack.pop #should be in the context's see handler
1525
+ when TernaryContext:
1526
+ tok.ternary=true
1527
+ @parsestack.pop #should be in the context's see handler
1444
1528
  when ExpectDoOrNlContext: #should be in the context's see handler
1445
1529
  @parsestack.pop
1446
1530
  assert @parsestack.last.starter[/^(while|until|for)$/]
1447
- @moretokens.last.as=";"
1531
+ tok.as=";"
1532
+ when ExpectThenOrNlContext,WhenParamListContext:
1533
+ #should be in the context's see handler
1534
+ @parsestack.pop
1535
+ tok.as="then"
1448
1536
  when RescueSMContext:
1449
- @moretokens.last.as=";"
1450
- else @moretokens.last.as="then"
1537
+ tok.as=";"
1538
+ else fail ": not expected in #{@parsestack.last.class}->#{@parsestack.last.starter}"
1451
1539
  end
1452
1540
 
1453
1541
  #end ternary context, if any
@@ -1503,7 +1591,9 @@ end
1503
1591
  /[A-Z_0-9]$/i===result and klass=VarNameToken
1504
1592
  end
1505
1593
  result
1506
- else error= "unexpected char starting symbol: #{nc.chr}"
1594
+ else
1595
+ error= "unexpected char starting symbol: #{nc.chr}"
1596
+ '_'
1507
1597
  end
1508
1598
  result= lexerror(klass.new(result,start,notbare ? ':' : ''),error)
1509
1599
  if open
@@ -1843,7 +1933,10 @@ end
1843
1933
  then #hack-o-rama: probly cases left out above
1844
1934
  @offset_adjust=@min_offset_adjust
1845
1935
  a= abort_noparens!
1846
- ExpectDoOrNlContext===@parsestack.last and @parsestack.pop
1936
+ case @parsestack.last #these should be in the see:semi handler
1937
+ when ExpectDoOrNlContext: @parsestack.pop
1938
+ when ExpectThenOrNlContext: @parsestack.pop
1939
+ end
1847
1940
  assert !@parsestack.empty?
1848
1941
  @parsestack.last.see self,:semi
1849
1942
 
@@ -2011,7 +2104,7 @@ end
2011
2104
  when /^(#{RUBYOPERATORREX}|#{INNERBOUNDINGWORDS}|do)$/o.token_pat
2012
2105
  #regexs above must match whole string
2013
2106
  #assert(@last_operative_token==$&) #disabled 'cause $& is now always nil :(
2014
- return true
2107
+ return true if OperatorToken===@last_operative_token || KeywordToken===@last_operative_token
2015
2108
  when NewlineToken, nil, #nil means we're still at beginning of file
2016
2109
  /^([({\[]|or|not|and|if|unless|then|elsif|else|class|module|def|
2017
2110
  while|until|begin|for|in|case|when|ensure|defined\?)$
@@ -2065,15 +2158,31 @@ end
2065
2158
  end
2066
2159
 
2067
2160
 
2068
- #-----------------------------------
2069
- def biop(ch) #match /%=?/ (% or %=)
2161
+ #-----------------------------------
2162
+ def caret(ch) #match /^=?/ (^ or ^=) (maybe unary ^ too)
2163
+ if @enable_macro and (@last_token_maybe_implicit and
2164
+ @last_token_maybe_implicit.ident=='(') || unary_op_expected?(ch)
2165
+ result=OperatorToken.new(read(1),input_position)
2166
+ result.unary=true
2167
+ result
2168
+ else
2169
+ biop ch
2170
+ end
2171
+ end
2172
+
2173
+ #-----------------------------------
2174
+ def biop(ch) #match /%=?/ (% or %=)
2070
2175
  assert(ch[/^[%^]$/])
2176
+ oldpos=input_position
2071
2177
  result=getchar
2072
2178
  if eat_next_if(?=)
2073
2179
  result << ?=
2074
2180
  end
2075
- return operator_or_methname_token( result)
2181
+ result= operator_or_methname_token( result)
2182
+ result.offset=oldpos
2183
+ return result
2076
2184
  end
2185
+
2077
2186
  #-----------------------------------
2078
2187
  def tilde(ch) #match ~
2079
2188
  assert(ch=='~')
@@ -2322,10 +2431,8 @@ end
2322
2431
  lexerror kw,"mismatched braces: #{origch}#{ch}\n" +
2323
2432
  "matching brace location", @filename, line
2324
2433
  end
2325
- if BlockContext===ctx
2326
- localvars.end_block
2327
- @moretokens.last.as="end"
2328
- end
2434
+ localvars.end_block if BlockContext===ctx
2435
+ @moretokens.last.as="end" if BlockContext===ctx or BeginEndContext===ctx
2329
2436
  if ParamListContext==ctx.class
2330
2437
  assert ch==')'
2331
2438
  kw.set_callsite! #not needed?
@@ -2372,6 +2479,7 @@ end
2372
2479
  ParamListContext===@parsestack[-2] ||
2373
2480
  ParamListContextNoParen===@parsestack[-2] ||
2374
2481
  WhenParamListContext===@parsestack[-2] ||
2482
+ ListImmedContext===@parsestack[-2] ||
2375
2483
  (RescueSMContext===@parsestack[-2] && @parsestack[-2].state==:rescue) ||
2376
2484
  (DefContext===@parsestack[-2] && !@parsestack[-2].in_body)
2377
2485
  @parsestack.pop
@@ -2394,7 +2502,10 @@ end
2394
2502
  assert @moretokens.empty?
2395
2503
  @moretokens.push(*abort_noparens!)
2396
2504
  @parsestack.last.see self,:semi
2397
- if ExpectDoOrNlContext===@parsestack.last #should be in context's see:semi handler
2505
+ case @parsestack.last #should be in context's see:semi handler
2506
+ when ExpectThenOrNlContext
2507
+ @parsestack.pop
2508
+ when ExpectDoOrNlContext
2398
2509
  @parsestack.pop
2399
2510
  assert @parsestack.last.starter[/^(while|until|for)$/]
2400
2511
  end
@@ -2417,7 +2528,7 @@ end
2417
2528
  #-----------------------------------
2418
2529
  #tokenify_results_of :identifier
2419
2530
  save_offsets_in(*CHARMAPPINGS.values.uniq-[
2420
- :symbol_or_op,:open_brace,:whitespace,:exclam,:backquote
2531
+ :symbol_or_op,:open_brace,:whitespace,:exclam,:backquote,:caret
2421
2532
  ])
2422
2533
  #save_offsets_in :symbol
2423
2534