The CASE statement takes a single 32-bit value and compares it to other 32-bit values. A string is represented by the address of the first byte of a block of bytes containing the string terminated with a zero byte. Comparing addresses doesn't do what you want. String literals are not combined by the Spin compiler, so, when you write STRING("something") and later write STRING("something"), you get two different addresses and two copies of the string in your object program.
One solution to this is to have a dictionary and always look up strings that are entered by the program's user. FemtoBasic does this using a dictionary of keywords. You can see the routine involved in FemtoBasic.spin (tokenize) which scans through an input line, substituting a byte containing the number of the keyword + 128 for the characters of the keyword. It also recognizes the string quote (") so it doesn't try to lookup keywords in the string. The dictionary is near the beginning of the program.
For special purpose, remember that you can pack a 4-character string into one long, for example,
"/cmd"
That can be long-aligned with other 4-byte strings and then used as longs in case statements, for example for a command parser. Note that those strings are low-endian, so the "/" above is in the least significant byte.
You can also use a hash function to create 32-bit signatures for strings of any length. The following example was created based upon the FNV Hash:
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
FNV_PRIME = 16777619
FNV_OFFSET = 2166136261
OBJ
pst : "Parallax Serial Terminal"
PUB start | addr
pst.start(9600)
addr := @testing
repeat
pst.str(addr)
pst.char(" ")
pst.dec(hash(addr))
pst.char(13)
addr += strsize(addr) + 1
while byte[addr]
PUB hash(str_addr) | ch
result := FNV_OFFSET
repeat while (ch := byte[str_addr++])
result := (result * FNV_PRIME) ^ ch
DAT
testing byte "Testing 123.", 0
fourscore byte "Fourscore and seven years ago.", 0
uc_prop byte "The Propeller is a really cool chip.", 0
lc_prop byte "The propeller is a really cool chip.", 0
byte 0
For use with case statements, the target strings would all be pre-hashed and the results stored. Then the input string would be hashed and that result used in the case statement to compare with the pre-hashed values.
Obviously, hashing is a many-to-one mapping, so it's possible to have two strings return the same hash value. But the likelihood of that, given a finite set of strings, is vanishingly small.
You can do like Mike says at run-time, or at compile time in a CON block with = for a predefined command set. Or pack them in DATa statements:
DAT
dummy long 0 ' to assure long alignment
leftmove byte "/lmv"
rightmove byte "/rmv"
getdegC byte "/g*C"
setpin byte "/spn"
etc. Then when commands come in over whatever channel, look for the start character, e.g., the "/", and use the type of code Mike suggested to parse the incoming command into a long, low endian. Then use CASE to compare that with the predefined command names and take the appropriate actions.
CASE myValue
leftmove :
rightmove :
getdegC :
yadayada
Comments
The CASE statement takes a single 32-bit value and compares it to other 32-bit values. A string is represented by the address of the first byte of a block of bytes containing the string terminated with a zero byte. Comparing addresses doesn't do what you want. String literals are not combined by the Spin compiler, so, when you write STRING("something") and later write STRING("something"), you get two different addresses and two copies of the string in your object program.
One solution to this is to have a dictionary and always look up strings that are entered by the program's user. FemtoBasic does this using a dictionary of keywords. You can see the routine involved in FemtoBasic.spin (tokenize) which scans through an input line, substituting a byte containing the number of the keyword + 128 for the characters of the keyword. It also recognizes the string quote (") so it doesn't try to lookup keywords in the string. The dictionary is near the beginning of the program.
"/cmd"
That can be long-aligned with other 4-byte strings and then used as longs in case statements, for example for a command parser. Note that those strings are low-endian, so the "/" above is in the least significant byte.
Given a 3 character string in the array aChar, you could also have Then you'd call makePackedString(@aChar) and the returned value would be the 3 characters in a single long.
You could also supply a literal, so you could write: makePackedString(string("foo"))
Paul
For use with case statements, the target strings would all be pre-hashed and the results stored. Then the input string would be hashed and that result used in the case statement to compare with the pre-hashed values.
Obviously, hashing is a many-to-one mapping, so it's possible to have two strings return the same hash value. But the likelihood of that, given a finite set of strings, is vanishingly small.
-Phil
etc. Then when commands come in over whatever channel, look for the start character, e.g., the "/", and use the type of code Mike suggested to parse the incoming command into a long, low endian. Then use CASE to compare that with the predefined command names and take the appropriate actions.
I can follow this - i think.
I love it when you guys talk dirty.