What exacly is the usage of . in Smalltalk? Based on my understanding, it is the separator of different statements, but can be omitted if statement is at the end. Is that correct?
The . is a statement separator like ; in Pascal (usually used at the end of lines). The motivation (reason) being that the ordinary sentences in English end with ..
The places it must/could be omitted are:
Variable definition
Comments
One statement block or last statement at the block
At the end of a method
When you define a #selector or #selector: message
An example method from Smalltalk/X-jv:
selectorAsRegistryName: aSelector
"Splits selector into string words with spaces.
For example: itemName becomes 'Item Name'"
| registryName selectorCollection |
registryName := String new.
selectorCollection := aSelector asCollectionOfSubCollectionsSeparatedByAnyForWhich:[:ch | ch isUppercase ] withSeparatorsIncluded:true.
selectorCollection at: 1 put: selectorCollection copy first asUppercaseFirst. "/ first string must be uppercase too
selectorCollection do: [ :eachString |
registryName := registryName isEmpty ifTrue: [ eachString ]
ifFalse: [ registryName, Character space, eachString ]
].
^ registryName
Related
I'm trying to represent the BYOND DM language strings in lexer form (See http://byond.com and http://byond.com/docs/ref). Here are the rules for strings:
The string start and end with double quotes. i.e. "hello world" evaluates to hello world
A backslash acts as an escape character, which can escape the end quote. i.e. "hello\"world" evaluates to hello"world
Newlines in the string can be ignored by ending the line with a backslash. i.e. "hello\
world" evaluates to helloworld
If the string opens/closes with the sequence {"/"} respectively, newlines are allowed and entered into the final string. The sequence \\\n is still ignored
The string can contain embedded expressions inside braces which are formatted into the result. Backslashes can escape the opening brace. i.e. "hello [ "world" ] \[" evaluates to hello world [ at run-time. Any expression can go in the braces (calls, math, etc...)
If the starting quote/curly brace is prefixed with '#' escape sequences and embedded expressions are disabled for the string. i.e. #{"hello [worl\d"} and #"hello [worl\d" both evaluate to hello [worl\d
I am trying to construct ANTLR4 .g4 lexer rules to tokenize these strings. I figure there's 4 (or more) token types I'd need:
Normal string. i.e "hello world", #"hello world", #{"hello world"} or {"hello world"}
String start before embedded expression. i.e. "hello [ or {"hello [
String end after embedded expression. i.e. ] world" or ] world"}
String in between two embedded expressions. i.e. ] hello world [
Here are my (incomplete and unsuccessful) attempts:
LSTRING: '"' ('\\[' | ~[[\r\n])* '[';
RSTRING: ']' ('\\"' | ~["\r\n])* '"';
CSTRING: ']' ('\\[' | ~[[\r\n])* '[';
FSTRING: '"' ('\\"' | ~["\r\n])* '"';
If this can't be solved in the lexer, I can write the parser rules on my own with the tokens #, {", "}, [, ], \\, and ". But, I figure I'd give this a shot since it'd be more performant.
I solved it with the following lexer tidbits. Permalink
...
#lexer::members
{
ulong regularAccessLevel;
System.Collections.Generic.Stack<bool> multiString = new System.Collections.Generic.Stack<bool>();
}
...
VERBATIUM_STRING: '#"' (~["\r\n])* '"';
MULTILINE_VERBATIUM_STRING: '#{"' (~'"')* '"}';
MULTI_STRING_START: '{"' { multiString.Push(true); } -> pushMode(INTERPOLATION_STRING);
STRING_START: '"' { multiString.Push(false); } -> pushMode(INTERPOLATION_STRING);
...
LBRACE: '[' { ++regularAccessLevel; };
RBRACE: ']' { if(regularAccessLevel > 0) --regularAccessLevel; else if(multiString.Count > 0) { PopMode(); } };
...
mode INTERPOLATION_STRING;
CHAR_INSIDE: '\\\''
| '\\"'
| '\\['
| '\\\\'
| '\\0'
| '\\a'
| '\\b'
| '\\f'
| '\\n'
| '\\r'
| '\\t'
| '\\v'
;
EMBED_START: '[' -> pushMode(DEFAULT_MODE);
MULTI_STRING_CLOSE: {multiString.Peek()}? '"}' { multiString.Pop(); PopMode(); };
STRING_CLOSE: {!multiString.Peek()}? '"' { multiString.Pop(); PopMode(); };
STRING_INSIDE: {!multiString.Peek()}? ~('[' | '\\' | '"' | '\r' | '\n')+;
MULTI_STRING_INSIDE: {multiString.Peek()}? ~('[' | '\\' | '"')+;
Certain strings can cause it to emit multiple STRING_INSIDE/MULTI_STRING_INSIDE tokens in sequence, but this is acceptable since the parser will eat it all anyway.
A lot of it came from reading the C# interpolated strings in the antlr4 examples permalink
I am using Smalltalk to type in Transcript window a 1-9 multiplication table .
Here is my code:
1 to: 9 do: [:i|
1 to: i do: [:j|
Transcript show: j.
Transcript show: ' * '.
Transcript show: i.
Transcript show: ' = '.
Transcript show: j * i.
Transcript show: ' '.
].
Transcript show: ' '; cr.
].
As can be seen, the above code, although working fine, looks far from beautiful and concise.
I had hoped to write something like :
Transcript show: j '*' i '=' j * i.
Unfortunately, they are wrong. I remember C has a very nice way to handle that issue of mine.
Like, printf("%d * %d = %d ", j, i, j * i);
Is there a more elegant way to make Smalltalk codes elegant in this situation ?
Peter solves this problem. Thanks.
More to ask:
How to display "Escape character" in Smalltalk.
I know in C, printf("%d * %d = %d\n", j, i, j * i); seems fine.
But in Smalltalk, Transcript show: ('{1} * {2} = {3}cr.' format: {i. j. i * j}). is not OKAY.
How can I solve that?
String expansion
Like, printf("%d * %d = %d ", j, i, j * i);
There are some options for formatting, but nothing comparable to printf (afaik)
'<1p> * <2p> = <3p>' expandMacrosWith: 2 with: 3 with: 2 * 3.
"same as the following:"
'<1p> * <2p> = <3p>' expandMacrosWithArguments: {2. 3. 2 * 3}.
'{1} * {2} = {3}' format: {2. 3. 2 * 3}.
So your code could be rewritten as the following
1 to: 9 do: [ :i |
1 to: i do: [ :j |
Transcript
show: ('{1} * {2} = {3}' format: {j. i. j * i});
show: ' '
].
Transcript show: ' '; cr.
].
Update re: escape characters
There are no escape characters in Strings with the only exception being single quote ('), which you "escape" by doubling it ('It''s doubled!')
To write new line, or tabulator, you can
type it into the string
a := 'two
lines'. (note that StackOverflow replaced the tab with spaces :/)
Join strings
b := 'two', String cr, String tab, 'lines'.
Tell write stream to add it
c := String streamContents: [ :stream | stream << 'two'; cr; tab; << 'lines' ].
use expandMacros
d := 'two<n><t>lines' expandMacros.
a = b = c = d
I would suggest two changes:
Have the code work with a generic Stream and use it with the Transcript
dumpTableOn: aStream
<your code here>
And then evaluate
self dumpTableOn: Transcript
Split your code in two methods
dumpTableOn: aStream
1 to: 9 do: [:i |
1 to: i do: [:j |
self dump: i times: j on: aStream.
aStream space].
aStream cr]
dump: i times: j on: aStream
aStream
nextPutAll: j asString;
nextPutAll: ' * ';
nextPutAll: i asString;
nextPutAll: ' = ';
nextPutAll: (j * i) asString
Here is a less verbose version of the method above
dump: i times: j on: aStream
aStream
print: j;
print: ' * ';
print: i;
print: ' = ';
print: j * i
Note that #print: doesn't require us to convert the argument to a String first.
Note also that the cascade doesn't create intermediate Strings, while the expression
j asString, '*', i asString, '=', (j*i) asString
creates 4 intermediate strings (one for every #,). Not a big deal, except that we wouldn't be taking full advantage of the Stream protocol, whose whole point is to free the client from the need of concatenating things on its side.
If you'd prefer to use something similar to printf, Squeak has the expandMacrosWith: family of methods (browse class String).
Your example could be written as:
1 to: 9 do: [:i|
1 to: i do: [:j|
Transcript show: ('<1p> * <2p> = <3p> ' expandMacrosWith: j with: i with: j*i)
].
Transcript show: ' '; cr.
].
The receiver of expandMacros... contains placeholders between angled brackets. I can't find the relevant documentation in Squeak, the following is taken from the comment to the equivalent implementation in Dolphin Smalltalk:
Expand the receiver with replacable arguments.
e.g.
'Hello <D><N><1P> <D>' expandMacrosWithArguments: #('World' 1).
'Hello <2?Good:Bad> <1S>' expandMacrosWithArguments: #('World' false)
<nD> expands the nth parameter using it's #displayString
<nP> expands the nth parameter using it's #printString
<nS> expands the nth parameter treating it as a <String>
<N> expands as a newline combination
<T> expands as a TAB
<n?xxxx:yyyy> if the nth parameter is true expands to 'xxxx' else 'expands to yyyy'
I have string like "Hello World". I need to split this string to Hello, World`` using space delimiter in Smalltalk.
In Java the code looks as follows
String message = "Hello world"
message.split(' ');
How do I do this in Smalltalk?
| message parts |
message := 'Hello world'.
parts := message substrings. "this is an array"
Transcript
show: parts first;
show: parts last
Or to define delimiter: message substrings: ' '
Closer to Java:
'Hello World' splitOn: Character space.
also works with:
'Hello World' splitOn: ' '.
or (more funky):
[ :each | each isSeparator ] split: 'Hello World'.
'Hello World' splitOn: [ :each | each isSeparator ].
in a very buggy version of smalltalk on another site, this works but spliton does not.
s := 'Hello World' substrings '\n'.
n := s at: 2.
Transcript show: 's at 2='.
Transcript show: n.
Transcript show: ''; cr.
I'm trying to get from:
'hello how are you today'
to
'helloHowAreYouToday'
And I thought asCapitalizedPhrase asLegalSelector would do the trick, but it doesn't.
What's the proper way to do this?
EDIT:
I think I should clarify my question; I already have a way to transform a string into a camelCase selector:
|aString aCamelCaseString|
aString := aString findTokens: $ .
aCamelCaseString := aString first.
aString allButFirst do: [:each | aCamelCaseString := aCamelCaseString , each capitalized].
I was just wondering whether Pharo has a standard system method to achieve the same :)
How about this?
| tokens |
tokens := 'this is a selector' findTokens: Character space.
tokens allButFirst
inject: tokens first
into: [:selector :token | selector, token capitalized]
I don't think there's an existing method doing this.
Here's an implementation that solves your problem:
input := 'hello how are you today'.
output := String streamContents: [ :stream |
| capitalize |
capitalize := false.
input do: [ :char |
char = Character space
ifTrue: [ capitalize := true ]
ifFalse: [
stream nextPut: (capitalize
ifTrue: [ char asUppercase ]
ifFalse: [ char ]).
capitalize := false ] ] ].
Edit: note, in comparison to Frank's solution this one is longer but it does not break for empty input and it does not create a new string instance for each step since it streams over the input, which is more efficient (in case you have large strings).
You don't say which version of Pharo you're using, but in the stable 5.0,
'hello world this is a selector' asCamelCase asValidSelector
yields
helloWorldThisIsASelector
To get what I'm using run:
curl get.pharo.org/50+vm | bash
I know this is old but Squeak has a useful implementation (String>>asCamelCase) which basically does this:
(String
streamContents: [:stream | 'hello world' substrings
do: [:sub | stream nextPutAll: sub capitalized]]) asLegalSelector
It is possible to overide rebol system words like print, make etc., so is it possible to do the same with the path operator ? Then what's the syntax ?
Another possible approach is to use REBOL meta-programming capabilities and preprocess your own code to catch path accesses and add your handler code. Here's an example :
apply-my-rule: func [spec [block!] /local value][
print [
"-- path access --" newline
"object:" mold spec/1 newline
"member:" mold spec/2 newline
"value:" mold set/any 'value get in get spec/1 spec/2 newline
"--"
]
:value
]
my-do: func [code [block!] /local rule pos][
parse code rule: [
any [
pos: path! (
pos: either object? get pos/1/1 [
change/part pos reduce ['apply-my-rule to-block pos/1] 1
][
next pos
]
) :pos
| into rule ;-- dive into nested blocks
| skip ;-- skip every other values
]
]
do code
]
;-- example usage --
obj: make object! [
a: 5
]
my-do [
print mold obj/a
]
This will give you :
-- path access --
object: obj
member: a
value: 5
--
5
Another (slower but more flexible) approach could also be to pass your code in string mode to the preprocessor allowing freeing yourself from any REBOL specific syntax rule like in :
my-alternative-do {
print mold obj..a
}
The preprocessor code would then spot all .. places and change the code to properly insert calls to 'apply-my-rule, and would in the end, run the code with :
do load code
There's no real limits on how far you can process and change your whole code at runtime (the so-called "block mode" of the first example being the most efficient way).
You mean replace (say)....
print mold system/options
with (say)....
print mold system..options
....where I've replaced REBOL's forward slash with dot dot syntax?
Short answer: no. Some things are hardwired into the parser.