"This file only defines methods on Syntax Nodes for the purpose of generating static source output which can be used as valid input. These methods make a general attempt at comprehensible output. All of them assume that the previously-called method responsibly adds a line-ending when possible." Syntax addPrototype: #SourceWriter derivedFrom: {Cloneable}. "This visitor is a dispatch point for these methods and may be overridden." Syntax SourceWriter addSlot: #printsPrettily valued: True. "Whether comments are added to the source. The default is not to." Syntax SourceWriter addSlot: #surroundingArity valued: 3. "The arity of a message-send context. This is significant if > 0, and should be set to 0 otherwise. Comparisons are made with a current arity, and if the current is greater, the message is surrounded with precedence-preserving parentheses." w@(Syntax SourceWriter traits) print: node@(Syntax Node traits) on: s "All printable nodes need to override this method." [ shouldOverrideThis ]. _@(Syntax SourceWriter traits) print: _@(Syntax ImplicitArgument traits) on: _ "The target of implicit message sends to the context. Prints nothing." [ ]. w@(Syntax SourceWriter traits) print: node@(Syntax Comment traits) on: s "(Optionally) print out the comment on the following line." [ w print: node value on: s. w printsPrettily ifTrue: [s nextPut: $". s ; node comment. s nextPut: $"]. s nextPut: $\n. w ]. w@(Syntax SourceWriter traits) print: node inParenthesesOn: s [ s nextPut: $(. w print: node on: s. s nextPut: $). w ]. _@(Syntax SourceWriter traits) print: node@(Syntax Literal traits) on: s [ node value printOn: s ]. w@(Syntax SourceWriter traits) printSendOf: selector with: args on: s "Accepts a method name and array of arguments, and print the appropriate source on the stream." [| previousArity | previousArity: w surroundingArity. (selector isUnarySelector and: [args size = 1]) ifTrue: [w surroundingArity: 1. w print: args first on: s. s nextPut: $\s. s ; (selector as: String)]. (selector isBinarySelector and: [args size = 2]) ifTrue: [w surroundingArity: 2. previousArity <= 2 ifTrue: [s nextPut: $(]. w print: args first on: s. s nextPut: $\s. s ; (selector as: String). s nextPut: $\s. w print: args second on: s. previousArity <= 2 ifTrue: [s nextPut: $)]]. "For keyword sends, print the first argument, partition the selector by the colons, (at an offset of 1 to synchronize indices for looping), and then loop from one to the end, printing the keywords and arguments alternately." selector isKeywordSelector ifTrue: [| name names | name: (selector as: String). names: (name splitWith: ':'). w surroundingArity: 3. previousArity > 0 ifTrue: [s nextPut: $(]. w print: args first on: s. 1 below: args size do: [| :index | s nextPut: $\s. s ; ((names at: index - 1) as: String). s nextPut: $:. s nextPut: $\s. w print: (args at: index) on: s]. previousArity > 0 ifTrue: [s nextPut: $)]]. w surroundingArity: previousArity. w ]. w@(Syntax SourceWriter traits) print: node@(Syntax Macro traits) on: s "This should not be called yet anyway." [ s nextPut: $`. w printSendOf: node selector with: node args on: s ]. w@(Syntax SourceWriter traits) print: node@(Syntax StoreVariable traits) on: s [ w printSendOf: ((node variable name as: String) ; ':') intern with: {Syntax ImplicitArgument. node value} on: s ]. w@(Syntax SourceWriter traits) print: node@(Syntax LoadVariable traits) on: s [ w printSendOf: node variable name intern with: {Syntax ImplicitArgument} on: s ]. w@(Syntax SourceWriter traits) print: node@(Syntax Message traits) on: s [ w printsPrettily ifTrue: [w printSendOf: node selector with: node arguments on: s] ifFalse: [s nextPut: $(. node selector printOn: s. s ; ' sendTo: '. node arguments printOn: s. s nextPut: $)]. w ]. w@(Syntax SourceWriter traits) print: node@(Syntax OptionalKeywords traits) on: s [ s ; ' ('. w print: node message on: s. w keywords with: w arguments do: [| :key :arg | s ; ' ' ; key ; ' ' ; arg]. w ; ') '. w ]. w@(Syntax SourceWriter traits) print: node@(Syntax CompoundStatement traits) on: s [ w surroundingArity: 0. node statements do: [| :statement | w print: statement on: s] separatedBy: [s ; '.\n']. w ]. w@(Syntax SourceWriter traits) print: node@(Syntax Array traits) on: s [ s nextPut: ${. resend. s nextPut: $}. w ]. w@(Syntax SourceWriter traits) print: node@(Syntax Parenthesis traits) on: s [ s nextPut: $(. resend. s nextPut: $). w ]. w@(Syntax SourceWriter traits) print: node@(Syntax Block traits) on: s [ s nextPut: $[. node localVariables size = 0 ifFalse: ["Print the method header." s ; '|'. node inputVariables do: [| :inputVar | s nextPut: $\s. s ; ':' ; (inputVar name as: String)]. node localVariables do: [| :localVar | (node inputVariables includes: localVar) ifFalse: [ s nextPut: $\s. s ; (localVar name as: String) ]]. s ; ' | ']. resend. s ; ']'. ]. w@(Syntax SourceWriter traits) printArg: arg withRole: role on: s [ s ; (arg name as: String). ((role is: Syntax Literal) and: [role value == NoRole]) ifFalse: [ s nextPut: $@. w print: role on: s. "This sometimes add an unwanted space!" ] ]. w@(Syntax SourceWriter traits) printMethodHeader: node@(Syntax MethodDefinition traits) on: s "Accepts a method name and array of arguments, and print the appropriate source on the stream." [ | args selector | selector: node selector. args: node inputVariables. (selector isUnarySelector and: [args size = 1]) ifTrue: [ | role | role: (node roles first). w printArg: args first withRole: role on: s. s nextPut: $\s. s ; (selector as: String) ]. (selector isBinarySelector and: [args size = 2]) ifTrue: [ | role0 role1 | role0: (node roles first). role1: (node roles second). w printArg: args first withRole: role0 on: s. s nextPut: $\s. s ; (selector as: String). s nextPut: $\s. w printArg: args second withRole: roles1 on: s ]. "For keyword sends, print the first argument, partition the selector by the colons, (at an offset of 1 to synchronize indices for looping), and then loop from one to the end, printing the keywords and arguments alternately." selector isKeywordSelector ifTrue: [| name names | name: (selector as: String). names: (name splitWith: ':'). w printArg: args first withRole: node roles first on: s. 1 below: args size do: [| :index | s nextPut: $\s. s ; ((names at: index - 1) as: String). s nextPut: $:. s nextPut: $\s. w printArg: (args at: index) withRole: (node roles at: index) on: s]]. w ]. w@(Syntax SourceWriter traits) print: node@(Syntax MethodDefinition traits) on: s [ s nextPut: $\n. w printMethodHeader: node on: s. s nextPut: $\s. resend. w ]. w@(Syntax SourceWriter traits) print: node@(Syntax Return traits) on: s [ s ; '^ '. w surroundingArity: 2. w print: node value on: s. w ]. _@(Syntax SourceWriter traits) print: _@(Syntax Resend traits) on: s [ s ; 'resend'. ].