Shop OBEX P1 Docs P2 Docs Learn Events
Using a Variable in a String — Parallax Forums

Using a Variable in a String

Scott EScott E Posts: 7
edited 2006-11-27 01:11 in Propeller 1
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))

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2006-11-26 15:09
    string() is strictly a constant-based function. The compiler sticks all the pieces together and produces a constant byte string with a zero at the end. You cannot use a variable within it. What you seem to want to do is to display the value of a variable and this is easy, but not using string(). You have to do something like:
    text.str(string(13, "X="))
    text.dec(x)
    
    


    This is a little more work and doesn't look as nice, but it does work.
  • Scott EScott E Posts: 7
    edited 2006-11-26 17:36
    Mike

    Thanks for your reply. I figured as much but would rather someone with a little more experience confirm it.

    Scott
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2006-11-26 18:29
    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
  • M. K. BorriM. K. Borri Posts: 279
    edited 2006-11-26 23:28
    I wrote a RPN expression parser for this purpose... it's not really finalized enough for me to post it, though, but I can try and clean it up.
  • Scott EScott E Posts: 7
    edited 2006-11-27 00:08
    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
  • M. K. BorriM. K. Borri Posts: 279
    edited 2006-11-27 01:11
    
    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.0 
                         
    
    




    This 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]
Sign In or Register to comment.