Using a Variable in a String
Can someone explain how to use a variable in a "string". If you use a constant it works fine.
See example below
Thanks
Scott
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
z=$31
Var
Long x
Long y
OBJ
text : "tv_text"
num : "numbers"
PUB start | i
'start term
text.start(12)
x:=$31
'This will work
text.str(string(13," Z=",z))
'This will not work
text.str(string(13," X=",x))
See example below
Thanks
Scott
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
z=$31
Var
Long x
Long y
OBJ
text : "tv_text"
num : "numbers"
PUB start | i
'start term
text.start(12)
x:=$31
'This will work
text.str(string(13," Z=",z))
'This will not work
text.str(string(13," X=",x))

Comments
This is a little more work and doesn't look as nice, but it does work.
Thanks for your reply. I figured as much but would rather someone with a little more experience confirm it.
Scott
My format object may help somewhat. You can read about it here. With it, you can combine the str, dec, and other functions into a single call, using a C-like (printf) structure.
-Phil
Thanks for the tip on your format object. When I first looked at it, it was quite confusing but after spending a few minutes reading the description it became quite clear.
This object should improve the efficiency. May have to print out your description and keep it handy until it becomes 2nd nature but a lot sure can be done with one line of code
M.K. Borri I sure would like to see what you have been working on as well.
Thanks to both of you
obj m: "DynamicMathLib" ' use your favorite math library here I guess... var byte OpsList[noparse][[/noparse]20] long NumList[noparse][[/noparse]16] byte tempstr[noparse][[/noparse]48] 'long pcnt 'byte ocnt 'byte ncnt 'byte pt 'long tempnum 'long stack[noparse][[/noparse]10] ' I take a string in and parse it into two lists, one contains constants, the other contains operation tokens ' for reverse polish notation. The # symbol means "get the next constant from the list and push it on the ' stack" and a letter A-Z means "get the corresponding variable and push it". Everything is floats. ' This thing can probably be optimized a lot. pub ExpressionParserRPN (InputStringAddr, InputVarAddr, Mark) ExpressionTokenizer(InputStringAddr, @OpsList, @NumList, Mark) return ExpressionParser(InputVarAddr, @OpsList, @NumList) pub ExpressionTokenizer (InputStringAddr, OpsListAddr, NumListAddr, Mark) | pcnt, ocnt, ncnt, pt, tempnum pcnt := 0 { repeat tempstr[noparse][[/noparse]pcnt] := byte[noparse][[/noparse]InputStringAddr + pcnt] until byte[noparse][[/noparse]InputStringAddr + pcnt++] == 0 tempstr[noparse][[/noparse]pcnt] := 0 } bytemove(@tempstr, InputStringAddr, 47) tempstr[noparse][[/noparse]47] := 0 'floatout := m.ffloat(pcnt) ncnt~ repeat pcnt := s.ParseNextFloat(@tempstr, @tempnum) if pcnt <> -1 long[noparse][[/noparse]NumListAddr + ncnt] := tempnum ncnt += 4 until pcnt == -1 pcnt~ ocnt~ repeat case (tempstr[noparse][[/noparse]pcnt]) "=", "+", "*", "/", "\", "-", "_", "^", ":", "!", "|", "$", "<", ">", "%", "?", "=": byte[noparse][[/noparse]OpsListAddr + ocnt] := tempstr[noparse][[/noparse]pcnt] ocnt++ "A".."Z": byte[noparse][[/noparse]OpsListAddr + ocnt] := tempstr[noparse][[/noparse]pcnt] ocnt++ "#": byte[noparse][[/noparse]OpsListAddr + ocnt] := tempstr[noparse][[/noparse]pcnt] ocnt++ repeat pcnt++ until tempstr[noparse][[/noparse]pcnt] <> "#" pcnt-- pcnt++ until tempstr[noparse][[/noparse]pcnt] == 0 if byte[noparse][[/noparse]OpsListAddr + ocnt - 1] <> 0 byte[noparse][[/noparse]OpsListAddr + ocnt++]~ ' ok so far if (Mark) byte[noparse][[/noparse]InputStringAddr] := 0 ' marks a string as already parsed, so don't do it twice -- erases first character of it. pub ExpressionParser (InputVarAddr, OpsListAddr, NumListAddr) | stack[noparse][[/noparse]10], pcnt, ocnt, ncnt, pt, tempnum tempnum~ ' also acts as "padding" in case we start with an operation ncnt~ ocnt~ pt := -4 repeat case byte[noparse][[/noparse]OpsListAddr + ocnt] 0 : return stack[noparse][[/noparse]pt] ' that's all folks! "#": pt := pt + 4 stack[noparse][[/noparse]pt] := long[noparse][[/noparse]NumListAddr + ncnt] ncnt := ncnt + 4 "A".."Z": if (InputVarAddr) pt := pt + 4 stack[noparse][[/noparse]pt] := long[noparse][[/noparse]InputVarAddr + (byte[noparse][[/noparse]OpsListAddr + ocnt] - "A")*4] ' A is 0, B is 1 (4), C is 2 (8) etc. "+": pt := pt - 4 stack[noparse][[/noparse]pt] := m.fadd(stack[noparse][[/noparse]pt], stack[noparse][[/noparse]pt + 4]) "*": pt := pt - 4 stack[noparse][[/noparse]pt] := m.fmul(stack[noparse][[/noparse]pt], stack[noparse][[/noparse]pt + 4]) "/": pt := pt - 4 stack[noparse][[/noparse]pt] := m.fdiv(stack[noparse][[/noparse]pt], stack[noparse][[/noparse]pt + 4]) "\": pt := pt - 4 stack[noparse][[/noparse]pt] := m.fdiv(stack[noparse][[/noparse]pt + 4], stack[noparse][[/noparse]pt]) "-": pt := pt - 4 stack[noparse][[/noparse]pt] := m.fsub(stack[noparse][[/noparse]pt], stack[noparse][[/noparse]pt + 4]) "_": pt := pt - 4 stack[noparse][[/noparse]pt] := m.fsub(stack[noparse][[/noparse]pt + 4], stack[noparse][[/noparse]pt]) "^": pt := pt - 4 stack[noparse][[/noparse]pt] := m.fpow(stack[noparse][[/noparse]pt], stack[noparse][[/noparse]pt + 4]) ":": pt := pt - 4 stack[noparse][[/noparse]pt] := m.fpow(stack[noparse][[/noparse]pt + 4], stack[noparse][[/noparse]pt]) ">": pt := pt - 4 stack[noparse][[/noparse]pt] := m.fabs(m.ffloat(m.fcmp(stack[noparse][[/noparse]pt], stack[noparse][[/noparse]pt + 4]) > 0)) "<": pt := pt - 4 stack[noparse][[/noparse]pt] := m.fabs(m.ffloat(m.fcmp(stack[noparse][[/noparse]pt], stack[noparse][[/noparse]pt + 4]) < 0)) "?": pt := pt - 4 ' general compare operation, returns -1 0 +1 stack[noparse][[/noparse]pt] := m.ffloat(m.fcmp(stack[noparse][[/noparse]pt], stack[noparse][[/noparse]pt + 4])) "=": 'p_cnt := p_cnt - 4 ' compares to nearest integer otherwise it's unusable stack[noparse][[/noparse]pt] := m.fabs(m.ffloat( m.fround(stack[noparse][[/noparse]pt]) == m.fround(stack[noparse][[/noparse]pt + 4]) ) ) "%": tempnum := stack[noparse][[/noparse]pt - 4] ' NOT a percent operation, swaps X and Y instead (thanks Dave!) stack[noparse][[/noparse]pt - 4] := stack[noparse][[/noparse]pt] stack[noparse][[/noparse]pt] := tempnum tempnum~ "=": pt := pt - 4 ' compares to nearest integer otherwise it's unusable stack[noparse][[/noparse]pt] := m.fabs(m.ffloat( m.fround(stack[noparse][[/noparse]pt]) == m.fround(stack[noparse][[/noparse]pt + 4]) ) ) ' unary operators "|": stack[noparse][[/noparse]pt] := m.fabs(stack[noparse][[/noparse]pt]) "!": stack[noparse][[/noparse]pt] := m.fneg(stack[noparse][[/noparse]pt]) "$": stack[noparse][[/noparse]pt] := m.fmul(m.fsign(stack[noparse][[/noparse]pt]), m.fsqr(m.fabs(stack[noparse][[/noparse]pt]))) ocnt++ pub ParseNextInt(StringAddress, ReturnValueAddress) | curs1, curs2, pointy, temp, sign temp := 0 curs1 := 0 curs2 := 0 pointy := 0 sign := 1 repeat pointy := pointy + 1 if (byte[noparse][[/noparse]StringAddress+pointy] == $00) return -1 until (IsAsciiDigit(byte[noparse][[/noparse]StringAddress+pointy]) == true)' or byte[noparse][[/noparse]StringAddress+pointy] == "-") curs1 := pointy repeat pointy := pointy + 1 until (IsAsciiDigit(byte[noparse][[/noparse]StringAddress+pointy]) == false) curs2 := pointy pointy := curs1 repeat (curs2 - curs1) ' if (byte[noparse][[/noparse]StringAddress+pointy] == "-") ' sign := -1 ' else temp := temp * 10 + (byte[noparse][[/noparse]StringAddress+pointy] - $30) byte[noparse][[/noparse]StringAddress+pointy] := "#" pointy := pointy + 1 if (byte [noparse][[/noparse]StringAddress + curs1 - 1] == "-") byte [noparse][[/noparse]StringAddress + curs1 - 1] := "#" sign := -1 if (byte [noparse][[/noparse]StringAddress + curs1 - 1] == "+") byte [noparse][[/noparse]StringAddress + curs1 - 1] := "#" sign := +1 long[noparse][[/noparse]ReturnValueAddress] := (temp*sign) return pointy pub ParseNextFloat(StringAddress, ReturnValueAddress) | beforedecimal, afterdecimal, dp1, dp2 dp2 := dp1 := ParseNextInt(StringAddress, @beforedecimal) ' tells me after how many digits i got the dec point beforedecimal := m.ffloat(beforedecimal) if (byte[noparse][[/noparse]StringAddress + dp1] == ".") byte[noparse][[/noparse]StringAddress + dp1] := "#" dp2 := ParseNextInt(StringAddress, @afterdecimal) ' tells me after how many digits i got the end of the number afterdecimal := m.ffloat(afterdecimal) ' now dp2 - dp1 contain the number of digits after the dec point if any if (afterdecimal and (dp2 > ++dp1)) afterdecimal := m.fdiv(afterdecimal, tenf[noparse][[/noparse]dp2 - dp1]) beforedecimal := m.fadd(beforedecimal, afterdecimal) long[noparse][[/noparse]ReturnValueAddress] := beforedecimal return dp2 pub IsAsciiDigit(ByteVal) if (ByteVal > $2F and ByteVal < $3A) return true return false DAT tenf long 1.0, 10.0, 100.0, 1_000.0, 10_000.0, 100_000.0, 1_000_000.0, 10_000_000.0, 100_000_000.0, 1_000_000_000.0This works for me; I don't know if it'll help you. It was written as part of a TV-based HP calculator emulator that my lab manager asked me to put together.
What happens is that, for example,
ExpressionParserRPN(string("4 5 + 3 *"), false, false)
will return 27.0 (the Mark parameter zeroes out the input string, that exists for application-specific reason), and
var
long DataAddress[noparse][[/noparse]26]
code
ExpressionParserRPN(string("4 5 + 3 * B +"), @DataAddress, false)
will return 27.0 plus whatever float is at the address @DataAddress + 4 , which can be a constant specified with a dat block, or a variable. This limits to A...Z variables, but that's because "it was good enough at the time" essentially...
As always, if you have any suggestions, please share [noparse]:)[/noparse]