Eliot Miranda uploaded a new version of Compiler to project The Inbox:
http://source.squeak.org/inbox/Compiler.quasiliteral-eem.280.mcz ==================== Summary ==================== Name: Compiler.quasiliteral-eem.280 Author: eem Time: 4 May 2014, 1:49:28.091 pm UUID: d6f78930-4e27-4378-ad72-beb74ca0d13d Ancestors: Compiler-nice.279 [Derived from Compiler-nice.279, and renamed from the bogusly named Compiler.quasiquote-eem.248] Add a quasi-literal form for "string interpolation", that allows convenient embedding of expressions within a format string, and provides a convenient way of embedding literal strings within an alternative literal string syntax whose string delimiter is different. e.g. `hello [#cruel] world!` evaluates to 'hello cruel world'. `S1[B1]...SN[BN]SN+1` is equivalent to { 'S1'. [B1] value. ... 'SN'. [BN] value. 'SN+1' } concatenateQuasiLiteral where concatenateQuasiLiteral sends asString to each element and answers the concatenation of those elements. however, single-statement blocks are inlined, so e.g. the above `hello [#cruel] world!` is compiled as { 'hello '. #cruel. ' world!' } concatenateQuasiLiteral See e.g. Tests.quasiliteral-eem.296 for tests and examples. =============== Diff against Compiler-nice.279 =============== Item was added: + ----- Method: Array>>concatenateQuasiLiteral (in category '*Compiler-support') ----- + concatenateQuasiLiteral + "This method is used in compilation of quasi-quote constructs. + It MUST NOT be deleted or altered." + + | s sz | + sz := self size. + s := WriteStream on: (String new: sz * 16). + 1 to: sz do: + [:i| s nextPutAll: (self at: i) asString]. + ^s contents! Item was added: + ----- Method: Decompiler>>checkForMacroMessage:selector:arguments: (in category 'control') ----- + checkForMacroMessage: rcvr selector: selector arguments: args + ^ (selector == #concatenateQuasiLiteral + and: [self checkForQuasiLiteral: rcvr selector: selector arguments: args]) + or: [(#closureCopy:copiedValues: == selector + and: [self checkForClosureCopy: rcvr arguments: args]) + or: [#blockCopy: == selector + and: [self checkForBlockCopy: rcvr]]]! Item was added: + ----- Method: Decompiler>>checkForQuasiLiteral:selector:arguments: (in category 'control') ----- + checkForQuasiLiteral: rcvr "<BraceNode>" selector: selector "<Symbol>" arguments: args "<Array>" + stack addLast: + ((MessageNode new + receiver: rcvr + selector: (SelectorNode new key: #concatenateQuasiLiteral code: nil) + arguments: args + precedence: 1) + notePrintingSelector: #printQuasiLiteralOn:indent:; + yourself). + ^true! Item was changed: ----- Method: Decompiler>>send:super:numArgs: (in category 'instruction decoding') ----- send: selector super: superFlag numArgs: numArgs | args rcvr selNode msgNode messages | args := Array new: numArgs. (numArgs to: 1 by: -1) do: [:i | args at: i put: stack removeLast]. rcvr := stack removeLast. superFlag ifTrue: [rcvr := constructor codeSuper]. + (self checkForMacroMessage: rcvr selector: selector arguments: args) ifFalse: - ((#(blockCopy: closureCopy:copiedValues:) includes: selector) - and: [self checkForBlock: rcvr selector: selector arguments: args]) ifFalse: [selNode := constructor codeAnySelector: selector. rcvr == CascadeFlag ifTrue: ["May actually be a cascade or an ifNil: for value." self willJumpIfFalse ifTrue: "= generated by a case macro" [selector == #= ifTrue: [" = signals a case statement..." statements addLast: args first. stack addLast: rcvr. "restore CascadeFlag" ^ self]. selector == #== ifTrue: [" == signals an ifNil: for value..." stack removeLast; removeLast. rcvr := stack removeLast. stack addLast: IfNilFlag; addLast: (constructor codeMessage: rcvr selector: selNode arguments: args). ^ self]] ifFalse: [(self willJumpIfTrue and: [selector == #==]) ifTrue: [" == signals an ifNotNil: for value..." stack removeLast; removeLast. rcvr := stack removeLast. stack addLast: IfNilFlag; addLast: (constructor codeMessage: rcvr selector: selNode arguments: args). ^ self]]. msgNode := constructor codeCascadedMessage: selNode arguments: args. stack last == CascadeFlag ifFalse: ["Last message of a cascade" statements addLast: msgNode. messages := self popTo: stack removeLast. "Depth saved by first dup" msgNode := constructor codeCascade: stack removeLast messages: messages]] ifFalse: [msgNode := constructor codeMessage: rcvr selector: selNode arguments: args]. stack addLast: msgNode]! Item was changed: ----- Method: MessageNode class>>initialize (in category 'class initialization') ----- initialize "MessageNode initialize" MacroSelectors := #( ifTrue: ifFalse: ifTrue:ifFalse: ifFalse:ifTrue: and: or: whileFalse: whileTrue: whileFalse whileTrue to:do: to:by:do: caseOf: caseOf:otherwise: ifNil: ifNotNil: ifNil:ifNotNil: ifNotNil:ifNil: + repeat + nil "space for concatenateQuasiLiteral" ). - repeat ). MacroTransformers := #( transformIfTrue: transformIfFalse: transformIfTrueIfFalse: transformIfFalseIfTrue: transformAnd: transformOr: transformWhile: transformWhile: transformWhile: transformWhile: transformToDo: transformToDo: transformCase: transformCase: transformIfNil: transformIfNil: transformIfNilIfNotNil: transformIfNotNilIfNil: + transformRepeat: + nil "space for concatenateQuasiLiteral" ). - transformRepeat: ). MacroEmitters := #( emitCodeForIf:encoder:value: emitCodeForIf:encoder:value: emitCodeForIf:encoder:value: emitCodeForIf:encoder:value: emitCodeForIf:encoder:value: emitCodeForIf:encoder:value: emitCodeForWhile:encoder:value: emitCodeForWhile:encoder:value: emitCodeForWhile:encoder:value: emitCodeForWhile:encoder:value: emitCodeForToDo:encoder:value: emitCodeForToDo:encoder:value: emitCodeForCase:encoder:value: emitCodeForCase:encoder:value: emitCodeForIfNil:encoder:value: emitCodeForIfNil:encoder:value: emitCodeForIf:encoder:value: emitCodeForIf:encoder:value: + emitCodeForRepeat:encoder:value: + nil "space for concatenateQuasiLiteral"). - emitCodeForRepeat:encoder:value:). MacroSizers := #( sizeCodeForIf:value: sizeCodeForIf:value: sizeCodeForIf:value: sizeCodeForIf:value: sizeCodeForIf:value: sizeCodeForIf:value: sizeCodeForWhile:value: sizeCodeForWhile:value: sizeCodeForWhile:value: sizeCodeForWhile:value: sizeCodeForToDo:value: sizeCodeForToDo:value: sizeCodeForCase:value: sizeCodeForCase:value: sizeCodeForIfNil:value: sizeCodeForIfNil:value: sizeCodeForIf:value: sizeCodeForIf:value: + sizeCodeForRepeat:value: + nil "space for concatenateQuasiLiteral"). - sizeCodeForRepeat:value:). MacroPrinters := #( printIfOn:indent: printIfOn:indent: printIfOn:indent: printIfOn:indent: printIfOn:indent: printIfOn:indent: printWhileOn:indent: printWhileOn:indent: printWhileOn:indent: printWhileOn:indent: printToDoOn:indent: printToDoOn:indent: printCaseOn:indent: printCaseOn:indent: printIfNil:indent: printIfNil:indent: printIfNilNotNil:indent: printIfNilNotNil:indent: + printRepeatOn:indent: + printQuasiLiteralOn:indent:)! - printRepeatOn:indent:)! Item was added: + ----- Method: MessageNode>>notePrintingSelector: (in category 'macro transformations') ----- + notePrintingSelector: printingSelectorSymbol + "decompile" + + special := MacroPrinters indexOf: printingSelectorSymbol! Item was added: + ----- Method: MessageNode>>printQuasiLiteralOn:indent: (in category 'printing') ----- + printQuasiLiteralOn: aStream indent: level + aStream nextPut: $`. + receiver elements do: + [:parseNode| + (parseNode isLiteralNode + and: [parseNode key class == 'literal' class]) + ifTrue: + [parseNode key do: + [:char| + ('$`[' includes: char) ifTrue: + [aStream nextPut: $$]. + aStream nextPut: char]] + ifFalse: + [(parseNode isMessageNode + and: [parseNode selector key == #value + and: [parseNode receiver isBlockNode]]) + ifTrue: + [parseNode receiver printOn: aStream indent: 0] + ifFalse: + [aStream nextPut: $[. + parseNode printOn: aStream indent: 0. + aStream nextPut: $]]]]. + aStream nextPut: $`! Item was changed: ----- Method: Parser>>advance (in category 'scanning') ----- advance | this | prevMark := hereMark. prevEnd := hereEnd. this := here. here := token. hereType := tokenType. hereMark := mark. hereEnd := source position - (aheadChar == DoItCharacter ifTrue: [hereChar == DoItCharacter ifTrue: [0] ifFalse: [1]] ifFalse: [2]). + hereType ~~ #backQuote ifTrue: + [self scanToken]. - self scanToken. "Transcript show: 'here: ', here printString, ' mark: ', hereMark printString, ' end: ', hereEnd printString; cr." ^this! Item was added: + ----- Method: Parser>>nonQuasiLiteralExpression (in category 'expression types') ----- + nonQuasiLiteralExpression + + (hereType == #word and: [tokenType == #leftArrow]) + ifTrue: [^ self assignment: self variable]. + hereType == #leftBrace + ifTrue: [self braceExpression] + ifFalse: [self primaryExpression ifFalse: [^ false]]. + (self messagePart: 3 repeat: true) + ifTrue: [hereType == #semicolon ifTrue: [self cascade]]. + ^ true! Item was changed: ----- Method: Parser>>primaryExpression (in category 'expression types') ----- primaryExpression hereType == #word ifTrue: [parseNode := self variable. (parseNode isUndefTemp and: [self interactive]) ifTrue: [self queryUndefined]. parseNode nowHasRef. ^ true]. hereType == #leftBracket ifTrue: [self advance. self blockExpression. ^true]. hereType == #leftBrace ifTrue: [self braceExpression. ^true]. + hereType == #backQuote + ifTrue: + [self advance. + self quasiLiteralExpression. + ^true]. hereType == #leftParenthesis ifTrue: [self advance. self expression ifFalse: [^self expected: 'expression']. (self match: #rightParenthesis) ifFalse: [^self expected: 'right parenthesis']. ^true]. (hereType == #string or: [hereType == #number or: [hereType == #literal or: [hereType == #character]]]) ifTrue: [parseNode := encoder encodeLiteral: self advance. ^true]. (here == #- and: [tokenType == #number and: [1 + hereEnd = mark]]) ifTrue: [self advance. parseNode := encoder encodeLiteral: self advance negated. ^true]. ^false! Item was added: + ----- Method: Parser>>quasiLiteralExpression (in category 'expression types') ----- + quasiLiteralExpression + "`quasi-quote` + => { elements } concatenateQuasiLiteral + => MessageNode receiver: BraceNode selector: #concatenateQuasiLiteral. + + The syntax of quasi-quote is + quasi-quote := $` (characters | blockExpression) * $` + characters := (unescapedCharacter | $\ escapedCharacter) * + + The semantics of quasi-quote are that each blockExpression is evaluated + left-to-right in the scope of the enclosing method or block. The sequence + of interspersed character sequences and expressions are concatenated + left-to-right, sending asString to each element immediately prior to concatenation. + The concatenation is then the result of the expression. It is always a new string. + + The implementation inlines single-statement blocks into the brace expression that + comprises the receiver of concatenateQuasiLiteral" + + | elements locations stringStream loc | + elements := OrderedCollection new. + locations := OrderedCollection new. + stringStream := WriteStream on: (String new: 16). + [loc := hereMark + requestorOffset. + hereType == #doit ifTrue: + [^self expected: 'back quote']. + hereType == #leftBracket + ifTrue: + [self scanToken; advance. + parseNode := nil. + self blockExpression. + parseNode statements size = 1 + ifTrue: + [elements addLast: parseNode statements first] + ifFalse: + [elements addLast: (MessageNode new + receiver: parseNode + selector: #value + arguments: #() + precedence: 1 + from: encoder)]. + source position: hereMark - 1. + [source peek ~~ $]] whileTrue: + [source position: source position - 1]. + source next. + self step; step. + self setHereTypeForQuasiLiteral. + locations addLast: loc] + ifFalse: + [(self scanQuasiLiteralCharactersUsing: stringStream) ifNotNil: + [:lit| + elements addLast: lit. + locations addLast: loc]]. + hereType ~~ #backQuote] whileTrue. + parseNode := MessageNode new + receiver: (BraceNode new elements: elements sourceLocations: locations) + selector: #concatenateQuasiLiteral + arguments: #() + precedence: 1 + from: encoder. + self scanToken; advance. + ^true! Item was added: + ----- Method: Parser>>scanQuasiLiteralCharactersUsing: (in category 'scanning') ----- + scanQuasiLiteralCharactersUsing: stringStream + "Answer the next non-empty sequence of characters in a quasi-quote string, or nil, if none." + stringStream reset. + [hereChar ~~ $` and: [hereChar ~~ $[ and: [hereChar ~~ DoItCharacter]]] whileTrue: + [(hereChar == $$ and: ['`[$' includes: aheadChar]) + ifTrue: + [stringStream nextPut: aheadChar. self step] + ifFalse: + [stringStream nextPut: hereChar]. + self step]. + self setHereTypeForQuasiLiteral. + ^stringStream position > 0 ifTrue: + [encoder encodeLiteral: stringStream contents]! Item was added: + ----- Method: Parser>>setHereTypeForQuasiLiteral (in category 'scanning') ----- + setHereTypeForQuasiLiteral + "Set hereType appropriately based on hereChar. Used only for quasi-quote parsing." + hereChar == $` + ifTrue: + [hereType := #backQuote. + self step] + ifFalse: + [hereChar == $[ + ifTrue: + [hereType := #leftBracket. + self step] + ifFalse: + [hereChar == $] + ifTrue: + [hereType := #rightBracket. + self step] + ifFalse: + [hereChar == DoItCharacter ifTrue: + [hereType := #doit]]]]! Item was changed: ----- Method: Scanner class>>initializeTypeTable (in category 'initialization') ----- initializeTypeTable "self initializeTypeTable" | newTable | newTable := Array new: 256 withAll: #xBinary. "default" newTable atAll: #(9 10 12 13 32 ) put: #xDelimiter. "tab lf ff cr space" newTable atAll: ($0 asciiValue to: $9 asciiValue) put: #xDigit. 1 to: 255 do: [:index | (Character value: index) isLetter ifTrue: [newTable at: index put: #xLetter]]. newTable at: $" asciiValue put: #xDoubleQuote. newTable at: $# asciiValue put: #xLitQuote. newTable at: $$ asciiValue put: #xDollar. newTable at: $' asciiValue put: #xSingleQuote. + newTable at: $` asciiValue put: #backQuote. newTable at: $: asciiValue put: #xColon. newTable at: $( asciiValue put: #leftParenthesis. newTable at: $) asciiValue put: #rightParenthesis. newTable at: $. asciiValue put: #period. newTable at: $; asciiValue put: #semicolon. newTable at: $[ asciiValue put: #leftBracket. newTable at: $] asciiValue put: #rightBracket. newTable at: ${ asciiValue put: #leftBrace. newTable at: $} asciiValue put: #rightBrace. newTable at: $^ asciiValue put: #upArrow. newTable at: $_ asciiValue put: #xUnderscore. newTable at: $| asciiValue put: #verticalBar. TypeTable := newTable "bon voyage!!"! |
Free forum by Nabble | Edit this page |