<html>
<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="Content-Type">
</head>
<body bgcolor="#FFFFFF" text="#000000">
<font face="Georgia">I'm reminded at this point of SmalltalkAgents
which used curly quotes (single and double) so that literals and
comments could nest. FWIW<br>
<br>
</font>
<div class="moz-cite-prefix">On 2/6/13 1:14 AM, Eliot Miranda wrote:<br>
</div>
<blockquote
cite="mid:CAC20JE062uSyB4Lj9u_SHnyuHdebv4nS8e_+PpVnZj7uKxiYAQ@mail.gmail.com"
type="cite">Hi All,
<div><br>
</div>
<div> I had fun implementing a quasi-quote for Squeak today.
This is a convenient way of embedding substrings in format
strings (a little like printf), and, because it uses a different
quote character, a convenient way of embedding code form other
languages in a string literal.</div>
<div><br>
</div>
<div>An example of the former usage is</div>
<div> `hello [#cruel] world`</div>
<div>which evaluates to</div>
<div> 'hello cruel world'</div>
<div>And</div>
<div> `Float pi is [Float pi]`</div>
<div>
evaluates to</div>
<div> 'Float pi is 3.141592653589793'</div>
<div><br>
</div>
<div>An example of the latter use is that one can write</div>
<div> `printf("%s: %c\\n", "a string", 'C');`</div>
<div>instead of</div>
<div> 'printf("%s: %c\n", "a string", ''C'');'</div>
<div><br>
</div>
<div>This last example shows a limitation; The use of \ to escape
characters ($\ $[ and $`) in quasi-quote might not be such a
good choice.</div>
<div><br>
</div>
<div><br>
</div>
<div>Anyway I thought I'd put this in the in-box for people to
play with and savage. Please let me know what you think, both
about the semantics and the implementation. This is a quick
hack and I'm sure that there's plenty of scope for clean-up.</div>
<div><br>
</div>
<div>cheers</div>
<div>Eliot<br>
<br>
<div class="gmail_quote">On Tue, Feb 5, 2013 at 9:54 PM, <span
dir="ltr"><<a moz-do-not-send="true"
href="mailto:commits@source.squeak.org" target="_blank">commits@source.squeak.org</a>></span>
wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0
.8ex;border-left:1px #ccc solid;padding-left:1ex">A new
version of Compiler was added to project The Inbox:<br>
<a moz-do-not-send="true"
href="http://source.squeak.org/inbox/Compiler.quasiquote-eem.248.mcz"
target="_blank">http://source.squeak.org/inbox/Compiler.quasiquote-eem.248.mcz</a><br>
<br>
==================== Summary ====================<br>
<br>
Name: Compiler.quasiquote-eem.248<br>
Author: eem<br>
Time: 5 February 2013, 9:54:20.317 pm<br>
UUID: ef044906-3339-48cc-856b-9b5172e3e81b<br>
Ancestors: Compiler-cwp.247<br>
<br>
Add a quasi-quote form that allows convenient embedding<br>
of substrings within a format string, and provides a<br>
convenient way of embedding literal strings within an<br>
alternative literal string whose string delimiter is
different.<br>
<br>
e.g.<br>
`hello [#cruel] world!`<br>
evaluates to<br>
'hello cruel world'.<br>
<br>
`S1[B1]...SN[BN]SN+1`<br>
<br>
is equivalent to<br>
{ 'S1'. [B1] value. ... 'SN'. [BN] value. 'SN+1' }
concatenateQuasiQuote<br>
where concatenateQuasiQuote sends asString to each<br>
element and answers the concatenation of those elements.<br>
<br>
however, single-statement blocks are inlined, so e.g. the<br>
above `hello [#cruel] world!` is compiled as<br>
{ 'hello '. #cruel. ' world!' }
concatenateQuasiQuote<br>
<br>
See Tests.quasiquote-eem.188 for tests and examples.<br>
<br>
=============== Diff against Compiler-cwp.247
===============<br>
<br>
Item was added:<br>
+ ----- Method: Array>>concatenateQuasiQuote (in
category '*Compiler-support') -----<br>
+ concatenateQuasiQuote<br>
+ "This method is used in compilation of quasi-quote
constructs.<br>
+ It MUST NOT be deleted or altered."<br>
+<br>
+ | s sz |<br>
+ sz := self size.<br>
+ s := WriteStream on: (String new: sz * 16).<br>
+ 1 to: sz do:<br>
+ [:i| s nextPutAll: (self at: i) asString].<br>
+ ^s contents!<br>
<br>
Item was removed:<br>
- ----- Method:
Decompiler>>checkForBlock:selector:arguments: (in
category 'control') -----<br>
- checkForBlock: receiver selector: selector arguments:
arguments<br>
- selector == #blockCopy: ifTrue:<br>
- [^self checkForBlockCopy: receiver].<br>
- self assert: selector == #closureCopy:copiedValues:.<br>
- ^self checkForClosureCopy: receiver arguments:
arguments!<br>
<br>
Item was added:<br>
+ ----- Method:
Decompiler>>checkForMacroMessage:selector:arguments:
(in category 'control') -----<br>
+ checkForMacroMessage: rcvr selector: selector arguments:
args<br>
+ ^ (selector == #concatenateQuasiQuote<br>
+ and: [self checkForQuasiQuote: rcvr
selector: selector arguments: args])<br>
+ or: [(#closureCopy:copiedValues: == selector<br>
+ and: [self checkForClosureCopy: rcvr
arguments: args])<br>
+ or: [#blockCopy: == selector<br>
+ and: [self checkForBlockCopy: rcvr]]]!<br>
<br>
Item was added:<br>
+ ----- Method:
Decompiler>>checkForQuasiQuote:selector:arguments: (in
category 'control') -----<br>
+ checkForQuasiQuote: rcvr "<BraceNode>" selector:
selector "<Symbol>" arguments: args "<Array>"<br>
+ stack addLast:<br>
+ ((MessageNode new<br>
+ receiver: rcvr<br>
+ selector: (SelectorNode new
key: #concatenateQuasiQuote code: nil)<br>
+ arguments: args<br>
+ precedence: 1)<br>
+ notePrintingSelector:
#printQuasiQuoteOn:indent:;<br>
+ yourself).<br>
+ ^true!<br>
<br>
Item was changed:<br>
----- Method: Decompiler>>send:super:numArgs: (in
category 'instruction decoding') -----<br>
send: selector super: superFlag numArgs: numArgs<br>
<br>
| args rcvr selNode msgNode messages |<br>
args := Array new: numArgs.<br>
(numArgs to: 1 by: -1) do:<br>
[:i | args at: i put: stack removeLast].<br>
rcvr := stack removeLast.<br>
superFlag ifTrue: [rcvr := constructor codeSuper].<br>
+ (self checkForMacroMessage: rcvr selector: selector
arguments: args) ifFalse:<br>
- ((#(blockCopy: closureCopy:copiedValues:) includes:
selector)<br>
- and: [self checkForBlock: rcvr selector: selector
arguments: args]) ifFalse:<br>
[selNode := constructor codeAnySelector:
selector.<br>
rcvr == CascadeFlag<br>
ifTrue:<br>
["May actually be a cascade
or an ifNil: for value."<br>
self willJumpIfFalse<br>
ifTrue: "= generated
by a case macro"<br>
[selector ==
#= ifTrue:<br>
[" =
signals a case statement..."<br>
statements addLast: args first.<br>
stack addLast: rcvr. "restore CascadeFlag"<br>
^
self].<br>
selector ==
#== ifTrue:<br>
["
== signals an ifNil: for value..."<br>
stack removeLast; removeLast.<br>
rcvr
:= stack removeLast.<br>
stack addLast: IfNilFlag;<br>
addLast: (constructor<br>
codeMessage: rcvr<br>
selector: selNode<br>
arguments: args).<br>
^
self]]<br>
ifFalse:<br>
[(self
willJumpIfTrue and: [selector == #==]) ifTrue:<br>
["
== signals an ifNotNil: for value..."<br>
stack removeLast; removeLast.<br>
rcvr
:= stack removeLast.<br>
stack addLast: IfNilFlag;<br>
addLast: (constructor<br>
codeMessage: rcvr<br>
selector: selNode<br>
arguments: args).<br>
^
self]].<br>
msgNode := constructor<br>
codeCascadedMessage: selNode<br>
arguments: args.<br>
stack last == CascadeFlag
ifFalse:<br>
["Last message of a
cascade"<br>
statements addLast:
msgNode.<br>
messages := self
popTo: stack removeLast. "Depth saved by first dup"<br>
msgNode :=
constructor<br>
codeCascade: stack removeLast<br>
messages: messages]]<br>
ifFalse:<br>
[msgNode := constructor<br>
codeMessage: rcvr<br>
selector: selNode<br>
arguments: args].<br>
stack addLast: msgNode]!<br>
<br>
Item was changed:<br>
----- Method: MessageNode class>>initialize (in
category 'class initialization') -----<br>
initialize<br>
"MessageNode initialize"<br>
MacroSelectors :=<br>
#( ifTrue: ifFalse: ifTrue:ifFalse:
ifFalse:ifTrue:<br>
and: or:<br>
whileFalse: whileTrue: whileFalse
whileTrue<br>
to:do: to:by:do:<br>
caseOf: caseOf:otherwise:<br>
ifNil: ifNotNil: ifNil:ifNotNil:
ifNotNil:ifNil:<br>
+ repeat<br>
+ nil "space for
concatenateQuasiQuote" ).<br>
- repeat ).<br>
MacroTransformers :=<br>
#( transformIfTrue: transformIfFalse:
transformIfTrueIfFalse: transformIfFalseIfTrue:<br>
transformAnd: transformOr:<br>
transformWhile: transformWhile:
transformWhile: transformWhile:<br>
transformToDo: transformToDo:<br>
transformCase: transformCase:<br>
transformIfNil: transformIfNil:
transformIfNilIfNotNil: transformIfNotNilIfNil:<br>
+ transformRepeat:<br>
+ nil "space for
concatenateQuasiQuote" ).<br>
- transformRepeat: ).<br>
MacroEmitters :=<br>
#( emitCodeForIf:encoder:value:
emitCodeForIf:encoder:value:<br>
emitCodeForIf:encoder:value:
emitCodeForIf:encoder:value:<br>
emitCodeForIf:encoder:value:
emitCodeForIf:encoder:value:<br>
emitCodeForWhile:encoder:value:
emitCodeForWhile:encoder:value:<br>
emitCodeForWhile:encoder:value:
emitCodeForWhile:encoder:value:<br>
emitCodeForToDo:encoder:value:
emitCodeForToDo:encoder:value:<br>
emitCodeForCase:encoder:value:
emitCodeForCase:encoder:value:<br>
emitCodeForIfNil:encoder:value:
emitCodeForIfNil:encoder:value:<br>
emitCodeForIf:encoder:value:
emitCodeForIf:encoder:value:<br>
+ emitCodeForRepeat:encoder:value:<br>
+ nil "space for
concatenateQuasiQuote").<br>
- emitCodeForRepeat:encoder:value:).<br>
MacroSizers :=<br>
#( sizeCodeForIf:value:
sizeCodeForIf:value: sizeCodeForIf:value:
sizeCodeForIf:value:<br>
sizeCodeForIf:value:
sizeCodeForIf:value:<br>
sizeCodeForWhile:value:
sizeCodeForWhile:value: sizeCodeForWhile:value:
sizeCodeForWhile:value:<br>
sizeCodeForToDo:value:
sizeCodeForToDo:value:<br>
sizeCodeForCase:value:
sizeCodeForCase:value:<br>
sizeCodeForIfNil:value:
sizeCodeForIfNil:value: sizeCodeForIf:value:
sizeCodeForIf:value:<br>
+ sizeCodeForRepeat:value:<br>
+ nil "space for
concatenateQuasiQuote").<br>
- sizeCodeForRepeat:value:).<br>
MacroPrinters :=<br>
#( printIfOn:indent: printIfOn:indent:
printIfOn:indent: printIfOn:indent:<br>
printIfOn:indent: printIfOn:indent:<br>
printWhileOn:indent:
printWhileOn:indent: printWhileOn:indent:
printWhileOn:indent:<br>
printToDoOn:indent:
printToDoOn:indent:<br>
printCaseOn:indent:
printCaseOn:indent:<br>
printIfNil:indent:
printIfNil:indent: printIfNilNotNil:indent:
printIfNilNotNil:indent:<br>
+ printRepeatOn:indent:<br>
+ printQuasiQuoteOn:indent:)!<br>
- printRepeatOn:indent:)!<br>
<br>
Item was added:<br>
+ ----- Method: MessageNode>>notePrintingSelector: (in
category 'macro transformations') -----<br>
+ notePrintingSelector: printingSelectorSymbol<br>
+ "decompile"<br>
+<br>
+ special := MacroPrinters indexOf:
printingSelectorSymbol!<br>
<br>
Item was added:<br>
+ ----- Method: MessageNode>>printQuasiQuoteOn:indent:
(in category 'printing') -----<br>
+ printQuasiQuoteOn: aStream indent: level<br>
+ aStream nextPut: $`.<br>
+ receiver elements do:<br>
+ [:parseNode|<br>
+ (parseNode isLiteralNode<br>
+ and: [parseNode key class == 'literal'
class])<br>
+ ifTrue:<br>
+ [parseNode key do:<br>
+ [:char|<br>
+ ('`[\' includes:
char) ifTrue:<br>
+ [aStream
nextPut: $\].<br>
+ aStream nextPut:
char]]<br>
+ ifFalse:<br>
+ [(parseNode isMessageNode<br>
+ and: [parseNode selector
key == #value<br>
+ and: [parseNode receiver
isBlockNode]])<br>
+ ifTrue:<br>
+ [parseNode
receiver printOn: aStream indent: 0]<br>
+ ifFalse:<br>
+ [aStream
nextPut: $[.<br>
+ parseNode
printOn: aStream indent: 0.<br>
+ aStream
nextPut: $]]]].<br>
+ aStream nextPut: $`!<br>
<br>
Item was changed:<br>
----- Method: Parser>>advance (in category
'scanning') -----<br>
advance<br>
| this |<br>
prevMark := hereMark.<br>
prevEnd := hereEnd.<br>
this := here.<br>
here := token.<br>
hereType := tokenType.<br>
hereMark := mark.<br>
hereEnd := source position - (aheadChar ==
DoItCharacter<br>
ifTrue: [hereChar == DoItCharacter<br>
ifTrue: [0]<br>
ifFalse: [1]]<br>
ifFalse: [2]).<br>
+ hereType ~~ #backQuote ifTrue:<br>
+ [self scanToken].<br>
- self scanToken.<br>
"Transcript show: 'here: ', here printString, '
mark: ', hereMark printString, ' end: ', hereEnd
printString; cr."<br>
^this!<br>
<br>
Item was changed:<br>
----- Method: Parser>>expression (in category
'expression types') -----<br>
expression<br>
<br>
+ (hereType == #word and: [tokenType == #leftArrow])
ifTrue:<br>
+ [^self assignment: self variable].<br>
+ hereType == #backQuote<br>
+ ifTrue: [self quasiQuoteExpression]<br>
+ ifFalse:<br>
+ [hereType == #leftBrace<br>
+ ifTrue: [self
braceExpression]<br>
+ ifFalse:<br>
+ [self
primaryExpression ifFalse:<br>
+ [^false]]].<br>
+ (self messagePart: 3 repeat: true) ifTrue:<br>
+ [hereType == #semicolon ifTrue:<br>
+ [self cascade]].<br>
+ ^true!<br>
- (hereType == #word and: [tokenType == #leftArrow])<br>
- ifTrue: [^ self assignment: self variable].<br>
- hereType == #leftBrace<br>
- ifTrue: [self braceExpression]<br>
- ifFalse: [self primaryExpression ifFalse: [^
false]].<br>
- (self messagePart: 3 repeat: true)<br>
- ifTrue: [hereType == #semicolon ifTrue:
[self cascade]].<br>
- ^ true!<br>
<br>
Item was added:<br>
+ ----- Method: Parser>>nonQuasiQuoteExpression (in
category 'expression types') -----<br>
+ nonQuasiQuoteExpression<br>
+<br>
+ (hereType == #word and: [tokenType == #leftArrow])<br>
+ ifTrue: [^ self assignment: self variable].<br>
+ hereType == #leftBrace<br>
+ ifTrue: [self braceExpression]<br>
+ ifFalse: [self primaryExpression ifFalse: [^
false]].<br>
+ (self messagePart: 3 repeat: true)<br>
+ ifTrue: [hereType == #semicolon ifTrue:
[self cascade]].<br>
+ ^ true!<br>
<br>
Item was added:<br>
+ ----- Method: Parser>>quasiQuoteExpression (in
category 'expression types') -----<br>
+ quasiQuoteExpression<br>
+ "`quasi-quote`<br>
+ => { elements } concatenateQuasiQuote<br>
+ => MessageNode receiver:
BraceNode selector: #concatenateQuasiQuote.<br>
+<br>
+ The syntax of quasi-quote is<br>
+ quasi-quote := $` (characters |
blockExpression) * $`<br>
+ characters := (unescapedCharacter | $\
escapedCharacter) *<br>
+<br>
+ The semantics of quasi-quote are that each
blockExpression is evaluated<br>
+ left-to-right in the scope of the enclosing method
or block. The sequence<br>
+ of interspersed character sequences and expressions
are concatenated<br>
+ left-to-right, sending asString to each element
immediately prior to concatenation.<br>
+ The concatenation is then the result of the
expression. It is always a new string.<br>
+<br>
+ The implementation inlines single-statement blocks
into the brace expression that<br>
+ comprises the receiver of concatenateQuasiQuote"<br>
+<br>
+ | elements locations stringStream loc |<br>
+ elements := OrderedCollection new.<br>
+ locations := OrderedCollection new.<br>
+ stringStream := WriteStream on: (String new: 16).<br>
+ [loc := hereMark + requestorOffset.<br>
+ hereType == #doit ifTrue:<br>
+ [^self expected: 'back quote'].<br>
+ hereType == #leftBracket<br>
+ ifTrue:<br>
+ [self scanToken; advance.<br>
+ parseNode := nil.<br>
+ self blockExpression.<br>
+ parseNode statements size = 1<br>
+ ifTrue:<br>
+ [elements addLast:
parseNode statements first]<br>
+ ifFalse:<br>
+ [elements addLast:
(MessageNode new<br>
+
receiver: parseNode<br>
+
selector: #value<br>
+
arguments: #()<br>
+
precedence: 1<br>
+
from: encoder)].<br>
+ source position: hereMark - 1.<br>
+ [source peek ~~ $]] whileTrue:<br>
+ [source position: source
position - 1].<br>
+ source next.<br>
+ self step; step.<br>
+ self setHereTypeForQuasiQuote.<br>
+ locations addLast: loc]<br>
+ ifFalse:<br>
+ [(self
scanQuasiQuoteCharactersUsing: stringStream) ifNotNil:<br>
+ [:lit|<br>
+ elements addLast: lit.<br>
+ locations addLast: loc]].<br>
+ hereType ~~ #backQuote] whileTrue.<br>
+ parseNode := MessageNode new<br>
+ receiver: (BraceNode
new elements: elements sourceLocations: locations)<br>
+ selector:
#concatenateQuasiQuote<br>
+ arguments: #()<br>
+ precedence: 1<br>
+ from: encoder.<br>
+ self scanToken; advance.<br>
+ ^true!<br>
<br>
Item was changed:<br>
+ ----- Method: Parser>>queriedUnusedTemporaries (in
category 'temps') -----<br>
- ----- Method: Parser>>queriedUnusedTemporaries (in
category 'accessing') -----<br>
queriedUnusedTemporaries<br>
<br>
queriedUnusedTemporaries ifNil:<br>
[queriedUnusedTemporaries := Dictionary
new].<br>
^queriedUnusedTemporaries!<br>
<br>
Item was added:<br>
+ ----- Method: Parser>>scanQuasiQuoteCharactersUsing:
(in category 'scanning') -----<br>
+ scanQuasiQuoteCharactersUsing: stringStream<br>
+ "Answer the next non-empty sequence of characters in
a quasi-quote string, or nil, if none."<br>
+ stringStream reset.<br>
+ [hereChar ~~ $` and: [hereChar ~~ $[ and: [hereChar
~~ DoItCharacter]]] whileTrue:<br>
+ [hereChar == $\<br>
+ ifTrue:<br>
+ [stringStream nextPut:
aheadChar. self step]<br>
+ ifFalse:<br>
+ [stringStream nextPut:
hereChar].<br>
+ self step].<br>
+ self setHereTypeForQuasiQuote.<br>
+ ^stringStream position > 0 ifTrue:<br>
+ [encoder encodeLiteral: stringStream
contents]!<br>
<br>
Item was added:<br>
+ ----- Method: Parser>>setHereTypeForQuasiQuote (in
category 'scanning') -----<br>
+ setHereTypeForQuasiQuote<br>
+ "Set hereType appropriately based on hereChar. Used
only for quasi-quote parsing."<br>
+ hereChar == $`<br>
+ ifTrue:<br>
+ [hereType := #backQuote.<br>
+ self step]<br>
+ ifFalse:<br>
+ [hereChar == $[<br>
+ ifTrue:<br>
+ [hereType :=
#leftBracket.<br>
+ self step]<br>
+ ifFalse:<br>
+ [hereChar ==
DoItCharacter ifTrue:<br>
+ [hereType :=
#doit]]]!<br>
<br>
Item was changed:<br>
+ ----- Method: Parser>>tempsMark (in category
'temps') -----<br>
- ----- Method: Parser>>tempsMark (in category
'accessing') -----<br>
tempsMark<br>
^ tempsMark!<br>
<br>
Item was changed:<br>
+ ----- Method: Parser>>tempsMark: (in category
'temps') -----<br>
- ----- Method: Parser>>tempsMark: (in category
'accessing') -----<br>
tempsMark: aNumber<br>
tempsMark := aNumber!<br>
<br>
Item was changed:<br>
----- Method: Scanner class>>initializeTypeTable (in
category 'initialization') -----<br>
initializeTypeTable<br>
"self initializeTypeTable"<br>
<br>
| newTable |<br>
newTable := Array new: 256 withAll: #xBinary.
"default"<br>
newTable atAll: #(9 10 12 13 32 ) put: #xDelimiter.
"tab lf ff cr space"<br>
newTable atAll: ($0 asciiValue to: $9 asciiValue)
put: #xDigit.<br>
<br>
1 to: 255<br>
do: [:index |<br>
(Character value: index) isLetter<br>
ifTrue: [newTable at: index
put: #xLetter]].<br>
<br>
newTable at: $" asciiValue put: #xDoubleQuote.<br>
newTable at: $# asciiValue put: #xLitQuote.<br>
newTable at: $$ asciiValue put: #xDollar.<br>
newTable at: $' asciiValue put: #xSingleQuote.<br>
+ newTable at: $` asciiValue put: #backQuote.<br>
newTable at: $: asciiValue put: #xColon.<br>
newTable at: $( asciiValue put: #leftParenthesis.<br>
newTable at: $) asciiValue put: #rightParenthesis.<br>
newTable at: $. asciiValue put: #period.<br>
newTable at: $; asciiValue put: #semicolon.<br>
newTable at: $[ asciiValue put: #leftBracket.<br>
newTable at: $] asciiValue put: #rightBracket.<br>
newTable at: ${ asciiValue put: #leftBrace.<br>
newTable at: $} asciiValue put: #rightBrace.<br>
newTable at: $^ asciiValue put: #upArrow.<br>
newTable at: $_ asciiValue put: #xUnderscore.<br>
newTable at: $| asciiValue put: #verticalBar.<br>
TypeTable := newTable "bon voyage!!"!<br>
<br>
<br>
</blockquote>
</div>
<br>
<br clear="all">
<div><br>
</div>
-- <br>
best,
<div>Eliot</div>
</div>
<br>
<fieldset class="mimeAttachmentHeader"></fieldset>
<br>
<pre wrap="">
</pre>
</blockquote>
<br>
</body>
</html>