Ticket #25: extIndexing.patch
File extIndexing.patch, 19.9 KB (added by hopscc, 16 years ago) |
---|
extended Index patch with fixed line endings |
-
Source/CobraLang.cs
1087 1087 } 1088 1088 } 1089 1089 1090 static private int ProcessIndexArgs(int count, int? idx) { 1091 if (idx==null) 1092 idx = 0; 1093 else if (idx < 0) { 1094 idx += count; 1095 //run off front should throw bounds exc 1096 //if (idx < 0) { 1097 // idx = 0; 1098 //} 1099 } 1100 return (int) idx; 1101 } 1102 1103 static public char GetExtIndexed(string s, int? idx) { 1104 //Console.WriteLine("GetIndex String"); 1105 if (s==null) 1106 throw new NullReferenceException("Cannot index null."); 1107 int i = ProcessIndexArgs(s.Length, idx); 1108 return s[i]; 1109 } 1110 1111 static public T GetExtIndexed<T>(T[] array, int? idx) { 1112 //Console.WriteLine("GetIndex Array"); 1113 if (array==null) 1114 throw new NullReferenceException("Cannot index null array."); 1115 int i = ProcessIndexArgs(array.Length, idx); 1116 return array[i]; // array.GetValue(i); 1117 } 1118 1119 static public object GetExtIndexed(System.Collections.IList list, int? idx) { 1120 if (list==null) 1121 throw new NullReferenceException("Cannot slice null."); 1122 int i = ProcessIndexArgs(list.Count, idx); 1123 return list[i]; 1124 } 1125 1126 static public T GetExtIndexed<T>(IList<T> list, int? idx) { 1127 //Console.WriteLine("GetIndex IList<T>"); 1128 if (list==null) 1129 throw new NullReferenceException("Cannot index null IList<T>."); 1130 int i = ProcessIndexArgs(list.Count, idx); 1131 return list[i]; 1132 } 1133 1134 static public T GetExtIndexed<T>(List<T> list, int? idx) { 1135 //Console.WriteLine("GetIndex List<T>"); 1136 if (list==null) 1137 throw new NullReferenceException("Cannot slice null List."); 1138 int i = ProcessIndexArgs(list.Count, idx); 1139 return list[i]; 1140 } 1141 1142 /* hops: Is this needed (for dynamic sequences) ?? 1143 static public object GetExtIndexed(Object obj, int? idx) { 1144 // this is for when obj is type `dynamic` 1145 if (obj==null) 1146 throw new NullReferenceException("Cannot Index null."); 1147 if (obj is String) 1148 return GetExtIndexed((String)obj, idx); 1149 else if (obj is ArrayList) 1150 return GetExtIndexed((ArrayList)obj, idx); 1151 else if (obj is IList) 1152 return GetExtIndexed((IList)obj, idx); 1153 else 1154 throw new Exception(String.Format("GetExtIndexed unhandled Type {0}", obj)); 1155 } 1156 */ 1157 1090 1158 static public object GetIndexerValue(Object obj, params object[] args) { 1091 1159 if (obj==null) 1092 1160 throw new ArgumentNullException("obj"); -
Source/Expr.cobra
1393 1393 _type = _definition.resultType 1394 1394 assert _type 1395 1395 1396 def toCobraSource as String is override1396 def toCobraSource(startDelim as String, stopDelim as String) as String 1397 1397 sb = StringBuilder() 1398 1398 sb.append(_target.toCobraSource) 1399 sb.append( r'[')1399 sb.append(startDelim) 1400 1400 sep = '' 1401 1401 for arg in _args 1402 1402 sb.append(sep) 1403 1403 sb.append(arg.toCobraSource) 1404 1404 sep = ', ' 1405 sb.append( ']')1405 sb.append(stopDelim) 1406 1406 return sb.toString 1407 1408 def toCobraSource as String is override 1409 return .toCobraSource(r'[', ']') 1407 1410 1408 1411 def writeSharpDef(sw as SharpWriter, parens as bool) is override 1409 1412 if _target.type.isDynamic … … 1414 1417 expr.writeSharpDef(sw, false) 1415 1418 sw.write(')') 1416 1419 return 1420 1417 1421 if parens 1418 1422 sw.write('(') 1419 if _target inherits IdentifierExpr 1420 if _target.isKindOf(.compiler.typeType) 1421 # here we're favoring "Foo[]" being an array type rather than a shared indexer 1422 sw.write(_target.name) 1423 handled = true 1424 if not handled 1425 _target.writeSharpDef(sw) 1423 .writeTarget(sw) 1426 1424 sw.write(r'[') 1427 1425 sep = '' 1428 1426 for expr in _args … … 1441 1439 expr.writeSharpBreakdownItems(sw) 1442 1440 sw.write(', -1') 1443 1441 1442 def writeTarget(sw as SharpWriter) 1443 if _target inherits IdentifierExpr 1444 if _target.isKindOf(.compiler.typeType) 1445 # here we're favoring "Foo[]" being an array type rather than a shared indexer 1446 sw.write(_target.name) 1447 handled = true 1448 if not handled 1449 _target.writeSharpDef(sw) 1450 1451 class ExtIndexExpr 1452 inherits IndexExpr 1453 """ 1454 Extended Index Expression - Maps index onto function supporting 1455 conversion of single -ve arg to offset from end of sequence 1456 """ 1444 1457 1458 def init(token as IToken, target as Expr, args as List<of Expr>) 1459 base.init(token, target, args) 1460 1461 def toCobraSource as String is override 1462 return .toCobraSource(r'[[', ']]') 1463 1464 def writeSharpDef(sw as SharpWriter, parens as bool) is override 1465 if _target.type.isDynamic # TODO: should this also be overloaded ? 1466 base.writeSharpDef(sw, parens) 1467 return 1468 1469 if not _isSequence(_target.type to !) or _args.count <> 1 1470 base.writeSharpDef(sw, parens) 1471 return 1472 1473 if parens 1474 sw.write('(') 1475 # GetExtIndexed(target, idx) 1476 sw.write('CobraImp.GetExtIndexed(') 1477 _target.writeSharpDef(sw, false) 1478 sw.write(', ') 1479 _args[0].writeSharpDefInContext(sw) 1480 sw.write(')') 1481 if parens 1482 sw.write(')') 1483 1484 1445 1485 class IsNilExpr 1446 1486 inherits Expr 1447 1487 -
Source/CobraParser.cobra
1087 1087 isNames = List<of String>(_isNamesStack) 1088 1088 if .peek.which=='IS' 1089 1089 isNames = .isDeclNames 1090 attribs = .hasAttribs1090 #attribs = .hasAttribs 1091 1091 assert .last.which=='EOL' 1092 1092 .undo # need the EOL 1093 1093 if .optionalIndent … … 1096 1096 else 1097 1097 if .optionalIndent 1098 1098 isNames = .isDeclNames 1099 attribs = .hasAttribs1099 #attribs = .hasAttribs 1100 1100 docString = .docString 1101 1101 .dedent 1102 1102 return BoxVar(tok, .curBox, identifier, type, isNames, initExpr, docString) … … 2279 2279 var _binaryOpPrec = { 2280 2280 # CANNOT USE 0 AS A VALUE IN THIS DICTIONARY 2281 2281 'DOT': 80, 2282 'LBRACKET_DBL': 80, 2282 2283 'LBRACKET': 80, 2283 2284 'LPAREN': 80, 2284 2285 'ARRAY_OPEN': 80, … … 2400 2401 token = .grab 2401 2402 exprs = .commaSepExprs('RPAREN') 2402 2403 return .expression(precedence, PostCallExpr(token, left, exprs)) 2404 else if peek=='LBRACKET_DBL' 2405 # requires special handling - extIndexExpr 2406 return .expression(precedence, .extIndexExpr(left to !)) 2403 2407 else 2404 2408 # most operators are one-word affairs 2405 2409 if op is nil … … 2767 2771 for expr in exprs 2768 2772 assert expr, exprs 2769 2773 return IndexExpr(token, left, exprs) 2770 2774 2775 def extIndexExpr(left as Expr) as Expr 2776 token = .expect('LBRACKET_DBL') 2777 exprs = .commaSepExprs(['RBRACKET_DBL'], false) 2778 return ExtIndexExpr(token, left, exprs) 2779 2771 2780 def literalList as ListLit 2772 2781 token = .expect('LBRACKET') 2773 2782 exprs = .commaSepExprs(['RBRACKET'], true) -
Source/CobraTokenizer.cobra
50 50 var _inSubstStringSingle = false 51 51 var _inSubstStringDouble = false 52 52 var _inDocString = false 53 var _lBracketDblCount as int 54 var _rBSpecStack as Collections.Stack = Collections.Stack() 53 55 54 56 def init 55 57 base.init … … 62 64 base.addInfo(sb) 63 65 sb.append('_indentCount=[_indentCount], ') 64 66 sb.append('_substLBracketCount=[_substLBracketCount], ') 67 sb.append('_lBracketDblCount=[_lBracketDblCount], ') 65 68 sb.append('_inSubstStringSingle=[_inSubstStringSingle], ') 66 69 sb.append('_inDocString=[_inDocString]') 67 70 … … 116 119 r'STRING_RAW_DOUBLE r"[^"\n]*"', 117 120 118 121 # substituted strings 122 r'RBRACKET_DBL_SPECIAL ]]', 119 123 r'RBRACKET_SPECIAL ]', 120 124 r"STRING_START_SINGLE '", # see "def make_STRING_FOO_BAR" 121 125 r"STRING_PART_SINGLE '", … … 159 163 r'ASSIGN =', 160 164 r'LPAREN \(', 161 165 r'RPAREN \)', 166 r'LBRACKET_DBL \[\[', 162 167 r'LBRACKET \[', 168 r'RBRACKET_DBL \]\]', 163 169 r'RBRACKET \]', 164 170 r'LCURLY \{', 165 171 r'RCURLY \}', … … 210 216 base._reset 211 217 _indentCount = 0 212 218 _substLBracketCount = 0 219 _lBracketDblCount = 0 213 220 214 221 def makeSTRING_START_SINGLE(definition as String) as TokenDef? 215 222 return StringStartTokenDef('STRING_START_SINGLE', definition[0]) … … 228 235 229 236 def makeSTRING_STOP_DOUBLE(definition as String) as TokenDef? 230 237 return StringStopTokenDef('STRING_STOP_DOUBLE', definition[0]) 231 238 239 def makeLBRACKET_DBL(definition as String) as TokenDef? 240 return LBracketDblTokenDef('LBRACKET_DBL', definition, this) 241 232 242 def afterStart is override 233 243 base.afterStart 234 244 # CC: 235 245 # _tokenDefsByWhich['STRING_PART_SINGLE'].isActiveCall = def(tokenizer)=tokenizer.inSubstStringSingle 236 246 # _tokenDefsByWhich['STRING_STOP_SINGLE'].isActiveCall = def(tokenizer)=tokenizer.inSubstStringSingle 237 for which in 'RBRACKET_SPECIAL STRING_PART_SINGLE STRING_STOP_SINGLE STRING_PART_DOUBLE STRING_STOP_DOUBLE STRING_PART_FORMAT'.split(c' ')247 for which in 'RBRACKET_SPECIAL RBRACKET_DBL_SPECIAL RBRACKET_DBL STRING_PART_SINGLE STRING_STOP_SINGLE STRING_PART_DOUBLE STRING_STOP_DOUBLE STRING_PART_FORMAT'.split(c' ') 238 248 _tokenDefsByWhich[which].isActive = false 239 249 240 250 def isActiveCall(tok as TokenDef) as bool is override … … 610 620 _tokenDefsByWhich['STRING_STOP_DOUBLE'].isActive = false 611 621 _tokenDefsByWhich['STRING_PART_FORMAT'].isActive = false 612 622 return tok 623 624 # Swap out and deactivate a (possibly inactive) lookout Token for 625 # another one. 626 # pair to unSwapActive() 627 def _swapActive(currTokName as String, nextTokName as String) 628 _rBSpecStack.push(_tokenDefsByWhich[currTokName].isActive) 629 _tokenDefsByWhich[currTokName].isActive = false 630 _tokenDefsByWhich[nextTokName].isActive = true 631 632 # Deactivate and swapout a lookout Token for a (possibly inactive) 633 # previous paired one 634 # pair to swapActive() 635 def _unSwapActive(currTokName as String, prevTokName as String) 636 require 637 _rBSpecStack.count 638 body 639 _tokenDefsByWhich[currTokName].isActive = false 640 _tokenDefsByWhich[prevTokName].isActive = _rBSpecStack.pop to bool 641 642 def onLBRACKET_DBL(tok as IToken) as IToken 643 _lBracketDblCount += 1 644 if _inSubstStringSingle or _inSubstStringDouble 645 # change to lookout for ']]' rather than ']' closing subst in String. 646 ._swapActive('RBRACKET_SPECIAL', 'RBRACKET_DBL_SPECIAL') 647 else 648 if _lBracketDblCount==1 649 _tokenDefsByWhich['RBRACKET_DBL'].isActive = true 650 return tok 651 652 def onRBRACKET_DBL(tok as IToken) as IToken 653 _lBracketDblCount -= 1 654 if _lBracketDblCount == 0 655 _tokenDefsByWhich['RBRACKET_DBL'].isActive = false 656 return tok 657 658 def onRBRACKET_DBL_SPECIAL(tok as IToken) as IToken 659 require 660 _inSubstStringSingle or _inSubstStringDouble 661 _lBracketDblCount 662 body 663 _lBracketDblCount -= 1 664 if _lBracketDblCount == 0 665 # revert back to any previous RBRACKET_SPECIAL lookout 666 ._unSwapActive('RBRACKET_DBL_SPECIAL', 'RBRACKET_SPECIAL') 667 tok.which = 'RBRACKET_DBL' 668 return tok 613 669 614 615 670 def onLBRACKET(tok as IToken) as IToken 616 671 if _inSubstStringSingle or _inSubstStringDouble 617 672 _substLBracketCount += 1 618 673 if _substLBracketCount==1 619 _tokenDefsByWhich['RBRACKET_SPECIAL'].isActive = true674 ._swapActive('RBRACKET_DBL_SPECIAL', 'RBRACKET_SPECIAL') 620 675 assert _tokenDefsByWhich['STRING_PART_FORMAT'].isActive 621 676 _tokenDefsByWhich['STRING_PART_FORMAT'].isActive = false 622 677 return tok … … 628 683 body 629 684 _substLBracketCount -= 1 630 685 if _substLBracketCount == 0 631 _tokenDefsByWhich['RBRACKET_SPECIAL'].isActive = false686 ._unSwapActive('RBRACKET_SPECIAL', 'RBRACKET_DBL_SPECIAL') 632 687 assert not _tokenDefsByWhich['STRING_PART_FORMAT'].isActive 633 688 _tokenDefsByWhich['STRING_PART_FORMAT'].isActive = true 634 689 tok.which = 'RBRACKET' # tricky, tricky. the parser never sees an RBRACKET_SPECIAL … … 886 941 if c == quote, return sb.toString 887 942 if c == c'\\', isEscaped = true 888 943 return nil 944 945 class LBracketDblTokenDef 946 inherits TokenRegexDef 947 """ 948 This would work using default TokenRegexDef except that matches 949 '[[' in literal lists of lists or arrays of lists 950 e.g. @[[1,2],[3,4]]. [['a',1],['b',2]] 951 Thats avoidable by inserting whitespace into literal to disambiguate 952 the [[ token e.g. @[ [1,2], [3,4] ] [ ['a',1], ['b',2] ] 953 But it's nicer if we handle it regardless of ws 954 """ 955 956 var _tknizr as Tokenizer 957 958 def init(which as String, regExSource as String, tknizer as Tokenizer) 959 base.init(which, regExSource) 960 _tknizr = tknizer 961 962 # do a regex match and succeed if match and lastToken is an id 963 def match(input as String) as TokenMatch? is override 964 reMatch = _re.match(input) 965 if reMatch.success and _ 966 _tknizr.lastToken and _tknizr.lastToken.which == 'ID' 967 assert reMatch.index == 0 968 assert reMatch.value 969 # print "LBracketDbl match and ID" 970 return TokenRegexMatch(reMatch) 971 else 972 return nil -
Tests/100-basics/067-extended-indexing.cobra
1 namespace Test 2 3 class ExtIdx 4 shared 5 var _a= @['abc', 'def', 'ghi', 'jkl', 'mno', 'pqr', 'stu', 6 'vw', 'xyz', 7 ] 8 var _b = @[1,2,3,4,5] 9 var _list = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqr', 10 'stu', 'vw', 'xyz', 11 ] 12 13 pro [i as int] as int 14 get 15 return i*i 16 set 17 pass 18 19 def checkPropIdx is shared 20 x=ExtIdx() 21 assert x[1]==1 22 assert x[2]==4 23 assert x[3]==9 24 assert x[-3]==9 25 assert x[[-3]]==9 26 x[1] = 5 27 x[2] = 6 28 29 def extListIdx is shared 30 l = _list 31 assert l[0] == 'abc' 32 assert l[l.count-1] == 'xyz' 33 t as String = l[1] 34 s = t 35 assert s==t 36 assert t==l[1] 37 assert l[0] == 'abc' 38 assert l[1] == 'def' 39 assert l[2] == 'ghi' 40 assert l[3] == 'jkl' 41 assert l[4] == 'mno' 42 assert l[5] == 'pqr' 43 assert l[6] == 'stu' 44 assert l[7] == 'vw' 45 assert l[8] == 'xyz' 46 assert l[0] == 'abc' 47 48 # List Ext Indexing positive indexes 49 50 l = _list 51 assert l[[0]] == 'abc' 52 assert l[[l.count-1]] == 'xyz' 53 t as String = l[[1]] 54 s = t 55 assert s==t 56 assert t==l[[1]] 57 assert l[[0]] == 'abc' 58 assert l[[1]] == 'def' 59 assert l[[2]] == 'ghi' 60 assert l[[3]] == 'jkl' 61 assert l[[4]] == 'mno' 62 assert l[[5]] == 'pqr' 63 assert l[[6]] == 'stu' 64 assert l[[7]] == 'vw' 65 assert l[[8]] == 'xyz' 66 assert l[[0]] == 'abc' 67 68 # List Ext Indexing negative values 69 70 l = _list 71 x = l[[-1]] 72 assert x == 'xyz' 73 assert l[[l.count-1]] == 'xyz' 74 y = l[[-1]] 75 assert l[[l.count-1]] == y 76 t as String = l[[-2]] 77 s = t 78 assert s==t 79 assert t==l[[-2]] 80 assert s==l[[-2]] 81 assert l[[-9]] == 'abc' 82 assert l[[-8]] == 'def' 83 assert l[[-7]] == 'ghi' 84 assert l[[-6]] == 'jkl' 85 assert l[[-5]] == 'mno' 86 assert l[[-4]] == 'pqr' 87 assert l[[-3]] == 'stu' 88 assert l[[-2]] == 'vw' 89 assert l[[-1]] == 'xyz' 90 expect ArgumentOutOfRangeException 91 assert l[[-19]] == 'abc' 92 93 94 def extStrIdx is shared 95 96 s as String = 'aoeu' 97 c as char = s[0] 98 assert c==c'a' 99 100 assert '[s][s]'=='aoeuaoeu' 101 102 # now the real test: 103 assert '[s[0]]'=='a' 104 105 c = s[[s.length-1]] 106 assert c==c'u' 107 c=s[0] 108 assert c==c'a' 109 c=s[1] 110 assert c==c'o' 111 c=s[2] 112 assert c==c'e' 113 c=s[3] 114 assert c==c'u' 115 116 # String ext indexing positive indexes 117 s = 'aoeu' 118 c = s[0] 119 assert c==c'a' 120 121 assert '[s][s]'=='aoeuaoeu' 122 123 c = s[[s.length-1]] 124 assert c==c'u' 125 c=s[[0]] 126 assert c==c'a' 127 c=s[[1]] 128 assert c==c'o' 129 c=s[[2]] 130 assert c==c'e' 131 c=s[[3]] 132 assert c==c'u' 133 134 # String ext indexing negative indexes 135 136 s = 'aoeu' 137 c = s[[-1]] 138 assert c==c'u' 139 140 assert '[s][s]'=='aoeuaoeu' 141 142 # now the real test: 143 # assert '[s[-1] ]'=='u' 144 assert '[s[[-1]]]'=='u' 145 ## breaks here a = [[1,2],[3,4]] # treat as [ [1,2], [3,4] ] 146 147 c = s[s.length-1] 148 assert c==c'u' 149 c=s[[-1]] 150 assert c==c'u' 151 c=s[[-2]] 152 assert c==c'e' 153 c=s[[-3]] 154 assert c==c'o' 155 c=s[[-4]] 156 assert c==c'a' 157 expect IndexOutOfRangeException 158 c=s[[-5]] 159 assert c==c'a' 160 expect IndexOutOfRangeException 161 c=s[[-6]] 162 assert c==c'a' 163 164 def extArrIdx is shared 165 a = _a 166 assert a[0] == 'abc' 167 assert a[a.length-1] == 'xyz' 168 t as String = a[1] 169 s = t 170 assert s==t 171 assert s==a[1] 172 173 b = _b 174 assert b[0] == 1 175 assert b[b.length-1] == 5 176 bt = b[1] 177 bs = bt 178 assert bs==bt 179 assert bs==b[1] 180 assert b[0] == 1 181 assert b[1] == 2 182 assert b[2] == 3 183 assert b[3] == 4 184 assert b[4] == 5 185 186 # Array ext indexing positive indexes 187 a = _a 188 assert a[[0]] == 'abc' 189 assert a[[a.length-1]] == 'xyz' 190 t as String = a[[1]] 191 s = t 192 assert s==t 193 assert s==a[[1]] 194 195 b = _b 196 assert b[[0]] == 1 197 assert b[[b.length-1]] == 5 198 bt = b[[1]] 199 bs = bt 200 assert bs==bt 201 assert bs==b[[1]] 202 assert b[[0]] == 1 203 assert b[[1]] == 2 204 assert b[[2]] == 3 205 assert b[[3]] == 4 206 assert b[[4]] == 5 207 208 # Array ext Indexing - negative indexes 209 a = _a 210 assert a[[-1]] == 'xyz' 211 expect IndexOutOfRangeException 212 assert a[[-10]] == 'abc' 213 t as String = a[[-2]] 214 s = t 215 assert s==t 216 assert s==a[[-2]] 217 218 #b = @[1,2,3,4,5] 219 b = _b 220 assert b[[-1]] == 5 221 expect IndexOutOfRangeException 222 assert b[[-10]] == 1 223 bt = b[[-3]] 224 bs = bt 225 assert bs==bt 226 assert bs==b[[-3]] 227 assert b[[0]] == 1 228 assert b[[-1]] == 5 229 assert b[[-2]] == 4 230 assert b[[-3]] == 3 231 assert b[[-4]] == 2 232 assert b[[-5]] == 1 233 expect IndexOutOfRangeException 234 assert b[[-6]] == 1 235 236 assert b[[-1]] == b[b.length-1] 237 assert b[[-2]] == 4 238 assert b[[-3]] == 3 239 assert b[[-4]] == 2 240 assert b[[-5]] == 1 241 expect IndexOutOfRangeException 242 assert b[[-6]] == 1 243 244 def arrNegIdx1 is shared 245 a = _a #@['abc', 'def', 'ghi', 'jkl', 'mno', 'pqr', 'stu', 'vw', 'xyz'] 246 i=-1 247 assert a[[i]] == 'xyz' 248 i=-10 249 expect IndexOutOfRangeException 250 assert a[[i]] == 'abc' 251 i=-2 252 t as String = a[[i]] 253 s = t 254 assert s==t 255 assert s==a[[i]] 256 257 #b = @[1,2,3,4,5] 258 b = _b 259 i=-1 260 assert b[[i]] == 5 261 i=-10 262 expect IndexOutOfRangeException 263 assert b[[i]] == 1 264 i=-3 265 bt = b[[i]] 266 bs = bt 267 assert bs==bt 268 assert bs==b[[i]] 269 i=0 270 assert b[[i]] == 1 271 j=5 272 for i in -1:-5:-1 273 assert b[[i]] == j 274 if j>1 275 j -= 1 276 277 # Extended indexing in String substitutions 278 def extIdxInStrSubst is shared 279 280 s as String = 'aoeu' 281 c as char = s[0] 282 assert c==c'a' 283 ia = @[1,2,-1,3] 284 285 c = s[[-1]] 286 assert c==c'u' 287 288 # now the real tests: 289 assert '[s[[-1]]]'=='u' 290 291 assert '[s[ia[1]]]!'=='e!' 292 assert '[s[[ia[1]]]]!' == 'e!' 293 assert '[s[[ia[1] ]] ]!' == 'e!' 294 assert '[s[[ia[[1]]]]]!' == 'e!' 295 296 assert '[s[[ia[[-1]]]]]!' == 'u!' 297 assert '[ s[[ ia[[-1]] ]] ]!' == 'u!' 298 assert '-[s[[ia[[-2]]]]]!' == '-u!' 299 300 def tokenCompileTest is shared 301 # these will fail to compile with recognition of dbl [ 302 # as LBRACKET_DBL if tokeniser not clever 303 a = [[1,2],[3,4]] # treat as [ [1,2], [3,4] ] 304 b = @[[1,1],[2,2]] # treat as @[ [1,1],[2,2] ] 305 c = [1, [2,3], [[4,5],[6,7]]] 306 assert a.count == 2 307 assert b.length == 2 308 assert c.count == 3 309 310 def main is shared 311 .extListIdx 312 .extArrIdx 313 .extStrIdx 314 .extIdxInStrSubst 315 316 .checkPropIdx 317