1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275
|
"======================================================================
|
| Refactoring Browser - Smalltalk parser and scanner
|
|
======================================================================"
"======================================================================
|
| Copyright 1998-2000 The Refactory, Inc.
|
| This file is distributed together with GNU Smalltalk.
|
======================================================================"
Object subclass: #RBParser
instanceVariableNames: 'scanner currentToken nextToken errorBlock tags source '
classVariableNames: ''
poolDictionaries: ''
category: 'Refactory-Parser'!
!RBParser methodsFor: 'accessing'!
errorBlock: aBlock
errorBlock := aBlock.
scanner notNil ifTrue: [scanner errorBlock: aBlock]!
initializeParserWith: aString type: aSymbol
source := aString.
self scanner: (self scannerClass perform: aSymbol
with: (ReadStream on: aString)
with: self errorBlock)!
initializeParserWithStream: aStream type: aSymbol
source := nil.
self scanner: (self scannerClass perform: aSymbol
with: aStream
with: self errorBlock)!
parseExpression
| node |
node := self parseStatements: false.
self atEnd ifFalse: [self parserError: 'Unknown input at end'].
^node!
parseSmalltalk
[ self parseDoits ]
whileTrue: [ self parseMethodDefinitionList ].
^self result!
parseMethod: aString
| node |
node := self parseMethod.
self atEnd ifFalse: [self parserError: 'Unknown input at end'].
node source: aString.
^node!
scannerClass
^RBScanner! !
!RBParser methodsFor: 'error handling'!
errorBlock
^errorBlock isNil
ifTrue: [[:message :position | ]]
ifFalse: [errorBlock]!
errorPosition
^currentToken start!
parserWarning: aString
"Raise a Warning"
Warning signal: aString!
parserError: aString
"Evaluate the block. If it returns raise an error"
self errorBlock value: aString value: self errorPosition.
self error: aString! !
!RBParser methodsFor: 'initialize-release'!
scanner: aScanner
scanner := aScanner.
tags := nil.
self step! !
!RBParser methodsFor: 'private'!
addCommentsTo: aNode
aNode comments: scanner getComments!
nextToken
^nextToken isNil
ifTrue: [nextToken := scanner next]
ifFalse: [nextToken]!
step
nextToken notNil ifTrue:
[currentToken := nextToken.
nextToken := nil.
^currentToken].
currentToken := scanner next! !
!RBParser methodsFor: 'private-parsing'!
parseArgs
| args |
args := OrderedCollection new.
[currentToken isIdentifier]
whileTrue: [args add: self parseVariableNode].
^args!
parseArrayConstructor
| position node |
position := currentToken start.
self step.
node := RBArrayConstructorNode new.
node left: position.
node body: (self parseStatements: false).
(currentToken isSpecial and: [currentToken value == $}])
ifFalse: [self parserError: '''}'' expected'].
node right: currentToken start.
self step.
^node!
parseAssignment
"Need one token lookahead to see if we have a ':='. This method could
make it possible to assign the literals true, false and nil."
| node position |
(currentToken isIdentifier and: [self nextToken isAssignment])
ifFalse: [^self parseCascadeMessage].
node := self parseVariableNode.
position := currentToken start.
self step.
^RBAssignmentNode variable: node
value: self parseAssignment
position: position!
parseBinaryMessage
| node |
node := self parseUnaryMessage.
[ currentToken isBinary]
whileTrue: [node := self parseBinaryMessageWith: node].
^node!
parseBinaryMessageWith: aNode
| binaryToken |
binaryToken := currentToken.
self step.
^RBMessageNode receiver: aNode
selectorParts: (Array with: binaryToken)
arguments: (Array with: self parseUnaryMessage)!
parseBinaryPattern
| binaryToken |
currentToken isBinary
ifFalse: [self parserError: 'Message pattern expected'].
binaryToken := currentToken.
self step.
^RBMethodNode selectorParts: (Array with: binaryToken)
arguments: (Array with: self parseVariableNode)!
parseBlock
| position node |
position := currentToken start.
self step.
node := self parseBlockArgsInto: RBBlockNode new.
node left: position.
node body: (self parseStatements: false).
(currentToken isSpecial and: [currentToken value == $]])
ifFalse: [self parserError: ''']'' expected'].
node right: currentToken start.
self step.
^node!
parseBlockArgsInto: node
| verticalBar args colons |
args := OrderedCollection new: 2.
colons := OrderedCollection new: 2.
verticalBar := false.
[currentToken isSpecial and: [currentToken value == $:]] whileTrue:
[colons add: currentToken start.
self step. ":"
verticalBar := true.
args add: self parseVariableNode].
verticalBar ifTrue:
[currentToken isBinary
ifTrue:
[node bar: currentToken start.
currentToken value == #|
ifTrue: [self step]
ifFalse:
[currentToken value == #'||'
ifTrue:
["Hack the current token to be the start
of temps bar"
currentToken value: #|;
start: currentToken start + 1]
ifFalse: [self parserError: '''|'' expected']]]
ifFalse:
[(currentToken isSpecial and: [currentToken value == $]])
ifFalse: [self parserError: '''|'' expected']]].
node arguments: args;
colons: colons.
^node!
parseCascadeMessage
| node receiver messages semicolons |
node := self parseKeywordMessage.
(currentToken isSpecial
and: [currentToken value == $; and: [node isMessage]]) ifFalse: [^node].
receiver := node receiver.
messages := OrderedCollection new: 3.
semicolons := OrderedCollection new: 3.
messages add: node.
[currentToken isSpecial and: [currentToken value == $;]] whileTrue:
[semicolons add: currentToken start.
self step.
messages add: (currentToken isIdentifier
ifTrue: [self parseUnaryMessageWith: receiver]
ifFalse:
[currentToken isKeyword
ifTrue: [self parseKeywordMessageWith: receiver]
ifFalse:
[| temp |
currentToken isBinary ifFalse: [self parserError: 'Message expected'].
temp := self parseBinaryMessageWith: receiver.
temp == receiver ifTrue: [self parserError: 'Message expected'].
temp]])].
^RBCascadeNode messages: messages semicolons: semicolons!
parseDoits
" Parses the stuff to be executed until a
! <class expression> methodsFor: <category string> ! "
| node |
[ self atEnd ifTrue: [ ^false ].
node := self parseStatements: false.
self step. "gobble doit terminating bang"
node statements size > 0 and: [ self evaluate: node ]
] whileFalse.
^true
!
parseKeywordMessage
^self parseKeywordMessageWith: self parseBinaryMessage!
parseKeywordMessageWith: node
| args isKeyword keywords |
args := OrderedCollection new: 3.
keywords := OrderedCollection new: 3.
isKeyword := false.
[currentToken isKeyword] whileTrue:
[keywords add: currentToken.
self step.
args add: self parseBinaryMessage.
isKeyword := true].
^isKeyword
ifTrue:
[RBMessageNode receiver: node
selectorParts: keywords
arguments: args]
ifFalse: [node]!
parseKeywordPattern
| keywords args |
keywords := OrderedCollection new: 2.
args := OrderedCollection new: 2.
[currentToken isKeyword] whileTrue:
[keywords add: currentToken.
self step.
args add: self parseVariableNode].
^RBMethodNode selectorParts: keywords arguments: args!
parseMessagePattern
^currentToken isIdentifier
ifTrue: [self parseUnaryPattern]
ifFalse:
[currentToken isKeyword
ifTrue: [self parseKeywordPattern]
ifFalse: [self parseBinaryPattern]]!
parseMethod
| methodNode |
methodNode := self parseMessagePattern.
self parseResourceTag.
self addCommentsTo: methodNode.
methodNode body: (self parseStatements: true).
methodNode tags: tags.
^methodNode!
parseMethodDefinitionList
"Called after first !, expecting a set of bang terminated
method definitions, followed by a bang"
| node start stop |
[ scanner atEnd or: [ currentToken isSpecial
and: [ currentToken value == $! ] ] ] whileFalse: [
start := currentToken start - 1.
node := self parseMethod.
"One -1 accounts for base-1 vs. base-0 (as above), the
other drops the bang because we have a one-token lookahead."
stop := currentToken start - 2.
node source: (scanner stream segmentFrom: start to: stop).
self step. "gobble method terminating bang"
self compile: node
].
self step.
self endMethodList
!
parseOptimizedExpression
| position node |
position := currentToken start.
self step.
node := RBOptimizedNode
left: position
body: (self parseStatements: false)
right: currentToken start.
(currentToken isSpecial and: [currentToken value == $)])
ifFalse: [self parserError: ''')'' expected'].
self step.
^node!
parseParenthesizedExpression
| leftParen node |
leftParen := currentToken start.
self step.
node := self parseAssignment.
^(currentToken isSpecial and: [currentToken value == $)])
ifTrue:
[node addParenthesis: (leftParen to: currentToken start).
self step.
node]
ifFalse: [self parserError: ''')'' expected']!
parsePatternBlock
| position node |
position := currentToken start.
self step.
node := self parseBlockArgsInto: RBPatternBlockNode new.
node left: position.
node body: (self parseStatements: false).
(currentToken isSpecial and: [currentToken value == $}])
ifFalse: [self parserError: '''}'' expected'].
node right: currentToken start.
self step.
^node!
parsePrimitiveIdentifier
| value token |
token := currentToken.
value := currentToken value.
self step.
value = 'true' ifTrue:
[^RBLiteralNode literalToken: (RBLiteralToken value: true
start: token start
stop: token start + 3)].
value = 'false' ifTrue:
[^RBLiteralNode literalToken: (RBLiteralToken value: false
start: token start
stop: token start + 4)].
value = 'nil' ifTrue:
[^RBLiteralNode literalToken: (RBLiteralToken value: nil
start: token start
stop: token start + 2)].
^RBVariableNode identifierToken: token!
parsePrimitiveLiteral
| token |
token := currentToken.
self step.
^RBLiteralNode literalToken: token!
parsePrimitiveObject
currentToken isIdentifier ifTrue: [^self parsePrimitiveIdentifier].
currentToken isLiteral ifTrue: [^self parsePrimitiveLiteral].
currentToken isSpecial ifTrue:
[currentToken value == $[ ifTrue: [^self parseBlock].
currentToken value == ${ ifTrue: [^self parseArrayConstructor].
currentToken value == $( ifTrue: [^self parseParenthesizedExpression]].
currentToken isPatternBlock ifTrue: [^self parsePatternBlock].
currentToken isOptimized ifTrue: [^self parseOptimizedExpression].
self parserError: 'Variable expected'!
parseResourceTag
| start |
[currentToken isBinary and: [currentToken value == #<]] whileTrue:
[start := currentToken start.
self step.
[scanner atEnd or: [currentToken isBinary and: [currentToken value == #>]]]
whileFalse: [self step].
(currentToken isBinary and: [currentToken value == #>])
ifFalse: [self parserError: '''>'' expected'].
tags isNil
ifTrue: [tags := OrderedCollection with: (start to: currentToken stop)]
ifFalse: [tags add: (start to: currentToken stop)].
self step]!
parseStatementList: tagBoolean into: sequenceNode
| statements return periods returnPosition node |
return := false.
statements := OrderedCollection new.
periods := OrderedCollection new.
self addCommentsTo: sequenceNode.
tagBoolean ifTrue: [self parseResourceTag].
[self atEnd
or: [currentToken isSpecial and: ['!])}' includes: currentToken value]]]
whileFalse:
[return ifTrue: [self parserError: 'End of statement list encounted'].
(currentToken isSpecial and: [currentToken value == $^])
ifTrue:
[returnPosition := currentToken start.
self step.
node := RBReturnNode return: returnPosition value: self parseAssignment.
self addCommentsTo: node.
statements add: node.
return := true]
ifFalse:
[node := self parseAssignment.
self addCommentsTo: node.
statements add: node].
(currentToken isSpecial and: [currentToken value == $.])
ifTrue:
[periods add: currentToken start.
self step]
ifFalse: [return := true]].
sequenceNode
statements: statements;
periods: periods.
^sequenceNode!
parseStatements: tagBoolean
| args leftBar rightBar |
args := #().
leftBar := rightBar := nil.
currentToken isBinary ifTrue: [currentToken value == #|
ifTrue:
[leftBar := currentToken start.
self step.
args := self parseArgs.
(currentToken isBinary and: [currentToken value = #|])
ifFalse: [self parserError: '''|'' expected'].
rightBar := currentToken start.
self step]
ifFalse: [currentToken value == #'||'
ifTrue:
[rightBar := (leftBar := currentToken start) + 1.
self step]]].
^self parseStatementList: tagBoolean into: (RBSequenceNode
leftBar: leftBar
temporaries: args
rightBar: rightBar)!
parseUnaryMessage
| node |
node := self parsePrimitiveObject.
[currentToken isIdentifier]
whileTrue: [node := self parseUnaryMessageWith: node].
^node!
parseUnaryMessageWith: aNode
| selector |
selector := currentToken.
self step.
^RBMessageNode receiver: aNode
selectorParts: (Array with: selector)
arguments: #()!
parseUnaryPattern
| selector |
selector := currentToken.
self step.
^RBMethodNode selectorParts: (Array with: selector) arguments: #()!
parseVariableNode
| node |
currentToken isIdentifier
ifFalse: [self parserError: 'Variable name expected'].
node := RBVariableNode identifierToken: currentToken.
self step.
^node! !
!RBParser methodsFor: 'overridable - parsing file-ins'!
compile: node
"do nothing by default"
!
endMethodList
"do nothing by default"
!
evaluate: node
"This should be overridden because its result affects the parsing
process: true means 'start parsing methods', false means 'keep
evaluating'. By default, always answer false."
^false
! !
!RBParser methodsFor: 'testing'!
atEnd
^currentToken class == RBToken! !
RBParser class
instanceVariableNames: ''!
!RBParser class methodsFor: 'accessing'!
parseExpression: aString
^self parseExpression: aString onError: nil!
parseExpression: aString onError: aBlock
| node parser |
parser := self new.
parser errorBlock: aBlock.
parser initializeParserWith: aString type: #on:errorBlock:.
node := parser parseExpression.
^(node statements size == 1 and: [node temporaries isEmpty])
ifTrue: [node statements first]
ifFalse: [node]!
parseMethod: aString
^self parseMethod: aString onError: nil!
parseMethod: aString onError: aBlock
| parser |
parser := self new.
parser errorBlock: aBlock.
parser initializeParserWith: aString type: #on:errorBlock:.
^parser parseMethod: aString!
parseRewriteExpression: aString
^self parseRewriteExpression: aString onError: nil!
parseRewriteExpression: aString onError: aBlock
| node parser |
parser := self new.
parser errorBlock: aBlock.
parser initializeParserWith: aString type: #rewriteOn:errorBlock:.
node := parser parseExpression.
^(node statements size == 1 and: [node temporaries isEmpty])
ifTrue: [node statements first]
ifFalse: [node]!
parseRewriteMethod: aString
^self parseRewriteMethod: aString onError: nil!
parseRewriteMethod: aString onError: aBlock
| parser |
parser := self new.
parser errorBlock: aBlock.
parser initializeParserWith: aString type: #rewriteOn:errorBlock:.
^parser parseMethod: aString!
parseSmalltalk: aString
^self parseSmalltalk: aString onError: nil!
parseSmalltalk: aString onError: aBlock
| parser |
parser := self new.
parser errorBlock: aBlock.
parser initializeParserWith: aString type: #on:errorBlock:.
parser parseSmalltalk.
^parser result!
parseSmalltalkStream: aStream
^self parseSmalltalkStream: aStream onError: nil!
parseSmalltalkStream: aStream onError: aBlock
| parser |
parser := self new.
parser errorBlock: aBlock.
parser initializeParserWithStream: aStream type: #on:errorBlock:.
parser parseSmalltalk.
^parser result!
parseSmalltalkFileIn: aFilename
^self parseSmalltalkFileIn: aFilename onError: nil!
parseSmalltalkFileIn: aFilename onError: aBlock
| parser file |
file := FileStream open: aFilename mode: FileStream read.
parser := self new.
parser errorBlock: aBlock.
parser initializeParserWithStream: file type: #on:errorBlock:.
parser parseSmalltalk.
^parser result!
!RBParser class methodsFor: 'parsing'!
parseMethodPattern: aString
| parser |
parser := self new.
parser errorBlock: [:error :position | ^nil].
parser initializeParserWith: aString type: #on:errorBlock:.
^parser parseMessagePattern selector! !
Stream subclass: #RBScanner
instanceVariableNames: 'stream buffer tokenStart currentCharacter characterType classificationTable saveComments comments extendedLanguage errorBlock '
classVariableNames: 'ClassificationTable PatternVariableCharacter '
poolDictionaries: ''
category: 'Refactory-Parser'!
!RBScanner methodsFor: 'accessing'!
classificationTable: anObject
classificationTable := anObject!
contents
| contentsStream |
contentsStream := WriteStream on: (Array new: 50).
self do: [:each | contentsStream nextPut: each].
^contentsStream contents!
errorBlock: aBlock
errorBlock := aBlock!
extendedLanguage
^extendedLanguage!
extendedLanguage: aBoolean
extendedLanguage := aBoolean!
flush!
getComments
| oldComments |
comments isEmpty ifTrue: [^nil].
oldComments := comments.
comments := OrderedCollection new: 1.
^oldComments!
ignoreComments
saveComments := false!
next
| token |
buffer reset.
tokenStart := stream position.
characterType == #eof ifTrue: [^RBToken start: tokenStart + 1]. "The EOF token should occur after the end of input"
token := self scanToken.
self stripSeparators.
^token!
nextPut: anObject
"Provide an error notification that the receiver does not
implement this message."
self shouldNotImplement!
saveComments
saveComments := true!
scanToken
"fast-n-ugly. Don't write stuff like this. Has been found to cause cancer in laboratory rats. Basically a
case statement. Didn't use Dictionary because lookup is pretty slow."
characterType == #alphabetic ifTrue: [^self scanIdentifierOrKeyword].
(characterType == #digit
or: [currentCharacter == $- and: [(self classify: stream peek) == #digit]])
ifTrue: [^self scanNumber].
characterType == #binary ifTrue: [^self scanBinary: RBBinarySelectorToken].
characterType == #special ifTrue: [^self scanSpecialCharacter].
currentCharacter == $' ifTrue: [^self scanLiteralString].
currentCharacter == $# ifTrue: [^self scanLiteral].
currentCharacter == $$ ifTrue: [^self scanLiteralCharacter].
(extendedLanguage and: [currentCharacter == PatternVariableCharacter])
ifTrue: [^self scanPatternVariable].
^self scannerError: 'Unknown character'!
position
^stream position!
stream
^stream! !
!RBScanner methodsFor: 'error handling'!
errorBlock
^errorBlock isNil
ifTrue: [[:message :position | ]]
ifFalse: [errorBlock]!
errorPosition
^stream position!
scannerError: aString
"Evaluate the block. If it returns raise an error"
self errorBlock value: aString value: self errorPosition.
self error: aString! !
!RBScanner methodsFor: 'initialize-release'!
on: aStream
buffer := WriteStream on: (String new: 60).
stream := aStream.
classificationTable := self class classificationTable.
saveComments := true.
extendedLanguage := false.
comments := OrderedCollection new! !
!RBScanner methodsFor: 'private'!
classify: aCharacter
| index |
aCharacter isNil ifTrue: [^nil].
index := aCharacter asInteger.
index == 0 ifTrue: [^#separator].
index > 255 ifTrue: [^nil].
^classificationTable at: index!
previousStepPosition
^characterType == #eof
ifTrue: [stream position]
ifFalse: [stream position - 1]!
step
stream atEnd ifTrue:
[characterType := #eof.
^currentCharacter := nil].
currentCharacter := stream next.
characterType := self classify: currentCharacter.
^currentCharacter! !
!RBScanner methodsFor: 'private-scanning numbers'!
isDigit: aChar base: base
aChar isNil ifTrue: [ ^false ].
base <= 10 ifTrue: [
aChar isDigit ifFalse: [ ^false ].
^(aChar value - $0 value) < base
].
^aChar isUppercase
ifTrue: [ (aChar value - $A value) < (base - 10) ]
ifFalse: [ aChar isDigit ]!
scanDigits: ch base: base
| c num |
c := ch.
num := 0.
[
[ c == $_ ] whileTrue: [ self step. c := currentCharacter ].
c notNil and: [ self isDigit: c base: base ]
] whileTrue: [
num := num * base + c digitValue.
self step.
c := currentCharacter
].
^num!
scanExtendedLiterals
| token |
self step.
currentCharacter == $(
ifTrue:
[self step.
^RBOptimizedToken start: tokenStart].
self scannerError: 'Expecting parentheses'!
scanFraction: ch num: num base: base return: aBlock
| c scale result |
c := ch.
scale := 0.
result := num.
[
[ c == $_ ] whileTrue: [ self step. c := currentCharacter ].
c notNil and: [ self isDigit: c base: base ]
] whileTrue: [ result := result * base + c digitValue.
self step.
c := currentCharacter.
scale := scale - 1.
].
aBlock value: result value: scale!
scanNumberValue
| mantissaParsed isNegative base exponent scale ch num |
mantissaParsed := isNegative := false.
base := 10.
exponent := nil.
currentCharacter == $- ifFalse: [ "could be radix"
num := self scanDigits: currentCharacter base: 10.
currentCharacter == $r
ifTrue: [ base := num truncated.
self step. "skip over 'r'" ]
ifFalse: [ mantissaParsed := true ] ].
"Here we've either
a) parsed base, an 'r' and are sitting on the following character
b) parsed the integer part of the mantissa, and are sitting on the char
following it, or
c) parsed nothing and are sitting on a - sign."
currentCharacter == $- ifTrue: [
mantissaParsed ifTrue: [ ^num ].
isNegative := true.
self step. "skip '-'"
].
mantissaParsed ifFalse: [
(self isDigit: currentCharacter base: base)
ifTrue: [ num := self scanDigits: currentCharacter base: base ]
ifFalse: [ self error: 'malformed number' ]
].
currentCharacter == $. ifTrue: [
stream peek isDigit
ifTrue: [
self step.
self scanFraction: currentCharacter
num: num
base: base
return: [ :n :s | num := n. exponent := s ].
]
].
isNegative ifTrue: [ num := num negated ].
currentCharacter == $s ifTrue: [
self step.
currentCharacter isNil ifTrue: [ currentCharacter := Character space ].
currentCharacter isDigit
ifTrue: [ scale := self scanDigits: ch base: 10 ]
ifFalse: [
exponent isNil ifTrue: [ exponent := 0 ].
"Might sit on the beginning of an identifier such as 123stu,
or on a ScaledDecimal literal lacking the scale such as 123s"
(currentCharacter == $_ or: [ currentCharacter isLetter ])
ifTrue: [ stream skip: -1. currentCharacter := $s ]
ifFalse: [ scale := exponent negated ]
].
^num asScaledDecimal: exponent scale: scale
].
currentCharacter == $e ifTrue: [ num := num asFloatE ] ifFalse: [
currentCharacter == $d ifTrue: [ num := num asFloatD ] ifFalse: [
currentCharacter == $q ifTrue: [ num := num asFloatQ ] ifFalse: [
^exponent isNil
ifTrue: [ num ]
ifFalse: [ num asFloatE * (base raisedToInteger: exponent) ]
]]].
ch := currentCharacter.
self step.
currentCharacter isNil ifTrue: [ currentCharacter := Character space ].
(currentCharacter == $_ or: [ currentCharacter isLetter ])
ifTrue: [ stream skip: -1. currentCharacter := ch ].
currentCharacter == $-
ifTrue: [
self step.
exponent := exponent -
(self scanDigits: currentCharacter base: 10).
]
ifFalse: [
currentCharacter isDigit ifTrue: [
exponent := exponent +
(self scanDigits: currentCharacter base: 10).
].
].
^num * (base raisedToInteger: exponent)
! !
!RBScanner methodsFor: 'private-scanning'!
scanAnySymbol
characterType == #alphabetic ifTrue: [^self scanSymbol].
characterType == #binary ifTrue: [^self scanBinary: RBLiteralToken].
^RBToken new!
scanBinary: aClass
"This doesn't parse according to the ANSI draft. It only parses 1 or 2 letter binary tokens."
| val |
buffer nextPut: currentCharacter.
self step.
(characterType == #binary and: [currentCharacter ~~ $-]) ifTrue:
[buffer nextPut: currentCharacter.
self step].
val := buffer contents.
val := val asSymbol.
^aClass value: val start: tokenStart!
scanByteArray
| byteStream number |
byteStream := WriteStream on: (ByteArray new: 100).
self step.
[self stripSeparators.
characterType == #digit] whileTrue:
[number := self scanNumber value.
(number isInteger and: [number between: 0 and: 255])
ifFalse: [self scannerError: 'Expecting 8-bit integer'].
byteStream nextPut: number].
currentCharacter == $] ifFalse: [self scannerError: ''']'' expected'].
self step. "]"
^RBLiteralToken
value: byteStream contents
start: tokenStart
stop: self previousStepPosition!
scanIdentifierOrKeyword
| tokenType token |
self scanName.
token := self scanNamespaceName.
token isNil
ifTrue: [
tokenType := (currentCharacter == $: and: [stream peek ~~ $=])
ifTrue:
[buffer nextPut: currentCharacter.
self step. ":"
RBKeywordToken]
ifFalse: [RBIdentifierToken].
token := tokenType value: buffer contents start: tokenStart
].
^token!
scanNamespaceName
| token |
currentCharacter == $.
ifTrue:
[(stream atEnd or: [(self classify: stream peek) ~~ #alphabetic])
ifTrue: [^nil]]
ifFalse:
[(currentCharacter == $: and: [stream peek == $:])
ifFalse: [^nil].
self step].
buffer nextPut: $. .
self step.
self scanName.
token := self scanNamespaceName.
token isNil ifTrue: [
token := RBIdentifierToken value: buffer contents start: tokenStart ].
^token!
scanLiteral
self step.
self stripSeparators.
characterType == #alphabetic ifTrue: [^self scanSymbol].
characterType == #binary
ifTrue: [^(self scanBinary: RBLiteralToken) stop: self previousStepPosition].
currentCharacter == $' ifTrue: [^self scanStringSymbol].
currentCharacter == $( ifTrue: [^self scanLiteralArray].
currentCharacter == $[ ifTrue: [^self scanByteArray].
currentCharacter == ${ ifTrue: [^self scanQualifier].
currentCharacter == $# ifTrue: [^self scanExtendedLiterals].
self scannerError: 'Expecting a literal type'!
scanLiteralArray
| arrayStream start |
arrayStream := WriteStream on: (Array new: 10).
self step.
start := tokenStart.
[self stripSeparators.
tokenStart := stream position.
currentCharacter == $)] whileFalse:
[arrayStream nextPut: self scanLiteralArrayParts.
buffer reset].
self step.
^RBLiteralToken
value: arrayStream contents
start: start
stop: self previousStepPosition!
scanLiteralArrayParts
currentCharacter == $# ifTrue: [^self scanLiteral].
characterType == #alphabetic
ifTrue:
[| token value |
token := self scanSymbol.
value := token value.
value == #nil ifTrue: [token value: nil].
value == #true ifTrue: [token value: true].
value == #false ifTrue: [token value: false].
^token].
(characterType == #digit
or: [currentCharacter == $- and: [(self classify: stream peek) == #digit]])
ifTrue: [^self scanNumber].
characterType == #binary
ifTrue: [^(self scanBinary: RBLiteralToken) stop: self previousStepPosition].
currentCharacter == $' ifTrue: [^self scanLiteralString].
currentCharacter == $$ ifTrue: [^self scanLiteralCharacter].
currentCharacter == $( ifTrue: [^self scanLiteralArray].
currentCharacter == $[ ifTrue: [^self scanByteArray].
^self scannerError: 'Unknown character in literal array'!
scanLiteralCharacter
| token |
self step. "$"
token := RBLiteralToken value: currentCharacter
start: tokenStart
stop: stream position.
self step. "char"
^token!
scanLiteralString
self step.
[currentCharacter isNil
ifTrue: [self scannerError: 'Unmatched '' in string literal.'].
currentCharacter == $' and: [self step ~~ $']]
whileFalse:
[buffer nextPut: currentCharacter.
self step].
^RBLiteralToken
value: buffer contents
start: tokenStart
stop: self previousStepPosition!
scanPatternVariable
buffer nextPut: currentCharacter.
self step.
currentCharacter == ${
ifTrue:
[self step.
^RBPatternBlockToken value: '`{' start: tokenStart].
[characterType == #alphabetic] whileFalse:
[characterType == #eof ifTrue: [self scannerError: 'Pattern variable expected'].
buffer nextPut: currentCharacter.
self step].
^self scanIdentifierOrKeyword!
scanName
[characterType == #alphabetic or: [characterType == #digit]] whileTrue:
[buffer nextPut: currentCharacter.
self step]!
scanNumber
^RBLiteralToken
value: self scanNumberValue
start: tokenStart
stop: self previousStepPosition!
scanQualifier
| nameStream |
self step. "{"
nameStream := WriteStream on: (String new: 10).
[currentCharacter == $}] whileFalse:
[nameStream nextPut: currentCharacter.
self step].
self step. "}"
^RBBindingToken
value: nameStream contents
start: tokenStart
stop: self previousStepPosition!
scanSpecialCharacter
| character |
currentCharacter == $: ifTrue:
[self step.
^currentCharacter == $=
ifTrue:
[self step.
RBAssignmentToken start: tokenStart]
ifFalse: [RBSpecialCharacterToken value: $: start: tokenStart]].
character := currentCharacter.
self step.
^RBSpecialCharacterToken value: character start: tokenStart!
scanStringSymbol
| literalToken |
literalToken := self scanLiteralString.
literalToken value: literalToken value asSymbol.
^literalToken!
scanSymbol
| lastPosition hasColon value startPosition |
hasColon := false.
startPosition := lastPosition := stream position.
[characterType == #alphabetic] whileTrue:
[self scanName.
currentCharacter == $:
ifTrue:
[buffer nextPut: $:.
hasColon := true.
lastPosition := stream position.
self step]].
value := buffer contents.
(hasColon and: [value last ~~ $:])
ifTrue:
[stream position: lastPosition.
self step.
value := value copyFrom: 1 to: lastPosition - startPosition + 1].
^RBLiteralToken
value: value asSymbol
start: tokenStart
stop: self previousStepPosition!
stripComment
| start stop |
start := stream position.
[self step == $"] whileFalse:
[characterType == #eof
ifTrue: [self scannerError: 'Unmatched " in comment.']].
stop := stream position.
self step.
saveComments ifFalse: [^self].
comments add: (start to: stop)!
stripSeparators
[[characterType == #separator]
whileTrue: [self step].
currentCharacter == $"]
whileTrue: [self stripComment]! !
!RBScanner methodsFor: 'testing'!
atEnd
^characterType == #eof!
isReadable
^true!
isWritable
^false! !
RBScanner class
instanceVariableNames: ''!
!RBScanner class methodsFor: 'accessing'!
classificationTable
ClassificationTable isNil ifTrue: [self initialize].
^ClassificationTable!
patternVariableCharacter
^PatternVariableCharacter! !
!RBScanner class methodsFor: 'class initialization'!
initialize
PatternVariableCharacter := $`.
ClassificationTable := Array new: 255.
self initializeChars: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_' to: #alphabetic.
self initializeChars: '01234567890' to: #digit.
self initializeChars: '%&*+,-/<=>?@\~|' to: #binary.
self initializeChars: '{}().:;[]^!' to: #special.
#(9 10 12 13 26 32) do: [:i | ClassificationTable at: i put: #separator]!
initializeChars: characters to: aSymbol
characters do: [:c | ClassificationTable at: c asInteger put: aSymbol]! !
!RBScanner class methodsFor: 'instance creation'!
on: aStream
| str |
str := self basicNew on: aStream.
str step.
str stripSeparators.
^str!
on: aStream errorBlock: aBlock
| str |
str := self basicNew on: aStream.
str errorBlock: aBlock;
step;
stripSeparators.
^str!
rewriteOn: aStream
| str |
str := self basicNew on: aStream.
str extendedLanguage: true; ignoreComments.
str step.
str stripSeparators.
^str!
rewriteOn: aStream errorBlock: aBlock
| str |
str := self basicNew on: aStream.
str extendedLanguage: true;
ignoreComments;
errorBlock: aBlock;
step;
stripSeparators.
^str! !
!RBScanner class methodsFor: 'testing'!
isSelector: aSymbol
| scanner token |
scanner := self basicNew.
scanner on: (ReadStream on: aSymbol asString).
scanner step.
token := scanner scanAnySymbol.
token isLiteral ifFalse: [^false].
token value isEmpty ifTrue: [^false].
^scanner atEnd!
isVariable: aString
| scanner token |
aString isString ifFalse: [^false].
aString isEmpty ifTrue: [^false].
(ClassificationTable at: aString first asInteger) == #alphabetic
ifFalse: [^false].
scanner := self basicNew.
scanner on: (ReadStream on: aString asString).
scanner errorBlock: [:s :p | ^false].
scanner step.
token := scanner scanIdentifierOrKeyword.
token isKeyword ifTrue: [^false].
^scanner atEnd! !
RBScanner initialize!
!PositionableStream methodsFor: 'compiling'!
name
"Answer a string that represents what the receiver is streaming on"
^'(%1 %2)' bindWith: self species article with: self species name
!
segmentFrom: startPos to: endPos
"Answer an object that, when sent #asString, will yield the result
of sending `copyFrom: startPos to: endPos' to the receiver"
^self copyFrom: startPos to: endPos
! !
!FileStream methodsFor: 'compiling'!
segmentFrom: startPos to: endPos
"Answer an object that, when sent #asString, will yield the result
of sending `copyFrom: startPos to: endPos' to the receiver"
^FileSegment
on: self name
startingAt: startPos
for: endPos - startPos + 1.
! !
|