Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

SE script engine

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    SE script engine

    '
    This is a script engine and a demo for it.
    Please report, any feedback is welcome

    The 3 provided files below are:

    se_engine.inc
    se_errors.inc
    se_engine_demo.bas

    Compiled demo:
    http://megaexplorer.com/miscfiles/se_engine_demo.zip

    Language description:
    This is a demo for my rough, proof of concept script engine
    framework, or basic interprete, as you like.
    Supported dialect is a expandable powerbasic subset.
    Please see below for keywords detailed description.
    There are two variables types, numbers and strings.
    Numbers are internally stored as PB singles, strings
    are dynamic strings. There is not a predefined variables
    count limit. Loops and if/then can be nested to any deep,
    because they are managed by state machine not recursively.
    Variables must be declared prior use and are all 'globals'.
    No functions and subs yet. I have a version of this with them
    but more testing is need. Well...this one also needs more
    testing

    Save this as "se_engine.inc"
    Code:
    #IF 0
    '########################################
    '########################################
    '###############  SE Script engine
    '###############  2007 by Gus del Solar
    '###############  GDSSIS software
    '
    'Language description:
    'This is a demo for my rough, proof of concept script engine
    'framework, or basic interprete, as you like.
    'Supported dialect is a expandable powerbasic subset.
    'Please see below for keywords detailed description.
    'There are two variables types, numbers and strings.
    'Numbers are internally stored as PB singles, strings
    'are dynamic strings. There is not a predefined variables
    'count limit. Loops and if/then can be nested to any deep,
    'because they are managed by state machine not recursively.
    'Variables must be declared prior use and are all 'globals'.
    'No functions and subs yet. I have a version of this with them
    'but more testing is need. Well...this one also needs more
    'testing :)
    '
    'Application:
    'Could be useful as didactic tool, configuration tool, etc.
    '
    'Internal description:
    'The whole script is scanned byte by byte and tokenized using
    'pointers, so quite fast. Built in functions tokens are
    'loaded with a function definition code that includes argument
    'count, argument types, return type, etc.
    'Then a preprocessor does several pass to the tokenized code,
    'I prefered a well formated code to allow fast execution later.
    'All jumps and loops addresses are precalculated and set
    'at preprocessor level. Variabled are dimmed in that stage also.
    'Numeric expressions are converted to postfix while
    'processed, then solved with the help of a little stack and
    'pointers. No arrays or strings involved in math processing.
    'Set %DEBUG_FLAG to 1 to see detailed debug information.
    '
    'Detailed description:
    '
    ' VARIABLES TYPES SUPPORT
    '  numbers (PB singles)
    '  strings (PB dynamic strings)
    '
    ' COMMENTS
    ' '
    '
    ' NUMERIC OPERATORS SUPPORT
    '  +, -, *, /, \, ^, =
    '  <, >, =<, <=, >=, =>, <>, ><
    '  not, istrue, isfalse
    '  and, or, xor
    '  imp, eqv, mod
    '
    ' STRING OPERATORS SUPPORT
    '  + , =, <, >, <=, =<, =>, >=
    '
    ' FLOW CONTROL SUPPORT (any deep)
    '  if <cond> then, elseif <cond> then, else, end if
    '  exit, exit if, exit do/loop, exit for
    '  for var = n/var/expr to n/var/expr [step n/var/expr], next
    '  do, loop
    '  do <cond>, loop
    '  do, loop <cond>
    '  do while/until <cond>, loop
    '  do, loop while/until <cond>
    '  while <cond>, wend
    '  goto label
    '
    ' STRING FUNCTIONS SUPPORT
    '  left (st expr,n expr)
    '  mid  (st expr,n expr,n expr)
    '  right (st expr,n expr)
    '  trim (st expr)
    '  lcase (st expr)
    '  ucase (st expr)
    '  mcase (st expr)
    '  hcase (st expr) ThIs iS HcAsE (very ugly)
    '  str (n expr)
    '  chr (n expr)
    '  space (n expr)
    '  string (n expr,n expr)
    '  bin (n expr)
    '  hex (n expr)
    '  acode (st expr)
    '  ucode (st expr)
    '  nul (n expr)
    '  date
    '  time
    '
    ' NUMERIC FUNCTIONS SUPPORT
    '  asc (st expr)
    '  instr (st expr, st expr)
    '  len (st expr)
    '  val (st expr)
    '  rnd (n min, n max)
    '  even (n expr)
    '  prglines
    '  timer
    '  ceil (n expr)
    '  int (n expr)
    '  fix (n expr)
    '  sqr (n expr), sin (n expr)
    '  cos (n expr), tan (n expr), atn (n expr), exp2, exp10 (n expr)
    '  exp (n expr), log10 (n expr), abs (n expr), log2 (n expr)
    '  log (n expr), sgn (n expr), fac (n expr), pi
    '
    ' MISC
    '  randomize (no args, always uses timer as seed)
    '
    ' LABELS
    '  standard labels
    '
    ' I/O SUPPORT (demos)
    '  print, ? (will print to result textbox)
    '  stdout (st expr)
    '  stdin
    '  waitkey
    '  beep
    '  msgbox (st text, n style, st tittle)
    '
    'Note:
    ' No optional arguments.
    '
    'Whishlist:
    '  some script <-> caller communication
    '  more data types, unsigned, ext, types
    '  arrays
    '  functions, subs (and private subs and functions)
    '  objects ?
    '  better type detect
    '  asm math processing
    '  static, locals (all globals now)
    '  funny girls, money, cars, a timemachine
    '
    '  =====================================================
    '  COPYRIGHT AND PERMISSION NOTICE (copied from elsewhere)
    '  =====================================================
    '  Copyright (c) 2007 - 2007, Gus del Solar
    '  All rights reserved.
    '  Permission to use and copy this software for non
    '  commercial purpose always keeping this notice. For
    '  commercial please contact the author at gds3k3 gmail com
    '  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
    '  ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
    '  TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    '  PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
    '  RIGHTS. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    '  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    '  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
    '  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
    '  THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    '
    #ENDIF
    
    %DEBUG_FLAG = 0                     'set this one to 1 to see debug info
    
    %ASCII_NULL              =   0      '
    %ASCII_TAB               =   9      'TAB
    %ASCII_LINEFEED          =  10      'LF
    %ASCII_ENTER             =  13      'CR
    %ASCII_ESCAPE            =  27      'ESC
    %ASCII_SPACE             =  32      '
    %ASCII_DOUBLEQUOTE       =  34      '"
    %ASCII_AMPER             =  38      '&
    %ASCII_APOSTROPHE        =  39      ''
    %ASCII_PARENTHESES_OPEN  =  40      '(
    %ASCII_PARENTHESES_CLOSE =  41      ')
    %ASCII_MUL               =  42      '*
    %ASCII_PLUS              =  43      '-
    %ASCII_COMMA             =  44      ',
    %ASCII_MINUS             =  45      '+
    %ASCII_PERIOD            =  46      '.
    %ASCII_DIV               =  47      '/
    %ASCII_NUM0              =  48      '0
    %ASCII_NUM9              =  57      '9
    %ASCII_COLON             =  58      ':
    %ASCII_SEMICOLON         =  59      ';
    %ASCII_LESS              =  60      '<
    %ASCII_EQUAL             =  61      '=
    %ASCII_MORE              =  62      '>
    %ASCII_QUESTION          =  63      '?
    %ASCII_UCASE_A           =  65      'A
    %ASCII_UCASE_N           =  78      'N
    %ASCII_UCASE_S           =  83      'S
    %ASCII_UCASE_Z           =  90      'Z
    %ASCII_INTDIV            =  92      '\
    %ASCII_BRACKETOPEN       =  91      '[
    %ASCII_BRACKETCLOSE      =  93      ']
    %ASCII_CARET             =  94      '^
    %ASCII_UNDERSCORE        =  95      '_
    %ASCII_LCASE_A           =  97      'a
    %ASCII_LCASE_B           =  98      'b
    %ASCII_LCASE_C           =  99      'c
    %ASCII_LCASE_E           = 101      'e
    %ASCII_LCASE_F           = 102      'f
    %ASCII_LCASE_I           = 105      'i
    %ASCII_LCASE_M           = 109      'm
    %ASCII_LCASE_X           = 120      'x
    %ASCII_LCASE_Z           = 122      'z
    %ASCII_TILDE             = 126      '~
    %ASCII_OR                = 179      '|
    
    %SE_TOKEN_UNDEFINED   =  0
    %SE_TOKEN_DELIMITER   =  1
    %SE_TOKEN_NUMBER      =  2
    %SE_TOKEN_ALPHA       =  3
    %SE_TOKEN_STRING      =  4
    %SE_TOKEN_CRLF        =  5
    %SE_TOKEN_COLON       =  6
    %SE_TOKEN_EOL         = 13
    %SE_TOKEN_CONDITION   =  8
    %SE_TOKEN_NUMEXPR     =  9
    %SE_TOKEN_STREXPR     = 10
    %SE_TOKEN_ASSIGN      = 11
    %SE_TOKEN_PRINT       = 14
    
    %SE_MAXTOKENLEN       = 64        'max token lenght, tokens are text constants, numbers, labels and var names
    %SE_MAXEXPRLEN        = 128       'max expression len
    %SE_MAXTOKENS         = 512       '512 tokens should be enough for anyone...bill said that not me
    %SE_MAXCODETOKENS     = 2048      'small codetokens buffer, use 8192 or more, no checking of this
    
    'function definition flags
    %SE_FDEF_USRSUB     = &h00800000  'user sub
    %SE_FDEF_USRFUN     = &h00400000  'user function
    %SE_FDEF_OPERATOR   = &h20000000  'operator
    %SE_FDEF_FLOWCTRL   = &h00100000  'flow control
    %SE_FDEF_RETNOT     = &h00200000  'no return
    %SE_FDEF_BINFUN     = &h10000000  'built in function
    %SE_FDEF_RETSNG     = &h01000000  'numeric return
    %SE_FDEF_RETLNG     = &h02000000  'numeric return
    %SE_FDEF_RETDWD     = &h04000000  'numeric return
    %SE_FDEF_RETSTR     = &h08000000  'string return
    %SE_FDEF_ARGS1      = &h00000100  'has 1 arg
    %SE_FDEF_ARGS2      = &h00000200  'has 2 arg
    %SE_FDEF_ARGS3      = &h00000300  'has 3 arg
    %SE_FDEF_ARGS4      = &h00000400  'has 4 arg
    %SE_FDEF_ARG1NUM    = &h00001000  'arg1 is number
    %SE_FDEF_ARG1STR    = &h00002000  'arg1 is string
    %SE_FDEF_ARG2NUM    = &h00004000  'arg2 is number
    %SE_FDEF_ARG2STR    = &h00008000  'arg2 is string
    %SE_FDEF_ARG3NUM    = &h00010000  'arg3 is number
    %SE_FDEF_ARG3STR    = &h00020000  'arg3 is string
    %SE_FDEF_ARG4NUM    = &h00040000  'arg4 is number
    %SE_FDEF_ARG4STR    = &h00080000  'arg4 is string
    %SE_FDEF_PARARGS    = &h00000800  'args are parenthised
    %SE_FDEF_RETNUM     = %SE_FDEF_RETDWD OR %SE_FDEF_RETSNG OR %SE_FDEF_RETLNG  'any numeric return
    '%SE_FDEF_ARG1OPT    = &h01000000  'arg1 is optional
    '%SE_FDEF_ARG2OPT    = &h02000000  'arg2 is optional
    '%SE_FDEF_ARG3OPT    = &h04000000  'arg3 is optional
    '%SE_FDEF_ARG4OPT    = &h08000000  'arg4 is optional
    
    %SE_FDEF_ARGMASK    = &h00000700???
    %SE_FDEF_PARARGS    = &h00000800???
    %SE_FDEF_RETMASK    = &h00f00000???
    %SE_FDEF_ARGTYPE    = &h000ff000???
    %SE_FDEF_SUBMASK    = &h00800000???
    %SE_FDEF_FUNMASK    = &h00400000???
    
    MACRO funargs(fun) = (fun AND %SE_FDEF_ARGMASK) \ 256                 'function arguments count
    MACRO parargs(fun) = ISTRUE(fun AND %SE_FDEF_PARARGS) '\ %SE_FDEF_PARARGS    'are arguments parenthised?
    MACRO isnumvar(xr) = ISTRUE(xr AND %SE_FDEF_RETNUM)
    
    %SE_TOKENCMD_ABS      =   1 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_ACODE    =   2 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1STR OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_AND      =   3 OR %SE_FDEF_OPERATOR
    %SE_TOKENCMD_AS       =   4 OR %SE_FDEF_RETNOT                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_ASC      =   5 OR %SE_FDEF_RETNUM                                           OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_ATN      =   6 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_BEEP     =  11 OR %SE_FDEF_RETNOT                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_BIN      =  10 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_CASE     =  12 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_CMD      =  13 OR %SE_FDEF_RETSTR                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_CEIL     =  15 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_CHR      =  18 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_CINT     =  16 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_COS      =  17 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_DATE     =  24 OR %SE_FDEF_RETSTR                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_DECR     =  25 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_DO       =  26 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_DWORD    =  27 OR %SE_FDEF_RETNOT                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_ENVGET   =  28 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1STR OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_ENVSET   =  29 OR %SE_FDEF_RETNOT                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_EXTRACT  =  30
    %SE_TOKENCMD_END      =  31 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_ELSE     =  32 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_ELSEIF   =  33 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_EQV      =  34 OR %SE_FDEF_OPERATOR
    %SE_TOKENCMD_ERASE    =  35 OR %SE_FDEF_RETNOT                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_EVEN     =  36 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_EXP      =  37 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_EXP2     =  38 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_EXP10    =  39 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_EXIT     =  40 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_FAC      =  41 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_FIX      =  42 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_FOR      =  43 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_FUNCTION =  44 OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_FRAC     =  45 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_FORMAT   =  46
    %SE_TOKENCMD_FUNNAME  =  47 OR %SE_FDEF_RETSTR                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_GOTO     =  50 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_GOSUB    =  51 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_HCASE    =  55 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1STR OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_HEX      =  56 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_IF       =  66 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_IMP      =  63 OR %SE_FDEF_OPERATOR
    %SE_TOKENCMD_INCR     =  65 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_INSTR    =  60 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS3 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_ARG2STR OR %SE_FDEF_ARG3STR OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_INT      =  64 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_ISTRUE   =  61 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_ISFALSE  =  62 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_JOIN     =  63
    %SE_TOKENCMD_MAXS     =  66
    %SE_TOKENCMD_MINS     =  67
    %SE_TOKENCMD_MAX      =  70 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_MSGBOX   =  71 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS3 OR %SE_FDEF_ARG1STR OR %SE_FDEF_ARG2NUM OR %SE_FDEF_ARG3STR OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_MCASE    =  72 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1STR OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_MID      =  73 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS3 OR %SE_FDEF_ARG1STR OR %SE_FDEF_ARG2NUM OR %SE_FDEF_ARG3NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_MIN      =  74 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_MOD      =  75 OR %SE_FDEF_OPERATOR
    %SE_TOKENCMD_NEXT     =  76 OR %SE_FDEF_FLOWCTRL
    %SE_TOKENCMD_NOT      =  77 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_NUMBERS  =  78 OR %SE_FDEF_RETNOT                                       OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_NUL      =  79 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_LCASE    =  80 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1STR OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_LEFT     =  81 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS2 OR %SE_FDEF_ARG1STR OR %SE_FDEF_ARG2NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_LEN      =  82 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1STR OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_LOG      =  83 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_LOG2     =  84 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_LOG10    =  85 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_LOOP     =  86 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_LET      =  87 OR %SE_FDEF_RETNOT                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_LONG     =  88 OR %SE_FDEF_RETNOT                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_LTRIM    =  89 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1STR OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_OCT      =  94 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_OR       =  95 OR %SE_FDEF_OPERATOR
    %SE_TOKENCMD_PRGLINES =  96 OR %SE_FDEF_RETNUM                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_PI       =  97 OR %SE_FDEF_RETNUM                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_RESET    = 100 OR %SE_FDEF_RETNOT                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_RANDOM   = 101 OR %SE_FDEF_RETNOT                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_RETURN   = 102 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_RIGHT    = 103 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS2 OR %SE_FDEF_ARG1STR OR %SE_FDEF_ARG2NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_RND      = 104 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS2 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_ARG2NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_ROUND    = 105 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_REMAIN   = 106
    %SE_TOKENCMD_REMOVE   = 107
    %SE_TOKENCMD_REPEAT   = 108
    %SE_TOKENCMD_RETAIN   = 109
    %SE_TOKENCMD_RTRIM    = 110 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1STR OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_SIN      = 114 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_SGN      = 115 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_SPACE    = 116 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_SQR      = 117 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_STDERR   = 118                                                              OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_STDIN    = 119 OR %SE_FDEF_RETSTR                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_STDOUT   = 120 OR %SE_FDEF_RETNOT OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1STR OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_STR      = 121 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_STRING   = 122 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS2 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_ARG2STR OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_STEP     = 123 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_SUB      = 124 OR %SE_FDEF_RETNOT                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_SELECT   = 125 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_STRINGS  = 126 OR %SE_FDEF_RETNOT                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_SINGLE   = 127 OR %SE_FDEF_RETNOT                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_STRDEL   = 128
    %SE_TOKENCMD_STRINS   = 129
    %SE_TOKENCMD_STRREV   = 130
    %SE_TOKENCMD_TAB      = 133 OR %SE_FDEF_RETSTR                                           OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_TAN      = 134 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1NUM OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_TIME     = 135 OR %SE_FDEF_RETSTR                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_THEN     = 136 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_TIMER    = 137 OR %SE_FDEF_RETNUM                                           OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_TO       = 138 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_TRIM     = 139 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1STR OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_UCASE    = 143 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1STR OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_UCODE    = 144 OR %SE_FDEF_RETSTR OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1STR OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_UNTIL    = 145 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_VAL      = 146 OR %SE_FDEF_RETNUM OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1STR OR %SE_FDEF_BINFUN OR %SE_FDEF_PARARGS
    %SE_TOKENCMD_WEND     = 147 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_WHILE    = 148 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_WAITKEY  = 149 OR %SE_FDEF_RETNOT
    %SE_TOKENCMD_XOR      = 150 OR %SE_FDEF_OPERATOR                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_PRINT    = 151 OR %SE_FDEF_RETNOT OR %SE_FDEF_ARGS1 OR %SE_FDEF_ARG1STR OR %SE_FDEF_BINFUN
    
    'misc internal use
    %SE_TOKENCMD_ENDIF    = 201 OR %SE_FDEF_FLOWCTRL OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_DOW      = 202 OR %SE_FDEF_FLOWCTRL OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_DOU      = 203 OR %SE_FDEF_FLOWCTRL OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_ENDFUN   = 204 OR %SE_FDEF_FLOWCTRL OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_EXITIF   = 205 OR %SE_FDEF_FLOWCTRL OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_EXITDO   = 206 OR %SE_FDEF_FLOWCTRL OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_EXITWHI  = 207 OR %SE_FDEF_FLOWCTRL OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_EXITFOR  = 208 OR %SE_FDEF_FLOWCTRL OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_EXITFUN  = 209 OR %SE_FDEF_FLOWCTRL OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_EXITSUB  = 210 OR %SE_FDEF_FLOWCTRL OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_EXITSEL  = 211 OR %SE_FDEF_FLOWCTRL OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_LOOPW    = 212 OR %SE_FDEF_FLOWCTRL OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_LOOPU    = 213 OR %SE_FDEF_FLOWCTRL OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_GLETNUM  = 214 OR %SE_FDEF_RETNOT   OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_GLETSTR  = 215 OR %SE_FDEF_RETNOT   OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_RSTNUM   = 216 OR %SE_FDEF_RETNOT   OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_RSTSTR   = 217 OR %SE_FDEF_RETNOT   OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_ERNUM    = 218 OR %SE_FDEF_RETNOT   OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_ERSTR    = 219 OR %SE_FDEF_RETNOT   OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_LABEL    = 220 OR %SE_FDEF_FLOWCTRL OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_PRENUM   = 223 OR %SE_FDEF_RETNOT
    %SE_TOKENCMD_PRESTR   = 224 OR %SE_FDEF_RETNOT
    %SE_TOKENCMD_GNUMVAR  = 225 OR %SE_FDEF_RETNUM
    %SE_TOKENCMD_GSTRVAR  = 226 OR %SE_FDEF_RETSTR
    %SE_TOKENCMD_FUNNUM   = 227 OR %SE_FDEF_RETNUM OR %SE_FDEF_USRFUN OR %SE_FDEF_PARARGS 'numeric user function
    %SE_TOKENCMD_ENDSUB   = 228 OR %SE_FDEF_FLOWCTRL OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_ENDSEL   = 229 OR %SE_FDEF_FLOWCTRL OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_ENDSCR   = 230 OR %SE_FDEF_FLOWCTRL OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_JUMPTO   = 231 OR %SE_FDEF_FLOWCTRL OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_FUNSTR   = 232 OR %SE_FDEF_RETSTR  OR %SE_FDEF_USRFUN OR %SE_FDEF_PARARGS 'string user function
    %SE_TOKENCMD_OPERAT   = 0   OR %SE_FDEF_OPERATOR
    %SE_TOKENCMD_NUMCONST = 0   OR %SE_FDEF_RETNUM
    %SE_TOKENCMD_STRCONST = 0   OR %SE_FDEF_RETSTR
    %SE_TOKENCMD_INCLUDE  = 233
    %SE_TOKENCMD_IFS      = 234 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    %SE_TOKENCMD_ELSEIFS  = 235 OR %SE_FDEF_FLOWCTRL                                         OR %SE_FDEF_BINFUN
    
    'argument types
    %SE_ARGTYPE_NUMMSK    = %SE_FDEF_ARG1NUM OR %SE_FDEF_ARG2NUM OR %SE_FDEF_ARG3NUM OR %SE_FDEF_ARG4NUM
    %SE_ARGTYPE_STRMSK    = %SE_FDEF_ARG1STR OR %SE_FDEF_ARG2STR OR %SE_FDEF_ARG3STR OR %SE_FDEF_ARG4STR
    %SE_ARGTYPE_VARNUM    = %SE_TOKENCMD_GNUMVAR OR %SE_ARGTYPE_NUMMSK
    %SE_ARGTYPE_VARSTR    = %SE_TOKENCMD_GSTRVAR OR %SE_ARGTYPE_STRMSK
    %SE_ARGTYPE_CONNUM    = %SE_FDEF_RETNUM OR %SE_ARGTYPE_NUMMSK
    %SE_ARGTYPE_CONSTR    = %SE_FDEF_RETSTR OR %SE_ARGTYPE_STRMSK
    %SE_ARGTYPE_BINNUM    = %SE_FDEF_RETNUM OR %SE_FDEF_BINFUN OR %SE_ARGTYPE_NUMMSK
    %SE_ARGTYPE_BINSTR    = %SE_FDEF_RETSTR OR %SE_FDEF_BINFUN OR %SE_ARGTYPE_STRMSK
    %SE_ARGTYPE_USRNUM    = %SE_FDEF_RETNUM OR %SE_FDEF_USRFUN OR %SE_ARGTYPE_NUMMSK
    %SE_ARGTYPE_USRSTR    = %SE_FDEF_RETSTR OR %SE_FDEF_USRFUN OR %SE_ARGTYPE_STRMSK
    %SE_ARGTYPE_EXPNUM    = %SE_FDEF_RETNUM OR %SE_FDEF_OPERATOR OR %SE_ARGTYPE_NUMMSK
    %SE_ARGTYPE_EXPSTR    = %SE_FDEF_RETSTR OR %SE_FDEF_OPERATOR OR %SE_ARGTYPE_STRMSK
    
    $ALLOWEDCHARS     = "0123456789_abcdefghijklmnopqrstuvwxyz"   'I hope I don't forget any letter
    $ALLOWEDFIRSTCHAR = "_abcdefghijklmnopqrstuvwxyz"
    
    'lookfor function calling flags
    %SE_LOOK4_NUM         = %SE_TOKEN_NUMBER
    %SE_LOOK4_ALPHA       = %SE_TOKEN_ALPHA
    %SE_LOOK4_STRING      = %SE_TOKEN_STRING
    %SE_LOOK4_EOL         = %SE_TOKEN_EOL
    
    %SE_LOOK4_COMMA       = %ASCII_COMMA
    %SE_LOOK4_EQUAL       = %ASCII_EQUAL
    %SE_LOOK4_PAROPEN     = %ASCII_PARENTHESES_OPEN
    %SE_LOOK4_PARCLOSE    = %ASCII_PARENTHESES_CLOSE
    %SE_LOOK4_SEMICOLON   = %ASCII_SEMICOLON
    
    %SE_LOOK4_IF          = 00000200
    %SE_LOOK4_MATCHPAR    = 00000201
    %SE_LOOK4_THEN        = %SE_TOKENCMD_THEN  '00000202
    %SE_LOOK4_ELSEIF      = 00000208
    %SE_LOOK4_ELSE        = 00000210
    %SE_LOOK4_ENDIF       = 00000211
    %SE_LOOK4_DO          = 00000212
    %SE_LOOK4_LOOP        = 00000214
    %SE_LOOK4_WHILE       = 00000215
    %SE_LOOK4_WEND        = 00000216
    %SE_LOOK4_FOR         = 00000217
    %SE_LOOK4_NEXT        = 00000218
    %SE_LOOK4_FUNCTION    = 00000219
    %SE_LOOK4_ENDFUNC     = 00000220
    %SE_LOOK4_ENDSEL      = 00000221
    %SE_LOOK4_LABEL       = 00000222
    %SE_LOOK4_DOWHILE     = %SE_TOKENCMD_WHILE '00000223
    %SE_LOOK4_DOUNTIL     = %SE_TOKENCMD_UNTIL '00000224
    %SE_LOOK4_TO          = %SE_TOKENCMD_TO
    %SE_LOOK4_STEP        = %SE_TOKENCMD_STEP
    
    'math stack constants
    %SE_EVAL_STACK        = 0
    %SE_EVAL_OUTPT        = 1
    %SE_EVAL_PRECE        = 2
    %SE_EVAL_OTYPE        = 3
    %SE_EVAL_STYPE        = 4
    
    TYPE se_token_type
         tktype     AS WORD
         token      AS ASCIIZ * %SE_MAXTOKENLEN
         addrs      AS DWORD
    END TYPE
    
    TYPE se_codetoken
         token      AS DWORD               'token address, if any
         lncnt      AS WORD                'source code line number
         secnt      AS BYTE                'statement counter
         tktype     AS BYTE                '%SE_TOKEN_xxx or ascii delimiter
         cmd        AS DWORD               '%SE_TOKENCMD_xxx and function definition
         arg1       AS DWORD               'arguments by context
         arg2       AS DWORD               '
         arg3       AS DWORD               '
         arg4       AS DWORD               '
         pre        AS SINGLE              '
    END TYPE
    
    UNION numbers_union
         dwd        AS DWORD               'used to convert types
         sng        AS SINGLE              '
         lng        AS LONG                '
    END UNION
    
    TYPE ses_type                          'main program struct, this one carry all pointers
         tk         AS se_token_type PTR   'tokens ptr
         ct         AS se_codetoken PTR    'codetokens ptr
         tks        AS DWORD               'tokens count
         ctks       AS DWORD               'codetokens count
         pc         AS DWORD               'program counter
         msp        AS DWORD               'math stack address
         errcode    AS DWORD               'lasterror
         nvarsn     AS DWORD PTR           'points to numeric vars names array
         svarsn     AS DWORD PTR           'points to string vars names array
         nvars      AS SINGLE PTR          'points to numeric vars contents array
         svars      AS STRING PTR          'points to string vars contents array
         ubn        AS LONG                'ubound numeric vars array
         ubs        AS LONG                'ubound string vars array
    END TYPE
    
    #INCLUDE "se_errors.inc"
    
    DECLARE FUNCTION lookfor(BYVAL ses AS ses_type PTR, BYVAL start AS LONG, BYVAL match AS DWORD) AS DWORD
    DECLARE FUNCTION processnumber(BYVAL ses AS ses_type PTR, BYVAL start AS LONG, BYVAL ends AS LONG) AS SINGLE
    DECLARE FUNCTION processstring(BYVAL ses AS ses_type PTR, BYVAL start AS LONG, BYVAL ends AS LONG) AS STRING
    DECLARE FUNCTION funtext(code AS LONG) AS STRING
    DECLARE FUNCTION prtcode(BYVAL ses AS ses_type PTR, BYVAL start AS LONG, BYVAL ends AS LONG) AS DWORD
    DECLARE FUNCTION fundefs(code AS STRING) AS LONG
    DECLARE FUNCTION argtypetext(code AS DWORD) AS STRING
    DECLARE FUNCTION execfun(BYVAL ses AS ses_type PTR) AS DWORD
    
    'debug helper macro
    MACRO ifdbgx(x)
    #IF %DEBUG_FLAG
    x
    #ENDIF
    END MACRO
    
    'adds double quotes
    MACRO pdq(stext) = $DQ + stext + $DQ
    
    'extracts double quotes
    FUNCTION extdq(stext AS STRING) AS STRING
        LOCAL stmp1 AS STRING
        stmp1 = TRIM$(stext)
        IF LEFT$(stmp1, 1) = $DQ THEN stmp1 = MID$(stmp1, 2)
        IF RIGHT$(stmp1, 1) = $DQ THEN stmp1 = LEFT$(stmp1, LEN(stmp1) - 1)
        FUNCTION = stmp1
    END FUNCTION
    
    'takes an argument type code and returns a descriptive string, for debug
    FUNCTION argtypetext2(code AS DWORD) AS STRING
             IF ISTRUE(code AND %SE_FDEF_RETNUM) THEN
                 FUNCTION = "%SE_ARGTYPE_RETNUM": EXIT FUNCTION
             ELSEIF ISTRUE(code AND %SE_FDEF_RETSTR) THEN
                 FUNCTION = "%SE_ARGTYPE_RETSTR": EXIT FUNCTION
             END IF
    END FUNCTION
    
    'takes an argument type code and returns a descriptive string, for debug
    FUNCTION argtypetext(code AS DWORD) AS STRING
             LOCAL stmp1 AS STRING
             IF ISTRUE(code AND %SE_FDEF_OPERATOR) THEN
                IF ISTRUE(code AND %SE_FDEF_RETNUM) THEN
                    FUNCTION = "%SE_ARGTYPE_EXPNUM": EXIT FUNCTION
                ELSEIF ISTRUE(code AND %SE_FDEF_RETSTR) THEN
                    FUNCTION = "%SE_ARGTYPE_EXPSTR": EXIT FUNCTION
                END IF
             END IF
             IF ISTRUE(code AND %SE_ARGTYPE_VARNUM) THEN
                 FUNCTION = "%SE_ARGTYPE_VARNUM": EXIT FUNCTION
             ELSEIF ISTRUE(code AND %SE_ARGTYPE_VARSTR) THEN
                 FUNCTION = "%SE_ARGTYPE_VARSTR": EXIT FUNCTION
             END IF
             IF ISTRUE(code AND %SE_ARGTYPE_CONNUM) THEN
                 FUNCTION = "%SE_ARGTYPE_CONNUM": EXIT FUNCTION
             ELSEIF ISTRUE(code AND %SE_ARGTYPE_CONSTR) THEN
                 FUNCTION = "%SE_ARGTYPE_CONSTR": EXIT FUNCTION
             END IF
             IF ISTRUE(code AND %SE_ARGTYPE_BINNUM) THEN
                 FUNCTION = "%SE_ARGTYPE_BINNUM": EXIT FUNCTION
             ELSEIF ISTRUE(code AND %SE_ARGTYPE_BINSTR) THEN
                 FUNCTION = "%SE_ARGTYPE_BINSTR": EXIT FUNCTION
             END IF
             IF ISTRUE(code AND %SE_ARGTYPE_USRNUM) THEN
                 FUNCTION = "%SE_ARGTYPE_USRNUM": EXIT FUNCTION
             ELSEIF ISTRUE(code AND %SE_ARGTYPE_USRSTR) THEN
                 FUNCTION = "%SE_ARGTYPE_USRSTR": EXIT FUNCTION
             END IF
             IF ISFALSE(LEN(stmp1)) THEN FUNCTION = "%SE_ARGTYPE_UNKNOW"
    END FUNCTION
    
    'takes an argument type code and returns a descriptive string, for debug
    FUNCTION typearg(BYVAL fun AS DWORD) AS DWORD
          IF ((fun AND %SE_FDEF_RETNUM) = %SE_FDEF_RETNUM) THEN
              FUNCTION = %SE_FDEF_RETNUM
          ELSEIF ((fun AND %SE_FDEF_RETSTR) = %SE_FDEF_RETSTR) THEN
              FUNCTION = %SE_FDEF_RETSTR
          ELSEIF ((fun AND %SE_FDEF_RETNOT) = %SE_FDEF_RETNOT) THEN
              FUNCTION = %SE_FDEF_RETNOT
          ELSEIF isnumvar(fun) THEN
              FUNCTION = %SE_FDEF_RETNUM
          END IF
    END FUNCTION
    
    FUNCTION checkarg(BYVAL ses AS ses_type PTR, BYVAL startin AS LONG, BYVAL arg AS LONG) AS DWORD
        LOCAL count, tmp1, tmp2, start AS LONG, result, parflag, alpha, level AS DWORD
        dbcprint("     CheckArg In : [" + FORMAT$(startin) + "-" + FORMAT$(arg) + "]")
        start = startin
    
        'I always get confussed with many stacked parenthessis...
        'fff[((2+4)+fun(2,5)), 3]     fun[(((((fun(x, fun(2)))))))]            nada str str
        '    ((2+4)+fun(2,5))              ((((fun(x, fun(2))))))              nada num num
        '     (2+4)+fun(2,5)                (((fun(x, fun(2)))))               num  num num
        '                                    ((fun(x, fun(2))))                num  str error
        '                                     (fun(x, fun(2)))                 str  num error
        '                                      fun(x, fun(2))                  str  str str
    
        INCR count
        WHILE (count < arg)
              start = lookfor(ses, start + 1, %SE_LOOK4_COMMA)
              IF start THEN INCR count ELSE EXIT DO
        WEND
        start = IIF&(start, start, startin)
    
        DO
              INCR start
              SELECT CASE @ses.@ct[start].tktype
                     CASE %ASCII_PARENTHESES_OPEN
                          INCR parflag
                     CASE %ASCII_PARENTHESES_CLOSE
                          DECR parflag
                          IF (parflag < 0) THEN
                             dbcprint("     CheckArg out: 1")
                             EXIT FUNCTION   'syntax error
                          END IF
                     CASE %SE_TOKEN_STRING
                          IF ISFALSE(result) THEN
                             result = %SE_ARGTYPE_EXPSTR
                          ELSEIF (typearg(result) <> %SE_FDEF_RETSTR) THEN
                             dbcprint("     CheckArg out: 2")
                             EXIT FUNCTION   'type mismatch
                          END IF
                     CASE %SE_TOKEN_NUMBER
                          IF ISFALSE(result) THEN
                             result = %SE_ARGTYPE_EXPNUM
                          ELSEIF (typearg(result) <> %SE_FDEF_RETNUM) THEN
                             dbcprint("     CheckArg out: 3")
                             EXIT FUNCTION   'type mismatch
                          END IF
                     CASE %SE_TOKEN_ALPHA
                          IF ISFALSE(result) THEN
                             result = SWITCH&((typearg(@ses.@ct[start].cmd) = %SE_FDEF_RETNUM), %SE_ARGTYPE_EXPNUM, (typearg(@ses.@ct[start].cmd) = %SE_FDEF_RETSTR), %SE_ARGTYPE_EXPSTR)
                          ELSEIF (typearg(result) <> typearg(@ses.@ct[start].cmd)) THEN
                             dbcprint("     CheckArg out: 4")
                             EXIT FUNCTION   'type mismatch
                          END IF
                          IF funargs(@ses.@ct[start].cmd) THEN
                             IF parargs(@ses.@ct[start].cmd) THEN
                                start = @ses.@ct[start].arg2
                             'else
                             '   dbcprint("     CheckArg out: 5")
                             '   EXIT FUNCTION
                             END IF
                          END IF
                     CASE %ASCII_COMMA
                          IF ISFALSE(parflag) THEN
                             dbcprint("     CheckArg out: 6")
                             EXIT DO         'finished
                          END IF
                     CASE %SE_TOKEN_EOL
                          dbcprint("     CheckArg out: 7")
                          EXIT DO            'finished
              END SELECT
        LOOP WHILE (start < @ses.@ct[start].arg2)
    
        FUNCTION = result
        dbcprint("     CheckArg out: start [" +  FORMAT$(startin) + "] found[" + FORMAT$(start) + "-" + FORMAT$(arg) + "] result " + argtypetext2(result))
    END FUNCTION
    
    'takes argument arg, solve it and return the result as single   (n1, n2,...)
    FUNCTION procargn(BYVAL ses AS ses_type PTR, BYVAL start AS LONG, BYVAL arg AS LONG) AS SINGLE
        ifdbgx(TRACE PRINT FUNCNAME$)
        ifdbgx(TRACE PRINT CALLSTK$(1))
        LOCAL count, tmp1 AS LONG
        dbcprint("     procargN In : [" + FORMAT$(start) + "-" + FORMAT$(0) + "]")
        INCR start
        DO
             tmp1 = lookfor(ses, start, %SE_LOOK4_COMMA)
             INCR count
             IF (count = arg) THEN EXIT DO
             INCR tmp1
             start = tmp1
        LOOP WHILE tmp1
        dbcprint("     procargN out: [" + FORMAT$(start) + "-" + FORMAT$(tmp1 - 1) + "]")
        IF tmp1 THEN
           FUNCTION = processnumber(ses, start, tmp1 - 1)
        END IF
    END FUNCTION
    
    'takes argument arg, solve it and return the result as string    (st1, st2,...)
    FUNCTION procargs(BYVAL ses AS ses_type PTR, BYVAL start AS LONG, BYVAL arg AS LONG) AS STRING
        ifdbgx(TRACE PRINT FUNCNAME$)
        ifdbgx(TRACE PRINT CALLSTK$(1))
        LOCAL count, tmp1 AS LONG
        dbcprint("     procargS In : [" + FORMAT$(start) + "-" + FORMAT$(0) + "]")
        INCR start
        DO
             tmp1 = lookfor(ses, start, %SE_LOOK4_COMMA)
             INCR count
             IF (count = arg) THEN EXIT DO
             INCR tmp1
             start = tmp1
        LOOP WHILE tmp1
        dbcprint("     procargS out: [" + FORMAT$(start) + "-" + FORMAT$(tmp1 - 1) + "]")
        IF tmp1 THEN
           FUNCTION = processstring(ses, start, tmp1 - 1)
        END IF
    END FUNCTION
    
    'This function call itself to solve arguments
    'but the math engine is not recursive. Expression is
    'converted to postfix then solved without recursion.
    'A stack is constructed using a little array and pointers.
    'Operators precedence is set in tokenizer function (can be changed)
    FUNCTION processnumber(BYVAL ses AS ses_type PTR, BYVAL start AS LONG, BYVAL ends AS LONG) AS SINGLE
        ifdbgx(TRACE PRINT FUNCNAME$)
        ifdbgx(TRACE PRINT CALLSTK$(1))
        LOCAL count AS LONG
        LOCAL tmp1, tmp2, xpc AS LONG, ido, ids AS LONG, result, sgn1, sgn2 AS SINGLE
        LOCAL sp AS SINGLE PTR
        sp = @ses.msp
        count = ends - start
        dbcprint("     processnumber start-ends: [" + FORMAT$(start) + "-" + FORMAT$(ends) + "]")
        IF (count < 0) THEN
           dbcprint("     processnumber Result: nothing to do")
           EXIT FUNCTION
        END IF
        IF count > %SE_MAXEXPRLEN THEN
           'dbcprint("err expression too long/complex")
           @ses.errcode = %SE_ERROR_EXPRTOOCOMPLEX
           dbcprint(se_errortext(ses))
           tprint se_errortext(ses)
           EXIT FUNCTION
        END IF
        @sp[%SE_EVAL_PRECE OF 4, ids OF %SE_MAXEXPRLEN] = 1000
        FOR xpc = start TO ends                 'replace vars, solve built in functions, store in postfix
            tmp1 = @ses.@ct[xpc].arg1
            tmp2 = @ses.@ct[xpc].arg2
            SELECT CASE AS LONG @ses.@ct[xpc].tktype
                   CASE %SE_TOKEN_NUMBER        'push to output
                          IF (count = 0) THEN
                             ifdbgx(result = @ses.@ct[xpc].pre)
                             dbcprint("     processnumber Result value: " + FORMAT$(result))
                             FUNCTION = @ses.@ct[xpc].pre: EXIT FUNCTION
                          END IF
                          @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = @ses.@ct[xpc].pre
                          @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                          INCR ido
                   CASE %SE_TOKEN_ALPHA         'push to output
                        SELECT CASE AS LONG @ses.@ct[xpc].cmd
                               CASE %SE_TOKENCMD_GNUMVAR
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = @ses.@nvars[@ses.@ct[xpc].arg1]
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_GSTRVAR
                                    @ses.errcode = %SE_ERROR_NUMEXPECTED
                                    EXIT SELECT
                               CASE %SE_TOKENCMD_ISTRUE
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = ISTRUE(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_ISFALSE
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = ISFALSE(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_NOT
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = NOT(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_INSTR
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = INSTR(procargn(ses, tmp1, 1), procargs(ses, tmp1, 2), procargs(ses, tmp1, 3))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_LEN
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = LEN(processstring(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_VAL
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = VAL(processstring(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_ASC
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = ASC(processstring(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_EVEN
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = (1 AND (processnumber(ses, tmp1 + 1, tmp2 - 1)))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_ABS
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = ABS(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_INT
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = INT(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_SGN
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = SGN(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_FRAC
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = FRAC(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_FIX
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = FIX(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_CEIL
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = CEIL(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_EXP
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = EXP(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_EXP2
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = EXP2(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_EXP10
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = EXP10(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_COS
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = COS(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_ATN
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = ATN(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_SIN
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = SIN(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_TAN
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = TAN(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_SQR
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = SQR(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_LOG
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = LOG(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_LOG2
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = LOG2(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_LOG10
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = LOG10(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_RND
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = RND(procargn(ses, tmp1, 1), procargn(ses, tmp1, 2))
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_PI
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = ATN(1) * 4
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_PRGLINES
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = @ses.@ct[@ses.ctks].lncnt
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_FAC      'factorial, arbitrary 50 limit
                                    sgn1 = processnumber(ses, tmp1 + 1, tmp2 - 1)
                                    IF (sgn1 > 50) THEN
                                       @ses.errcode = %SE_ERROR_OVERFLOW
                                       EXIT FUNCTION
                                    END IF
                                    result = 1
                                    FOR sgn2 = 1 TO sgn1
                                        result = result * sgn2
                                    NEXT
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = result
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_TIMER
                                    @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] =  TIMER
                                    @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = %SE_TOKENCMD_PRENUM
                                    INCR ido
                               CASE %SE_TOKENCMD_RND
                               CASE ELSE
                                    @ses.errcode = %SE_ERROR_NUMEXPECTED
                        END SELECT
                        IF ((ido = 1) AND ISFALSE(count)) THEN   'if no operand, exit
                           dbcprint("     processnumber Result value: " + FORMAT$(@sp[%SE_EVAL_OUTPT OF 4, ido - 1 OF %SE_MAXEXPRLEN]))
                           FUNCTION = @sp[%SE_EVAL_OUTPT OF 4, ido - 1 OF %SE_MAXEXPRLEN]
                           EXIT FUNCTION
                        END IF
                   CASE %ASCII_PARENTHESES_OPEN         'push to stack
                        @sp[%SE_EVAL_STYPE OF 4, ids OF %SE_MAXEXPRLEN] = @ses.@ct[xpc].tktype
                        @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN] = @ses.@ct[xpc].tktype
                        @sp[%SE_EVAL_PRECE OF 4, ids OF %SE_MAXEXPRLEN] = @ses.@ct[xpc].arg1
                        INCR ids
                   CASE %ASCII_PARENTHESES_CLOSE        'pop stack to output until ( then discard both
                        IF ids THEN
                           DECR ids
                           WHILE (ids AND (@sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN] <> %ASCII_PARENTHESES_OPEN))
                                 @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                                 @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = @sp[%SE_EVAL_STYPE OF 4, ids OF %SE_MAXEXPRLEN]
                                 INCR ido
                                 DECR ids
                           WEND                         'ids = (
                        ELSE
                           @ses.errcode = %SE_ERROR_PAROPENEEXPECTED
                        END IF
                   CASE > %ASCII_SPACE                  'operator
                          WHILE ((ido < %SE_MAXEXPRLEN) AND (ids > 0) AND (@ses.@ct[xpc].arg1 <= @sp[%SE_EVAL_PRECE OF 4, ids - 1 OF %SE_MAXEXPRLEN]))
                                DECR ids                'pop to output
                                @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                                @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = @sp[%SE_EVAL_STYPE OF 4, ids OF %SE_MAXEXPRLEN]
                                INCR ido
                          WEND                          'then push
                          @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN] = @ses.@ct[xpc].tktype
                          @sp[%SE_EVAL_PRECE OF 4, ids OF %SE_MAXEXPRLEN] = @ses.@ct[xpc].arg1
                          @sp[%SE_EVAL_STYPE OF 4, ids OF %SE_MAXEXPRLEN] = @ses.@ct[xpc].tktype
                          INCR ids
            END SELECT
        NEXT
    
        'just to see how it goes
        #IF %DEBUG_FLAG
        LOCAL stmp1, stmp2, stmp3 AS STRING
        stmp2 = ""
        FOR tmp1 = start TO ends
            SELECT CASE AS LONG @ses.@ct[tmp1].tktype
                   CASE %SE_TOKEN_UNDEFINED
                   CASE %SE_TOKEN_NUMBER
                        stmp2 = stmp2 + "N"
                        stmp3 = stmp3 + FORMAT$(@ses.@ct[tmp1].pre)
                   CASE %SE_TOKEN_ALPHA
                        IF (@ses.@ct[tmp1].cmd = %SE_TOKENCMD_PRENUM) THEN
                           stmp2 = stmp2 + "F"
                           stmp3 = stmp3 + FORMAT$(@ses.@ct[tmp1].pre)
                        ELSEIF (@ses.@ct[tmp1].cmd = %SE_TOKENCMD_GNUMVAR) THEN
                           stmp2 = stmp2 + "V"
                           stmp3 = stmp3 + FORMAT$(@ses.@ct[tmp1].arg1) + " val[" + FORMAT$(@ses.@nvars[@ses.@ct[tmp1].arg1]) + "]"
                        END IF
                   CASE ELSE
                        stmp2 = stmp2 + CHR$(@ses.@ct[tmp1].tktype)
                        stmp3 = stmp3 + CHR$(@ses.@ct[tmp1].tktype)
            END SELECT
        NEXT
        #ENDIF
    
        WHILE (ids > 0)    'pop all stack to output
              DECR ids
              @sp[%SE_EVAL_OUTPT OF 4, ido OF %SE_MAXEXPRLEN] = @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
              @sp[%SE_EVAL_OTYPE OF 4, ido OF %SE_MAXEXPRLEN] = @sp[%SE_EVAL_STYPE OF 4, ids OF %SE_MAXEXPRLEN]
              INCR ido
        WEND
    
        'all expression is on output stack in postfix format now, so try to solve
    
        'postfix solver, pseudocode from wiki (?)
        'a real asm stack would be nicer and faster
        'while more tokens (operands or operators) to read
        '    Read next token
        '    if token is an operand
        '        Push token onto stack
        '    else if token is an operator ("op", say)
        '        Pop stack and put value in temp2
        '        Pop stack and put value in temp1
        '        Push the value of temp1 op temp2 back onto stack
        '    endif
        'endwhile
        'Pop value from stack and return it
    
        ifdbgx(stmp1 = "")
        DECR ido
        FOR tmp1 = 0 TO ido
            SELECT CASE AS LONG @sp[%SE_EVAL_OTYPE OF 4, tmp1 OF %SE_MAXEXPRLEN]
                   CASE %SE_TOKENCMD_PRENUM
                        'a operand, push to stack
                        ifdbgx(stmp1 = stmp1 + FORMAT$(@sp[%SE_EVAL_OUTPT OF 4, tmp1 OF %SE_MAXEXPRLEN]))
                        @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN] = @sp[%SE_EVAL_OUTPT OF 4, tmp1 OF %SE_MAXEXPRLEN]
                        @sp[%SE_EVAL_STYPE OF 4, ids OF %SE_MAXEXPRLEN] = @sp[%SE_EVAL_OTYPE OF 4, tmp1 OF %SE_MAXEXPRLEN]
                        INCR ids
                   CASE > %ASCII_SPACE
                        '> SPACE = operator, pop two operands, do the math and push result
                        ifdbgx(stmp1 = stmp1 + CHR$(@sp[%SE_EVAL_OUTPT OF 4, tmp1 OF %SE_MAXEXPRLEN]))
                        DECR ids
                        result = @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN]
                        SELECT CASE AS CONST @sp[%SE_EVAL_OTYPE OF 4, tmp1 OF %SE_MAXEXPRLEN]
                               CASE %ASCII_EQUAL              'operator =
                                    @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN] = result = @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                               CASE %ASCII_PLUS               'operator +
                                    @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN] = result + @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                               CASE %ASCII_MINUS              'operator -
                                    @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN] = result - @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                               CASE %ASCII_MUL                'operator *
                                    @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN] = result * @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                               CASE %ASCII_INTDIV             'operator /
                                    @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN] = result \ @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                               CASE %ASCII_DIV                'operator /
                                    @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN] = result / @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                               CASE %ASCII_CARET              'operator ^
                                    @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN] = result ^ @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                               CASE %ASCII_LESS               'operator <
                                    @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN] = result < @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                               CASE %ASCII_MORE               'operator >
                                    @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN] = result > @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                               CASE %ASCII_LCASE_A            'operator <>
                                    @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN] = result <> @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                               CASE %ASCII_LCASE_B            'operator <=
                                    @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN] = result <= @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                               CASE %ASCII_LCASE_C            'operator =>
                                    @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN] = result => @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                               CASE %ASCII_LCASE_M            'operator MOD
                                    @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN] = result MOD @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                               CASE %ASCII_AMPER              'operator AND
                                    @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN] = result AND @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                               CASE %ASCII_OR                 'operator OR
                                    @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN] = result OR @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                               CASE %ASCII_LCASE_X            'operator XOR
                                    @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN] = result XOR @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                               CASE %ASCII_LCASE_E            'operator EQV
                                    @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN] = result EQV @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                               CASE %ASCII_LCASE_I            'operator IMP
                                    @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN] = result IMP @sp[%SE_EVAL_STACK OF 4, ids OF %SE_MAXEXPRLEN]
                        END SELECT
            END SELECT
        NEXT
        dbcprint("     processnumber Input : " + stmp2 + " [" + stmp3 + "]" + "(" + FORMAT$(start) + "-" + FORMAT$(ends) + ")")
        dbcprint("     processnumber Prefix: " + stmp1)
        IF (ids > 1) THEN
           @ses.errcode = %SE_ERROR_MATHMODULE
        ELSE
           'all right, result is first on stack (and should be the only one...)
           result = @sp[%SE_EVAL_STACK OF 4, ids - 1 OF %SE_MAXEXPRLEN]
           dbcprint("     processnumber Result: " + stmp1)
           dbcprint("     processnumber Result value: " + FORMAT$(result))
           FUNCTION = result
        END IF
    END FUNCTION
    
    'must improve this one...processes print statement argument
    FUNCTION processprint(BYVAL ses AS ses_type PTR, BYVAL start AS LONG, BYVAL ends AS LONG) AS STRING
        LOCAL count AS LONG, stmp1, result AS STRING
        LOCAL tmp1, tmp2, tmp3, xpc, nested, ope AS LONG, olen AS DWORD
        count = ends - start
        dbcprint("     processprint input: (" + FORMAT$(start) + "-" + FORMAT$(ends) + ")"))
        IF (count < 0) THEN EXIT FUNCTION
        tmp1 = start
        tmp3 = %ASCII_COMMA
        ope  = %ASCII_SEMICOLON
        WHILE tmp3
              tmp2 = lookfor(ses, tmp1, %SE_LOOK4_COMMA)
              IF ISFALSE(tmp2) THEN
                 tmp2 = lookfor(ses, tmp1, %SE_LOOK4_SEMICOLON)
                 tmp3 = %ASCII_SEMICOLON
              END IF
              IF ISFALSE(tmp2) THEN
                 tmp2 = lookfor(ses, tmp1, %SE_LOOK4_EOL)
                 tmp3 = 0
              END IF
              stmp1 = processstring(ses, tmp1, tmp2 - 1)
              SELECT CASE AS CONST ope
                     CASE %ASCII_COMMA
                          tmp1 = 10 - olen
                          WHILE (tmp1 < 0)
                            INCR count
                            tmp1 = (10 * count) - olen
                          WEND
                          result = result + SPACE$(tmp1) + extdq(stmp1)
                     CASE %ASCII_PLUS, %ASCII_SEMICOLON
                          result = result + extdq(stmp1)
              END SELECT
              tmp1 = tmp2 + 1
              olen = LEN(stmp1)
              ope  = tmp3
        WEND
        dbcprint("     processprint result: " + result)
        FUNCTION = result
    END FUNCTION
    
    'processes an string expression, replaces vars by its values, solves string functions and evaluations
    FUNCTION processstring(BYVAL ses AS ses_type PTR, BYVAL start AS LONG, BYVAL ends AS LONG) AS STRING
        ifdbgx(TRACE PRINT FUNCNAME$)
        ifdbgx(TRACE PRINT CALLSTK$(1))
        LOCAL count AS LONG, stmp1, result, comp AS STRING
        LOCAL tmp1, tmp2, xpc, nested, ope, alpha AS LONG, plus AS DWORD
        count = ends - start
        IF (count < 0) THEN EXIT FUNCTION
        #IF %DEBUG_FLAG
        stmp1 = ""
        FOR xpc = start TO ends
            SELECT CASE AS CONST @ses.@ct[xpc].tktype
                   CASE %SE_TOKEN_ALPHA
                        SELECT CASE AS LONG @ses.@ct[xpc].cmd
                               CASE %SE_TOKENCMD_GNUMVAR
                                    stmp1 = stmp1 + "N"
                               CASE %SE_TOKENCMD_GSTRVAR
                                    stmp1 = stmp1 + "V"
                               CASE ELSE
                                    stmp1 = stmp1 + "F"
                        END SELECT
                   CASE %SE_TOKEN_STRING
                        stmp1 = stmp1 + "S"
                   CASE ELSE
                        stmp1 = stmp1 + CHR$(@ses.@ct[xpc].tktype)
            END SELECT
        NEXT
        dbcprint("     processstring input: [" + stmp1 + "]" + "(" + FORMAT$(start) + "-" + FORMAT$(ends) + ")"))
        #ENDIF
    
        'replace vars and solve built in functions
        FOR xpc = start TO ends
            IF ((@ses.@ct[xpc].tktype = %ASCII_PARENTHESES_OPEN) AND(alpha))THEN
               INCR nested
               ITERATE
            ELSEIF ((@ses.@ct[xpc].tktype = %ASCII_PARENTHESES_CLOSE) AND(alpha)) THEN
               DECR nested
               ITERATE
            END IF
            IF (nested = 0) THEN
               IF plus THEN
                  SELECT CASE AS CONST @ses.@ct[xpc].tktype
                         CASE %ASCII_SPACE, %SE_TOKEN_UNDEFINED
                         CASE %ASCII_PLUS
                              plus = 0
                              alpha = 0
                         CASE %ASCII_LESS, %ASCII_MORE, %ASCII_EQUAL, %ASCII_LCASE_A, _
                              %ASCII_LCASE_B, %ASCII_LCASE_C
                              ope = @ses.@ct[xpc].tktype
                              comp = result
                              result = ""
                              plus = 0
                         CASE ELSE
                              @ses.errcode = %SE_ERROR_PLUSEXPECTED
                  END SELECT
               ELSE
                  SELECT CASE AS CONST @ses.@ct[xpc].tktype
                         CASE %SE_TOKEN_ALPHA
                              plus = 1
                              alpha = 1
                              tmp1 = @ses.@ct[xpc].arg1
                              tmp2 = @ses.@ct[xpc].arg2
                              SELECT CASE AS LONG @ses.@ct[xpc].cmd
                                     CASE %SE_TOKENCMD_GNUMVAR
                                          @ses.errcode = %SE_ERROR_STREXPECTED
                                     CASE %SE_TOKENCMD_GSTRVAR
                                          dbcprint("     processstring: %SE_TOKENCMD_GSTRVAR " + FORMAT$(xpc) + " " + FORMAT$(@ses.@ct[xpc].arg1) + " " + FORMAT$(@ses.ubs))
                                          stmp1 = @ses.@svars[@ses.@ct[xpc].arg1]
                                          dbcprint("     processstring: %SE_TOKENCMD_GSTRVAR ok " + FORMAT$(xpc) + " " + FORMAT$(@ses.@ct[xpc].arg1) + "[" + stmp1 + "]")
                                          result = result + stmp1
                                          dbcprint("     processstring: %SE_TOKENCMD_GSTRVAR ok " + FORMAT$(xpc) + " " + FORMAT$(@ses.@ct[xpc].arg1))
                                     CASE %SE_TOKENCMD_STR
                                          dbcprint("     processstring: %SE_TOKENCMD_STR ")
                                          result = result + STR$(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                          dbcprint("     processstring: %SE_TOKENCMD_STR " + result)
                                     CASE %SE_TOKENCMD_CHR
                                          dbcprint("     processstring: %SE_TOKENCMD_CHR ")
                                          result = result + CHR$(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                     CASE %SE_TOKENCMD_SPACE
                                          result = result + SPACE$(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                     CASE %SE_TOKENCMD_STRING
                                          result = result + STRING$(procargn(ses, tmp1, 1), procargs(ses, tmp1, 2))
                                     CASE %SE_TOKENCMD_RIGHT
                                          result = result + RIGHT$(procargs(ses, tmp1, 1), procargn(ses, tmp1, 2))
                                     CASE %SE_TOKENCMD_LEFT
                                          result = result + LEFT$(procargs(ses, tmp1, 1), procargn(ses, tmp1, 2))
                                     CASE %SE_TOKENCMD_MID
                                          result = result + MID$(procargs(ses, tmp1, 1), procargn(ses, tmp1, 2), procargn(ses, tmp1, 3))
                                     CASE %SE_TOKENCMD_TRIM
                                          result = result + TRIM$(processstring(ses, tmp1 + 1, tmp2 - 1))
                                     CASE %SE_TOKENCMD_LCASE
                                          result = result + LCASE$(processstring(ses, tmp1 + 1, tmp2 - 1))
                                     CASE %SE_TOKENCMD_UCASE
                                          result = result + UCASE$(processstring(ses, tmp1 + 1, tmp2 - 1))
                                     CASE %SE_TOKENCMD_MCASE
                                          result = result + MCASE$(processstring(ses, tmp1 + 1, tmp2 - 1))
                                     CASE %SE_TOKENCMD_HCASE
                                          stmp1 = (processstring(ses, tmp1 + 1, tmp2 - 1))
                                          FOR tmp1 = 1 TO LEN(stmp1)
                                              IF (tmp1 AND 1) THEN
                                                 MID$(stmp1, tmp1, 1) = UCASE$(MID$(stmp1, tmp1, 1))
                                              ELSE
                                                 MID$(stmp1, tmp1, 1) = LCASE$(MID$(stmp1, tmp1, 1))
                                              END IF
                                          NEXT
                                          result = result + stmp1
                                     CASE %SE_TOKENCMD_HEX
                                          result = result + HEX$(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                     CASE %SE_TOKENCMD_OCT
                                          result = result + OCT$(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                     CASE %SE_TOKENCMD_BIN
                                          result = result + BIN$(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                     CASE %SE_TOKENCMD_NUL
                                          result = result + NUL$(processnumber(ses, tmp1 + 1, tmp2 - 1))
                                     CASE %SE_TOKENCMD_DATE
                                          result = result + DATE$
                                     CASE %SE_TOKENCMD_TIME
                                          result = result + TIME$
                                     CASE %SE_TOKENCMD_ACODE
                                          result = result + ACODE$(processstring(ses, tmp1 + 1, tmp2 - 1))
                                     CASE %SE_TOKENCMD_UCODE
                                          result = result + UCODE$(processstring(ses, tmp1 + 1, tmp2 - 1))
                                     CASE %SE_TOKENCMD_STDIN
                                          result = result + stdin
                                     CASE %SE_TOKENCMD_TAB
                                          result = result + TAB$(procargs(ses, tmp1, 1), procargn(ses, tmp1, 2))
                                     CASE ELSE
                                          'IF (ISTRUE(@ses.@ct[xpc].cmd AND %SE_FDEF_RETSTR) AND ISTRUE(isusrfun(@ses.@ct[xpc].cmd))) THEN
                                          '   'funcall
                                          'ELSE
                                             @ses.errcode = %SE_ERROR_STREXPECTED
                                          'END IF
                              END SELECT
                         CASE %SE_TOKEN_STRING
                              result = result + extdq((@ses.@tk[@ses.@ct[xpc].token].token))
                              dbcprint("     processstring: %SE_TOKEN_STRING " + result)
                              plus = 1
                         CASE %ASCII_SPACE, %SE_TOKEN_UNDEFINED
                         CASE ELSE
                              IF (@ses.@ct[xpc].cmd AND %SE_FDEF_RETNUM) THEN
                                 @ses.errcode = %SE_ERROR_STREXPECTED
                              END IF
                  END SELECT
               END IF
            END IF
            IF @ses.errcode THEN EXIT FUNCTION
        NEXT
        SELECT CASE AS CONST ope
               CASE %ASCII_LESS      '<
                    IF (comp < result) THEN result = "true" ELSE result = "false"
               CASE %ASCII_MORE      '>
                    IF (comp > result) THEN result = "true" ELSE result = "false"
               CASE %ASCII_EQUAL     '=
                    IF (comp = result) THEN result = "true" ELSE result = "false"
               CASE %ASCII_LCASE_A   '<>
                    IF (comp = result) THEN result = "false" ELSE result = "true"
               CASE %ASCII_LCASE_B   '<=
                    IF (comp = result) THEN result = "true": EXIT SELECT
                    IF (comp < result) THEN result = "true" ELSE result = "false"
               CASE %ASCII_LCASE_C   '=>
                    IF (comp = result) THEN result = "true": EXIT SELECT
                    IF (comp > result) THEN result = "true" ELSE result = "false"
        END SELECT
        dbcprint("     processstring result: " + result)
        FUNCTION = result
    END FUNCTION
    
    'this code actually 'executes' the script
    FUNCTION execfun(BYVAL ses AS ses_type PTR) AS DWORD
        ifdbgx(TRACE PRINT FUNCNAME$)
        ifdbgx(TRACE PRINT CALLSTK$(1))
        LOCAL stmp1, dbg AS STRING
        LOCAL tmp1, tmp2 AS LONG
        DO
            IF (@ses.@ct[@ses.pc].tktype =  %SE_TOKEN_ALPHA) THEN
               ifdbgx(dbg = "               CT:" + FORMAT$(@ses.pc, "000") + " LN:" + FORMAT$(@ses.@ct[@ses.pc].lncnt, "000") + " SN:" + FORMAT$(@ses.@ct[@ses.pc].secnt, "000") + IIF$(@ses.@ct[@ses.pc].cmd, " exec: " + funtext(@ses.@ct[@ses.pc].cmd), ""))
               dbcprint(dbg)
               SELECT CASE AS LONG @ses.@ct[@ses.pc].cmd
                      CASE %SE_TOKENCMD_JUMPTO, %SE_TOKENCMD_GOTO, %SE_TOKENCMD_LOOP, %SE_TOKENCMD_WEND, _
                           %SE_TOKENCMD_EXITIF, %SE_TOKENCMD_EXITDO, %SE_TOKENCMD_EXITWHI, _
                           %SE_TOKENCMD_EXITFOR, %SE_TOKENCMD_EXITSEL
                           'a jump is in @ses.@ct[pc].arg1
                           IF @ses.@ct[@ses.pc].arg1 THEN
                              dbcprint("                 jump from "  + FORMAT$(@ses.pc) + " to " + FORMAT$(@ses.@ct[@ses.pc].arg1))
                              @ses.pc = @ses.@ct[@ses.pc].arg1
                           END IF
                      CASE %SE_TOKEN_UNDEFINED, %SE_TOKENCMD_ENDIF, %SE_TOKENCMD_ELSE, %SE_TOKENCMD_DO
                           'just ignore these
                           EXIT SELECT
                      CASE %SE_TOKENCMD_IF, %SE_TOKENCMD_ELSEIF
                           'arg1 true branch
                           'arg2 elseif/else branch, or 0 if none
                           'arg3 endif
                           'arg4 end condition
                           'false branch = arg2 or arg3 if arg2 = 0
                           'IF evalcond(ses, pc) THEN
                           IF processnumber(ses, @ses.pc + 1, @ses.@ct[@ses.pc].arg4) THEN
                              ifdbgx(stmp1 = "                 if cond TRUE  jump from "  + FORMAT$(@ses.pc) + " to " + FORMAT$(@ses.@ct[@ses.pc].arg1))
                              @ses.pc = @ses.@ct[@ses.pc].arg1
                           ELSE
                              ifdbgx(stmp1 = "                 if cond FALSE jump from "  + FORMAT$(@ses.pc) + " to " + FORMAT$(@ses.@ct[@ses.pc].arg2))
                              @ses.pc = IIF&(@ses.@ct[@ses.pc].arg2, @ses.@ct[@ses.pc].arg2, @ses.@ct[@ses.pc].arg3)
                           END IF
                           dbcprint(stmp1)
                      CASE %SE_TOKENCMD_IFS, %SE_TOKENCMD_ELSEIFS
                           stmp1 = processstring(ses, @ses.pc + 1, @ses.@ct[@ses.pc].arg4)
                           IF stmp1 = "true" THEN
                              ifdbgx(stmp1 = "                 ifs cond TRUE  jump from "  + FORMAT$(@ses.pc) + " to " + FORMAT$(@ses.@ct[@ses.pc].arg1))
                              @ses.pc = @ses.@ct[@ses.pc].arg1
                           ELSE
                              ifdbgx(stmp1 = "                 ifs cond FALSE jump from "  + FORMAT$(@ses.pc) + " to " + FORMAT$(@ses.@ct[@ses.pc].arg2))
                              @ses.pc = IIF&(@ses.@ct[@ses.pc].arg2, @ses.@ct[@ses.pc].arg2, @ses.@ct[@ses.pc].arg3)
                           END IF
                           dbcprint(stmp1)
                      CASE %SE_TOKENCMD_DOW, %SE_TOKENCMD_WHILE, %SE_TOKENCMD_LOOPW
                           'do/dow/dou
                           'loop/loopw/loopu
                           'arg1 = after do   (true branch)
                           'arg2 = after loop (false branch)
                           'arg3 = start condition (after while/until if any)
                           'arg4 = end condition
                           IF processnumber(ses, @ses.@ct[@ses.pc].arg3, @ses.@ct[@ses.pc].arg4) THEN
                              ifdbgx(stmp1 = "                 dow cond TRUE  jump from "  + FORMAT$(@ses.pc) + " to " + FORMAT$(@ses.@ct[@ses.pc].arg1))
                              @ses.pc = @ses.@ct[@ses.pc].arg1
                           ELSE
                              ifdbgx(stmp1 = "                 dow cond FALSE jump from "  + FORMAT$(@ses.pc) + " to " + FORMAT$(@ses.@ct[@ses.pc].arg2))
                              @ses.pc = @ses.@ct[@ses.pc].arg2
                           END IF
                           dbcprint(stmp1)
                      CASE %SE_TOKENCMD_DOU, %SE_TOKENCMD_LOOPU
                           IF ISFALSE(processnumber(ses, @ses.@ct[@ses.pc].arg3, @ses.@ct[@ses.pc].arg4)) THEN
                              ifdbgx(stmp1 = "                 dow cond TRUE branch jump from "  + FORMAT$(@ses.pc) + " to " + FORMAT$(@ses.@ct[@ses.pc].arg1))
                              @ses.pc = @ses.@ct[@ses.pc].arg1
                           ELSE
                              ifdbgx(stmp1 = "                 dow cond FALSE branch jump from "  + FORMAT$(@ses.pc) + " to " + FORMAT$(@ses.@ct[@ses.pc].arg2))
                              @ses.pc = @ses.@ct[@ses.pc].arg2
                           END IF
                           dbcprint(stmp1)
                      CASE %SE_TOKENCMD_FOR
                           'complex one, missed a match of my favorite soccer team solving this one :@
                           'for
                           ' arg1 = equal + 1        start = arg1     : arg2 - 1
                           ' arg2 = to addr          max   = arg2 + 1 : step/eol
                           ' arg3 = step/eol         step  = arg3 + 1 : eol
                           ' arg4 = var idx
                           ' pre  = step value
                           'to
                           ' arg1 = step addr if any
                           ' arg2 = false branch
                           ' arg3 = next addr
                           ' arg4 = eol
                           'step
                           ' arg1 = eol
                           'next
                           ' arg1 = for addr
                           ' arg2 = to addr
                           ' arg3 = true branch
                           ' arg4 = false branch
                           'for/next setup
                           dbcprint("                 for loop setup " + FORMAT$(@ses.pc))
                           'assign var with start
                           @ses.@nvars[@ses.@ct[@ses.pc].arg4] = processnumber(ses, @ses.@ct[@ses.pc].arg1, _
                                                                              @ses.@ct[@ses.pc].arg2 - 1)
                           dbcprint("                 for start: " + FORMAT$(@ses.@nvars[@ses.@ct[@ses.pc].arg4]))
                           'to  pre = max
                           @ses.@ct[@ses.@ct[@ses.pc].arg2].pre = processnumber(ses, @ses.@ct[@ses.pc].arg2 + 1, _
                                                                               @ses.@ct[@ses.pc].arg3 - 1)
                           dbcprint("                 for max  : " + FORMAT$(@ses.@ct[@ses.@ct[@ses.pc].arg2].pre))
                           'if step, for pre = step
                           IF @ses.@ct[@ses.@ct[@ses.pc].arg2].arg1 THEN
                              @ses.@ct[@ses.pc].pre = processnumber(ses, @ses.@ct[@ses.@ct[@ses.pc].arg2].arg1 + 1, _
                                                                   @ses.@ct[@ses.@ct[@ses.pc].arg2].arg4 - 1)
                           ELSE
                              @ses.@ct[@ses.pc].pre = 1
                           END IF
                           dbcprint("                 for step : " + FORMAT$(@ses.@ct[@ses.pc].pre))
                           'initial evaluation
                           'positive step?
                           IF (@ses.@ct[@ses.pc].pre => 0) THEN
                              IF (@ses.@nvars[@ses.@ct[@ses.pc].arg4] <= @ses.@ct[@ses.@ct[@ses.pc].arg2].pre) THEN
                                 @ses.pc = @ses.@ct[@ses.pc].arg3 '+ 1
                                 dbcprint("                 for true branch jumpto " + FORMAT$(@ses.pc))
                              ELSE
                                 @ses.pc = @ses.@ct[@ses.@ct[@ses.pc].arg2].arg2
                                 dbcprint("                 for false branch jumpto " + FORMAT$(@ses.pc))
                              END IF
                           ELSE
                              'negative step, compare backwards
                              IF (@ses.@nvars[@ses.@ct[@ses.pc].arg4] => @ses.@ct[@ses.@ct[@ses.pc].arg2].pre) THEN
                                 @ses.pc = @ses.@ct[@ses.pc].arg3 '+ 1
                                 dbcprint("                 for true branch jumpto " + FORMAT$(@ses.pc))
                              ELSE
                                 @ses.pc = @ses.@ct[@ses.@ct[@ses.pc].arg2].arg2
                                 dbcprint("                 for false branch jumpto " + FORMAT$(@ses.pc))
                              END IF
                           END IF
                      CASE %SE_TOKENCMD_NEXT
                           'next evaluation
                           'add step to var
                           dbcprint("                 next var    = " + FORMAT$(@ses.@nvars[@ses.@ct[@ses.@ct[@ses.pc].arg1].arg4]))
                           @ses.@nvars[@ses.@ct[@ses.@ct[@ses.pc].arg1].arg4] = @ses.@nvars[@ses.@ct[@ses.@ct[@ses.pc].arg1].arg4] + @ses.@ct[@ses.@ct[@ses.pc].arg1].pre
                           dbcprint("                 next step   = " + FORMAT$(@ses.@ct[@ses.@ct[@ses.pc].arg1].pre))
                           dbcprint("                 next result = " + FORMAT$(@ses.@nvars[@ses.@ct[@ses.@ct[@ses.pc].arg1].arg4]))
                           dbcprint("                 next max    = " + FORMAT$(@ses.@ct[@ses.@ct[@ses.pc].arg2].pre))
                           'check step sign
                           IF (@ses.@ct[@ses.@ct[@ses.pc].arg1].pre => 0) THEN
                              IF (@ses.@nvars[@ses.@ct[@ses.@ct[@ses.pc].arg1].arg4] <= @ses.@ct[@ses.@ct[@ses.pc].arg2].pre) THEN
                                 @ses.pc = @ses.@ct[@ses.pc].arg3
                                 dbcprint("                 next true branch jumpto " + FORMAT$(@ses.pc))
                              ELSE
                                 @ses.pc = @ses.@ct[@ses.pc].arg4
                                 dbcprint("                 next false branch jumpto " + FORMAT$(@ses.pc))
                              END IF
                           ELSE
                              IF (@ses.@nvars[@ses.@ct[@ses.@ct[@ses.pc].arg1].arg4] => @ses.@ct[@ses.@ct[@ses.pc].arg2].pre) THEN
                                 @ses.pc = @ses.@ct[@ses.pc].arg3
                                 dbcprint("                 next true branch jumpto " + FORMAT$(@ses.pc))
                              ELSE
                                 @ses.pc = @ses.@ct[@ses.pc].arg4
                                 dbcprint("                 next false branch jumpto " + FORMAT$(@ses.pc))
                              END IF
                           END IF
                      CASE %SE_TOKENCMD_INCR
                           'global var
                           IF ISTRUE(@ses.@ct[@ses.pc].arg1) THEN INCR @ses.@nvars[@ses.@ct[@ses.pc].arg1]
                      CASE %SE_TOKENCMD_DECR
                           'global var
                           IF ISTRUE(@ses.@ct[@ses.pc].arg1) THEN DECR @ses.@nvars[@ses.@ct[@ses.pc].arg1]
                      CASE %SE_TOKENCMD_GLETNUM
                           'global numeric var assignment
                           'arg1 = var index
                           'arg2 = after '='
                           'arg3 = eol - 1
                           @ses.@nvars[@ses.@ct[@ses.pc].arg1] = processnumber(ses, @ses.@ct[@ses.pc].arg2, @ses.@ct[@ses.pc].arg3)
                      CASE %SE_TOKENCMD_GLETSTR
                           'global string var assignment
                           'arg1 = var
                           'arg2 = start expr
                           'arg3 = end expr
                           stmp1 = processstring(ses, @ses.@ct[@ses.pc].arg2, @ses.@ct[@ses.pc].arg3)
                           tmp1 = @ses.@ct[@ses.pc].arg1
                           dbcprint("               string " + stmp1 + " arg1: " + FORMAT$(tmp1) + " ubs: " + FORMAT$(@ses.ubs))
                           @ses.@svars[tmp1] = stmp1
                      CASE %SE_TOKENCMD_PRINT
                           tprint processprint(ses, @ses.@ct[@ses.pc].arg1, @ses.@ct[@ses.pc].arg2 - 1)
                      CASE %SE_TOKENCMD_STDOUT
                           stmp1 = processstring(ses, @ses.@ct[@ses.pc].arg1 + 1, @ses.@ct[@ses.pc].arg2 - 1)
                           'cprint stmp1
                           stdout stmp1
                      CASE %SE_TOKENCMD_STDERR
                      CASE %SE_TOKENCMD_BEEP
                           'BEEP!
                           BEEP
                      CASE %SE_TOKENCMD_RANDOM
                           'always use timer as seed
                           RANDOMIZE TIMER
                      CASE %SE_TOKENCMD_WAITKEY
                           waitkey
                      CASE %SE_TOKENCMD_MSGBOX
                           MSGBOX procargs(ses, @ses.@ct[@ses.pc].arg1, 1), procargn(ses, @ses.@ct[@ses.pc].arg1, 2), procargs(ses, @ses.@ct[@ses.pc].arg1, 3)
                      CASE %SE_TOKENCMD_ENDSCR
                           'script finished, it is a miracle it went so far
                           dbcprint("               Finished " + FORMAT$(@ses.pc))
                           EXIT DO
                      CASE ELSE
               END SELECT
            END IF
            IF @ses.errcode THEN
               dbcprint("               execfun error " + FORMAT$(@ses.pc))
               FUNCTION = 0
               EXIT FUNCTION
            END IF
            INCR @ses.pc
        LOOP UNTIL thstop
        dbcprint("               Execfun exiting..." + FORMAT$(@ses.pc))
    END FUNCTION
    
    'guess what it does
    FUNCTION checkvalidname(BYVAL ses AS ses_type PTR, BYVAL x AS STRING) AS LONG
        LOCAL tmp1 AS LONG
        IF ISFALSE(LEN(x)) THEN EXIT FUNCTION                                      'must have len...
        x = LCASE$(x)
        IF fundefs(x) THEN EXIT FUNCTION                                           'can't be a reserved word
        IF VERIFY(x, $ALLOWEDCHARS) THEN EXIT FUNCTION                             'must contain just these
        IF VERIFY(MID$(x, 1, 1), $ALLOWEDFIRSTCHAR) THEN EXIT FUNCTION             'must start with one of these
        'check here for user function names
        FUNCTION = 1   'everything seems ok
    END FUNCTION
    
    'looks for a matching codetoken as if/elseif/else/end if/endif/do/loop/while/wend/...and friends
    'used by tokenizer and others to find some things addresses
    FUNCTION lookfor(BYVAL ses AS ses_type PTR, BYVAL start AS LONG, BYVAL match AS DWORD) AS DWORD
        LOCAL stmp1, stmp2 AS STRING, tmp1, nested AS DWORD, pc AS LONG
        SELECT CASE AS LONG match
               CASE %SE_LOOK4_EOL, %SE_LOOK4_ALPHA, %SE_LOOK4_NUM, %SE_TOKEN_STRING, _
                    %SE_LOOK4_PAROPEN, %SE_LOOK4_PARCLOSE, %SE_LOOK4_EQUAL
                    FOR pc = start TO @ses.ctks - 1
                        IF (@ses.@ct[pc].tktype = match) THEN
                           FUNCTION = pc
                           EXIT FUNCTION
                        END IF
                        IF (@ses.@ct[pc].tktype = %SE_TOKEN_EOL) THEN EXIT FUNCTION
                    NEXT
               CASE %SE_LOOK4_COMMA
                    FOR pc = start TO @ses.ctks - 1
                        SELECT CASE AS CONST @ses.@ct[pc].tktype
                               CASE %ASCII_COMMA
                                    IF ISFALSE(nested) THEN
                                       FUNCTION = pc
                                       EXIT FUNCTION
                                    END IF
                               CASE %ASCII_PARENTHESES_CLOSE
                                    IF ISFALSE(nested) THEN
                                       FUNCTION = pc
                                       EXIT FUNCTION
                                    ELSE
                                       DECR nested
                                    END IF
                               CASE %ASCII_PARENTHESES_OPEN
                                    INCR nested
                               CASE %SE_TOKEN_EOL
                                    EXIT FUNCTION
                        END SELECT
                    NEXT
               CASE %SE_LOOK4_SEMICOLON
                    FOR pc = start TO @ses.ctks - 1
                        SELECT CASE AS CONST @ses.@ct[pc].tktype
                               CASE %ASCII_SEMICOLON
                                    IF ISFALSE(nested) THEN
                                       FUNCTION = pc
                                       EXIT FUNCTION
                                    END IF
                               CASE %ASCII_PARENTHESES_CLOSE
                                    IF ISFALSE(nested) THEN
                                       FUNCTION = pc
                                       EXIT FUNCTION
                                    ELSE
                                       DECR nested
                                    END IF
                               CASE %ASCII_PARENTHESES_OPEN
                                    INCR nested
                               CASE %SE_TOKEN_EOL
                                    EXIT FUNCTION
                        END SELECT
                    NEXT
               CASE %SE_LOOK4_MATCHPAR
                    INCR start
                    FOR pc = start TO @ses.ctks - 1
                        IF (@ses.@ct[pc].tktype = %SE_TOKEN_EOL) THEN EXIT FUNCTION
                        IF (@ses.@ct[pc].tktype = %ASCII_PARENTHESES_CLOSE) THEN
                           IF ISFALSE(nested) THEN
                              FUNCTION = pc
                              EXIT FUNCTION
                           ELSE
                              DECR nested
                           END IF
                        END IF
                        IF (@ses.@ct[pc].tktype = %ASCII_PARENTHESES_OPEN) THEN INCR nested
                    NEXT
               CASE %SE_LOOK4_STEP, %SE_LOOK4_TO, %SE_LOOK4_THEN, %SE_LOOK4_DOWHILE, %SE_LOOK4_DOUNTIL
                    FOR pc = start TO @ses.ctks - 1
                        IF (@ses.@ct[pc].tktype = %SE_TOKEN_EOL) THEN EXIT FUNCTION
                        IF (@ses.@ct[pc].cmd = match) THEN
                           FUNCTION = pc
                           EXIT FUNCTION
                        END IF
                    NEXT
               CASE %SE_LOOK4_ELSEIF, %SE_LOOK4_ELSE, %SE_LOOK4_ENDIF
                    FOR pc = start TO @ses.ctks - 1
                        SELECT CASE @ses.@ct[pc].cmd
                               CASE %SE_TOKENCMD_IF, %SE_TOKENCMD_IFS
                                    INCR nested
                                    'dbcprint ("  if nested + 1: " + FORMAT$(nested))
                               CASE %SE_TOKENCMD_ELSE
                                    IF (nested = 0) THEN
                                       IF (match = %SE_LOOK4_ELSE) THEN FUNCTION = pc: EXIT FUNCTION
                                    END IF
                               CASE %SE_TOKENCMD_ELSEIF, %SE_TOKENCMD_ELSEIFS
                                    IF (nested = 0) THEN
                                       IF (match = %SE_LOOK4_ELSEIF) THEN FUNCTION = pc: EXIT FUNCTION
                                    END IF
                               CASE %SE_TOKENCMD_ENDIF
                                    IF (nested = 0) THEN
                                       IF (match = %SE_LOOK4_ENDIF) THEN FUNCTION = pc: EXIT FUNCTION
                                    END IF
                                    DECR nested
                                    'dbcprint ("  if nested - 1: " + FORMAT$(nested))
                               CASE %SE_TOKENCMD_ENDFUN, %SE_TOKENCMD_ENDSUB, %SE_TOKENCMD_FUNCTION, %SE_TOKENCMD_SUB
                                    EXIT FUNCTION
                        END SELECT
                    NEXT
               CASE %SE_LOOK4_LOOP
                    FOR pc = start TO @ses.ctks - 1
                        SELECT CASE AS LONG @ses.@ct[pc].cmd
                               CASE %SE_TOKENCMD_LOOP, %SE_TOKENCMD_LOOPW, %SE_TOKENCMD_LOOPU
                                    IF (nested = 0) THEN
                                       FUNCTION = pc
                                       EXIT FUNCTION
                                    END IF
                                    DECR nested
                               CASE %SE_TOKENCMD_DO, %SE_TOKENCMD_DOW, %SE_TOKENCMD_DOU
                                    INCR nested
                               CASE %SE_TOKENCMD_ENDFUN, %SE_TOKENCMD_ENDSUB, %SE_TOKENCMD_FUNCTION, %SE_TOKENCMD_SUB
                                    EXIT FUNCTION
                        END SELECT
                    NEXT
               CASE %SE_LOOK4_WEND
                    FOR pc = start TO @ses.ctks - 1
                        SELECT CASE AS LONG @ses.@ct[pc].cmd
                               CASE %SE_TOKENCMD_WHILE
                                    IF (@ses.@ct[pc].secnt = 1) THEN    'no 'do while'/'loop while'
                                       INCR nested
                                    END IF
                               CASE %SE_TOKENCMD_WEND
                                    IF (nested = 0) THEN
                                       FUNCTION = pc 'lookfor(ses, pc + 1, %SE_LOOK4_EOL) + 1
                                       EXIT FUNCTION
                                    END IF
                                    DECR nested
                               CASE %SE_TOKENCMD_ENDFUN, %SE_TOKENCMD_ENDSUB, %SE_TOKENCMD_FUNCTION, %SE_TOKENCMD_SUB
                                    EXIT FUNCTION
                        END SELECT
                    NEXT
               CASE %SE_LOOK4_NEXT
                    FOR pc = start TO @ses.ctks - 1
                        SELECT CASE AS LONG @ses.@ct[pc].cmd
                               CASE %SE_TOKENCMD_FOR
                                    INCR nested
                               CASE %SE_TOKENCMD_NEXT
                                    IF (nested = 0) THEN
                                       FUNCTION = pc 'lookfor(ses, pc + 1, %SE_LOOK4_EOL) + 1
                                       EXIT FUNCTION
                                    END IF
                                    DECR nested
                               CASE %SE_TOKENCMD_ENDFUN, %SE_TOKENCMD_ENDSUB, %SE_TOKENCMD_FUNCTION, %SE_TOKENCMD_SUB
                                    EXIT FUNCTION
                        END SELECT
                    NEXT
               CASE %SE_LOOK4_ENDFUNC
                    FOR pc = start TO @ses.ctks - 1
                        IF @ses.@ct[pc].cmd = %SE_TOKENCMD_ENDFUN THEN
                           FUNCTION = pc
                           EXIT FUNCTION
                        ELSEIF @ses.@ct[pc].cmd = %SE_TOKENCMD_FUNCTION THEN
                           EXIT FUNCTION
                        ELSEIF @ses.@ct[pc].cmd = %SE_TOKENCMD_SUB THEN
                           EXIT FUNCTION
                        ELSEIF @ses.@ct[pc].cmd = %SE_TOKENCMD_ENDSUB THEN
                           EXIT FUNCTION
                        END IF
                    NEXT
               CASE %SE_LOOK4_ENDSEL
                    FOR pc = start TO @ses.ctks - 1
                        SELECT CASE AS LONG @ses.@ct[pc].cmd
                               CASE %SE_TOKENCMD_SELECT
                                    INCR nested
                               CASE %SE_TOKENCMD_ENDSEL
                                    IF (nested = 0) THEN
                                       FUNCTION = lookfor(ses, pc + 1, %SE_LOOK4_EOL) + 1
                                       EXIT FUNCTION
                                    END IF
                                    DECR nested
                               CASE %SE_TOKENCMD_ENDFUN, %SE_TOKENCMD_ENDSUB, %SE_TOKENCMD_FUNCTION, %SE_TOKENCMD_SUB
                                    EXIT FUNCTION
                        END SELECT
                    NEXT
        END SELECT
    END FUNCTION
    
    'count arguments from a list like (n1,n2,n3...st4), ignore args inside parenthesis
    FUNCTION countargs(BYVAL ses AS ses_type PTR, BYVAL start AS LONG, BYVAL ends AS LONG) AS DWORD
        LOCAL count, nested, something AS DWORD, pc AS LONG
        INCR start
        DECR ends
        FOR pc = start TO ends
            SELECT CASE AS CONST @ses.@ct[pc].tktype
                   CASE %SE_TOKEN_EOL            : EXIT FUNCTION
                   CASE %ASCII_PARENTHESES_CLOSE : DECR nested
                   CASE %ASCII_PARENTHESES_OPEN  : INCR nested
                   CASE %ASCII_COMMA
                        INCR something
                        IF ISFALSE(nested) THEN INCR count
                   CASE %ASCII_SPACE
                   CASE %SE_TOKEN_UNDEFINED
                   CASE ELSE
                        INCR something
            END SELECT
        NEXT
        FUNCTION = IIF&(something, count + 1, 0)
    END FUNCTION
    
    'This code takes the input string cointaining the whole script and parses its contents
    'filling an array of se_codetoken structs and another of se_token_type. Alpha strings
    'are checked, if identified as keywords they are not stored as tokens but loaded with
    'a function definition code. Then builtin functions are scanned, its arguments counted
    'and checked. Also all loops and if/then structs jumps are pre-calculated. Finally the
    'processed script is executed. Most errors are reported before that.
    'This routine could be improved to have less pass but I keep it this way for clarity.
    'Operators precedence is set here, and can be changed.
    '
    FUNCTION tokenizer(BYVAL inscript AS STRING PTR) AS DWORD
        IF ISFALSE(LEN(@inscript)) THEN EXIT FUNCTION
        LOCAL pcnt, lcnt, secnt, dqflag, alflag, numflag, comflag, eolflag, labcnt AS DWORD
        LOCAL last, this, tkcnt, buffcnt, lastk, pc AS DWORD
        LOCAL bptr, optr AS BYTE PTR
        LOCAL tokens() AS se_token_type, codetk() AS se_codetoken
        LOCAL tmp1, tmp2, tmp3, tmp4 AS LONG
        LOCAL buffout AS ASCIIZ * %SE_MAXTOKENLEN
        LOCAL script, stmp1, stmp2 AS STRING
        LOCAL strvar() AS STRING               'actual script global strings content
        LOCAL numvar() AS SINGLE               '  "      "    global numbers    "
        LOCAL strvarn() AS DWORD               'points to global strings name token
        LOCAL numvarn() AS DWORD               '  "        "     numbers   "    "
        LOCAL ses AS ses_type                  'main struct
        LOCAL sesptr AS DWORD                  'main struct pointer
        LOCAL var AS LONG                      'tmp
        LOCAL mstack() AS SINGLE               'math stack
        REDIM mstack(0 TO 4, 0 TO %SE_MAXEXPRLEN)
        REDIM strvar(0), numvar(0), strvarn(0), numvarn(0)
        REDIM tokens(%SE_MAXTOKENS)            'tokens array
        REDIM codetk(%SE_MAXCODETOKENS)        'codetokens array
        ses.msp        = VARPTR(mstack(0, 0))  'math stack pointer
        ses.tk         = VARPTR(tokens(0))     'tokens pointer
        ses.ct         = VARPTR(codetk(0))     'codetokens pointer
        'ses.funname    = 0
        sesptr         = VARPTR(ses)           'main struct pointer
        script         = @inscript + $CRLF     'ensure end
        optr           = VARPTR(buffout)       'two byte pointers to scan the input script
        bptr           = STRPTR(script)        '
    
        dbcprint("")
        dbcprint("##################################################")
        dbcprint("Tokenizing...")
    
        INCR pc
        INCR lcnt
        INCR secnt
        INCR tkcnt
    
        last = %SE_TOKEN_UNDEFINED
        this = %SE_TOKEN_UNDEFINED
    
        'start checking the script, byte by byte
        FOR pcnt = 0 TO LEN(script) - 1
            SELECT CASE AS LONG @bptr[pcnt]
                   CASE %ASCII_DOUBLEQUOTE
                        dbcprint("%ASCII_DOUBLEQUOTE")
                        dbcprint("%ASCII_STRING")
                        dqflag = NOT(dqflag)
                        this = %SE_TOKEN_STRING
                   CASE %ASCII_PARENTHESES_OPEN, %ASCII_PARENTHESES_CLOSE
                        dbcprint("%ASCII_PAR_()")
                        IF dqflag THEN
                           this = %SE_TOKEN_STRING
                        ELSE
                           'parflag = NOT(parflag)
                           this = %SE_TOKEN_DELIMITER
                        END IF
                   CASE %ASCII_MUL, %ASCII_PLUS, %ASCII_MINUS, _
                        %ASCII_BRACKETOPEN, %ASCII_BRACKETCLOSE, %ASCII_CARET, %ASCII_INTDIV, _
                        %ASCII_DIV, %ASCII_TAB, %ASCII_SPACE, %ASCII_COMMA, %ASCII_SEMICOLON
                        dbcprint("%ASCII_SPACE_ETC")
                        IF dqflag THEN
                           this = %SE_TOKEN_STRING
                        ELSE
                           this = %SE_TOKEN_DELIMITER
                        END IF
                   CASE %ASCII_LESS, %ASCII_MORE, %ASCII_EQUAL
                        dbcprint("%ASCII_SPACE_PAR_ETC")
                        IF dqflag THEN
                           this = %SE_TOKEN_STRING
                        ELSE
                           this = %SE_TOKEN_DELIMITER
                           SELECT CASE @bptr[pcnt]
                                  CASE %ASCII_LESS
                                       SELECT CASE @bptr[pcnt + 1]
                                              CASE %ASCII_MORE
                                                   @bptr[pcnt] = 97   '<> a
                                                   @bptr[pcnt + 1] = 32
                                              CASE %ASCII_EQUAL
                                                   @bptr[pcnt] = 98   '<= b
                                                   @bptr[pcnt + 1] = 32
                                       END SELECT
                                  CASE %ASCII_MORE
                                       SELECT CASE @bptr[pcnt + 1]
                                              CASE %ASCII_LESS
                                                   @bptr[pcnt] = 97   '<> a
                                                   @bptr[pcnt + 1] = 32
                                              CASE %ASCII_EQUAL
                                                   @bptr[pcnt] = 99   '>= c
                                                   @bptr[pcnt + 1] = 32
                                       END SELECT
                                  CASE %ASCII_EQUAL
                                       SELECT CASE @bptr[pcnt + 1]
                                              CASE %ASCII_LESS
                                                   @bptr[pcnt] = 98   '<= b
                                                   @bptr[pcnt + 1] = 32
                                              CASE %ASCII_MORE
                                                   @bptr[pcnt] = 99   '>= c
                                                   @bptr[pcnt + 1] = 32
                                       END SELECT
                           END SELECT
                        END IF
                   CASE %ASCII_COLON
                        IF dqflag THEN
                           this = %SE_TOKEN_STRING
                           dbcprint("%ASCII_STRING")
                        ELSE
                           this = %SE_TOKEN_COLON
                           dbcprint("%ASCII_COLON")
                        END IF
                   CASE %ASCII_LINEFEED, %ASCII_ENTER
                        dbcprint("%ASCII_CRLF")
                        this = %SE_TOKEN_CRLF
                        comflag = 0
                   CASE %ASCII_QUESTION
                        IF dqflag THEN
                           this = %SE_TOKEN_STRING
                           dbcprint("%ASCII_STRING")
                        ELSE
                           this = %SE_TOKEN_PRINT
                           dbcprint("%ASCII_QUESTION")
                        END IF
                   CASE %ASCII_APOSTROPHE
                        IF dqflag THEN
                           this = %SE_TOKEN_STRING
                           dbcprint("%ASCII_STRING")
                        ELSE
                           comflag = 1
                           this = %SE_TOKEN_CRLF
                           dbcprint("%ASCII_APOSTROPHE")
                        END IF
                   CASE %ASCII_NUM0 TO %ASCII_NUM9, %ASCII_PERIOD
                        IF dqflag THEN
                           this = %SE_TOKEN_STRING
                           dbcprint("%ASCII_STRING")
                        ELSE
                           numflag = IIF&((last = %SE_TOKEN_ALPHA), 0, 1)
                           dbcprint("NUMFLAG =" + IIF$(numflag, "1", "0"))
                           'this = %SE_TOKEN_ALPHA
                           this = IIF&(numflag, %SE_TOKEN_NUMBER, %SE_TOKEN_ALPHA)
                           dbcprint(IIF$(numflag, "%SE_TOKEN_NUMBER","%ASCII_ALPHA"))
                        END IF
                   CASE ELSE
                   '     dbcprint("%ASCII_???"
                   '     this = %SE_TOKEN_UNDEFINED
                   'CASE %ASCII_UCASE_A TO %ASCII_UCASE_Z, %ASCII_LCASE_A TO %ASCII_LCASE_Z, _
                   '     %ASCII_QUESTION
                        IF dqflag THEN
                           this = %SE_TOKEN_STRING
                           dbcprint("%ASCII_STRING")
                        ELSE
                           numflag = 0
                           this = %SE_TOKEN_ALPHA
                           dbcprint("%ASCII_ALPHA")
                        END IF
            END SELECT
            IF (last <> this) THEN
               IF (buffcnt > 0) THEN
                  SELECT CASE AS CONST last
                         CASE %SE_TOKEN_UNDEFINED
                              dbcprint("%SE_TOKEN_UNDEFINED")
                              eolflag = 0
                         CASE %SE_TOKEN_CRLF, %SE_TOKEN_COLON
                              eolflag = 1
                              dbcprint("%SE_TOKEN_CRLF")
                              codetk(pc).tktype = %SE_TOKEN_EOL
                              codetk(pc).lncnt = lcnt
                              codetk(pc).secnt = secnt
                              secnt = 1
                              INCR pc
                              IF (last <> %SE_TOKEN_COLON)THEN INCR lcnt
                              'IF (this = %SE_TOKEN_DELIMITER) THEN ITERATE
                         CASE %SE_TOKEN_DELIMITER
                              eolflag = 0
                              stmp1 = buffout
                              stmp2 = TRIM$(stmp1, ANY $SPC + CHR$(0))
                              'must be at least 1 char outside parenthessis
                              'if isfalse(parflag) then
                              'stmp2 = IIF$(LEN(stmp2), stmp2, $SPC)
                              FOR tmp1 = 1 TO LEN(stmp2)
                                  stmp1 = MID$(stmp2, tmp1, 1)
                                  dbcprint("%SE_TOKEN_DELIMITER " + stmp1 + " len: " + FORMAT$(LEN(stmp1)))
                                  codetk(pc).tktype = ASC(stmp1)
                                  codetk(pc).lncnt = lcnt
                                  codetk(pc).secnt = secnt
                                  codetk(pc).cmd = %SE_FDEF_OPERATOR
                                  'weight precedence               m & | x o i
                                  SELECT CASE codetk(pc).tktype
                                         CASE %ASCII_CARET             : tmp2 = 7      '^
                                         CASE %ASCII_MUL               : tmp2 = 5      '*
                                         CASE %ASCII_DIV               : tmp2 = 5      '/
                                         CASE %ASCII_INTDIV            : tmp2 = 4      '\
                                         CASE %ASCII_PLUS              : tmp2 = 2      '+
                                         CASE %ASCII_MINUS             : tmp2 = 2      '-
                                         CASE %ASCII_PARENTHESES_OPEN  : tmp2 = 0      '(
                                         CASE %ASCII_PARENTHESES_CLOSE : tmp2 = 0      ')
                                         CASE %ASCII_LCASE_A, _                        '<>
                                              %ASCII_LCASE_B, _                        '<=
                                              %ASCII_LCASE_C, _                        '=>
                                              %ASCII_MORE, _                           '>
                                              %ASCII_LESS, _                           '<
                                              %ASCII_EQUAL             : tmp2 = 1      '=
                                  END SELECT
                                  codetk(pc).arg1 = tmp2               '<- precedence
                                  INCR pc
                              NEXT
                         CASE %SE_TOKEN_ALPHA, %SE_TOKEN_STRING, %SE_TOKEN_NUMBER, %SE_TOKEN_PRINT
                              dbcprint(IIF$(numflag, "%SE_TOKEN_NUMBER" ,"%SE_TOKEN_OTHER"))
                              codetk(pc).lncnt = lcnt
                              codetk(pc).secnt = secnt
                              IF last = %SE_TOKEN_PRINT THEN last = %SE_TOKEN_ALPHA
                              tmp1 = 1
                              IF last = %SE_TOKEN_ALPHA THEN
                                 'check if label
                                 IF (this = %SE_TOKEN_COLON) THEN
                                    IF (eolflag) THEN
                                       dbcprint ("label at: " + FORMAT$(pc) + " " + buffout)
                                       codetk(pc).cmd = %SE_TOKENCMD_LABEL
                                       INCR labcnt
                                    END IF
                                 END IF
                                 stmp1 = buffout
                                 stmp1 = LCASE$(TRIM$(stmp1, ANY $SPC + CHR$(0)))
                                 'replace operators and weight precedence
                                 SELECT CASE stmp1
                                        CASE "mod"     : tmp1 = 0: tmp2 =  3: stmp1 = "m" 'm'
                                        CASE "and"     : tmp1 = 0: tmp2 = 10: stmp1 = "&" '&'
                                        CASE "or"      : tmp1 = 0: tmp2 = 11: stmp1 = "|" '|'
                                        CASE "xor"     : tmp1 = 0: tmp2 = 11: stmp1 = "x" 'x'
                                        CASE "eqv"     : tmp1 = 0: tmp2 = 12: stmp1 = "e" 'e'
                                        CASE "imp"     : tmp1 = 0: tmp2 = 13: stmp1 = "i" 'i'
                                        CASE "?"       : buffout = "print": buffcnt = 5
                                                         codetk(pc).tktype = %SE_TOKEN_ALPHA
                                 END SELECT
                                 IF tmp1 THEN
                                    codetk(pc).tktype = %SE_TOKEN_ALPHA
                                    tmp2 = fundefs(LCASE$(buffout))       'identify built in functions and statements
                                    IF tmp2 THEN                          'set fundef and don't save token
                                       SELECT CASE tmp2                   'insert a jumpto just before these
                                              CASE %SE_TOKENCMD_ELSE, %SE_TOKENCMD_ELSEIF
                                                   codetk(pc).tktype = %SE_TOKEN_ALPHA
                                                   codetk(pc).cmd = %SE_TOKENCMD_JUMPTO
                                                   INCR pc
                                              CASE %SE_TOKENCMD_LET       '
                                                   DECR secnt
                                       END SELECT
                                       codetk(pc).tktype = %SE_TOKEN_ALPHA
                                       codetk(pc).lncnt = lcnt
                                       codetk(pc).secnt = secnt
                                       codetk(pc).cmd = tmp2              '
                                       INCR secnt
                                       tmp1 = 0
                                    END IF
                                 ELSE
                                    codetk(pc).tktype = ASC(stmp1)
                                    codetk(pc).arg1   = tmp2   '<- precedence
                                    codetk(pc).cmd = %SE_FDEF_OPERATOR
                                    INCR secnt
                                 END IF
                              END IF                                      'do not save token for these
                              IF tmp1 THEN
                                 'codetk(pc).tktype = IIF&(numflag, %SE_TOKEN_NUMBER, last)
                                 IF numflag THEN
                                    codetk(pc).tktype = %SE_TOKEN_NUMBER
                                    codetk(pc).cmd = %SE_TOKENCMD_NUMCONST
                                    codetk(pc).pre = VAL(buffout)
                                 ELSE
                                    codetk(pc).tktype = last
                                    IF (last = %SE_TOKEN_STRING) THEN codetk(pc).cmd = %SE_TOKENCMD_STRCONST
                                 END IF
                                 INCR secnt
                                 FOR tmp1 = 1 TO tkcnt - 1
                                     IF (tokens(tmp1).token = buffout) THEN
                                        IF (codetk(pc).cmd = %SE_TOKENCMD_LABEL)THEN
                                           tokens(tmp1).addrs = pc
                                           codetk(pc).arg1 = pc
                                        END IF
                                        codetk(pc).token = tmp1
                                        dbcprint("                       token old " + buffout)
                                        tmp1 = -4
                                        EXIT FOR
                                     END IF
                                 NEXT
                                 IF (tmp1 <> -4) THEN
                                        dbcprint("                       token new " + buffout)
                                    IF (codetk(pc).cmd = %SE_TOKENCMD_LABEL)THEN
                                       tokens(tkcnt).addrs = pc
                                    END IF
                                    codetk(pc).token = tkcnt
                                    tokens(tkcnt).tktype = IIF&(numflag, %SE_TOKEN_NUMBER, last)
                                    'tokens(tkcnt).tklen = buffcnt
                                    tokens(tkcnt).token = LEFT$(buffout, buffcnt)
                                    INCR tkcnt
                                 END IF
                              END IF
                              numflag = 0
                              INCR pc
                         CASE ELSE
                              eolflag = 0
                  END SELECT
                  RESET buffout
                  buffcnt = 0
               END IF
               last = this
            END IF
            IF (ISFALSE(comflag) AND (buffcnt < %SE_MAXTOKENLEN)) THEN
               @optr[buffcnt] = @bptr[pcnt]
               INCR buffcnt
            END IF
        NEXT
    
        'script scanned and tokenized, process it
    
        'add an EOL
        codetk(pc).tktype = %SE_TOKEN_EOL
        codetk(pc).lncnt  = lcnt
        codetk(pc).secnt  = secnt
        INCR pc
        'add a ENDSCRIPT token
        codetk(pc).lncnt  = lcnt
        codetk(pc).secnt  = secnt
        codetk(pc).cmd = %SE_TOKENCMD_ENDSCR
        codetk(pc).tktype = %SE_TOKEN_ALPHA
    
        dbcprint(FORMAT$(pc) + " codetoken/s"))
        dbcprint(FORMAT$(tkcnt - 1) + " token/s"))
        dbcprint(FORMAT$(labcnt) + " label/s"))
    
        REDIM PRESERVE codetk(pc) 'free some mem
        ses.tks = tkcnt
        ses.ctks = pc
        ses.pc = 1
    
        'start analizing
        dbcprint("Analizing...pass 1")
        'replace some inconvenient secuences for easy handling ie. 'end if', 'do while', 'exit if', etc
        'also search for global var declares and 'dim' them
        DO
            IF codetk(ses.pc).tktype = %SE_TOKEN_ALPHA THEN
               tmp2 = 0
               SELECT CASE AS LONG codetk(ses.pc).cmd
                      CASE %SE_TOKENCMD_END
                           IF codetk(ses.pc + 1).cmd = %SE_TOKENCMD_IF THEN tmp2 = %SE_TOKENCMD_ENDIF
                           IF codetk(ses.pc + 1).cmd = %SE_TOKENCMD_SUB THEN tmp2 = %SE_TOKENCMD_ENDSUB: alflag = 0
                           IF codetk(ses.pc + 1).cmd = %SE_TOKENCMD_FUNCTION THEN tmp2 = %SE_TOKENCMD_ENDFUN: alflag = 0
                           IF codetk(ses.pc + 1).cmd = %SE_TOKENCMD_SELECT THEN tmp2 = %SE_TOKENCMD_ENDSEL
                      CASE %SE_TOKENCMD_EXIT
                           IF codetk(ses.pc + 1).cmd = %SE_TOKENCMD_IF THEN tmp2 = %SE_TOKENCMD_EXITIF
                           IF codetk(ses.pc + 1).cmd = %SE_TOKENCMD_SUB THEN tmp2 = %SE_TOKENCMD_EXITSUB
                           IF codetk(ses.pc + 1).cmd = %SE_TOKENCMD_FUNCTION THEN tmp2 = %SE_TOKENCMD_EXITFUN
                           IF codetk(ses.pc + 1).cmd = %SE_TOKENCMD_FOR THEN tmp2 = %SE_TOKENCMD_EXITFOR
                           IF codetk(ses.pc + 1).cmd = %SE_TOKENCMD_DO THEN tmp2 = %SE_TOKENCMD_EXITDO
                           IF codetk(ses.pc + 1).cmd = %SE_TOKENCMD_WHILE THEN tmp2 = %SE_TOKENCMD_EXITWHI
                           IF codetk(ses.pc + 1).cmd = %SE_TOKENCMD_WEND THEN tmp2 = %SE_TOKENCMD_EXITWHI
                           IF codetk(ses.pc + 1).cmd = %SE_TOKENCMD_LOOP THEN tmp2 = %SE_TOKENCMD_EXITDO
                           IF codetk(ses.pc + 1).cmd = %SE_TOKENCMD_SELECT THEN tmp2 = %SE_TOKENCMD_EXITSEL
                      CASE %SE_TOKENCMD_DO
                           IF codetk(ses.pc + 1).cmd = %SE_TOKENCMD_WHILE THEN tmp2 = %SE_TOKENCMD_DOW
                           IF codetk(ses.pc + 1).cmd = %SE_TOKENCMD_UNTIL THEN tmp2 = %SE_TOKENCMD_DOU
                      CASE %SE_TOKENCMD_LOOP
                           IF codetk(ses.pc + 1).cmd = %SE_TOKENCMD_WHILE THEN tmp2 = %SE_TOKENCMD_LOOPW
                           IF codetk(ses.pc + 1).cmd = %SE_TOKENCMD_UNTIL THEN tmp2 = %SE_TOKENCMD_LOOPU
                      CASE %SE_TOKENCMD_STRINGS
                           'dim global 'strings' type vars
                           dbcprint("strings")
                           tmp1 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_ALPHA)
                           IF tmp1 THEN
                              WHILE tmp1
                                IF checkvalidname(sesptr, (tokens(codetk(tmp1).token).token)) THEN
                                   ARRAY SCAN strvarn(), = codetk(tmp1).token, TO var
                                   ARRAY SCAN numvarn(), = codetk(tmp1).token, TO tmp3
                                   IF (ISTRUE(var) OR ISTRUE(tmp3)) THEN
                                      dbcprint("duplicated string var " + tokens(codetk(tmp1).token).token)
                                      ses.errcode = %SE_ERROR_DUPLICATEDECL
                                      EXIT, EXIT
                                   ELSE
                                      dbcprint("new string var " + tokens(codetk(tmp1).token).token)
                                      REDIM PRESERVE strvar(UBOUND(strvar) + 1)
                                      REDIM PRESERVE strvarn(UBOUND(strvarn) + 1)
                                      ses.ubs = UBOUND(strvar)
                                      strvar(ses.ubs) = ""
                                      strvarn(ses.ubs) = codetk(tmp1).token
                                      dbcprint("strings " + tokens(codetk(tmp1).token).token)
                                   END IF
                                ELSE
                                   'stmp1 = tokens(codetk(tmp1).token).token
                                   'dbcprint("name:" + stmp1)
                                   ses.errcode = %SE_ERROR_ILLEGALNAME
                                   EXIT, EXIT
                                END IF
                                tmp1 = lookfor(sesptr, tmp1 + 1, %SE_LOOK4_ALPHA)
                              WEND
                              WHILE (codetk(ses.pc).tktype <> %SE_TOKEN_EOL)
                                codetk(ses.pc).tktype = %ASCII_SPACE
                                INCR ses.pc
                              WEND
                           ELSE
                              ses.errcode = %SE_ERROR_SYNTAXERROR
                           END IF
                           dbcprint("strings ok")
                      CASE %SE_TOKENCMD_NUMBERS
                           'dim global 'numbers' type vars
                           dbcprint("numbers")
                           tmp1 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_ALPHA)
                           IF tmp1 THEN
                              WHILE tmp1
                                IF checkvalidname(sesptr, (tokens(codetk(tmp1).token).token)) THEN
                                   ARRAY SCAN numvarn(), = codetk(tmp1).token, TO var
                                   ARRAY SCAN strvarn(), = codetk(tmp1).token, TO tmp3
                                   IF (var OR tmp3) THEN
                                      ses.errcode = %SE_ERROR_DUPLICATEDECL
                                      EXIT, EXIT
                                   ELSE
                                      REDIM PRESERVE numvar(UBOUND(numvar) + 1)
                                      REDIM PRESERVE numvarn(UBOUND(numvar) + 1)
                                      ses.ubn = UBOUND(numvar)
                                      numvar(ses.ubn) = 0
                                      numvarn(ses.ubn) = codetk(tmp1).token
                                      dbcprint("numbers " + tokens(codetk(tmp1).token).token)
                                   END IF
                                ELSE
                                   ses.errcode = %SE_ERROR_ILLEGALNAME
                                   EXIT, EXIT
                                END IF
                                tmp1 = lookfor(sesptr, tmp1 + 1, %SE_LOOK4_ALPHA)
                              WEND
                              WHILE (codetk(ses.pc).tktype <> %SE_TOKEN_EOL)
                                codetk(ses.pc).tktype = %ASCII_SPACE
                                INCR ses.pc
                              WEND
                              dbcprint("numbers ok")
                           ELSE
                              ses.errcode = %SE_ERROR_SYNTAXERROR
                           END IF
               END SELECT
               IF ses.errcode THEN EXIT DO
               IF tmp2 THEN
                  codetk(ses.pc).cmd = tmp2
                  codetk(ses.pc + 1).tktype = %ASCII_SPACE
                  codetk(ses.pc + 1).cmd = %SE_TOKEN_UNDEFINED
               END IF
            END IF
            'preview results for debug
            #IF %DEBUG_FLAG
            stmp1 = ""
            SELECT CASE codetk(ses.pc).tktype
                   'case %SE_TOKEN_UNDEFINED: stmp1 = stmp1 + "<%SE_TOKEN_UNDEFINED>"
                   CASE %SE_TOKEN_EOL
                        stmp1 = "<%SE_TOKEN_EOL  >"
                   CASE %SE_TOKEN_DELIMITER
                        stmp1 = "<%SE_TOKEN_DELIM> = ?"
                   CASE %SE_TOKEN_NUMBER
                        stmp1 = "<%SE_TOKEN_NUMBE> = " + tokens(codetk(ses.pc).token).token
                   CASE %SE_TOKEN_ALPHA
                        IF codetk(ses.pc).cmd THEN
                           stmp1 = "<%SE_TOKEN_KEYWD> = " + FORMAT$((codetk(ses.pc).cmd AND &hff), "000") + " " + funtext(codetk(ses.pc).cmd)
                        ELSE
                           stmp1 = "<%SE_TOKEN_ALPHA> = " + tokens(codetk(ses.pc).token).token
                        END IF
                   CASE %SE_TOKEN_STRING
                        stmp1 = "<%SE_TOKEN_STRIN> = " + tokens(codetk(ses.pc).token).token
                   CASE %SE_TOKEN_CRLF
                        stmp1 = "<%SE_TOKEN_CRLF >"
                   CASE > %ASCII_ESCAPE
                        stmp1 = "<%SE_TOKEN_DELIM> = " + CHR$(codetk(ses.pc).tktype)
            END SELECT
              stmp1 = "nl: " + FORMAT$(codetk(ses.pc).lncnt, "000") + " tk: " + FORMAT$(ses.pc, "000") + " -> " + stmp1 ' + " " + FORMAT$(codetk(ses.pc).cmd, "000")
              dbcprint (stmp1)
            #ENDIF
    
            INCR ses.pc
    
        LOOP WHILE ses.pc <= ses.ctks
    
        IF ses.errcode THEN
           'exit if some error
           dbcprint(se_errortext(sesptr))
           tprint se_errortext(sesptr)
           EXIT FUNCTION
        END IF
    
        'delete all generated spaces from codetokens
        dbcprint("Analizing...pass 2")
        tmp1 = 1
        FOR pc = 1 TO ses.ctks
            IF (codetk(pc).tktype = %ASCII_SPACE) THEN
            ELSE
               POKE$ VARPTR(codetk(tmp1)), PEEK$(VARPTR(codetk(pc)), SIZEOF(se_codetoken))
               INCR tmp1
            END IF
        NEXT
        ses.ctks = tmp1 - 1
    
        'load ses, the main struct, with pointers to vars storage
        ses.nvarsn = VARPTR(numvarn(0))
        ses.nvars  = VARPTR(numvar(0))
        ses.svarsn = VARPTR(strvarn(0))
        ses.svars  = VARPTR(strvar(0))
    
        dbcprint("Analizing...pass 3")
        'all global vars declared, so set global vars references, saves many array scan
        'all vars .arg1 = var index and .cmd = %SE_TOKENCMD_GNUMVAR or %SE_TOKENCMD_GSTRVAR
        'also, check built in functions arguments, count them and set parenthesis positions arg1, arg2, arg3
        FOR pc = 1 TO ses.ctks - 1
            ses.pc = pc
            IF (codetk(pc).tktype = %SE_TOKEN_ALPHA) THEN
               IF (codetk(pc).cmd = 0) THEN                           '<- avoid assignments
                  dbcprint("checking alpha " + tokens(codetk(pc).token).token)
                  ARRAY SCAN numvarn(), = codetk(pc).token, TO var
                  IF var THEN
                     dbcprint("is %SE_TOKENCMD_GNUMVAR " + tokens(codetk(pc).token).token)
                     codetk(pc).cmd = %SE_TOKENCMD_GNUMVAR
                     codetk(pc).arg1 = var - 1
                  ELSE
                     ARRAY SCAN strvarn(), = codetk(pc).token, TO var
                     IF var THEN
                        dbcprint("is %SE_TOKENCMD_GSTRVAR " + tokens(codetk(pc).token).token)
                        codetk(pc).cmd = %SE_TOKENCMD_GSTRVAR
                        codetk(pc).arg1 = var - 1
                     ELSE
                        dbcprint("not found... " + tokens(codetk(pc).token).token)
                        '?
                     END IF
                  END IF
               ELSE
                  'just set arguments bounds and count, we will check each arg later and will need these bounds
                  SELECT CASE AS LONG codetk(pc).cmd
                         CASE %SE_TOKENCMD_BIN, %SE_TOKENCMD_OCT, %SE_TOKENCMD_HEX, %SE_TOKENCMD_CHR, _
                              %SE_TOKENCMD_SPACE, %SE_TOKENCMD_STR, %SE_TOKENCMD_NUL, _
                              %SE_TOKENCMD_LCASE, %SE_TOKENCMD_MCASE, %SE_TOKENCMD_HCASE, %SE_TOKENCMD_UCODE, %SE_TOKENCMD_UCASE, _
                              %SE_TOKENCMD_ACODE, %SE_TOKENCMD_TRIM, %SE_TOKENCMD_MID, %SE_TOKENCMD_RND, _
                              %SE_TOKENCMD_INT, %SE_TOKENCMD_ROUND, %SE_TOKENCMD_FIX, %SE_TOKENCMD_FRAC, %SE_TOKENCMD_ABS, _
                              %SE_TOKENCMD_CEIL, %SE_TOKENCMD_CINT, %SE_TOKENCMD_EVEN, %SE_TOKENCMD_ATN, %SE_TOKENCMD_TAN, _
                              %SE_TOKENCMD_COS, %SE_TOKENCMD_SGN, %SE_TOKENCMD_SQR, %SE_TOKENCMD_EXP2, %SE_TOKENCMD_EXP10, _
                              %SE_TOKENCMD_LOG, %SE_TOKENCMD_LOG2, %SE_TOKENCMD_LOG10, %SE_TOKENCMD_EXP, _
                              %SE_TOKENCMD_MIN, %SE_TOKENCMD_MAX, %SE_TOKENCMD_ISTRUE, %SE_TOKENCMD_ISFALSE, %SE_TOKENCMD_NOT, _
                              %SE_TOKENCMD_LEFT, %SE_TOKENCMD_RIGHT, %SE_TOKENCMD_STRING, %SE_TOKENCMD_INSTR, %SE_TOKENCMD_MSGBOX, _
                              %SE_TOKENCMD_LEN, %SE_TOKENCMD_VAL, %SE_TOKENCMD_ASC, %SE_TOKENCMD_STDOUT
                              'arg1 = (
                              'arg2 = )
                              'arg3 = number of parameters
                              'cmd  = function index and definition
                              IF funargs(codetk(pc).cmd) THEN                       'function has arguments?
                                 IF parargs(codetk(pc).cmd) THEN                    'are they parenthised?
                                    codetk(pc).arg1 = lookfor(sesptr, pc, %SE_LOOK4_PAROPEN)
                                    IF codetk(pc).arg1 THEN                         'look for bounds
                                       codetk(pc).arg2 = lookfor(sesptr, codetk(pc).arg1, %SE_LOOK4_MATCHPAR)
                                       IF codetk(pc).arg2 THEN
                                          codetk(pc).arg3 = countargs(sesptr, codetk(pc).arg1, codetk(pc).arg2)
                                          IF (codetk(pc).arg3 <> funargs(codetk(pc).cmd)) THEN
                                             dbcprint ("   parameter mismatch: 12 " + FORMAT$(codetk(pc).arg3) + " " + FORMAT$(funargs(codetk(pc).cmd)) + " " + FORMAT$(pc) + " " + funtext(codetk(pc).cmd))
                                             ses.errcode = %SE_ERROR_PARMISMATCH
                                             EXIT FOR
                                          END IF
                                       ELSE
                                          dbcprint ("  here ): " + funtext(codetk(pc).cmd) + " ln:" + FORMAT$(codetk(pc).lncnt))
                                          ses.errcode = %SE_ERROR_PARCLOSEEXPECTED
                                          EXIT FOR
                                       END IF
                                    ELSE
                                       dbcprint ("  here (: " + funtext(codetk(pc).cmd) + " ln:" + FORMAT$(codetk(pc).lncnt))
                                       ses.errcode = %SE_ERROR_PAROPENEEXPECTED
                                       EXIT FOR
                                    END IF
                                 END IF
                              END IF
                         CASE %SE_TOKENCMD_PRINT
                              codetk(pc).arg1 = pc + 1
                              codetk(pc).arg2 = lookfor(sesptr, codetk(pc).arg1, %SE_LOOK4_EOL)
                  END SELECT
               END IF
            END IF
        NEXT
    
        IF ses.errcode THEN
           'exit if some error
           dbcprint(se_errortext(sesptr))
           tprint se_errortext(sesptr)
           EXIT FUNCTION
        END IF
    
        dbcprint("  num vars: " + FORMAT$(ses.ubn))
        dbcprint("  str vars: " + FORMAT$(ses.ubs))
    
        dbcprint("Analizing...pass 4")
        'finally, precalculate all jumps and loops, check functions arguments types, etc
        ses.pc = 1
        DO
            IF codetk(ses.pc).tktype = %SE_TOKEN_EOL THEN
               INCR ses.pc
               ITERATE DO
            END IF
            IF codetk(ses.pc).tktype = %SE_TOKEN_ALPHA THEN
               'if (last <> codetk(ses.pc).lncnt) then secnt codetk(ses.pc).secnt
               dbcprint("analizing line " + FORMAT$(codetk(ses.pc).lncnt) + " of " + FORMAT$(lcnt) + " ctk: " + FORMAT$(ses.pc) + " of " + FORMAT$(ses.ctks))
               SELECT CASE AS LONG codetk(ses.pc).cmd
                      CASE %SE_TOKENCMD_JUMPTO
                           'ses.pc = ses.@ct[ses.pc].arg1
                      CASE %SE_TOKENCMD_IF, %SE_TOKENCMD_ELSEIF
                           'if/elseif
                           'arg1 = true branch, if/elseif eol
                           'arg2 = false branch, next elseif/else or 0
                           'arg3 = endif
                           'arg4 = end condition (then - 1)
                           tmp2 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_ENDIF)
                           dbcprint ("  endif " + FORMAT$(tmp2))
                           IF tmp2 THEN                                                 'endif?
                              codetk(tmp2).arg1 = ses.pc                                'mark owner
                              tmp1 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_ELSEIF)
                              dbcprint ("  elseif " + FORMAT$(tmp1))
                              IF ISFALSE(tmp1) THEN                                     'elseif?
                                 tmp1 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_ELSE)     'else?
                                 IF tmp1 THEN codetk(tmp1).arg1 = ses.pc                'mark owner
                              END IF
                              dbcprint ("  else " + FORMAT$(tmp1))
                              codetk(ses.pc).arg1 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_EOL)
                              IF tmp1 THEN codetk(tmp1 - 1).arg1 = tmp2                 'set jumpto endif
                              DECR tmp1
                              codetk(ses.pc).arg2 = IIF&(tmp1, tmp1, 0) 'tmp2)
                              tmp1 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_THEN)
                              dbcprint ("  then " + FORMAT$(tmp1))
                              IF tmp1 THEN                                              'then?
                                 codetk(ses.pc).arg3 = tmp2                             'endif address
                                 codetk(ses.pc).arg4 = tmp1 - 1                         'end condition
                                 'ses.pc = tmp1 + 1   'jump after 'then'
                              ELSE
                                 'err then expected
                                 ses.errcode = %SE_ERROR_EXITDOWDO
                                 dbcprint ("  no then")
                              END IF
                              IF ISTRUE(checkarg(sesptr, ses.pc, 1) AND %SE_FDEF_RETSTR) THEN
                                 codetk(ses.pc).cmd = IIF&((codetk(ses.pc).cmd = %SE_TOKENCMD_IF), %SE_TOKENCMD_IFS, %SE_TOKENCMD_ELSEIFS)
                              END IF
                           ELSE
                              'err end if expected
                              ses.errcode = %SE_ERROR_ENDIFEXPECTED
                              dbcprint ("   no endif")
                           END IF
                      CASE %SE_TOKENCMD_ELSE, %SE_TOKENCMD_ENDIF                         'just check these
                           IF ISFALSE(codetk(ses.pc).arg1) THEN
                              ses.errcode = %SE_ERROR_IFEXPECTED
                           END IF
                      CASE %SE_TOKENCMD_EXITDO
                           tmp1 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_LOOP)
                           IF ISFALSE(tmp1) THEN
                              ses.errcode = %SE_ERROR_EXITDOWDO
                           END IF
                           codetk(ses.pc).arg1 = tmp1 + 1
                      CASE %SE_TOKENCMD_EXITWHI
                           tmp1 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_WEND)
                           IF ISFALSE(tmp1) THEN
                              ses.errcode = %SE_ERROR_EXITWHILEWWHILE
                           END IF
                           codetk(ses.pc).arg1 = tmp1 + 1
                      CASE %SE_TOKENCMD_EXITIF
                           tmp1 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_ENDIF)
                           IF ISFALSE(tmp1) THEN
                              ses.errcode = %SE_ERROR_EXITIFWIF
                           END IF
                           codetk(ses.pc).arg1 = tmp1
                      CASE %SE_TOKENCMD_EXITFOR
                           tmp1 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_NEXT)
                           IF ISFALSE(tmp1) THEN
                              ses.errcode = %SE_ERROR_EXITFORWFOR
                           END IF
                           codetk(ses.pc).arg1 = tmp1 + 1
                      CASE %SE_TOKENCMD_EXITSEL
                           tmp1 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_ENDSEL)
                           IF ISFALSE(tmp1) THEN
                              ses.errcode = %SE_ERROR_EXITSELWSEL
                           END IF
                           codetk(ses.pc).arg1 = tmp1 + 1
                      CASE %SE_TOKENCMD_DO, %SE_TOKENCMD_DOW, %SE_TOKENCMD_DOU
                           'do/dow/dou
                           'loop/loopw/loopu
                           'arg1 = after do   (true branch)
                           'arg2 = after loop (false branch)
                           'arg3 = start condition (after while/until if any)
                           'arg4 = end condition
                           tmp2 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_LOOP)
                           IF tmp2 THEN
                              dbcprint ("  loop  " + FORMAT$(tmp2))
                              IF ((codetk(ses.pc).cmd <> %SE_TOKENCMD_DO) AND (codetk(tmp2).cmd <> %SE_TOKENCMD_LOOP)) THEN
                                 ses.errcode = %SE_ERROR_SYNTAXERROR
                              ELSE
                                 tmp3 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_EOL)
                                 tmp4 = lookfor(sesptr, tmp2 + 1, %SE_LOOK4_EOL)
                                 codetk(ses.pc).arg1 = tmp3                                              'after do
                                 codetk(ses.pc).arg2 = tmp4                                              'after loop
                                 codetk(tmp2).arg1 = ses.pc - 1'codetk(ses.pc).arg1
                                 codetk(tmp2).arg2 = codetk(ses.pc).arg2
                                 IF (codetk(ses.pc).cmd <> %SE_TOKENCMD_DO) THEN                         'dow/dou?
                                    dbcprint ("  do while/until " + FORMAT$(ses.pc))
                                    codetk(ses.pc).arg3 = ses.pc + 1                                     'start codition
                                    codetk(ses.pc).arg4 = tmp3 - 1                                       'end codition
                                 ELSEIF (codetk(tmp2).cmd <> %SE_TOKENCMD_LOOP) THEN                     'loopw/loopu?
                                    dbcprint ("  loop while/until " + FORMAT$(tmp2))
                                    codetk(tmp2).arg3 = tmp2 + 1                                         'start codition
                                    codetk(tmp2).arg4 = tmp4 - 1                                         'end codition
                                 END IF
                              END IF
                           ELSE
                              dbcprint ("err do wo/loop " + FORMAT$(ses.pc))
                              ses.errcode = %SE_ERROR_LOOPEXPECTED
                           END IF
                      CASE %SE_TOKENCMD_LOOP, %SE_TOKENCMD_LOOPW, %SE_TOKENCMD_LOOPU, %SE_TOKENCMD_WEND
                           IF ISFALSE(codetk(ses.pc).arg2) THEN
                              ses.errcode = %SE_ERROR_DOEXPECTED
                           END IF
                      CASE %SE_TOKENCMD_WHILE
                           'arg1 = after while eol (true branch)
                           'arg2 = after wend eol  (false branch)
                           'arg3 = start condition (after while)
                           'arg4 = end condition   (before eol)
                           tmp2 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_WEND)
                           tmp3 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_EOL)
                           IF tmp2 THEN
                              dbcprint ("  wend  " + FORMAT$(tmp2))
                              codetk(ses.pc).arg1 = tmp3 + 1                                        'after while
                              codetk(ses.pc).arg2 = lookfor(sesptr, tmp2 + 1, %SE_LOOK4_EOL) + 1    'after wend
                              codetk(tmp2).arg1 = ses.pc - 1                                        'before wend
                              codetk(tmp2).arg2 = codetk(ses.pc).arg2                                   '
                              codetk(ses.pc).arg3 = ses.pc + 1                                      'start codition
                              codetk(ses.pc).arg4 = tmp3 - 1                                        'end codition
                           ELSE
                              ses.errcode = %SE_ERROR_WENDEXPECTED
                           END IF
                      CASE %SE_TOKENCMD_FOR
                           'for
                           ' arg1 = equal + 1        start = arg1     : arg2 - 1
                           ' arg2 = to addr          max   = arg2 + 1 : step/eol
                           ' arg3 = step/eol         step  = arg3 + 1 : eol
                           ' arg4 = var idx
                           'to
                           ' arg1 = step addr if any
                           ' arg2 = false branch
                           ' arg3 = next addr
                           ' arg4 = eol (true branch)
                           'step
                           ' arg1 = eol
                           'next
                           ' arg1 = for addr
                           ' arg2 = to addr
                           ' arg3 = true branch
                           ' arg4 = false branch
                           tmp1 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_ALPHA)
                           dbcprint ("  for var " + FORMAT$(tmp1))
                           'IF tmp1 AND ISTRUE(codetk(tmp1).cmd = %SE_TOKENCMD_GNUMVAR) THEN   'num var?
                           'IF tmp1 THEN
                              IF isnumvar(codetk(tmp1).cmd) THEN   'num var?
                                 tmp2 = lookfor(sesptr, tmp1, %SE_LOOK4_EQUAL)
                                 dbcprint ("  for = " + FORMAT$(tmp2))
                                 IF tmp2 THEN
                                    tmp3 = lookfor(sesptr, tmp2, %SE_LOOK4_TO)
                                    dbcprint ("  for to " + FORMAT$(tmp3))
                                    IF tmp3 THEN
                                       tmp4 = lookfor(sesptr, tmp3, %SE_LOOK4_STEP)
                                       dbcprint ("  for step " + FORMAT$(tmp4))
                                       IF tmp4 THEN
                                          codetk(tmp3).arg1 = tmp4
                                          codetk(tmp4).arg1 = tmp1 = lookfor(sesptr, tmp3, %SE_LOOK4_EOL)
                                       ELSE
                                          tmp4 = lookfor(sesptr, tmp3 + 1, %SE_LOOK4_EOL)
                                       END IF
                                       'for setup
                                       codetk(ses.pc).arg1 = tmp2 + 1
                                       codetk(ses.pc).arg2 = tmp3
                                       codetk(ses.pc).arg3 = tmp4
                                       'to setup
                                       codetk(ses.pc).arg4 = codetk(tmp1).arg1
                                       codetk(tmp3).arg2 = lookfor(sesptr, tmp3, %SE_LOOK4_NEXT)
                                       codetk(tmp3).arg3 = codetk(tmp3).arg2
                                       codetk(tmp3).arg4 = lookfor(sesptr, tmp3, %SE_LOOK4_EOL)
                                       dbcprint ("  for next " + FORMAT$(codetk(tmp3).arg2))
                                       'next setup
                                       codetk(codetk(tmp3).arg2).arg1 = ses.pc
                                       codetk(codetk(tmp3).arg2).arg2 = tmp3
                                       codetk(codetk(tmp3).arg2).arg3 = codetk(tmp3).arg4
                                       codetk(codetk(tmp3).arg2).arg4 = codetk(tmp3).arg2
                                    ELSE
                                       ses.errcode = %SE_ERROR_TOEXPECTED
                                    END IF
                                 ELSE
                                    ses.errcode = %SE_ERROR_SYNTAXERROR
                                 END IF
                              ELSE
                                 dbcprint("cmd: " + BIN$(codetk(tmp1).cmd))
                                 ses.errcode = %SE_ERROR_NUMVAREXPECTED
                              END IF
                           'END IF
                      CASE %SE_TOKENCMD_NEXT
                           IF ISFALSE(codetk(ses.pc).arg2) THEN
                              ses.errcode = %SE_ERROR_FOREXPECTED
                           END IF
                      CASE %SE_TOKENCMD_LET, %SE_TOKENCMD_GNUMVAR, %SE_TOKENCMD_GSTRVAR
                           'arg1 = var index
                           'arg2 = after '='
                           'arg3 = eol - 1
                           dbcprint("let")
                           IF (codetk(ses.pc).cmd = %SE_TOKENCMD_LET) THEN INCR ses.pc
                           IF (codetk(ses.pc).secnt = 1) THEN
                              IF (codetk(ses.pc + 1).tktype = %ASCII_EQUAL) THEN
                                 SELECT CASE codetk(ses.pc).cmd
                                        CASE %SE_TOKENCMD_GNUMVAR
                                             codetk(ses.pc).cmd = %SE_TOKENCMD_GLETNUM
                                        CASE %SE_TOKENCMD_GSTRVAR
                                             codetk(ses.pc).cmd = %SE_TOKENCMD_GLETSTR
                                        CASE ELSE
                                             ses.errcode = %SE_ERROR_SYNTAXERROR
                                 END SELECT
                                 tmp3 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_EOL)
                                 IF ISTRUE(tmp3) THEN
                                    codetk(ses.pc).arg2 = ses.pc + 2                  'after =
                                    codetk(ses.pc).arg3 = tmp3 - 1                    'eol - 1
                                    'ses.pc = tmp3
                                    dbcprint ("   var: " + FORMAT$(codetk(ses.pc).arg1))
                                    dbcprint ("     =: " + FORMAT$(ses.pc + 1))
                                    dbcprint ("   EOL: " + FORMAT$(tmp3))
                                    dbcprint("let ok")
                                 ELSE
                                    '?...
                                    ses.errcode = %SE_ERROR_SYNTAXERROR
                                    dbcprint(se_errortext(sesptr))
                                 END IF
                              ELSE
                                 ses.errcode = %SE_ERROR_SYNTAXERROR
                              END IF
                           END IF
                      CASE %SE_TOKENCMD_INCR, %SE_TOKENCMD_DECR
                           'tmp1 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_ALPHA)
                           dbcprint("incr/decr ")
                           tmp1 = ses.pc + 1
                           SELECT CASE codetk(tmp1).cmd
                                  CASE %SE_TOKENCMD_GNUMVAR
                                       codetk(ses.pc).arg1 = codetk(tmp1).arg1
                                  CASE ELSE
                                       dbcprint("incr/decr error")
                                       ses.errcode = %SE_ERROR_VAREXPECTED
                           END SELECT
                      CASE %SE_TOKENCMD_GOTO
                           tmp1 = lookfor(sesptr, ses.pc + 1, %SE_LOOK4_ALPHA)
                           IF tmp1 THEN
                              IF tokens(codetk(tmp1).token).addrs THEN
                                 codetk(ses.pc).arg1 = tokens(codetk(tmp1).token).addrs
                              ELSE
                                 'undefined equate
                                 dbcprint("goto 1 " + tokens(codetk(tmp1).token).token)
                                 ses.errcode = %SE_ERROR_UNDEFEQUATE
                              END IF
                           ELSE
                              'syntax error
                              dbcprint("goto 2 " + tokens(codetk(tmp1).token).token)
                              ses.errcode = %SE_ERROR_SYNTAXERROR
                           END IF
                      CASE %SE_TOKENCMD_RESET
                           'codetk(ses.pc).cmd = %SE_TOKENCMD_RSTNUM '/ STR
                      CASE %SE_TOKENCMD_ERASE
                           'codetk(ses.pc).cmd = %SE_TOKENCMD_ERNUM  '/ STR
                      CASE %SE_TOKENCMD_STDERR
                      CASE %SE_TOKENCMD_STDIN
                      CASE %SE_TOKENCMD_ENDSCR, %SE_TOKENCMD_END
                           EXIT DO
                      CASE %SE_TOKENCMD_PRGLINES, %SE_TOKENCMD_PI, %SE_TOKENCMD_BEEP
                           '0 arg, returns number, static
                      CASE %SE_TOKENCMD_TIMER
                           '0 arg, returns number, dynamic
                      CASE %SE_TOKENCMD_DATE, %SE_TOKENCMD_TIME
                           '0 arg, returns string, dynamic
                      CASE %SE_TOKENCMD_LEN, %SE_TOKENCMD_VAL, %SE_TOKENCMD_ASC, %SE_TOKENCMD_LEFT, %SE_TOKENCMD_RIGHT, _
                           %SE_TOKENCMD_SPACE, %SE_TOKENCMD_STR, _
                           %SE_TOKENCMD_LCASE, %SE_TOKENCMD_MCASE, %SE_TOKENCMD_HCASE, %SE_TOKENCMD_UCODE, %SE_TOKENCMD_UCASE, _
                           %SE_TOKENCMD_BIN, %SE_TOKENCMD_OCT, %SE_TOKENCMD_HEX, %SE_TOKENCMD_CHR, _
                           %SE_TOKENCMD_ACODE, %SE_TOKENCMD_TRIM, %SE_TOKENCMD_INSTR, %SE_TOKENCMD_NUL, _
                           %SE_TOKENCMD_INT, %SE_TOKENCMD_ROUND, %SE_TOKENCMD_FIX, %SE_TOKENCMD_FRAC, %SE_TOKENCMD_ABS, _
                           %SE_TOKENCMD_CEIL, %SE_TOKENCMD_CINT, %SE_TOKENCMD_EVEN, %SE_TOKENCMD_ATN, %SE_TOKENCMD_TAN, _
                           %SE_TOKENCMD_COS, %SE_TOKENCMD_SGN, %SE_TOKENCMD_SQR, %SE_TOKENCMD_EXP2, %SE_TOKENCMD_EXP10, _
                           %SE_TOKENCMD_LOG, %SE_TOKENCMD_LOG2, %SE_TOKENCMD_LOG10, %SE_TOKENCMD_EXP, %SE_TOKENCMD_MID, _
                           %SE_TOKENCMD_MIN, %SE_TOKENCMD_MAX, %SE_TOKENCMD_ISTRUE, %SE_TOKENCMD_ISFALSE, %SE_TOKENCMD_NOT, _
                           %SE_TOKENCMD_STRING, %SE_TOKENCMD_MSGBOX, _
                           %SE_TOKENCMD_STDOUT, %SE_TOKENCMD_RND
                           'arg1 = (
                           'arg2 = )
                           'arg3 = arguments count
                           'cmd  = function index and definition
                           IF funargs(codetk(ses.pc).cmd) THEN                       'function has arguments?
                              IF parargs(codetk(ses.pc).cmd) THEN                    'are they parenthised?
                                 FOR tmp1 = 1 TO codetk(ses.pc).arg3                 'check all them
                                     tmp2 = checkarg(sesptr, codetk(ses.pc).arg1, tmp1)
                                     IF ISFALSE(tmp2 AND codetk(ses.pc).cmd) THEN
                                        dbcprint ("   parameter mismatch type : " + HEX$(codetk(ses.pc).cmd) + " " + HEX$(tmp2)))
                                        dbcprint ("   parameter mismatch count: " + FORMAT$(codetk(ses.pc).arg3) + " " + FORMAT$(funargs(codetk(ses.pc).cmd)) + " " + FORMAT$(ses.pc))
                                        ses.errcode = %SE_ERROR_PARMISMATCH
                                     END IF
                                 NEXT
                              END IF
                           END IF
                      CASE %SE_TOKENCMD_PRINT
                      CASE ELSE
               END SELECT
            END IF
            IF ses.errcode THEN EXIT DO
            INCR ses.pc
            IF (ses.pc => ses.ctks) THEN EXIT DO
        LOOP
    
        dbcprint("Printing...")
        ifdbgx(prtcode(sesptr, 1, ses.ctks))
        ifdbgx(FOR tmp1 = 1 TO ses.ubs: dbcprint("strings " + tokens(strvarn(tmp1)).token + " = " + strvar(tmp1)): NEXT)
        IF ses.errcode THEN
           'report error if any
           dbcprint (se_errortext(sesptr))
           tprint se_errortext(sesptr)
           EXIT FUNCTION
        END IF
    
        ses.errcode = %SE_ERROR_EXECUTING
        ifdbgx(stdout(se_errortext(sesptr)))
        'tprint se_errortext(sesptr)
        ses.errcode = %SE_ERROR_NOERROR
    
        'all done, exec the code!
        ses.pc = 1
        execfun sesptr
    
        'script exited, finished or had an error
        ifdbgx(stdout (se_errortext(sesptr)))
        'tprint se_errortext(sesptr)
        ses.errcode = %SE_ERROR_EXITING
        ifdbgx(stdout (se_errortext(sesptr)))
        'tprint se_errortext(sesptr)
    
        ifdbgx(TRACE CLOSE)
        FUNCTION = ses.errcode
    END FUNCTION
    
    'prints all formated codetokens to a textbox, for debug
    FUNCTION prtcode(BYVAL ses AS ses_type PTR, BYVAL start AS LONG, BYVAL ends AS LONG) AS DWORD
        LOCAL stmp1 AS STRING, tmp1 AS LONG
        stdout "########################################################"
        FOR tmp1 = start TO ends
            stmp1 = "CT:" + FORMAT$(tmp1, "000") + " LN:" + FORMAT$(@ses.@ct[tmp1].lncnt, "000") + " SN:" + FORMAT$(@ses.@ct[tmp1].secnt, "000")
            SELECT CASE @ses.@ct[tmp1].tktype
                   CASE %SE_TOKEN_ALPHA    : stmp1 = stmp1 + " A " + FORMAT$((@ses.@ct[tmp1].cmd AND &hff), "000") + " "
                        stmp1 = stmp1 + "a1:" + FORMAT$(@ses.@ct[tmp1].arg1, "000") + " "
                        stmp1 = stmp1 + "a2:" + FORMAT$(@ses.@ct[tmp1].arg2, "000") + " "
                        stmp1 = stmp1 + "a3:" + FORMAT$(@ses.@ct[tmp1].arg3, "000") + " "
                        stmp1 = stmp1 + "a4:" + FORMAT$(@ses.@ct[tmp1].arg4, "000") + " "
                        stmp1 = stmp1 + funtext(@ses.@ct[tmp1].cmd)
                   CASE %SE_TOKEN_NUMBER   : stmp1 = stmp1 + " N "
                   CASE %SE_TOKEN_EOL      : stmp1 = stmp1 + "EOL"
                   CASE %SE_TOKEN_STRING   : stmp1 = stmp1 + " S "
                   CASE %SE_TOKEN_DELIMITER: stmp1 = stmp1 + " D "
                   CASE > %ASCII_ESCAPE    : stmp1 = stmp1 + " " + CHR$(@ses.@ct[tmp1].tktype)+ " "
            END SELECT
            IF (@ses.@ct[tmp1].cmd = %SE_TOKENCMD_PRENUM) THEN
               stmp1 = stmp1 + " " + FORMAT$(@ses.@ct[tmp1].pre)
            END IF
            dbcprint(stmp1)
        NEXT
    END FUNCTION
    
    'takes function cmd and return function text name, for debug
    FUNCTION funtext(code AS LONG) AS STRING
             SELECT CASE AS LONG code
                    CASE %SE_TOKENCMD_ABS
                         FUNCTION = "%SE_TOKENCMD_ABS      "
                    CASE %SE_TOKENCMD_AND
                         FUNCTION = "%SE_TOKENCMD_AND      "
                    CASE %SE_TOKENCMD_ACODE
                         FUNCTION = "%SE_TOKENCMD_ACODE    "
                    CASE %SE_TOKENCMD_ASC
                         FUNCTION = "%SE_TOKENCMD_ASC      "
                    CASE %SE_TOKENCMD_ATN
                         FUNCTION = "%SE_TOKENCMD_ATN      "
                    CASE %SE_TOKENCMD_BEEP
                         FUNCTION = "%SE_TOKENCMD_BEEP     "
                    CASE %SE_TOKENCMD_BIN
                         FUNCTION = "%SE_TOKENCMD_BIN      "
                    CASE %SE_TOKENCMD_CASE
                         FUNCTION = "%SE_TOKENCMD_CASE     "
                    CASE %SE_TOKENCMD_CEIL
                         FUNCTION = "%SE_TOKENCMD_CEIL     "
                    CASE %SE_TOKENCMD_CHR
                         FUNCTION = "%SE_TOKENCMD_CHR      "
                    CASE %SE_TOKENCMD_CINT
                         FUNCTION = "%SE_TOKENCMD_CINT     "
                    CASE %SE_TOKENCMD_COS
                         FUNCTION = "%SE_TOKENCMD_COS      "
                    CASE %SE_TOKENCMD_DATE
                         FUNCTION = "%SE_TOKENCMD_DATE     "
                    CASE %SE_TOKENCMD_DECR
                         FUNCTION = "%SE_TOKENCMD_DECR     "
                    CASE %SE_TOKENCMD_DO
                         FUNCTION = "%SE_TOKENCMD_DO       "
                    CASE %SE_TOKENCMD_END
                         FUNCTION = "%SE_TOKENCMD_END      "
                    CASE %SE_TOKENCMD_ELSE
                         FUNCTION = "%SE_TOKENCMD_ELSE     "
                    CASE %SE_TOKENCMD_ELSEIF
                         FUNCTION = "%SE_TOKENCMD_ELSEIF   "
                    CASE %SE_TOKENCMD_EQV
                         FUNCTION = "%SE_TOKENCMD_EQV      "
                    CASE %SE_TOKENCMD_ERASE
                         FUNCTION = "%SE_TOKENCMD_ERASE    "
                    CASE %SE_TOKENCMD_EVEN
                         FUNCTION = "%SE_TOKENCMD_EVEN     "
                    CASE %SE_TOKENCMD_EXP
                         FUNCTION = "%SE_TOKENCMD_EXP      "
                    CASE %SE_TOKENCMD_EXP2
                         FUNCTION = "%SE_TOKENCMD_EXP2     "
                    CASE %SE_TOKENCMD_EXP10
                         FUNCTION = "%SE_TOKENCMD_EXP10    "
                    CASE %SE_TOKENCMD_EXIT
                         FUNCTION = "%SE_TOKENCMD_EXIT     "
                    CASE %SE_TOKENCMD_FIX
                         FUNCTION = "%SE_TOKENCMD_FIX      "
                    CASE %SE_TOKENCMD_FOR
                         FUNCTION = "%SE_TOKENCMD_FOR      "
                    CASE %SE_TOKENCMD_FUNCTION
                         FUNCTION = "%SE_TOKENCMD_FUNCTION "
                    CASE %SE_TOKENCMD_FRAC
                         FUNCTION = "%SE_TOKENCMD_FRAC     "
                    CASE %SE_TOKENCMD_GOTO
                         FUNCTION = "%SE_TOKENCMD_GOTO     "
                    CASE %SE_TOKENCMD_GOSUB
                         FUNCTION = "%SE_TOKENCMD_GOSUB    "
                    CASE %SE_TOKENCMD_HCASE
                         FUNCTION = "%SE_TOKENCMD_HCASE    "
                    CASE %SE_TOKENCMD_HEX
                         FUNCTION = "%SE_TOKENCMD_HEX      "
                    CASE %SE_TOKENCMD_IF
                         FUNCTION = "%SE_TOKENCMD_IF       "
                    CASE %SE_TOKENCMD_IMP
                         FUNCTION = "%SE_TOKENCMD_IMP      "
                    CASE %SE_TOKENCMD_INCR
                         FUNCTION = "%SE_TOKENCMD_INCR     "
                    CASE %SE_TOKENCMD_INSTR
                         FUNCTION = "%SE_TOKENCMD_INSTR    "
                    CASE %SE_TOKENCMD_INT
                         FUNCTION = "%SE_TOKENCMD_INT      "
                    CASE %SE_TOKENCMD_ISTRUE
                         FUNCTION = "%SE_TOKENCMD_ISTRUE   "
                    CASE %SE_TOKENCMD_ISFALSE
                         FUNCTION = "%SE_TOKENCMD_ISFALSE  "
                    CASE %SE_TOKENCMD_MAX
                         FUNCTION = "%SE_TOKENCMD_MAX      "
                    CASE %SE_TOKENCMD_MSGBOX
                         FUNCTION = "%SE_TOKENCMD_MSGBOX   "
                    CASE %SE_TOKENCMD_MCASE
                         FUNCTION = "%SE_TOKENCMD_MCASE    "
                    CASE %SE_TOKENCMD_MID
                         FUNCTION = "%SE_TOKENCMD_MID      "
                    CASE %SE_TOKENCMD_MIN
                         FUNCTION = "%SE_TOKENCMD_MIN      "
                    CASE %SE_TOKENCMD_MOD
                         FUNCTION = "%SE_TOKENCMD_MOD      "
                    CASE %SE_TOKENCMD_NEXT
                         FUNCTION = "%SE_TOKENCMD_NEXT     "
                    CASE %SE_TOKENCMD_NOT
                         FUNCTION = "%SE_TOKENCMD_NOT      "
                    CASE %SE_TOKENCMD_NUMBERS
                         FUNCTION = "%SE_TOKENCMD_NUMBERS  "
                    CASE %SE_TOKENCMD_LCASE
                         FUNCTION = "%SE_TOKENCMD_LCASE    "
                    CASE %SE_TOKENCMD_LEFT
                         FUNCTION = "%SE_TOKENCMD_LEFT     "
                    CASE %SE_TOKENCMD_LEN
                         FUNCTION = "%SE_TOKENCMD_LEN      "
                    CASE %SE_TOKENCMD_LOG
                         FUNCTION = "%SE_TOKENCMD_LOG      "
                    CASE %SE_TOKENCMD_LOG2
                         FUNCTION = "%SE_TOKENCMD_LOG2     "
                    CASE %SE_TOKENCMD_LOG10
                         FUNCTION = "%SE_TOKENCMD_LOG10    "
                    CASE %SE_TOKENCMD_LOOP
                         FUNCTION = "%SE_TOKENCMD_LOOP     "
                    CASE %SE_TOKENCMD_LET
                         FUNCTION = "%SE_TOKENCMD_LET      "
                    CASE %SE_TOKENCMD_OCT
                         FUNCTION = "%SE_TOKENCMD_OCT      "
                    CASE %SE_TOKENCMD_OR
                         FUNCTION = "%SE_TOKENCMD_OR       "
                    CASE %SE_TOKENCMD_PRGLINES
                         FUNCTION = "%SE_TOKENCMD_PRGLINES "
                    CASE %SE_TOKENCMD_PI
                         FUNCTION = "%SE_TOKENCMD_PI       "
                    CASE %SE_TOKENCMD_RANDOM
                         FUNCTION = "%SE_TOKENCMD_RANDOM   "
                    CASE %SE_TOKENCMD_RESET
                         FUNCTION = "%SE_TOKENCMD_RESET    "
                    CASE %SE_TOKENCMD_RETURN
                         FUNCTION = "%SE_TOKENCMD_RETURN   "
                    CASE %SE_TOKENCMD_RIGHT
                         FUNCTION = "%SE_TOKENCMD_RIGHT    "
                    CASE %SE_TOKENCMD_RND
                         FUNCTION = "%SE_TOKENCMD_RND      "
                    CASE %SE_TOKENCMD_ROUND
                         FUNCTION = "%SE_TOKENCMD_ROUND    "
                    CASE %SE_TOKENCMD_SGN
                         FUNCTION = "%SE_TOKENCMD_SGN      "
                    CASE %SE_TOKENCMD_SPACE
                         FUNCTION = "%SE_TOKENCMD_SPACE    "
                    CASE %SE_TOKENCMD_SQR
                         FUNCTION = "%SE_TOKENCMD_SQR      "
                    CASE %SE_TOKENCMD_STDERR
                         FUNCTION = "%SE_TOKENCMD_STDERR   "
                    CASE %SE_TOKENCMD_STDIN
                         FUNCTION = "%SE_TOKENCMD_STDIN    "
                    CASE %SE_TOKENCMD_STDOUT
                         FUNCTION = "%SE_TOKENCMD_STDOUT   "
                    CASE %SE_TOKENCMD_STR
                         FUNCTION = "%SE_TOKENCMD_STR      "
                    CASE %SE_TOKENCMD_STRING
                         FUNCTION = "%SE_TOKENCMD_STRING   "
                    CASE %SE_TOKENCMD_SUB
                         FUNCTION = "%SE_TOKENCMD_SUB      "
                    CASE %SE_TOKENCMD_SELECT
                         FUNCTION = "%SE_TOKENCMD_SELECT   "
                    CASE %SE_TOKENCMD_STRINGS
                         FUNCTION = "%SE_TOKENCMD_STRINGS  "
                    CASE %SE_TOKENCMD_TAN
                         FUNCTION = "%SE_TOKENCMD_TAN      "
                    CASE %SE_TOKENCMD_TIME
                         FUNCTION = "%SE_TOKENCMD_TIME     "
                    CASE %SE_TOKENCMD_THEN
                         FUNCTION = "%SE_TOKENCMD_THEN     "
                    CASE %SE_TOKENCMD_TIMER
                         FUNCTION = "%SE_TOKENCMD_TIMER    "
                    CASE %SE_TOKENCMD_TO
                         FUNCTION = "%SE_TOKENCMD_TO       "
                    CASE %SE_TOKENCMD_TRIM
                         FUNCTION = "%SE_TOKENCMD_TRIM     "
                    CASE %SE_TOKENCMD_UCASE
                         FUNCTION = "%SE_TOKENCMD_UCASE    "
                    CASE %SE_TOKENCMD_UCODE
                         FUNCTION = "%SE_TOKENCMD_UCODE    "
                    CASE %SE_TOKENCMD_UNTIL
                         FUNCTION = "%SE_TOKENCMD_UNTIL    "
                    CASE %SE_TOKENCMD_VAL
                         FUNCTION = "%SE_TOKENCMD_VAL      "
                    CASE %SE_TOKENCMD_WEND
                         FUNCTION = "%SE_TOKENCMD_WEND     "
                    CASE %SE_TOKENCMD_WHILE
                         FUNCTION = "%SE_TOKENCMD_WHILE    "
                    CASE %SE_TOKENCMD_XOR
                         FUNCTION = "%SE_TOKENCMD_XOR      "
                    CASE %SE_TOKENCMD_FUNSTR
                         FUNCTION = "%SE_TOKENCMD_FUNSTR   "
                    CASE %SE_TOKENCMD_ENDIF
                         FUNCTION = "%SE_TOKENCMD_ENDIF    "
                    CASE %SE_TOKENCMD_DOW
                         FUNCTION = "%SE_TOKENCMD_DOW      "
                    CASE %SE_TOKENCMD_DOU
                         FUNCTION = "%SE_TOKENCMD_DOU      "
                    CASE %SE_TOKENCMD_ENDFUN
                         FUNCTION = "%SE_TOKENCMD_ENDFUN   "
                    CASE %SE_TOKENCMD_EXITIF
                         FUNCTION = "%SE_TOKENCMD_EXITIF   "
                    CASE %SE_TOKENCMD_EXITDO
                         FUNCTION = "%SE_TOKENCMD_EXITDO   "
                    CASE %SE_TOKENCMD_EXITWHI
                         FUNCTION = "%SE_TOKENCMD_EXITWHI  "
                    CASE %SE_TOKENCMD_EXITFOR
                         FUNCTION = "%SE_TOKENCMD_EXITFOR  "
                    CASE %SE_TOKENCMD_EXITFUN
                         FUNCTION = "%SE_TOKENCMD_EXITFUN  "
                    CASE %SE_TOKENCMD_EXITSUB
                         FUNCTION = "%SE_TOKENCMD_EXITSUB  "
                    CASE %SE_TOKENCMD_EXITSEL
                         FUNCTION = "%SE_TOKENCMD_EXITSEL  "
                    CASE %SE_TOKENCMD_FAC
                         FUNCTION = "%SE_TOKENCMD_FAC      "
                    CASE %SE_TOKENCMD_LOOPW
                         FUNCTION = "%SE_TOKENCMD_LOOPW    "
                    CASE %SE_TOKENCMD_LOOPU
                         FUNCTION = "%SE_TOKENCMD_LOOPU    "
                    CASE %SE_TOKENCMD_GLETNUM
                         FUNCTION = "%SE_TOKENCMD_GLETNUM   "
                    CASE %SE_TOKENCMD_GLETSTR
                         FUNCTION = "%SE_TOKENCMD_GLETSTR   "
                    CASE %SE_TOKENCMD_RSTNUM
                         FUNCTION = "%SE_TOKENCMD_RSTNUM   "
                    CASE %SE_TOKENCMD_RSTSTR
                         FUNCTION = "%SE_TOKENCMD_RSTSTR   "
                    CASE %SE_TOKENCMD_ERNUM
                         FUNCTION = "%SE_TOKENCMD_ERNUM    "
                    CASE %SE_TOKENCMD_ERSTR
                         FUNCTION = "%SE_TOKENCMD_ERSTR    "
                    CASE %SE_TOKENCMD_LABEL
                         FUNCTION = "%SE_TOKENCMD_LABEL    "
                    CASE %SE_TOKENCMD_PRENUM
                         FUNCTION = "%SE_TOKENCMD_PRENUM   "
                    CASE %SE_TOKENCMD_PRESTR
                         FUNCTION = "%SE_TOKENCMD_PRESTR   "
                    CASE %SE_TOKENCMD_GNUMVAR
                         FUNCTION = "%SE_TOKENCMD_GNUMVAR   "
                    CASE %SE_TOKENCMD_GSTRVAR
                         FUNCTION = "%SE_TOKENCMD_GSTRVAR   "
                    CASE %SE_TOKENCMD_STEP
                         FUNCTION = "%SE_TOKENCMD_STEP     "
                    CASE %SE_TOKENCMD_FUNNUM
                         FUNCTION = "%SE_TOKENCMD_FUNNUM   "
                    CASE %SE_TOKENCMD_ENDSUB
                         FUNCTION = "%SE_TOKENCMD_ENDSUB   "
                    CASE %SE_TOKENCMD_ENDSEL
                         FUNCTION = "%SE_TOKENCMD_ENDSEL   "
                    CASE %SE_TOKENCMD_ENDSCR
                         FUNCTION = "%SE_TOKENCMD_ENDSCR   "
                    CASE %SE_TOKENCMD_JUMPTO
                         FUNCTION = "%SE_TOKENCMD_JUMPTO   "
                    CASE %SE_TOKENCMD_LABEL
                         FUNCTION = "%SE_TOKENCMD_LABEL    "
                    CASE %SE_TOKENCMD_OPERAT
                         FUNCTION = "%SE_TOKENCMD_OPERAT   "
                    CASE %SE_TOKENCMD_NUMCONST
                         FUNCTION = "%SE_TOKENCMD_NUMCONST "
                    CASE %SE_TOKENCMD_STRCONST
                         FUNCTION = "%SE_TOKENCMD_STRCONST "
                    CASE %SE_TOKENCMD_WAITKEY
                         FUNCTION = "%SE_TOKENCMD_WAITKEY  "
                    CASE %SE_TOKENCMD_CMD
                         FUNCTION = "%SE_TOKENCMD_CMD      "
                    CASE %SE_TOKENCMD_STRREV
                         FUNCTION = "%SE_TOKENCMD_STRREV   "
                    CASE %SE_TOKENCMD_STRDEL
                         FUNCTION = "%SE_TOKENCMD_STRDEL   "
                    CASE %SE_TOKENCMD_STRINGS
                         FUNCTION = "%SE_TOKENCMD_STRINGS  "
                    CASE %SE_TOKENCMD_STRING
                         FUNCTION = "%SE_TOKENCMD_STRING   "
                    CASE %SE_TOKENCMD_MAXS
                         FUNCTION = "%SE_TOKENCMD_MAXS     "
                    CASE %SE_TOKENCMD_MINS
                         FUNCTION = "%SE_TOKENCMD_MINS     "
                    CASE %SE_TOKENCMD_RETAIN
                         FUNCTION = "%SE_TOKENCMD_RETAIN   "
                    CASE %SE_TOKENCMD_REMAIN
                         FUNCTION = "%SE_TOKENCMD_REMAIN   "
                    CASE %SE_TOKENCMD_REMOVE
                         FUNCTION = "%SE_TOKENCMD_REMOVE   "
                    CASE %SE_TOKENCMD_REPEAT
                         FUNCTION = "%SE_TOKENCMD_REPEAT   "
                    CASE %SE_TOKENCMD_RTRIM
                         FUNCTION = "%SE_TOKENCMD_RTRIM    "
                    CASE %SE_TOKENCMD_JOIN
                         FUNCTION = "%SE_TOKENCMD_JOIN     "
                    CASE %SE_TOKENCMD_NUL
                         FUNCTION = "%SE_TOKENCMD_NUL      "
                    CASE %SE_TOKENCMD_ENVGET
                         FUNCTION = "%SE_TOKENCMD_ENVGET   "
                    CASE %SE_TOKENCMD_ENVSET
                         FUNCTION = "%SE_TOKENCMD_ENVSET   "
                    CASE %SE_TOKENCMD_FORMAT
                         FUNCTION = "%SE_TOKENCMD_FORMAT   "
                    CASE %SE_TOKENCMD_EXTRACT
                         FUNCTION = "%SE_TOKENCMD_EXTRACT  "
                    CASE %SE_TOKENCMD_TAB
                         FUNCTION = "%SE_TOKENCMD_TAB      "
                    CASE %SE_TOKENCMD_PRINT
                         FUNCTION = "%SE_TOKENCMD_PRINT    "
                    CASE %SE_TOKENCMD_IFS
                         FUNCTION = "%SE_TOKENCMD_IFS      "
                    CASE %SE_TOKENCMD_ELSEIFS
                         FUNCTION = "%SE_TOKENCMD_ELSEIFS  "
                    CASE ELSE
                         FUNCTION = "????????? [" + HEX$(code) + "] " + BIN$(code)
             END SELECT
    END FUNCTION
    
    'test a string, if a keyword return function name and definition constant
    FUNCTION fundefs(code AS STRING) AS LONG
             SELECT CASE AS CONST$ code
                    CASE  "as"
                          FUNCTION = %SE_TOKENCMD_AS
                    CASE  "abs"
                          FUNCTION = %SE_TOKENCMD_ABS
                    CASE  "and"
                          FUNCTION = %SE_TOKENCMD_AND
                    CASE  "acode"
                          FUNCTION = %SE_TOKENCMD_ACODE
                    CASE  "asc"
                          FUNCTION = %SE_TOKENCMD_ASC
                    CASE  "atn"
                          FUNCTION = %SE_TOKENCMD_ATN
                    CASE  "beep"
                          FUNCTION = %SE_TOKENCMD_BEEP
                    CASE  "bin"
                          FUNCTION = %SE_TOKENCMD_BIN
                    CASE  "case"
                          FUNCTION = %SE_TOKENCMD_CASE
                    CASE  "ceil"
                          FUNCTION = %SE_TOKENCMD_CEIL
                    CASE  "chr"
                          FUNCTION = %SE_TOKENCMD_CHR
                    CASE  "cint"
                          FUNCTION = %SE_TOKENCMD_CINT
                    CASE  "cos"
                          FUNCTION = %SE_TOKENCMD_COS
                    CASE  "command"
                          FUNCTION = %SE_TOKENCMD_CMD
                    CASE  "date"
                          FUNCTION = %SE_TOKENCMD_DATE
                    CASE  "decr"
                          FUNCTION = %SE_TOKENCMD_DECR
                    CASE  "do"
                          FUNCTION = %SE_TOKENCMD_DO
                    CASE  "end"
                          FUNCTION = %SE_TOKENCMD_END
                    CASE  "else"
                          FUNCTION = %SE_TOKENCMD_ELSE
                    CASE  "elseif"
                          FUNCTION = %SE_TOKENCMD_ELSEIF
                    CASE  "eqv"
                          FUNCTION = %SE_TOKENCMD_EQV
                    CASE  "erase"
                          FUNCTION = %SE_TOKENCMD_ERASE
                    CASE  "even"
                          FUNCTION = %SE_TOKENCMD_EVEN
                    CASE  "exp"
                          FUNCTION = %SE_TOKENCMD_EXP
                    CASE  "exp2"
                          FUNCTION = %SE_TOKENCMD_EXP2
                    CASE  "exp10"
                          FUNCTION = %SE_TOKENCMD_EXP10
                    CASE  "exit"
                          FUNCTION = %SE_TOKENCMD_EXIT
                    CASE  "fac"
                          FUNCTION = %SE_TOKENCMD_FAC
                    CASE  "fix"
                          FUNCTION = %SE_TOKENCMD_FIX
                    CASE  "for"
                          FUNCTION = %SE_TOKENCMD_FOR
                    CASE  "function"
                          FUNCTION = %SE_TOKENCMD_FUNCTION
                    CASE  "frac"
                          FUNCTION = %SE_TOKENCMD_FRAC
                    CASE  "goto"
                          FUNCTION = %SE_TOKENCMD_GOTO
                    CASE  "gosub"
                          FUNCTION = %SE_TOKENCMD_GOSUB
                    CASE  "hcase"
                          FUNCTION = %SE_TOKENCMD_HCASE
                    CASE  "hex"
                          FUNCTION = %SE_TOKENCMD_HEX
                    CASE  "if"
                          FUNCTION = %SE_TOKENCMD_IF
                    CASE  "imp"
                          FUNCTION = %SE_TOKENCMD_IMP
                    CASE  "incr"
                          FUNCTION = %SE_TOKENCMD_INCR
                    CASE  "instr"
                          FUNCTION = %SE_TOKENCMD_INSTR
                    CASE  "int"
                          FUNCTION = %SE_TOKENCMD_INT
                    CASE  "istrue"
                          FUNCTION = %SE_TOKENCMD_ISTRUE
                    CASE  "isfalse"
                          FUNCTION = %SE_TOKENCMD_ISFALSE
                    CASE  "max"
                          FUNCTION = %SE_TOKENCMD_MAX
                    CASE  "msgbox"
                          FUNCTION = %SE_TOKENCMD_MSGBOX
                    CASE  "mcase"
                          FUNCTION = %SE_TOKENCMD_MCASE
                    CASE  "mid"
                          FUNCTION = %SE_TOKENCMD_MID
                    CASE  "min"
                          FUNCTION = %SE_TOKENCMD_MIN
                    CASE  "mod"
                          FUNCTION = %SE_TOKENCMD_MOD
                    CASE  "next"
                          FUNCTION = %SE_TOKENCMD_NEXT
                    CASE  "not"
                          FUNCTION = %SE_TOKENCMD_NOT
                    CASE  "numbers"
                          FUNCTION = %SE_TOKENCMD_NUMBERS
                    CASE  "lcase"
                          FUNCTION = %SE_TOKENCMD_LCASE
                    CASE  "left"
                          FUNCTION = %SE_TOKENCMD_LEFT
                    CASE  "len"
                          FUNCTION = %SE_TOKENCMD_LEN
                    CASE  "log"
                          FUNCTION = %SE_TOKENCMD_LOG
                    CASE  "log2"
                          FUNCTION = %SE_TOKENCMD_LOG2
                    CASE  "log10"
                          FUNCTION = %SE_TOKENCMD_LOG10
                    CASE  "loop"
                          FUNCTION = %SE_TOKENCMD_LOOP
                    CASE  "let"
                          FUNCTION = %SE_TOKENCMD_LET
                    CASE  "oct"
                          FUNCTION = %SE_TOKENCMD_OCT
                    CASE  "or"
                          FUNCTION = %SE_TOKENCMD_OR
                    CASE  "prglines"
                          FUNCTION = %SE_TOKENCMD_PRGLINES
                    CASE  "pi"
                          FUNCTION = %SE_TOKENCMD_PI
                    CASE "randomize"
                          FUNCTION = %SE_TOKENCMD_RANDOM
                    CASE  "reset"
                          FUNCTION = %SE_TOKENCMD_RESET
                    CASE  "return"
                          FUNCTION = %SE_TOKENCMD_RETURN
                    CASE  "right"
                          FUNCTION = %SE_TOKENCMD_RIGHT
                    CASE  "rnd"
                          FUNCTION = %SE_TOKENCMD_RND
                    CASE  "round"
                          FUNCTION = %SE_TOKENCMD_ROUND
                    CASE  "sgn"
                          FUNCTION = %SE_TOKENCMD_SGN
                    CASE  "space"
                          FUNCTION = %SE_TOKENCMD_SPACE
                    CASE  "sqr"
                          FUNCTION = %SE_TOKENCMD_SQR
                    CASE  "stderr"
                          FUNCTION = %SE_TOKENCMD_STDERR
                    CASE  "step"
                          FUNCTION = %SE_TOKENCMD_STEP
                    CASE  "stdin"
                          FUNCTION = %SE_TOKENCMD_STDIN
                    CASE  "stdout"
                          FUNCTION = %SE_TOKENCMD_STDOUT
                    CASE  "str"
                          FUNCTION = %SE_TOKENCMD_STR
                    CASE  "string"
                          FUNCTION = %SE_TOKENCMD_STRING
                    CASE  "select"
                          FUNCTION = %SE_TOKENCMD_SELECT
                    CASE  "strings"
                          FUNCTION = %SE_TOKENCMD_STRINGS
                    CASE  "tan"
                          FUNCTION = %SE_TOKENCMD_TAN
                    CASE  "time"
                          FUNCTION = %SE_TOKENCMD_TIME
                    CASE  "then"
                          FUNCTION = %SE_TOKENCMD_THEN
                    CASE  "timer"
                          FUNCTION = %SE_TOKENCMD_TIMER
                    CASE  "to"
                          FUNCTION = %SE_TOKENCMD_TO
                    CASE  "trim"
                          FUNCTION = %SE_TOKENCMD_TRIM
                    CASE  "ucase"
                          FUNCTION = %SE_TOKENCMD_UCASE
                    CASE  "ucode"
                          FUNCTION = %SE_TOKENCMD_UCODE
                    CASE  "until"
                          FUNCTION = %SE_TOKENCMD_UNTIL
                    CASE  "val"
                          FUNCTION = %SE_TOKENCMD_VAL
                    CASE  "wend"
                          FUNCTION = %SE_TOKENCMD_WEND
                    CASE  "while"
                          FUNCTION = %SE_TOKENCMD_WHILE
                    CASE  "xor"
                          FUNCTION = %SE_TOKENCMD_XOR
                    CASE  "waitkey"
                          FUNCTION = %SE_TOKENCMD_WAITKEY
                    CASE  "funstr"
                          FUNCTION = %SE_TOKENCMD_FUNSTR
                    CASE  "endif"
                          FUNCTION = %SE_TOKENCMD_ENDIF
                    CASE  "dow"
                          FUNCTION = %SE_TOKENCMD_DOW
                    CASE  "dou"
                          FUNCTION = %SE_TOKENCMD_DOU
                    CASE  "endfun"
                          FUNCTION = %SE_TOKENCMD_ENDFUN
                    CASE  "exitif"
                          FUNCTION = %SE_TOKENCMD_EXITIF
                    CASE  "exitdo"
                          FUNCTION = %SE_TOKENCMD_EXITDO
                    CASE  "exitwhi"
                          FUNCTION = %SE_TOKENCMD_EXITWHI
                    CASE  "exitfor"
                          FUNCTION = %SE_TOKENCMD_EXITFOR
                    CASE  "exitfun"
                          FUNCTION = %SE_TOKENCMD_EXITFUN
                    CASE  "exitsub"
                          FUNCTION = %SE_TOKENCMD_EXITSUB
                    CASE  "exitsel"
                          FUNCTION = %SE_TOKENCMD_EXITSEL
                    CASE  "loopw"
                          FUNCTION = %SE_TOKENCMD_LOOPW
                    CASE  "loopu"
                          FUNCTION = %SE_TOKENCMD_LOOPU
                    CASE  "strreverse"
                          FUNCTION = %SE_TOKENCMD_STRREV
                    CASE  "strdelete"
                          FUNCTION = %SE_TOKENCMD_STRDEL
                    CASE  "strinsert"
                          FUNCTION = %SE_TOKENCMD_STRINS
                    CASE  "max"
                          FUNCTION = %SE_TOKENCMD_MAX
                    CASE  "max$"
                          FUNCTION = %SE_TOKENCMD_MAXS
                    CASE  "min"
                          FUNCTION = %SE_TOKENCMD_MIN
                    CASE  "min$"
                          FUNCTION = %SE_TOKENCMD_MINS
                    CASE  "retain"
                          FUNCTION = %SE_TOKENCMD_RETAIN
                    CASE  "remain"
                          FUNCTION = %SE_TOKENCMD_REMAIN
                    CASE  "remove"
                          FUNCTION = %SE_TOKENCMD_REMOVE
                    CASE  "repeat"
                          FUNCTION = %SE_TOKENCMD_REPEAT
                    CASE  "rtrim"
                          FUNCTION = %SE_TOKENCMD_RTRIM
                    CASE  "join"
                          FUNCTION = %SE_TOKENCMD_JOIN
                    CASE  "nul"
                          FUNCTION = %SE_TOKENCMD_NUL
                    CASE  "environ"
                          FUNCTION = %SE_TOKENCMD_ENVGET
                    CASE  "environ$"
                          FUNCTION = %SE_TOKENCMD_ENVSET
                    CASE  "format"
                          FUNCTION = %SE_TOKENCMD_FORMAT
                    CASE  "extract"
                          FUNCTION = %SE_TOKENCMD_EXTRACT
                    CASE  "tab"
                          FUNCTION = %SE_TOKENCMD_TAB
                    CASE  "print"
                          FUNCTION = %SE_TOKENCMD_PRINT
             END SELECT
    END FUNCTION
    Save this as "se_errors.inc"

    Code:
    %SE_ERROR_FIRST              = 150
    %SE_ERROR_NOERROR            = 0
    %SE_ERROR_IFEXPECTED         = %SE_ERROR_FIRST + 1
    %SE_ERROR_ENDIFEXPECTED      = %SE_ERROR_FIRST + 2
    %SE_ERROR_DOEXPECTED         = %SE_ERROR_FIRST + 3
    %SE_ERROR_LOOPEXPECTED       = %SE_ERROR_FIRST + 4
    %SE_ERROR_WENDEXPECTED       = %SE_ERROR_FIRST + 5
    %SE_ERROR_WHILEEXPECTED      = %SE_ERROR_FIRST + 6
    %SE_ERROR_FOREXPECTED        = %SE_ERROR_FIRST + 7
    %SE_ERROR_NEXTEXPECTED       = %SE_ERROR_FIRST + 8
    %SE_ERROR_STREXPECTED        = %SE_ERROR_FIRST + 9
    %SE_ERROR_NUMEXPECTED        = %SE_ERROR_FIRST + 10
    %SE_ERROR_STATEXPECTED       = %SE_ERROR_FIRST + 11
    %SE_ERROR_TOEXPECTED         = %SE_ERROR_FIRST + 12
    %SE_ERROR_NUMVAREXPECTED     = %SE_ERROR_FIRST + 13
    %SE_ERROR_STRVAREXPECTED     = %SE_ERROR_FIRST + 14
    %SE_ERROR_QUOTEEXPECTED      = %SE_ERROR_FIRST + 15
    %SE_ERROR_PARENEXPECTED      = %SE_ERROR_FIRST + 16
    %SE_ERROR_EXITDOWDO          = %SE_ERROR_FIRST + 17
    %SE_ERROR_EXITFORWFOR        = %SE_ERROR_FIRST + 18
    %SE_ERROR_EXITIFWIF          = %SE_ERROR_FIRST + 19
    %SE_ERROR_EXITWHILEWWHILE    = %SE_ERROR_FIRST + 20
    %SE_ERROR_ASEXPECTED         = %SE_ERROR_FIRST + 21
    %SE_ERROR_BYPASSEXPECTED     = %SE_ERROR_FIRST + 22
    %SE_ERROR_ASTYPEEXPECTED     = %SE_ERROR_FIRST + 23
    %SE_ERROR_FUNNAMEEXPECTED    = %SE_ERROR_FIRST + 24
    %SE_ERROR_NESTEDFUN          = %SE_ERROR_FIRST + 25
    %SE_ERROR_ONLYINFUN          = %SE_ERROR_FIRST + 26
    %SE_ERROR_NOSE_MAIN          = %SE_ERROR_FIRST + 27
    %SE_ERROR_FUNSUBEXPECTED     = %SE_ERROR_FIRST + 28
    
    %SE_ERROR_MISSINGDECL        = %SE_ERROR_FIRST + 30
    %SE_ERROR_ILLEGALNAME        = %SE_ERROR_FIRST + 31
    %SE_ERROR_ALREADYDECL        = %SE_ERROR_FIRST + 32
    %SE_ERROR_DUPLICATEDECL      = %SE_ERROR_FIRST + 33
    %SE_ERROR_MATHMODULE         = %SE_ERROR_FIRST + 34
    %SE_ERROR_FORSTEPZERO        = %SE_ERROR_FIRST + 35
    %SE_ERROR_PLUSEXPECTED       = %SE_ERROR_FIRST + 36
    %SE_ERROR_EXITING            = %SE_ERROR_FIRST + 37
    %SE_ERROR_STARTING           = %SE_ERROR_FIRST + 38
    %SE_ERROR_PARSING            = %SE_ERROR_FIRST + 39
    %SE_ERROR_EXECUTING          = %SE_ERROR_FIRST + 40
    %SE_ERROR_EXPRTOOCOMPLEX     = %SE_ERROR_FIRST + 41
    %SE_ERROR_SYNTAXERROR        = %SE_ERROR_FIRST + 42
    %SE_ERROR_EXITSELWSEL        = %SE_ERROR_FIRST + 43
    %SE_ERROR_ENDFUNEXPECTED     = %SE_ERROR_FIRST + 44
    %SE_ERROR_VAREXPECTED        = %SE_ERROR_FIRST + 45
    %SE_ERROR_PARMISMATCH        = %SE_ERROR_FIRST + 46
    %SE_ERROR_PARCLOSEEXPECTED   = %SE_ERROR_FIRST + 47
    %SE_ERROR_PAROPENEEXPECTED   = %SE_ERROR_FIRST + 48
    %SE_ERROR_OVERFLOW           = %SE_ERROR_FIRST + 49
    %SE_ERROR_UNDEFEQUATE        = %SE_ERROR_FIRST + 50
    
    'takes the main struct containing all the info and returns a descriptive error string
    FUNCTION se_errortext(BYVAL ses AS ses_type PTR) AS STRING
             LOCAL stmp1 AS STRING
             SELECT CASE AS LONG @ses.errcode
                    CASE %SE_ERROR_NOERROR
                         stmp1 = "no error"
                    CASE %SE_ERROR_IFEXPECTED
                         stmp1 = "syntax error 'if' expected"
                    CASE %SE_ERROR_ENDIFEXPECTED
                         stmp1 = "syntax error 'end if' expected"
                    CASE %SE_ERROR_DOEXPECTED
                         stmp1 = "syntax error 'do' without 'loop'"
                    CASE %SE_ERROR_LOOPEXPECTED
                         stmp1 = "syntax error 'loop' without 'do'"
                    CASE %SE_ERROR_WENDEXPECTED
                         stmp1 = "syntax error 'wend' without 'while'"
                    CASE %SE_ERROR_WHILEEXPECTED
                         stmp1 = "syntax error 'while' without 'wend'"
                    CASE %SE_ERROR_FOREXPECTED
                         stmp1 = "syntax error 'for' without 'next'"
                    CASE %SE_ERROR_NEXTEXPECTED
                         stmp1 = "syntax error 'next' without 'for'"
                    CASE %SE_ERROR_STREXPECTED
                         stmp1 = "error string operand expected"
                    CASE %SE_ERROR_NUMEXPECTED
                         stmp1 = "error numeric operand expected"
                    CASE %SE_ERROR_STATEXPECTED
                         stmp1 = "error statment expected"
                    CASE %SE_ERROR_TOEXPECTED
                         stmp1 = "syntax error 'to' expected"
                    CASE %SE_ERROR_NUMVAREXPECTED
                         stmp1 = "error numeric variable expected"
                    CASE %SE_ERROR_STRVAREXPECTED
                         stmp1 = "error string variable expected"
                    CASE %SE_ERROR_QUOTEEXPECTED
                         stmp1 = "error quote expected"
                    CASE %SE_ERROR_PARENEXPECTED
                         stmp1 = "error parenthessis expected"
                    CASE %SE_ERROR_EXITDOWDO
                         stmp1 = "error exit 'do' without 'do'"
                    CASE %SE_ERROR_EXITFORWFOR
                         stmp1 = "error exit 'for' without 'for'"
                    CASE %SE_ERROR_EXITIFWIF
                         stmp1 = "error exit 'if' without 'if'"
                    CASE %SE_ERROR_EXITWHILEWWHILE
                         stmp1 = "error exit 'while' without 'while'"
                    CASE %SE_ERROR_MISSINGDECL
                         stmp1 = "error missing declaration"
                    CASE %SE_ERROR_ILLEGALNAME
                         stmp1 = "error illegal name"
                    CASE %SE_ERROR_ALREADYDECL
                         stmp1 = "error variable already declared"
                    CASE %SE_ERROR_DUPLICATEDECL
                         stmp1 = "error duplicated declaration"
                    CASE %SE_ERROR_MATHMODULE
                         stmp1 = "some obscure math module error happened"
                    CASE %SE_ERROR_FORSTEPZERO
                         stmp1 = "error 'step' can't be zero"
                    CASE %SE_ERROR_PLUSEXPECTED
                         stmp1 = "error '+' expected "
                    CASE %SE_ERROR_EXITING
                         stmp1 = "exiting"
                    CASE %SE_ERROR_STARTING
                         stmp1 = "starting"
                    CASE %SE_ERROR_PARSING
                         stmp1 = "parsing"
                    CASE %SE_ERROR_EXECUTING
                         stmp1 = "executing"
                    CASE %SE_ERROR_EXPRTOOCOMPLEX
                         stmp1 = "expression too complex"
                    CASE %SE_ERROR_SYNTAXERROR
                         stmp1 = "syntax error"
                    CASE %SE_ERROR_EXITSELWSEL
                         stmp1 = "'exit select' w/o 'select'"
                    CASE %SE_ERROR_ENDFUNEXPECTED
                         stmp1 = "'end function' expected"
                    CASE %SE_ERROR_VAREXPECTED
                         stmp1 = "var name expected"
                    CASE %SE_ERROR_PARMISMATCH
                         stmp1 = "parameter mismatch"
                    CASE %SE_ERROR_PARCLOSEEXPECTED
                         stmp1 = "')' expected"
                    CASE %SE_ERROR_PAROPENEEXPECTED
                         stmp1 = "'(' expected"
                    CASE %SE_ERROR_OVERFLOW
                         stmp1 = "overflow"
                    CASE %SE_ERROR_UNDEFEQUATE
                         stmp1 = "undefined equate"
                    CASE %SE_ERROR_BYPASSEXPECTED
                         stmp1 = "'byval/byref/bycopy' expected"
                    CASE %SE_ERROR_ASTYPEEXPECTED
                         stmp1 = "'as long/single/dword/string' expected"
                    CASE %SE_ERROR_FUNNAMEEXPECTED
                         stmp1 = "function name expected"
                    CASE %SE_ERROR_NESTEDFUN
                         stmp1 = "'function/sub' can't be nested"
                    CASE %SE_ERROR_ONLYINFUN
                         stmp1 = "'function=' allowed only in functions"
                    CASE %SE_ERROR_NOSE_MAIN
                         stmp1 = "'se_main' not found..."
                    CASE ELSE
                         stmp1 = "unknown error code"
             END SELECT
    
             FUNCTION = "CT:" + FORMAT$(@ses.pc, "00000") + " LN:" + _
                        FORMAT$(@ses.@ct[@ses.pc].lncnt, "00000") + " SE:" + _
                        FORMAT$(@ses.@ct[@ses.pc].secnt, "00") + " FN:" + _
                        " :" + stmp1 + " [" + FORMAT$(@ses.errcode, "000") + "] "
    END FUNCTION
    
    
    'I GOT THIS ROUTINES FROM THE FORUM, THANKS TO THE AUTHORS
    
    FUNCTION STDOUT (Z AS STRING) AS LONG
    ' returns TRUE (non-zero) on success
       LOCAL hStdOut AS LONG, nCharsWritten AS LONG
       LOCAL w AS STRING
       STATIC CSInitialized AS LONG, CS AS CRITICAL_SECTION
       IF ISFALSE CSInitialized THEN
           InitializeCriticalSection CS
           CSInitialized = 1
       END IF
       EntercriticalSection Cs
       hStdOut = GetStdHandle (%STD_OUTPUT_HANDLE)
       SELECT CASE AS LONG hStdOut
           CASE %NULL, -1&
               AllocConsole
               hStdOut       = GetStdHandle (%STD_OUTPUT_HANDLE)
       END SELECT
       LeaveCriticalSection    CS
       w                     = Z & $CRLF
       FUNCTION              = WriteFile(hStdOut, BYVAL STRPTR(W), LEN(W),  nCharsWritten, BYVAL %NULL)
    END FUNCTION
    
    MACRO dbcprint (stext)
    #IF %DEBUG_FLAG
        stdout stext
    #ENDIF
    END MACRO
    
    FUNCTION stdin() AS STRING
       DIM hInput AS LONG
       DIM iRead AS LONG
       DIM iResult AS LONG
       DIM sBuffer AS STRING
       DIM sOutBuffer AS STRING
       hInput = GetStdHandle(%STD_INPUT_HANDLE)
       IF hInput THEN
          DO
             sBuffer = SPACE$(32000)
             iResult = ReadFile(hInput, BYVAL STRPTR(sBuffer), LEN(sBuffer), iRead, BYVAL %NULL)
             '- If there was an error, return nothing
             IF iResult = 0 THEN
                EXIT DO
             '- We're done if iRead is 0
             ELSEIF iRead = 0 THEN
                EXIT DO
             '- Otherwise, accumulate the buffer
             ELSE
                sOutBuffer = sOutBuffer + LEFT$(sBuffer, iRead)
             END IF
             '- Bail if there's nothing left to read.
             IF iRead < LEN(sBuffer) THEN
                EXIT DO
             END IF
          LOOP
       END IF
       FUNCTION = sOutBuffer
    END FUNCTION
    
    SUB WaitKey()
        LOCAL hStdIn AS DWORD
        LOCAL dwBytesRead AS DWORD
        LOCAL dwConsoleMode AS DWORD
        LOCAL lpBuffer AS INPUT_RECORD
        hStdIn = GetStdHandle(%STD_INPUT_HANDLE)
        IF hStdIn <> %NULL AND hStdIn <> %INVALID_HANDLE_VALUE THEN
            GetConsoleMode(hStdIn, dwConsoleMode)
            SetConsoleMode(hStdIn, %ENABLE_LINE_INPUT OR %ENABLE_ECHO_INPUT OR %ENABLE_PROCESSED_INPUT)
            FlushConsoleInputBuffer(hStdIn)
            dwBytesRead = 0
            DO
                PeekConsoleInput(hStdIn, lpBuffer, 1, dwBytesRead)
                SLEEP 0
            LOOP WHILE dwBytesRead = 0
            FlushConsoleInputBuffer(hStdIn)
            SetConsoleMode(hStdIn, dwConsoleMode)
        END IF
    END SUB
    Save this as "se_engine_demo.bas"

    Code:
    #PBFORMS CREATED V1.51
    '------------------------------------------------------------------------------
    ' The first line in this file is a PB/Forms metastatement.
    ' It should ALWAYS be the first line of the file. Other
    ' PB/Forms metastatements are placed at the beginning and
    ' end of "Named Blocks" of code that should be edited
    ' with PBForms only. Do not manually edit or delete these
    ' metastatements or PB/Forms will not be able to reread
    ' the file correctly.  See the PB/Forms documentation for
    ' more information.
    ' Named blocks begin like this:    #PBFORMS BEGIN ...
    ' Named blocks end like this:      #PBFORMS END ...
    ' Other PB/Forms metastatements such as:
    '     #PBFORMS DECLARATIONS
    ' are used by PB/Forms to insert additional code.
    ' Feel free to make changes anywhere else in the file.
    '------------------------------------------------------------------------------
    
    #COMPILE EXE
    #DIM ALL
    #DEBUG ERROR ON
    #STACK 1024 * 1024 * 4
    #TOOLS OFF
    '------------------------------------------------------------------------------
    '   ** Includes **
    '------------------------------------------------------------------------------
    #PBFORMS BEGIN INCLUDES
    #IF NOT %DEF(%WINAPI)
        #INCLUDE "WIN32API.INC"
    #ENDIF
    #INCLUDE "PBForms.INC"
    #PBFORMS END INCLUDES
    '------------------------------------------------------------------------------
    
    '------------------------------------------------------------------------------
    '   ** Constants **
    '------------------------------------------------------------------------------
    #PBFORMS BEGIN CONSTANTS
    %IDD_DIALOG1          =  101
    %IDC_TxSource         = 1001
    %IDC_txTokenizer      = 1003
    %IDR_MENU1            =  102
    %IDM_FILE_OPEN        = 1008
    %IDM_FILE_SAVE        = 1009
    %IDM_FILE_EXIT        = 1010
    %IDM_EDIT_COPY        = 1011
    %IDM_FILE_NEW         = 1012
    %IDM_RUN_RUN          = 1013
    %IDM_RUN_STOP         = 1016
    %IDM_RUN_DEBUG        = 1017
    %IDM_EDIT_RESETSOURCE = 1018
    %IDM_EDIT_RESETRESULT = 1019
    %IDM_HELP_HELP        = 1020
    %IDM_HELP_ABOUT       = 1021
    %IDD_dlgHelp          =  130
    %IDC_txHelp           = 1030
    %IDM_FILE_SAVEAS      = 1031
    #PBFORMS END CONSTANTS
    '------------------------------------------------------------------------------
    
    '------------------------------------------------------------------------------
    '   ** Declarations **
    '------------------------------------------------------------------------------
    DECLARE CALLBACK FUNCTION ShowDIALOG1Proc()
    DECLARE FUNCTION ShowDIALOG1(BYVAL hParent AS DWORD) AS LONG
    DECLARE FUNCTION AttachMENU1(BYVAL hDlg AS DWORD) AS DWORD
    DECLARE CALLBACK FUNCTION ShowdlgHelpProc()
    DECLARE FUNCTION ShowdlgHelp(BYVAL hDlg AS DWORD) AS LONG
    #PBFORMS DECLARATIONS
    '------------------------------------------------------------------------------
    
    GLOBAL g_hwnd AS LONG
    GLOBAL scriptstring AS STRING
    GLOBAL thstop AS LONG
    GLOBAL DEBUG_FLAG AS LONG
    
    FUNCTION tprint(BYVAL stext AS STRING) AS LONG
        LOCAL stmp1, stmp2, param AS STRING
        CONTROL GET TEXT g_hwnd, %IDC_txTokenizer TO stmp1
        stmp1 = stmp1 + $CRLF + stext
        CONTROL SET TEXT g_hwnd, %IDC_txTokenizer, stmp1
    END FUNCTION
    
    #INCLUDE "se_engine.INC"
    #INCLUDE "comdlg32.inc"
    
    '------------------------------------------------------------------------------
    '   ** Main Application Entry Point **
    '------------------------------------------------------------------------------
    FUNCTION PBMAIN()
        ShowDIALOG1 %HWND_DESKTOP
    END FUNCTION
    '------------------------------------------------------------------------------
    
    '------------------------------------------------------------------------------
    '   ** CallBacks **
    '------------------------------------------------------------------------------
    CALLBACK FUNCTION ShowDIALOG1Proc()
        LOCAL hthread AS DWORD
        LOCAL txt, file, stmp1 AS STRING, frr AS LONG
        STATIC filename AS STRING
        SELECT CASE AS LONG CBMSG
               CASE %WM_INITDIALOG
                    ' Initialization handler
                    g_hwnd = CBHNDL
                    TRY
                      frr = FREEFILE
                      file = "testcode2.txt"
                      OPEN file FOR INPUT AS frr
                      WHILE NOT EOF(frr)
                          LINE INPUT #frr, txt
                          stmp1 = stmp1 + txt + $CRLF
                      WEND
                      CLOSE frr
                      CONTROL SET TEXT CBHNDL, %IDC_TxSource, stmp1
                      filename = file
                      DIALOG SET TEXT CBHNDL, filename
                    CATCH
                    END TRY
                    settimer CBHNDL, 205, 100, %null
               CASE %WM_NCACTIVATE
                    STATIC hWndSaveFocus AS DWORD
                    IF ISFALSE CBWPARAM THEN
                       ' Save control focus
                       hWndSaveFocus = GetFocus()
                    ELSEIF hWndSaveFocus THEN
                       ' Restore control focus
                       SetFocus(hWndSaveFocus)
                       hWndSaveFocus = 0
                    END IF
               CASE %WM_DESTROY
                    thstop = 1
               CASE %WM_TIMER
                    killtimer CBHNDL, 205
                    CONTROL HANDLE CBHNDL, %IDC_TxSource TO frr
                    sendmessage frr, %EM_SETSEL, 0, 0
               CASE %WM_COMMAND
                    ' Process control notifications
                    SELECT CASE AS LONG CBCTL
                        CASE %IDC_TxSource
                             IF CBCTLMSG = %EN_SETFOCUS THEN
                                'sendmessage getdlgitem(g_hwnd, %IDC_TxSource), %EM_SETSEL, 0, 0
                             END IF
                        CASE %IDC_Txtokenizer
                             IF CBCTLMSG = %EN_SETFOCUS THEN
                                sendmessage getdlgitem(g_hwnd, %IDC_TxSource), %WM_SETFOCUS, 0, 0
                             END IF
                        CASE %IDM_FILE_OPEN
                             File = ""
                             txt = "Select a script file"
                             OpenFileDialog (0, txt, File , "", "*.*", "", 0)
                             IF LEN(File) THEN
                                TRY
                                  frr = FREEFILE
                                  OPEN file FOR INPUT AS frr
                                  WHILE NOT EOF(frr)
                                      LINE INPUT #frr, txt
                                      stmp1 = stmp1 + txt + $CRLF
                                  WEND
                                  CLOSE frr
                                  CONTROL SET TEXT CBHNDL, %IDC_TxSource, stmp1
                                  filename = file
                                  DIALOG SET TEXT CBHNDL, filename
                                CATCH
                                END TRY
                             END IF
                        CASE %IDM_FILE_SAVE
                             IF LEN(filename) THEN
                                TRY
                                  frr = FREEFILE
                                  file = filename
                                  OPEN file FOR BINARY AS frr
                                  CONTROL GET TEXT CBHNDL, %IDC_TxSource TO stmp1
                                  SEEK frr, 1
                                  PUT$ frr, stmp1
                                  SETEOF frr
                                CATCH
                                  MSGBOX "Error ocurred while saving " + file
                                END TRY
                                CLOSE frr
                             ELSE
                                DIALOG SEND CBHNDL, %WM_COMMAND, MAK(DWORD, %IDM_FILE_SAVEAS, 0), 0
                             END IF
                        CASE %IDM_FILE_SAVEAS
                             File = ""
                             txt = "Save file"
                             OpenFileDialog (0, txt, File , "", "*.*", "", 0)
                             IF LEN(file) THEN
                                TRY
                                  frr = FREEFILE
                                  OPEN file FOR BINARY AS frr
                                  IF (LOF(frr) > 1) THEN
                                     IF MSGBOX(file + " already exist, overwrite?", %MB_ICONQUESTION OR %MB_YESNO, "Overwrite") = %IDNO THEN
                                        CLOSE frr
                                        EXIT FUNCTION
                                     END IF
                                  END IF
                                  CONTROL GET TEXT CBHNDL, %IDC_TxSource TO stmp1
                                  SEEK frr, 1
                                  PUT$ frr, stmp1
                                  SETEOF frr
                                  filename = file
                                  DIALOG SET TEXT CBHNDL, filename
                                CATCH
                                  MSGBOX "Error ocurred while saving " + file
                                END TRY
                                CLOSE frr
                             END IF
                        CASE %IDM_FILE_EXIT
                             DIALOG END CBHNDL
                        CASE %IDM_EDIT_COPY
                        CASE %IDM_FILE_NEW
                        CASE %IDM_RUN_RUN
                             CONTROL GET TEXT CBHNDL, %IDC_TxSource TO scriptstring
                             THREAD CREATE tokenizer (VARPTR(scriptstring)) TO hthread
                             THREAD CLOSE hthread TO hthread
                        CASE %IDM_RUN_DEBUG
                        CASE %IDM_RUN_STOP
                             thstop = 1
                        CASE %IDM_EDIT_RESETSOURCE
                             CONTROL SET TEXT CBHNDL, %IDC_TxSource, ""
                        CASE %IDM_EDIT_RESETRESULT
                             CONTROL SET TEXT CBHNDL, %IDC_Txtokenizer, ""
                        CASE %IDM_HELP_HELP
                             ShowdlgHelp CBHNDL
                        CASE %IDM_HELP_ABOUT
                    END SELECT
           END SELECT
    END FUNCTION
    
    '------------------------------------------------------------------------------
    
    '------------------------------------------------------------------------------
    '   ** Dialogs **
    '------------------------------------------------------------------------------
    
    FUNCTION ShowDIALOG1(BYVAL hParent AS DWORD) AS LONG
        LOCAL lRslt  AS LONG
    
    #PBFORMS BEGIN DIALOG %IDD_DIALOG1->%IDR_MENU1->
        LOCAL hDlg   AS DWORD
        LOCAL hFont1 AS DWORD
    
        DIALOG NEW hParent, "SE_ENGINE DEMO", 271, 36, 406, 383, %WS_POPUP OR _
            %WS_BORDER OR %WS_DLGFRAME OR %WS_THICKFRAME OR %WS_CAPTION OR _
            %WS_SYSMENU OR %WS_MINIMIZEBOX OR %WS_MAXIMIZEBOX OR _
            %WS_CLIPSIBLINGS OR %WS_VISIBLE OR %DS_MODALFRAME OR %DS_3DLOOK OR _
            %DS_NOFAILCREATE OR %DS_SETFONT, %WS_EX_CONTROLPARENT OR %WS_EX_LEFT _
            OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR, TO hDlg
        CONTROL ADD TEXTBOX, hDlg, %IDC_TxSource, "", 4, 176, 398, 192, %WS_CHILD _
            OR %WS_VISIBLE OR %WS_TABSTOP OR %WS_HSCROLL OR %WS_VSCROLL OR _
            %ES_LEFT OR %ES_MULTILINE OR %ES_AUTOHSCROLL OR %ES_AUTOVSCROLL OR _
            %ES_WANTRETURN, %WS_EX_CLIENTEDGE OR %WS_EX_LEFT OR _
            %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR
        CONTROL SET COLOR    hDlg, %IDC_TxSource, %CYAN, RGB(0, 0, 128)
        CONTROL ADD TEXTBOX, hDlg, %IDC_txTokenizer, "", 4, 4, 398, 168, _
            %WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP OR %WS_HSCROLL OR _
            %WS_VSCROLL OR %ES_LEFT OR %ES_MULTILINE OR %ES_AUTOHSCROLL OR _
            %ES_AUTOVSCROLL, %WS_EX_CLIENTEDGE OR %WS_EX_LEFT OR _
            %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR
        CONTROL SET COLOR    hDlg, %IDC_txTokenizer, %CYAN, RGB(0, 0, 128)
    
        hFont1 = PBFormsMakeFont("Swedish", 8, 700, %FALSE, %FALSE, %FALSE, _
            %ANSI_CHARSET)
    
        CONTROL SEND hDlg, %IDC_TxSource, %WM_SETFONT, hFont1, 0
        CONTROL SEND hDlg, %IDC_txTokenizer, %WM_SETFONT, hFont1, 0
    
        AttachMENU1 hDlg
    #PBFORMS END DIALOG
    
        DIALOG SHOW MODAL hDlg, CALL ShowDIALOG1Proc TO lRslt
    
    #PBFORMS BEGIN CLEANUP %IDD_DIALOG1
        DeleteObject hFont1
    #PBFORMS END CLEANUP
    
        FUNCTION = lRslt
    END FUNCTION
    
    '------------------------------------------------------------------------------
    
    FUNCTION AttachMENU1(BYVAL hDlg AS DWORD) AS DWORD
    #PBFORMS BEGIN MENU %IDR_MENU1->%IDD_DIALOG1
        LOCAL hMenu   AS DWORD
        LOCAL hPopUp1 AS DWORD
    
        MENU NEW BAR TO hMenu
        MENU NEW POPUP TO hPopUp1
        MENU ADD POPUP, hMenu, "File", hPopUp1, %MF_ENABLED
            MENU ADD STRING, hPopUp1, "New", %IDM_FILE_NEW, %MF_ENABLED
            MENU ADD STRING, hPopUp1, "Open", %IDM_FILE_OPEN, %MF_ENABLED
            MENU ADD STRING, hPopUp1, "Save", %IDM_FILE_SAVE, %MF_ENABLED
            MENU ADD STRING, hPopUp1, "Save as...", %IDM_FILE_SAVEAS, %MF_ENABLED
            MENU ADD STRING, hPopUp1, "Exit", %IDM_FILE_EXIT, %MF_ENABLED
        MENU NEW POPUP TO hPopUp1
        MENU ADD POPUP, hMenu, "Edit", hPopUp1, %MF_ENABLED
            MENU ADD STRING, hPopUp1, "Reset source", %IDM_EDIT_RESETSOURCE, _
                %MF_ENABLED
            MENU ADD STRING, hPopUp1, "Reset result", %IDM_EDIT_RESETRESULT, _
                %MF_ENABLED
        MENU NEW POPUP TO hPopUp1
        MENU ADD POPUP, hMenu, "Run", hPopUp1, %MF_ENABLED
            MENU ADD STRING, hPopUp1, "Run", %IDM_RUN_RUN, %MF_ENABLED
            MENU ADD STRING, hPopUp1, "Stop", %IDM_RUN_STOP, %MF_ENABLED
        MENU NEW POPUP TO hPopUp1
        MENU ADD POPUP, hMenu, "Help", hPopUp1, %MF_ENABLED
            MENU ADD STRING, hPopUp1, "Help", %IDM_HELP_HELP, %MF_ENABLED
            MENU ADD STRING, hPopUp1, "About", %IDM_HELP_ABOUT, %MF_ENABLED
    
        MENU ATTACH hMenu, hDlg
    #PBFORMS END MENU
        FUNCTION = hMenu
    END FUNCTION
    
    '------------------------------------------------------------------------------
    
    CALLBACK FUNCTION ShowdlgHelpProc()
        LOCAL frr AS LONG, stmp1, txt AS STRING
        STATIC this AS LONG
        SELECT CASE AS LONG CBMSG
            CASE %WM_INITDIALOG
                ' Initialization handler
                 this = CBHNDL
                 TRY
                   frr = FREEFILE
                   OPEN "se_engine.inc" FOR INPUT AS frr
                   WHILE NOT EOF(frr)
                       LINE INPUT #frr, txt
                       IF INSTR(txt, "#ENDIF") THEN EXIT DO
                       IF ISFALSE(INSTR(txt, "#IF")) THEN
                          stmp1 = stmp1 + MID$(txt, 2) + $CRLF
                       END IF
                   WEND
                   CONTROL SET TEXT CBHNDL, %IDC_Txhelp, stmp1
                 CATCH
                 END TRY
                 CLOSE frr
            CASE %WM_NCACTIVATE
                STATIC hWndSaveFocus AS DWORD
                IF ISFALSE CBWPARAM THEN
                    ' Save control focus
                    hWndSaveFocus = GetFocus()
                ELSEIF hWndSaveFocus THEN
                    ' Restore control focus
                    SetFocus(hWndSaveFocus)
                    hWndSaveFocus = 0
                END IF
            CASE %WM_COMMAND
                ' Process control notifications
                SELECT CASE AS LONG CBCTL
                    CASE %IDC_txHelp
                         IF CBCTLMSG = %EN_SETFOCUS THEN
                            sendmessage getdlgitem(this, %IDC_txHelp), %EM_SETSEL, 0, 0
                         END IF
                END SELECT
        END SELECT
    END FUNCTION
    
    '------------------------------------------------------------------------------
    
    FUNCTION ShowdlgHelp(BYVAL hParent AS DWORD) AS LONG
        LOCAL lRslt  AS LONG
        STATIC already AS LONG
        IF already THEN EXIT FUNCTION
    #PBFORMS BEGIN DIALOG %IDD_dlgHelp->->
        LOCAL hDlg   AS DWORD
        LOCAL hFont1 AS DWORD
    
        DIALOG NEW hParent, "SE Script engine Help", 70, 70, 353, 225, %WS_POPUP _
            OR %WS_BORDER OR %WS_DLGFRAME OR %WS_SYSMENU OR %WS_CLIPSIBLINGS OR _
            %WS_VISIBLE OR %DS_MODALFRAME OR %DS_CENTER OR %DS_3DLOOK OR _
            %DS_NOFAILCREATE OR %DS_SETFONT, %WS_EX_CONTROLPARENT OR _
            %WS_EX_TOOLWINDOW OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR _
            %WS_EX_RIGHTSCROLLBAR, TO hDlg
        CONTROL ADD TEXTBOX, hDlg, %IDC_txHelp, "", 4, 4, 344, 216, %WS_CHILD OR _
            %WS_VISIBLE OR %WS_TABSTOP OR %WS_VSCROLL OR %ES_LEFT OR _
            %ES_MULTILINE OR %ES_AUTOHSCROLL OR %ES_READONLY, %WS_EX_CLIENTEDGE _
            OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR
        CONTROL SET COLOR    hDlg, %IDC_txHelp, %GREEN, %BLACK
    
        hFont1 = PBFormsMakeFont("Arial", 9, 700, %FALSE, %FALSE, %FALSE, _
            %ANSI_CHARSET)
    
        CONTROL SEND hDlg, %IDC_txHelp, %WM_SETFONT, hFont1, 0
    #PBFORMS END DIALOG
        already = 1
        DIALOG SHOW MODAL hDlg, CALL ShowdlgHelpProc TO lRslt
        already = 0
    #PBFORMS BEGIN CLEANUP %IDD_dlgHelp
        DeleteObject hFont1
    #PBFORMS END CLEANUP
    
        FUNCTION = lRslt
    END FUNCTION
    '------------------------------------------------------------------------------
    Last edited by Guest; 9 Nov 2007, 06:53 PM. Reason: Added a link for the compiled demo

    #2
    Sample script

    Sample scripts for se_engine.inc

    Save this as "testcode2.txt"
    The demo will ask for this one.

    Code:
    strings st1, st2, st3
    numbers n1, n2, n3, n4
    let st1 = "eheh1"
    let n1 = 1
    let st2 = "eheh1"
    incr n1
    ? "SE_ENGINE DEMO " + date + " " + left(time, 5)
    ? "start: " + str(timer)
    n3 = timer
    if 2+2=4 then
       for n2=1 to 3
          ? str(n1)
          incr n1
       next
    elseif "2"="4" then
       ? "i shouldn't be here..."
    else
       ? "oops"
    end if
    n4 = timer
    ? "end  : " + str(n4)
    ? "dif  : " + str(n4 - n3)
    ? str(prglines) + " lines of code"

    Comment


      #3
      Gus del Solar,

      FYI - If your looking for a open source interpretive Basic scripting laguage that has 10+ years of maturity behind it, check out http://www.scriptbasic.org and download the source and binaries. (Windows and Linux)

      Nice job on your attempt BTW !

      John

      Comment


        #4
        Hi John,

        I know scriptbasic, a great tool, I also used LUA another great script engine.
        But no other script engine would give me something this one would: lots of fun writing it

        Please post here further comments http://www.powerbasic.com/support/pb...ad.php?t=35463

        Regards, Gus

        Comment

        Working...
        X
        😀
        🥰
        🤢
        😎
        😡
        👍
        👎