"Fossies" - the Fresh Open Source Software Archive

Member "FreeBASIC-1.07.1-source/src/compiler/parser-expr-unary.bas" (27 Sep 2019, 17692 Bytes) of package /linux/privat/FreeBASIC-1.07.1-source.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Visual Basic source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "parser-expr-unary.bas": 1.07.0_vs_1.07.1.

    1 '' unary operators (NOT, @, *, ...) parsing
    2 ''
    3 '' chng: sep/2004 written [v1ctor]
    4 
    5 
    6 #include once "fb.bi"
    7 #include once "fbint.bi"
    8 #include once "parser.bi"
    9 #include once "ast.bi"
   10 #include once "pp.bi"
   11 
   12 declare function hCast( byval options as AST_CONVOPT ) as ASTNODE ptr
   13 
   14 private function hPPDefinedExpr( ) as ASTNODE ptr
   15     dim as FBSYMBOL ptr base_parent = any
   16     dim as integer is_defined = any
   17 
   18     '' DEFINED
   19     lexSkipToken( LEXCHECK_NODEFINE )
   20 
   21     '' '('
   22     if( lexGetToken( ) <> CHAR_LPRNT ) then
   23         errReport( FB_ERRMSG_EXPECTEDLPRNT )
   24     else
   25         lexSkipToken( LEXCHECK_NODEFINE )
   26     end if
   27 
   28     '' Identifier
   29     is_defined = (cIdentifier( base_parent, FB_IDOPT_NONE ) <> NULL)
   30     lexSkipToken( )
   31 
   32     '' ')'
   33     if( hMatch( CHAR_RPRNT ) = FALSE ) then
   34         errReport( FB_ERRMSG_EXPECTEDRPRNT )
   35         '' error recovery: skip until ')'
   36         hSkipUntil( CHAR_RPRNT, TRUE )
   37     end if
   38 
   39     function = astNewCONSTi( is_defined )
   40 end function
   41 
   42 '':::::
   43 ''NegNotExpression=   ('-'|'+'|) ExpExpression
   44 ''                |   NOT RelExpression
   45 ''                |   HighestPresExpr .
   46 ''
   47 function cNegNotExpression _
   48     ( _
   49         _
   50     ) as ASTNODE ptr
   51 
   52     dim as ASTNODE ptr negexpr = any
   53 
   54     select case lexGetToken( )
   55     '' '-'
   56     case CHAR_MINUS
   57         lexSkipToken( )
   58 
   59         '' ExpExpression
   60         negexpr = cExpExpression( )
   61         if( negexpr = NULL ) then
   62             errReport( FB_ERRMSG_EXPECTEDEXPRESSION )
   63             '' error recovery: fake a new node
   64             negexpr = astNewCONSTi( 0 )
   65         else
   66             negexpr = astNewUOP( AST_OP_NEG, negexpr )
   67         end if
   68 
   69         if( negexpr = NULL ) Then
   70             errReport( FB_ERRMSG_TYPEMISMATCH )
   71             '' error recovery: fake a new node
   72             negexpr = astNewCONSTi( 0 )
   73         end if
   74 
   75         return negexpr
   76 
   77     '' '+'
   78     case CHAR_PLUS
   79         lexSkipToken( )
   80 
   81         '' ExpExpression
   82         negexpr = cExpExpression(  )
   83         if( negexpr = NULL ) then
   84             errReport( FB_ERRMSG_EXPECTEDEXPRESSION )
   85             '' error recovery: fake a new node
   86             negexpr = astNewCONSTi( 0 )
   87         else
   88             negexpr = astNewUOP( AST_OP_PLUS, negexpr )
   89         end if
   90 
   91         if( negexpr = NULL ) Then
   92             errReport( FB_ERRMSG_TYPEMISMATCH )
   93             '' error recovery: fake a new node
   94             negexpr = astNewCONSTi( 0 )
   95         end if
   96 
   97         return negexpr
   98 
   99     '' NOT
  100     case FB_TK_NOT
  101         lexSkipToken( )
  102 
  103         '' RelExpression
  104         negexpr = cRelExpression(  )
  105         if( negexpr = NULL ) then
  106             errReport( FB_ERRMSG_EXPECTEDEXPRESSION )
  107             '' error recovery: fake a new node
  108             negexpr = astNewCONSTi( 0 )
  109         else
  110             negexpr = astNewUOP( AST_OP_NOT, negexpr )
  111         end if
  112 
  113         if( negexpr = NULL ) Then
  114             errReport( FB_ERRMSG_TYPEMISMATCH )
  115             '' error recovery: fake a new node
  116             negexpr = astNewCONSTi( 0 )
  117         end if
  118 
  119         return negexpr
  120     end select
  121 
  122     function = cHighestPrecExpr( NULL, NULL )
  123 
  124 end function
  125 
  126 ''::::
  127 function cStrIdxOrMemberDeref _
  128     ( _
  129         byval expr as ASTNODE ptr _
  130     ) as ASTNODE ptr
  131 
  132     dim as FBSYMBOL ptr subtype = any
  133     dim as integer dtype = any
  134 
  135     if( expr = NULL ) then exit function
  136 
  137     dtype = astGetFullType( expr )
  138     subtype = astGetSubType( expr )
  139 
  140     select case as const typeGet( dtype )
  141     '' zstring indexing?
  142     case FB_DATATYPE_CHAR, FB_DATATYPE_WCHAR, FB_DATATYPE_STRING, FB_DATATYPE_FIXSTR
  143         '' '['?
  144         if( lexGetToken( ) = CHAR_LBRACKET ) then
  145             expr = cMemberDeref( dtype, subtype, expr, TRUE )
  146         end if
  147 
  148         return expr
  149 
  150     '' udt '.' ?
  151     case FB_DATATYPE_STRUCT ', FB_DATATYPE_CLASS
  152         select case( lexGetToken( ) )
  153         case CHAR_DOT
  154             lexSkipToken( LEXCHECK_NOPERIOD )
  155 
  156             expr = cMemberAccess( dtype, subtype, expr )
  157             if( expr = NULL ) then
  158                 return NULL
  159             end if
  160 
  161             dtype = astGetFullType( expr )
  162             subtype = astGetSubType( expr )
  163 
  164         '' ('->' | '[')? (possible on non-pointer UDT types due to operator overloading)
  165         case FB_TK_FIELDDEREF, CHAR_LBRACKET
  166             expr = cMemberDeref( dtype, subtype, expr, TRUE )
  167         end select
  168 
  169     end select
  170 
  171     '' FuncPtrOrMemberDeref?
  172     if( typeIsPtr( dtype ) ) then
  173         dim as integer isfuncptr = FALSE, isfield = FALSE
  174 
  175         select case lexGetToken( )
  176         '' function ptr '(' ?
  177         case CHAR_LPRNT
  178             isfuncptr = (typeGetDtAndPtrOnly( dtype ) = typeAddrOf( FB_DATATYPE_FUNCTION ))
  179             isfield = isfuncptr
  180 
  181         '' ptr ('->' | '[') ?
  182         case FB_TK_FIELDDEREF, CHAR_LBRACKET
  183             isfield = TRUE
  184         end select
  185 
  186         if( isfield ) then
  187             expr = cFuncPtrOrMemberDeref( dtype, _
  188                                           subtype, _
  189                                           expr, _
  190                                           isfuncptr, _
  191                                           TRUE )
  192         end if
  193     end if
  194 
  195     function = expr
  196 
  197 end function
  198 
  199 ''::::
  200 '' HighestPrecExpr=   AddrOfExpression
  201 ''                |   ( DerefExpr
  202 ''                    | CastingExpr
  203 ''                    | PtrTypeCastingExpr
  204 ''                    | ParentExpression
  205 ''                    ) FuncPtrOrMemberDeref?
  206 ''                |   AnonType
  207 ''                |   Atom .
  208 ''
  209 function cHighestPrecExpr _
  210     ( _
  211         byval base_parent as FBSYMBOL ptr, _
  212         byval chain_ as FBSYMCHAIN ptr _
  213     ) as ASTNODE ptr
  214 
  215     dim as ASTNODE ptr expr = any
  216 
  217     select case lexGetToken( )
  218     '' AddrOfExpression
  219     case FB_TK_ADDROFCHAR
  220         return cAddrOfExpression( )
  221 
  222     '' DerefExpr
  223     case FB_TK_DEREFCHAR
  224         expr = cDerefExpression( )
  225 
  226     '' ParentExpression
  227     case CHAR_LPRNT
  228         dim as integer is_opt = fbGetPrntOptional( )
  229 
  230         expr = cParentExpression( )
  231 
  232         '' if parsing a SUB, don't call StrIdxOrMemberDeref() twice
  233         if( is_opt ) then
  234             return expr
  235         end if
  236 
  237     case else
  238 
  239         select case as const lexGetToken( )
  240         '' AddrOfExpression
  241         case FB_TK_VARPTR, FB_TK_PROCPTR, FB_TK_SADD, FB_TK_STRPTR
  242             return cAddrOfExpression( )
  243 
  244         '' CAST '(' DataType ',' Expression ')'
  245         case FB_TK_CAST
  246             '' CAST
  247             lexSkipToken( )
  248 
  249             expr = hCast( 0 )
  250 
  251         '' CPTR '(' DataType ',' Expression{int|uint|ptr} ')'
  252         case FB_TK_CPTR
  253             '' CPTR
  254             lexSkipToken( )
  255 
  256             expr = hCast( AST_CONVOPT_PTRONLY )
  257 
  258         '' OperatorNew
  259         case FB_TK_NEW
  260             expr = cOperatorNew( )
  261 
  262         '' Atom
  263         case else
  264             '' PP expression?
  265             if( fbGetIsPP( ) ) then
  266                 select case( lexGetToken( ) )
  267                 '' TYPEOF '(' Expression ')'
  268                 case FB_TK_TYPEOF
  269                     return astNewCONSTstr( ppTypeOf( ) )
  270 
  271                 '' DEFINED '(' Identifier ')'
  272                 case FB_TK_DEFINED
  273                     return hPPDefinedExpr( )
  274 
  275                 end select
  276             end if
  277 
  278             return cAtom( base_parent, chain_ )
  279 
  280         end select
  281 
  282     end select
  283 
  284     function = cStrIdxOrMemberDeref( expr )
  285 end function
  286 
  287 '' '(' DataType ',' Expression ')'
  288 private function hCast( byval options as AST_CONVOPT ) as ASTNODE ptr
  289     dim as integer dtype = any, errmsg = any
  290     dim as FBSYMBOL ptr subtype = any
  291     dim as ASTNODE ptr expr = any
  292 
  293     '' '('
  294     if( lexGetToken( ) <> CHAR_LPRNT ) then
  295         errReport( FB_ERRMSG_EXPECTEDLPRNT )
  296         '' error recovery: skip until ')', fake a node
  297         hSkipUntil( CHAR_RPRNT, TRUE )
  298         return astNewCONSTi( 0 )
  299     end if
  300     lexSkipToken( )
  301 
  302     '' DataType
  303     if( cSymbolType( dtype, subtype ) = FALSE ) then
  304         errReport( FB_ERRMSG_SYNTAXERROR )
  305         '' error recovery: skip until ',', create a fake type
  306         hSkipUntil( CHAR_COMMA )
  307 
  308         if( options and AST_CONVOPT_PTRONLY ) then
  309             dtype = typeAddrOf( FB_DATATYPE_VOID )
  310         else
  311             dtype = FB_DATATYPE_INTEGER
  312         end if
  313         subtype = NULL
  314     end if
  315 
  316     '' check for invalid types
  317     select case as const( typeGet( dtype ) )
  318     case FB_DATATYPE_VOID, FB_DATATYPE_FIXSTR
  319         errReport( FB_ERRMSG_INVALIDDATATYPES, TRUE )
  320         '' error recovery: create a fake type
  321         if( options and AST_CONVOPT_PTRONLY ) then
  322             dtype = typeAddrOf( FB_DATATYPE_VOID )
  323         else
  324             dtype = FB_DATATYPE_INTEGER
  325         end if
  326         subtype = NULL
  327 
  328     case FB_DATATYPE_INTEGER, FB_DATATYPE_UINT, _
  329          FB_DATATYPE_LONG, FB_DATATYPE_ULONG, _
  330          FB_DATATYPE_LONGINT, FB_DATATYPE_ULONGINT, _
  331          FB_DATATYPE_ENUM
  332 
  333         if( options and AST_CONVOPT_PTRONLY ) then
  334             if( fbPdCheckIsSet( FB_PDCHECK_CASTTONONPTR ) ) then
  335                 errReportWarn( FB_WARNINGMSG_CASTTONONPTR )
  336             end if
  337         end if
  338 
  339     case FB_DATATYPE_POINTER
  340         options or= AST_CONVOPT_PTRONLY
  341 
  342     case else
  343         if( options and AST_CONVOPT_PTRONLY ) then
  344             errReportWarn( FB_WARNINGMSG_CASTTONONPTR )
  345         end if
  346 
  347     end select
  348 
  349     '' ','
  350     if( lexGetToken( ) <> CHAR_COMMA ) then
  351         errReport( FB_ERRMSG_EXPECTEDCOMMA )
  352     else
  353         lexSkipToken( )
  354     end if
  355 
  356     '' Expression
  357     expr = cExpression( )
  358     if( expr = NULL ) then
  359         errReport( FB_ERRMSG_EXPECTEDEXPRESSION )
  360         '' error recovery: create a fake node
  361         expr = astNewCONSTz( dtype, subtype )
  362     end if
  363 
  364     options or= AST_CONVOPT_CHECKSTR
  365 
  366     '' -w constness implies -w funcptr
  367     if( (fbPdCheckIsSet( FB_PDCHECK_CASTFUNCPTR ) = FALSE) _
  368         and (fbPdCheckIsSet( FB_PDCHECK_CONSTNESS ) = FALSE) ) then
  369         options or= AST_CONVOPT_DONTWARNFUNCPTR
  370     end if
  371 
  372     expr = astNewCONV( dtype, subtype, expr, options, @errmsg )
  373     if( expr = NULL ) then
  374         if( errmsg = FB_ERRMSG_OK ) then
  375             if( options and AST_CONVOPT_PTRONLY ) then
  376                 errmsg = FB_ERRMSG_EXPECTEDPOINTER
  377             else
  378                 errmsg = FB_ERRMSG_TYPEMISMATCH
  379             end if
  380         end if
  381         errReport( errmsg, TRUE )
  382 
  383         '' error recovery: create a fake node
  384         expr = astNewCONSTz( dtype, subtype )
  385     end if
  386 
  387     '' ')'
  388     if( lexGetToken( ) <> CHAR_RPRNT ) then
  389         errReport( FB_ERRMSG_EXPECTEDRPRNT )
  390         '' error recovery: skip until next ')'
  391         hSkipUntil( CHAR_RPRNT, TRUE )
  392     else
  393         lexSkipToken( )
  394     end if
  395 
  396     function = expr
  397 end function
  398 
  399 '':::::
  400 ''DerefExpression   =   DREF+ HighestPresExpr .
  401 ''
  402 function cDerefExpression( ) as ASTNODE ptr
  403     dim as integer derefcnt = any
  404     dim as ASTNODE ptr expr = any
  405 
  406     '' DREF?
  407     if( lexGetToken( ) <> FB_TK_DEREFCHAR ) then
  408         return NULL
  409     end if
  410 
  411     '' DREF+
  412     derefcnt = 0
  413     do
  414         lexSkipToken( )
  415         derefcnt += 1
  416     loop while( lexGetToken( ) = FB_TK_DEREFCHAR )
  417 
  418     '' HighestPresExpr
  419     expr = cHighestPrecExpr( NULL, NULL )
  420     if( expr = NULL ) then
  421         errReport( FB_ERRMSG_EXPECTEDEXPRESSION )
  422         '' error recovery: fake a node
  423         return astNewCONSTi( 0 )
  424     end if
  425 
  426     function = astBuildMultiDeref( derefcnt, expr, astGetFullType( expr ), astGetSubType( expr ) )
  427 end function
  428 
  429 private function hProcPtrBody _
  430     ( _
  431         byval base_parent as FBSYMBOL ptr, _
  432         byval proc as FBSYMBOL ptr _
  433     ) as ASTNODE ptr
  434 
  435     dim as FBSYMBOL ptr sym = any
  436 
  437     '' '('')'?
  438     if( lexGetToken( ) = CHAR_LPRNT ) then
  439         lexSkipToken( )
  440         if( hMatch( CHAR_RPRNT ) = FALSE ) then
  441             errReport( FB_ERRMSG_EXPECTEDRPRNT )
  442             hSkipUntil( CHAR_RPRNT, TRUE )
  443         end if
  444     end if
  445 
  446     '' resolve overloaded procs
  447     if( symbIsOverloaded( proc ) ) then
  448         if( parser.ctxsym <> NULL ) then
  449             if( symbIsProc( parser.ctxsym ) ) then
  450                 sym = symbFindOverloadProc( proc, parser.ctxsym )
  451                 if( sym <> NULL ) then
  452                     proc = sym
  453                 end if
  454             end if
  455         end if
  456     end if
  457 
  458     '' taking the address of an method? pointer to methods not supported yet..
  459     if( symbIsMethod( proc ) ) then
  460         errReportEx( FB_ERRMSG_ACCESSTONONSTATICMEMBER, symbGetFullProcName( proc ) )
  461         return astNewCONSTi( 0 )
  462     end if
  463 
  464     if( symbCheckAccess( proc ) = FALSE ) then
  465         errReportEx( FB_ERRMSG_ILLEGALMEMBERACCESS, symbGetFullProcName( proc ) )
  466     end if
  467 
  468     '' call any necessary rtl callbacks...
  469     dim as FBRTLCALLBACK callback = symbGetProcCallback( proc )
  470     if( callback <> NULL ) then
  471         callback( proc )
  472     end if
  473 
  474     function = astBuildProcAddrof( proc )
  475 end function
  476 
  477 private function hVarPtrBody _
  478     ( _
  479         byval base_parent as FBSYMBOL ptr, _
  480         byval chain_ as FBSYMCHAIN ptr _
  481     ) as ASTNODE ptr
  482 
  483     dim as ASTNODE ptr expr = cHighestPrecExpr( base_parent, chain_ )
  484     if( expr = NULL ) then
  485         errReport( FB_ERRMSG_EXPECTEDIDENTIFIER )
  486         '' error recovery: fake a node
  487         return astNewCONSTi( 0 )
  488     end if
  489 
  490     '' skip any casting if they won't do any conversion
  491     '' TODO: replace astSkipNoConvCast() with astSkipConstCASTs()
  492     '' where applicable.  Need to verify.  On one hand, we
  493     '' probably don't care about CONST anymore, on the other
  494     '' hand, we need to make sure we don't prematurely optimize
  495     '' the CONST specifier away in the event that it is needed
  496     '' to be known with AST type checking later.
  497     dim as ASTNODE ptr t = astSkipConstCASTs( expr )
  498 
  499     select case as const astGetClass( t )
  500     case AST_NODECLASS_VAR, AST_NODECLASS_IDX, AST_NODECLASS_DEREF, _
  501          AST_NODECLASS_TYPEINI, AST_NODECLASS_CALLCTOR
  502 
  503     case AST_NODECLASS_FIELD
  504         '' can't take address of bitfields..
  505         if( symbFieldIsBitfield( t->sym ) ) then
  506             errReport( FB_ERRMSG_INVALIDDATATYPES )
  507             '' error recovery: fake a node
  508             astDelTree( expr )
  509             return astNewCONSTi( 0 )
  510         end if
  511 
  512     case else
  513         errReportEx( FB_ERRMSG_INVALIDDATATYPES, "for @ or VARPTR" )
  514         '' error recovery: fake a node
  515         astDelTree( expr )
  516         return astNewCONSTi( 0 )
  517     end select
  518 
  519     '' check op overloading
  520     scope
  521         dim as FBSYMBOL ptr proc = any
  522         dim as FB_ERRMSG err_num = any
  523 
  524         proc = symbFindSelfUopOvlProc( AST_OP_ADDROF, expr, @err_num )
  525         if( proc <> NULL ) then
  526             '' build a proc call
  527             expr = astBuildCall( proc, expr )
  528             if( expr = NULL ) then
  529                 expr = astNewCONSTi( 0 )
  530             end if
  531             return expr
  532         else
  533             if( err_num <> FB_ERRMSG_OK ) then
  534                 return astNewCONSTi( 0 )
  535             end if
  536         end if
  537     end scope
  538 
  539     function = astNewADDROF( expr )
  540 end function
  541 
  542 '':::::
  543 ''AddrOfExpression  =   VARPTR '(' HighPrecExpr ')'
  544 ''                  |   PROCPTR '(' Proc ('('')')? ')'
  545 ''                  |   '@' (Proc ('('')')? | HighPrecExpr)
  546 ''                  |   SADD|STRPTR '(' Variable{str}|Const{str}|Literal{str} ')' .
  547 ''
  548 function cAddrOfExpression( ) as ASTNODE ptr
  549     dim as ASTNODE ptr expr = NULL
  550 
  551     '' '@' (Proc ('('')')? | Variable)
  552     if( lexGetToken( ) = FB_TK_ADDROFCHAR ) then
  553         lexSkipToken( )
  554 
  555         '' not inside a WITH block?
  556         dim as integer check_id = TRUE
  557         if( parser.stmt.with ) then
  558             if( lexGetToken( ) = CHAR_DOT ) then
  559                 '' not a '..'?
  560                 check_id = (lexGetLookAhead( 1, LEXCHECK_NOPERIOD ) = CHAR_DOT)
  561             end if
  562         end if
  563 
  564         '' check if the address of function is being taken
  565         dim as FBSYMCHAIN ptr chain_ = NULL
  566         dim as FBSYMBOL ptr sym = NULL, base_parent = NULL
  567 
  568         if( check_id ) then
  569             chain_ = cIdentifier( base_parent, FB_IDOPT_DEFAULT or FB_IDOPT_ALLOWSTRUCT )
  570             sym = symbFindByClass( chain_, FB_SYMBCLASS_PROC )
  571         end if
  572 
  573         '' proc?
  574         if( sym <> NULL ) then
  575             lexSkipToken( )
  576             return hProcPtrBody( base_parent, sym )
  577         '' anything else..
  578         else
  579             return hVarPtrBody( base_parent, chain_ )
  580         end if
  581     end if
  582 
  583     select case as const lexGetToken( )
  584     '' VARPTR '(' Variable ')'
  585     case FB_TK_VARPTR
  586         lexSkipToken( )
  587 
  588         '' '('
  589         if( hMatch( CHAR_LPRNT ) = FALSE ) then
  590             errReport( FB_ERRMSG_EXPECTEDLPRNT )
  591             '' error recovery: skip until ')' and fake a node
  592             hSkipUntil( CHAR_RPRNT, TRUE )
  593             return astNewCONSTi( 0 )
  594         end if
  595 
  596         expr = hVarPtrBody( NULL, NULL )
  597 
  598         '' ')'
  599         if( hMatch( CHAR_RPRNT ) = FALSE ) then
  600             errReport( FB_ERRMSG_EXPECTEDRPRNT )
  601             '' error recovery: skip until ')'
  602             hSkipUntil( CHAR_RPRNT, TRUE )
  603         end if
  604 
  605     '' PROCPTR '(' Proc ('('')')? ')'
  606     case FB_TK_PROCPTR
  607         lexSkipToken( )
  608 
  609         '' '('
  610         if( hMatch( CHAR_LPRNT ) = FALSE ) then
  611             errReport( FB_ERRMSG_EXPECTEDLPRNT )
  612             '' error recovery: skip until ')' and fake a node
  613             hSkipUntil( CHAR_RPRNT, TRUE )
  614             return astNewCONSTi( 0 )
  615         end if
  616 
  617         '' proc?
  618         dim as FBSYMCHAIN ptr chain_ = any
  619         dim as FBSYMBOL ptr sym = any, base_parent = any
  620 
  621         chain_ = cIdentifier( base_parent, _
  622                               FB_IDOPT_DEFAULT or FB_IDOPT_ALLOWSTRUCT )
  623         sym = symbFindByClass( chain_, FB_SYMBCLASS_PROC )
  624         if( sym = NULL ) then
  625             errReport( FB_ERRMSG_UNDEFINEDSYMBOL )
  626             '' error recovery: skip until ')' and fake a node
  627             hSkipUntil( CHAR_RPRNT, TRUE )
  628             return astNewCONSTi( 0 )
  629         else
  630             lexSkipToken( )
  631         end if
  632 
  633         expr = hProcPtrBody( base_parent, sym )
  634 
  635         '' ')'
  636         if( hMatch( CHAR_RPRNT ) = FALSE ) then
  637             errReport( FB_ERRMSG_EXPECTEDRPRNT )
  638             '' error recovery: skip until ')'
  639             hSkipUntil( CHAR_RPRNT, TRUE )
  640         end if
  641 
  642     '' SADD|STRPTR '(' Variable{str} ')'
  643     case FB_TK_SADD, FB_TK_STRPTR
  644         lexSkipToken( )
  645 
  646         '' '('
  647         if( hMatch( CHAR_LPRNT ) = FALSE ) then
  648             errReport( FB_ERRMSG_EXPECTEDLPRNT )
  649             '' error recovery: skip until ')' and fake a node
  650             hSkipUntil( CHAR_RPRNT, TRUE )
  651             return astNewCONSTi( 0 )
  652         end if
  653 
  654         expr = cHighestPrecExpr( NULL, NULL )
  655         if( expr = NULL ) then
  656             errReport( FB_ERRMSG_INVALIDDATATYPES )
  657             '' error recovery: skip until ')' and fake a node
  658             hSkipUntil( CHAR_RPRNT, TRUE )
  659             return astNewCONSTi( 0 )
  660         end if
  661 
  662         dim as integer dtype = astGetDataType( expr )
  663 
  664         '' UDT? it might be a kind of z|wstring
  665         if( typeGet( dtype ) = FB_DATATYPE_STRUCT ) then
  666             var sym = astGetSubType( expr )
  667             if( symbGetUdtIsZstring( sym ) or symbGetUdtIsWstring( sym ) ) then
  668                 astTryOvlStringCONV( expr )
  669                 dtype = astGetDataType( expr )
  670             end if
  671         end if
  672 
  673         if( symbIsString( dtype ) = FALSE ) then
  674             errReport( FB_ERRMSG_INVALIDDATATYPES )
  675             '' error recovery: skip until ')' and fake a node
  676             hSkipUntil( CHAR_RPRNT, TRUE )
  677             astDelTree( expr )
  678             return astNewCONSTi( 0 )
  679         end if
  680 
  681         '' check for invalid classes (functions, etc)
  682 
  683         '' skip any casting if they won't do any conversion
  684         dim as ASTNODE ptr t = astSkipNoConvCAST( expr )
  685 
  686         select case as const astGetClass( t )
  687         case AST_NODECLASS_VAR, AST_NODECLASS_IDX, _
  688              AST_NODECLASS_DEREF, AST_NODECLASS_TYPEINI, _
  689              AST_NODECLASS_FIELD
  690 
  691         case else
  692             errReportEx( FB_ERRMSG_INVALIDDATATYPES, "for STRPTR" )
  693         end select
  694 
  695         '' varlen? do: *cast( [const] zstring const ptr ptr, @expr )
  696         select case dtype
  697         case FB_DATATYPE_STRING
  698             expr = astBuildStrPtr( expr )
  699 
  700         case FB_DATATYPE_WCHAR
  701             expr = astNewCONV( typeAddrOf( FB_DATATYPE_WCHAR ), _
  702                                NULL, _
  703                                astNewADDROF( expr ) )
  704 
  705         '' anything else: do cast( zstring ptr, @expr )
  706         case else
  707             expr = astNewCONV( typeAddrOf( FB_DATATYPE_CHAR ), _
  708                                NULL, _
  709                                astNewADDROF( expr ) )
  710         end select
  711 
  712         '' ')'
  713         if( hMatch( CHAR_RPRNT ) = FALSE ) then
  714             errReport( FB_ERRMSG_EXPECTEDRPRNT )
  715             '' error recovery: skip until ')'
  716             hSkipUntil( CHAR_RPRNT, TRUE )
  717         end if
  718 
  719     end select
  720 
  721     '' Allow indexing on VARPTR()/STRPTR()/etc. directly, they look
  722     '' like functions so this isn't ambigious, while for @ it would
  723     '' mess up the operator precedence:
  724     ''    @expr[i]  should be  @(expr[i]), not (@expr)[i]
  725     '' but for
  726     ''    varptr(expr)[i], that problem doesn't exist.
  727     function = cStrIdxOrMemberDeref( expr )
  728 end function