FreeBasic interpreter?

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
angros47
Posts: 2329
Joined: Jun 21, 2005 19:04

FreeBasic interpreter?

Post by angros47 »

QuickBasic was made of a compiler (bc.exe) and an interpreter (qb.exe).
The interpreter was, in my opinion, the best debugging instrument I've ever seen. But now, FreeBasic has only the compiler.
About a year ago, I tried to write an interpreter for the Ultrabasic compiler: it's written in QB and is very primitive.
Right now, it supports:
PRINT (to use with numbers, use STR$)
IF....THEN....ELSE
GOTO
SUB (without args...pretty useless)

it supports a decent parsing, integer variables and operator precedence.
I hope it could become a part of a FB-compatible interpreter, one day...but also a scripting engine, if someone need it.

Here is the source, ported to FB
The DATA lines contain a simple test program that will be run by the interpreter.

Code: Select all

DECLARE FUNCTION getoptype (text$)
DECLARE SUB Show.Error (Number%)
DECLARE FUNCTION CND.IF (Cond$)
DECLARE FUNCTION GetCommands (Line1$, Num)
DECLARE SUB Main (Lines, Start)
DECLARE FUNCTION Evaluate (Expression$)
DECLARE FUNCTION GetNum (Arg$, Num)
DECLARE FUNCTION GetPar$ (Arg$)
DECLARE FUNCTION GetString$ (Arg$, Num)
DECLARE FUNCTION GetType (Var$)
DECLARE SUB Precedence (Arg$)
DECLARE SUB SetVar (Var$, Valore$)
DECLARE FUNCTION GetVar (Var$)
DECLARE FUNCTION GetStringVar$ (Var$)
CONST tbyte = 1, tinteger = 2, tlong = 3, tsingle = 4
CONST tdouble = 5, textended = 6, tstring = 7, arraytype = 1, tnullstring = 8
CONST tlongsingle = 9
DIM SHARED codetext$
DIM SHARED VVariable(300)
DIM SHARED VStrings(300) AS STRING
DIM SHARED Variable$(300)
DIM SHARED CurVar





CLS
DIM SHARED Prog$(1000)

DO
  READ c$
  X = GetCommands(c$, 0)
  FOR I = 0 TO X
    IF I > 0 THEN X = GetCommands(c$, I)
    Prog$(Riga) = codetext$
    Riga = Riga + 1
  NEXT
LOOP UNTIL Prog$(Riga - 1) = "END"

Main Riga, 0



DATA "a = 0"
DATA "10"
DATA "A = A + 1"
DATA "if A = 15 then print STR$(999) else print STR$(A): GOTO 10"
DATA "END"

FUNCTION CND.IF (Cond$)
R = 0
Op = 0
FOR I = 1 TO LEN(Cond$)
  t$ = MID$(Cond$, I, 1)
  IF Op = 0 THEN
    P1$ = P1$ + t$
  ELSEIF Op = 1 THEN
    Comp$ = Comp$ + t$
  ELSE
    P2$ = P2$ + t$
  END IF
  IF t$ = " " THEN Op = Op + 1
  

  
  IF Op = 3 THEN 'Comparing....
    IF getoptype(P1$) <> getoptype(P2$) THEN STOP
    IF getoptype(P1$) = 2 THEN
    'For Numbers
      IF RTRIM$(LTRIM$(Comp$)) = "=" THEN
        P1 = Evaluate(P1$)
        P2 = Evaluate(P2$)
        IF P1 = P2 THEN R = -1 ELSE R = 0
      END IF
    END IF

    IF getoptype(P1$) = 1 THEN
    'For Strings
      IF RTRIM$(LTRIM$(Comp$)) = "=" THEN
        PP1$ = GetString$(P1$, 1)
        PP2$ = GetString$(P2$, 1)
        IF PP1$ = PP2$ THEN R = -1 ELSE R = 0
      END IF
    END IF

  END IF


NEXT
CND.IF = R

END FUNCTION

FUNCTION Evaluate (Expression$)
  Res = 0
  Par$ = ""
  E$ = LTRIM$(RTRIM$(Expression$)) + " "
  Precedence E$
  FOR I = 1 TO LEN(E$) + 1
    t$ = MID$(E$, I, 1)
    IF Par <> 0 THEN
      IF t$ = "(" THEN Par = Par + 1
      IF t$ = ")" THEN
        Par = Par - 1
        IF Par = 0 THEN
          Op$ = STR$(Evaluate(Par$))
          Par$ = ""
          t$ = " "
        END IF
      END IF
      Par$ = Par$ + t$

      t$ = ""
    END IF
    IF t$ = "(" THEN Par = Par + 1: t$ = ""
    IF t$ = " " THEN
      IF INSTR("+-*/\", Op$) <> 0 THEN
        Operat$ = Op$
      ELSE
        TT = ASC(UCASE$(LEFT$(Op$, 1)))
        IF TT > 64 AND TT < 91 THEN
          Op = GetVar(Op$)
        ELSE
          Op = VAL(Op$)
        END IF

        SELECT CASE Operat$
        CASE "+"
          Res = Res + Op
        CASE "-"
          Res = Res - Op
        CASE "*"
          Res = Res * Op
        CASE "/"
          Res = Res / Op
        CASE ""
          Res = Op
        END SELECT
      END IF
      
      Op$ = ""
    ELSE
      Op$ = Op$ + t$
    END IF
  NEXT
  Evaluate = Res
END FUNCTION

FUNCTION GetCommands (Line1$, Num)
  codetext$ = Line1$
  IF Line1$ = "" THEN Number = 0: EXIT FUNCTION

  linea$ = LTRIM$(Line1$) + " "
  isThen = 0

  Parsed$ = "": Apices = 0
  first$ = LTRIM$(LEFT$(linea$, INSTR(linea$, " ") - 1))

  IF LEFT$(linea$, 1) = "%" THEN EXIT FUNCTION
  IF UCASE$(first$) = "SUB" OR UCASE$(first$) = "FUNCTION" OR UCASE$(first$) = "DECLARE" THEN EXIT FUNCTION


  IF first$ = LTRIM$(STR$(VAL(first$))) THEN
    Parsed$ = first$ + ":" + CHR$(13)
    Number = Number + 1
    linea$ = MID$(linea$, LEN(first$) + 1)
  END IF

  IF RIGHT$(first$, 1) = ":" THEN
    Parsed$ = first$ + CHR$(13)
    Number = Number + 1
    linea$ = MID$(linea$, LEN(first$) + 1)
  END IF


  FOR I = 1 TO LEN(linea$)
    m$ = MID$(linea$, I, 1): IF J > 0 THEN J = J - 1: m$ = ""

    IF m$ = CHR$(34) THEN Apices = 1 - Apices
    IF Apices = 0 THEN
      IF m$ = ":" THEN m$ = CHR$(13): Number = Number + 1
      IF m$ = "'" THEN EXIT FOR


      IF UCASE$(MID$(linea$, I, 6)) = " THEN " THEN
        J = 4
        m$ = " THEN" + CHR$(13)
        isThen = isThen + 1
      END IF
      IF UCASE$(MID$(linea$, I, 6)) = " ELSE " THEN
        J = 4
        m$ = CHR$(13) + "ELSE" + CHR$(13)
        Number = Number + 2
      END IF

    END IF
    Parsed$ = Parsed$ + m$
  NEXT
  Parsed$ = RTRIM$(Parsed$)
  IF RIGHT$(Parsed$, 1) = CHR$(13) THEN Parsed$ = LEFT$(Parsed$, LEN(Parsed$) - 1)
  IF UCASE$(RIGHT$(Parsed$, 4)) = "THEN" THEN isThen = 0
  DO WHILE isThen
    Parsed$ = Parsed$ + CHR$(13) + "END IF"
    isThen = isThen - 1
    Number = Number + 2
  LOOP
  a = 0
  codetext$ = ""
  FOR I = 1 TO LEN(Parsed$)
    m$ = MID$(Parsed$, I, 1)
    IF m$ = CHR$(13) THEN a = a + 1: m$ = ""
    IF Num = a THEN codetext$ = codetext$ + m$
  NEXT

  GetCommands = Number' - Num


END FUNCTION

FUNCTION GetNum (Arg$, Num)
  I = 1
  FOR a = 1 TO Num
    text$ = ""
    DO
      t$ = MID$(Arg$, I, 1)
    
      I = I + 1
      text$ = text$ + t$
      IF t$ = "," THEN EXIT DO
    LOOP UNTIL I > LEN(Arg$)
  NEXT
  IF text$ = "" THEN text$ = "0"
  'Parse Text$
  GetNum = Evaluate(text$)
END FUNCTION

FUNCTION getoptype (text$)
  t$ = LTRIM$(text$) + " "
  first$ = RTRIM$(LEFT$(t$, INSTR(t$, " ") - 1))
  IF LEFT$(first$, 1) = CHR$(34) THEN getoptype = 1: EXIT FUNCTION
  IF first$ = "0" OR LTRIM$(RTRIM$(first$)) = LTRIM$(RTRIM$(STR$(VAL(first$)))) THEN
    getoptype = 2: EXIT FUNCTION
  ELSE

    FOR I = 1 TO CurVar
      IF UCASE$(first$) = UCASE$(MID$(Variable$(I), 2)) THEN
        tipo = ASC(LEFT$(Variable$(I), 1))
        IF tipo = tstring THEN getoptype = 1 ELSE getoptype = 2
        EXIT FUNCTION
      END IF
    NEXT
  END IF

END FUNCTION

FUNCTION GetPar$ (Arg$)
  Par = 0: Found = 0: Virgolette = 0: GetPar$ = ""
  FOR I = 1 TO LEN(Arg$)
    t$ = MID$(Arg$, I, 1)
    IF t$ = CHR$(34) THEN Virgolette = 1 - Virgolette
    IF Virgolette = 0 THEN
      IF t$ = ")" THEN Par = Par - 1
      IF Par = 0 AND Found = 1 THEN GetPar$ = R$:  'EXIT FOR
      IF Par > 0 THEN R$ = R$ + t$
      IF t$ = " " AND Par = 0 THEN EXIT FOR
      IF t$ = "(" THEN
        IF Found = 1 THEN R$ = R$ + ", "
        Par = Par + 1: Found = 1
      END IF
    END IF
  NEXT
END FUNCTION

FUNCTION GetString$ (Arg$, Num)
  'I = 1
  FOR a = 1 TO Num
    text$ = ""
    DO
      I = I + 1

      t$ = MID$(Arg$, I, 1)
     
      IF t$ = CHR$(34) THEN Virgolette = 1 - Virgolette: t$ = "": X$ = ""
      IF Virgolette THEN
        text$ = text$ + t$
      ELSE
        IF t$ = "," THEN EXIT DO
        IF MID$(Arg$, I, 4) = "STR$" THEN
          I = I + 3
          X$ = "": t$ = ""
          text$ = text$ + LTRIM$(STR$(Evaluate(GetPar$(MID$(Arg$, I)))))
        END IF
        IF MID$(Arg$, I, 4) = "CHR$" THEN
          I = I + 3
          X$ = "": t$ = ""
          text$ = text$ + LTRIM$(CHR$(Evaluate(GetPar$(MID$(Arg$, I)))))
        END IF
        IF t$ = " " THEN
          IF LTRIM$(X$) <> "" THEN text$ = text$ + GetStringVar$(X$)
          X$ = "": t$ = ""
        END IF
        IF t$ = "+" THEN t$ = ""
        IF t$ = "(" THEN Par = Par + 1: t$ = ""
        IF t$ = ")" THEN Par = Par - 1: t$ = ""
        IF Par = 0 THEN X$ = X$ + t$
      END IF
    LOOP UNTIL I >= LEN(Arg$)
  NEXT
  GetString$ = text$
END FUNCTION

FUNCTION GetStringVar$ (Var$)
  FOR I = 1 TO CurVar
    IF UCASE$(Var$) = UCASE$(MID$(Variable$(I), 2)) THEN EXIT FOR
  NEXT
  IF I = CurVar + 1 THEN PRINT "Variabile non definita": END
  GetStringVar$ = VStrings(I)

END FUNCTION

FUNCTION GetType (Var$)
  IF INSTR(Var$, "(") THEN V$ = LEFT$(Var$, INSTR(Var$, "(") - 1) ELSE V$ = Var$
  t$ = RIGHT$(V$, 1)
  SELECT CASE t$
    CASE "$"
      GetType = tstring
    CASE ELSE
      GetType = tlong
  END SELECT
END FUNCTION

FUNCTION GetVar (Var$)
  FOR I = 1 TO CurVar
    IF UCASE$(Var$) = UCASE$(MID$(Variable$(I), 2)) THEN EXIT FOR
  NEXT
  IF I = CurVar + 1 THEN PRINT "Variabile non definita": END
  GetVar = VVariable(I)
END FUNCTION

SUB Main (Lines, Start)
  REDIM Labels(1)
  REDIM Labels$(1)
  I = Start
  DO
    Riga$ = RTRIM$(LTRIM$(Prog$(I)))
    IF RIGHT$(Riga$, 1) = ":" THEN
      Labels$(curlabel) = LEFT$(Riga$, LEN(Riga$) - 1)
      Labels(curlabel) = I
      curlabel = curlabel + 1
      REDIM PRESERVE Labels$(curlabel)
      REDIM PRESERVE Labels(curlabel)
    END IF
    I = I + 1
  LOOP UNTIL I > Lines
  I = Start
  DO
    Riga$ = RTRIM$(LTRIM$(Prog$(I))) + " "
    Comando$ = RTRIM$(UCASE$(LEFT$(Riga$, INSTR(Riga$, " "))))
    Arg$ = LTRIM$(MID$(Riga$, INSTR(Riga$, " ") + 1))
   
   
    SELECT CASE Comando$
    CASE "CLS"
      CLS
    CASE "PRINT"
      PRINT GetString$(Arg$, 1)
    CASE "IF"
      IF RIGHT$(UCASE$(RTRIM$(Arg$)), 4) = "THEN" THEN Arg$ = LEFT$(RTRIM$(Arg$), LEN(RTRIM$(Arg$)) - 4)
      WorkIf = WorkIf + 1
      Res = CND.IF(Arg$)
      IF Res = 0 THEN
        DO
          IF UCASE$(Riga$) = "ELSE" THEN EXIT DO
          IF UCASE$(Riga$) = "END IF" THEN WorkIf = WorkIf + 1: EXIT DO
          I = I + 1
          IF I > Lines THEN Show.Error 2
          Riga$ = RTRIM$(LTRIM$(Prog$(I)))
          

        LOOP
      END IF
    CASE "ELSE"
      IF WorkIf > 0 THEN
        DO
          IF UCASE$(Riga$) = "ELSE" THEN EXIT DO
          IF UCASE$(Riga$) = "END IF" THEN WorkIf = WorkIf + 1: EXIT DO
          I = I + 1
          IF I > Lines THEN Show.Error 2
          Riga$ = RTRIM$(LTRIM$(Prog$(I)))


        LOOP
      ELSE
        Show.Error 3
      END IF

    CASE "GOTO"
      FOR a = 0 TO curlabel
        IF Labels$(a) = RTRIM$(Arg$) THEN I = Labels(a)
      NEXT
    CASE "SUB"
      END
    CASE "END"
      IF UCASE$(RTRIM$(Arg$)) = "SUB" THEN I = Lines + 1
      'END
    CASE ELSE
      IF LEFT$(Arg$, 1) = "=" THEN
        Arg$ = MID$(Arg$, 2)
        SetVar Comando$, Arg$
      ELSE
        FOR a = 0 TO Lines
          IF UCASE$(LEFT$(RTRIM$(LTRIM$(Prog$(a))), 3)) = "SUB" THEN
            Riga$ = UCASE$(RTRIM$(LTRIM$(Prog$(a)))) + " "
            IF RTRIM$(LTRIM$(MID$(Riga$, INSTR(Riga$, " ") + 1))) = Comando$ THEN
              insidesub = insidesub + 1
              Main Lines, a + 1
              insidesub = insidesub - 1
            END IF
          END IF
        NEXT
      END IF
    END SELECT
    I = I + 1
  LOOP UNTIL I > Lines

END SUB

SUB Precedence (Arg$)

Virgolette = 0
Prec$ = " */+-"
text$ = ""
DIM Op$(30)
'DO
'  a$ = getword$(wordtype)
'  IF par$ <> "" THEN a$ = a$ + "(" + par$ + ")"
'  IF a$ = ":" THEN text$ = MID$(codetext$, getcharpos + 1): EXIT DO
'  IF a$ = lineeof THEN EXIT DO
'  op$(R) = a$
'  R = R + 1
'LOOP
FOR I = 1 TO LEN(Arg$)
  t$ = MID$(Arg$, I, 1)
  IF t$ = CHR$(34) THEN Virgolette = 1 - Virgolette
  IF Virgolette = 0 THEN
    IF t$ = "(" THEN Par = Par + 1
    IF t$ = ")" THEN Par = Par - 1
    a$ = a$ + t$
    IF t$ = " " AND Par = 0 THEN
      Op$(R) = RTRIM$(a$)
      a$ = ""
      R = R + 1
    END IF
  END IF
NEXT



I = 1
Operand = 2
DO
  OldOp = Operand
  IF Op$(I) = "" THEN EXIT DO
  Operand = INT(INSTR(Prec$, Op$(I)) / 2)
  IF Operand = 0 THEN Operand = OldOp
  'IF i = 1 THEN OldOp = Operand

  IF OldOp > Operand THEN
    IF I = 1 THEN
      skp = 1
    ELSE
      Op$(I - 1) = "(" + Op$(I - 1)
    END IF
  ELSEIF OldOp < Operand THEN
    IF skp = 0 THEN
      Op$(I - 1) = Op$(I - 1) + ")"
    ELSE
      skp = 0
    END IF
  END IF

  I = I + 2

LOOP UNTIL I > R
Arg$ = ""
FOR I = 0 TO R: Arg$ = Arg$ + Op$(I) + " ": NEXT
Arg$ = RTRIM$(Arg$) + " " + text$
IF Operand = 1 AND skp = 0 THEN Arg$ = Arg$ + ") "
getcharpos = 0

END SUB

SUB SetVar (Var$, Valore$)
  FOR I = 1 TO CurVar
    IF UCASE$(Var$) = UCASE$(MID$(Variable$(I), 2)) THEN EXIT FOR
  NEXT
  IF I = CurVar + 1 THEN
    tipo = GetType(Var$)
    Variable$(I) = CHR$(tipo) + Var$
    CurVar = CurVar + 1
  ELSE
    tipo = ASC(LEFT$(Variable$(I), 1))
  END IF
  SELECT CASE tipo
  CASE tlong
    VVariable(I) = GetNum(Valore$, 1)
  CASE tstring
    VStrings(I) = GetString(Valore$, 1)
  CASE ELSE
  END SELECT
END SUB

SUB Show.Error (Number%)
  IF Number% = 2 THEN PRINT "Block IF whitout END IF"
  IF Number% = 3 THEN PRINT "ELSE whitout IF"
  END
END SUB

garshepp
Posts: 1
Joined: Jul 06, 2005 13:24
Location: Physical Location: Texas

Re: Tips and Tricks, angros47, PostPosted: Jun 23, 2005 18:3

Post by garshepp »

From what I gather the documentation does not exist for the syntax of FB. It seems one can use QB for now for simple programs and port the program to FB.

I want to write math modelling and math games. Of course TI graphing calculators made a lot of that unnecessary with PC Link cables and overhead and video projectors in high school nowdays.

I have some QB programs I want to port to FB. Maybe a conversion script in a utility like LISP or AWK, or in XyWrite (DOS word processor), could be how I'll want to put QB code into FB format, when the documentation is ready. I used such scripts to help TRY to convert QB to Turbo C.

JUST JUNK: I am getting into Linspire Desktop and some LINUX command line, but I use WIN 98 & XP mostly.

It would be interesting to dig up some scripts out of my x86 junk for BASICA spaghetti code and then QB BASIC interpreter subroutines.
jofers
Posts: 1525
Joined: May 27, 2005 17:18

Post by jofers »

FreeBASIC comes with a grammer file in the docs folder.

However, it's fast enough that using a compile+run on an ide is suitable for quick testing. Debugging support is currently being worked on, that I know of.
mjs
Site Admin
Posts: 842
Joined: Jun 16, 2005 19:50
Location: Germany

Post by mjs »

I guess that I'll find the time in a month to create a flex/bison scanner/parser.

Regards,
Mark
etko
Posts: 113
Joined: May 27, 2005 7:55
Location: Slovakia
Contact:

Post by etko »

Couldn't be just actual FB code extended to interpreter form?
angros47
Posts: 2329
Joined: Jun 21, 2005 19:04

Post by angros47 »

This is a newer version....
It supports:
SUBS/FUNCTIONS with parameters, recursion, emulated passing by reference, BYVAL keyword
IF is improved (not only =, but also >,<,>=,<=)
DO-LOOP (for now without parameters9
Var scoping (no shared vars, for now)
FOR-NEXT (STEP will come)
DIM (Yeah! Only one dimension, for now)
INPUT
PRINT will understand if parameter is numeric or string

Remember that lexer is a bit primitive... operators in expressions MUST be space-separated :

PRINT 1 + 1 'Right
PRINT 1+1 'Wrong



Code: Select all

DECLARE SUB GetSubVar (Var$, SubVar$)
DECLARE SUB CreateArray (Var$)
DECLARE SUB SetSubVar (Var$, Valore$)
DECLARE SUB BuildCallSymbol ()
DECLARE FUNCTION GetArgs (Arg$)

TYPE SubFunction
  Name AS STRING * 20
  Typ AS INTEGER    '0=SUB             1=Function
  Args(30) AS INTEGER
  Lines AS INTEGER

END TYPE


DECLARE FUNCTION GetOpType (Text$)
DECLARE SUB Show.Error (Number%)
DECLARE FUNCTION CND.IF (Cond$)
DECLARE FUNCTION GetCommands (Line1$, Num)
DECLARE SUB Main (Start)
DECLARE FUNCTION GetNum (Arg$)
DECLARE FUNCTION GetPar$ (Arg$)
DECLARE FUNCTION GetString$ (Arg$)
DECLARE FUNCTION GetType (Var$)
DECLARE SUB Precedence (Arg$)
DECLARE SUB SetVar (Var$, Valore$)
DECLARE FUNCTION GetVar (Var$)
DECLARE FUNCTION GetStringVar$ (Var$)


CONST tbyte = 1, tinteger = 2, tlong = 3, tsingle = 4
CONST tdouble = 5, textended = 6, tstring = 7, arraytype = 1, tnullstring = 8
CONST tlongsingle = 9

DIM SHARED codetext$
DIM SHARED VVariable(200, 10)
DIM SHARED VStrings(200, 10) AS STRING
DIM SHARED Variable$(200, 10)
DIM SHARED CurVar(10), Lines
DIM SHARED SubRoutines(100) AS SubFunction
DIM SHARED CountSubs, InsideSub
DIM SHARED Params
DIM SHARED Args$(30)


CLS
DIM SHARED Prog$(1000)

DO
  READ c$
  X = GetCommands(c$, 0)
  FOR I = 0 TO X
    IF I > 0 THEN X = GetCommands(c$, I)
    Prog$(Riga) = codetext$
    Riga = Riga + 1
  NEXT
LOOP UNTIL Prog$(Riga - 1) = "END"
Lines = Riga

BuildCallSymbol

Main 0








DATA "for a = 1 to 5"
DATA "for b = 1 to 5"
DATA "print STR$(a) + STR$(b)"
DATA "next"
DATA "next"
DATA "END"


'DATA dim c$(10)
'DATA c$(1) = "prova"
'DATA c$(2) = "prova2"
'DATA c$(3) = "prova3"
'DATA print c$(1 + 1)

'DATA "x$ = raddoppia$(c$(1))"
'DATA x = 15
'DATA print x$
'DATA print c$(1)

'DATA function Raddoppia$ (a$)
'DATA print a$
'DATA a$ = a$ + a$
'DATA raddoppia$ = "pippo"
'DATA end sub



DATA END

SUB BuildCallSymbol
  FOR a = 0 TO Lines
    IF UCASE$(LEFT$(RTRIM$(LTRIM$(Prog$(a))), 3)) = "SUB" OR UCASE$(LEFT$(RTRIM$(LTRIM$(Prog$(a))), 8)) = "FUNCTION" THEN
      Riga$ = UCASE$(RTRIM$(LTRIM$(Prog$(a)))) + " "
      Nome$ = (LTRIM$(MID$(Riga$, INSTR(Riga$, " ") + 1)))
      Arg$ = LTRIM$(RTRIM$(MID$(Nome$, INSTR(Nome$, " "))))
      Nome$ = RTRIM$(LEFT$(Nome$, INSTR(Nome$, " ")))


      SubRoutines(CountSubs).Name = Nome$
      SubRoutines(CountSubs).Typ = 0
      SubRoutines(CountSubs).Lines = a
                          
      Arg$ = GetPar$(Arg$)
      IF LTRIM$(Arg$) <> "" THEN
        Tot = GetArgs(Arg$)
        FOR I = 0 TO Tot
          SubRoutines(CountSubs).Args(I) = GetType(Args$(I))
        NEXT

      ELSE
        SubRoutines(CountSubs).Args(0) = 0
      END IF
      CountSubs = CountSubs + 1
    END IF
  NEXT

END SUB

FUNCTION CND.IF (Cond$)
R = 0
Op = 0
FOR I = 1 TO LEN(Cond$)
  T$ = MID$(Cond$, I, 1)
  IF Op = 0 THEN
    P1$ = P1$ + T$
  ELSEIF Op = 1 THEN
    Comp$ = Comp$ + T$
  ELSE
    P2$ = P2$ + T$
  END IF
  IF T$ = " " THEN Op = Op + 1
  

  
  IF Op = 3 THEN 'Comparing....
    IF GetOpType(P1$) <> GetOpType(P2$) THEN STOP
    IF GetOpType(P1$) = 2 THEN
    'For Numbers
      IF RTRIM$(LTRIM$(Comp$)) = "=" THEN
        P1 = GetNum(P1$)
        P2 = GetNum(P2$)
        IF P1 = P2 THEN R = -1 ELSE R = 0
      END IF
      IF RTRIM$(LTRIM$(Comp$)) = ">" THEN
        P1 = GetNum(P1$)
        P2 = GetNum(P2$)
        IF P1 > P2 THEN R = -1 ELSE R = 0
      END IF
      IF RTRIM$(LTRIM$(Comp$)) = "<" THEN
        P1 = GetNum(P1$)
        P2 = GetNum(P2$)
        IF P1 < P2 THEN R = -1 ELSE R = 0
      END IF
      IF RTRIM$(LTRIM$(Comp$)) = ">=" THEN
        P1 = GetNum(P1$)
        P2 = GetNum(P2$)
        IF P1 >= P2 THEN R = -1 ELSE R = 0
      END IF
      IF RTRIM$(LTRIM$(Comp$)) = "<=" THEN
        P1 = GetNum(P1$)
        P2 = GetNum(P2$)
        IF P1 <= P2 THEN R = -1 ELSE R = 0
      END IF
      IF RTRIM$(LTRIM$(Comp$)) = "<>" THEN
        P1 = GetNum(P1$)
        P2 = GetNum(P2$)
        IF P1 <> P2 THEN R = -1 ELSE R = 0
      END IF
    END IF

    IF GetOpType(P1$) = 1 THEN
    'For Strings
      IF RTRIM$(LTRIM$(Comp$)) = "=" THEN
        PP1$ = GetString$(P1$)
        PP2$ = GetString$(P2$)
        IF PP1$ = PP2$ THEN R = -1 ELSE R = 0
      END IF
    END IF

  END IF


NEXT
CND.IF = R

END FUNCTION

SUB CreateArray (Var$)

  IF INSTR(Var$, "(") = 0 THEN SetVar Var$, "": EXIT SUB

  I = CurVar(InsideSub) + 1
  Tipo = GetType(LEFT$(Var$, INSTR(Var$, "(") - 1))
  Variable$(I, InsideSub) = CHR$(Tipo + 64) + LEFT$(Var$, INSTR(Var$, "(") - 1)
  Par$ = GetPar$(Var$)
  CurVar(InsideSub) = CurVar(InsideSub) + GetNum(Par$)

END SUB

FUNCTION GetArgs (Arg$)
  G = 0
  Par = 0: Found = 0: Virgolette = 0:
  FOR I = 1 TO LEN(Arg$)
    T$ = MID$(Arg$, I, 1)
    IF T$ = CHR$(34) THEN Virgolette = 1 - Virgolette
    IF Virgolette = 0 THEN
      IF T$ = ")" THEN Par = Par - 1
      'IF T$ = " " AND Par = 0 THEN EXIT FOR
      IF T$ = "(" THEN Par = Par + 1
      IF T$ = "," AND Par = 0 THEN Args$(G) = R$: R$ = "": T$ = "": G = G + 1
    END IF
    R$ = R$ + T$

  NEXT
  Args$(G) = LTRIM$(RTRIM$(R$))
  GetArgs = G


END FUNCTION

FUNCTION GetCommands (Line1$, Num)
  codetext$ = Line1$
  IF Line1$ = "" THEN Number = 0: EXIT FUNCTION

  linea$ = LTRIM$(Line1$) + " "
  isThen = 0

  Parsed$ = "": Apices = 0
  First$ = LTRIM$(LEFT$(linea$, INSTR(linea$, " ") - 1))

  IF LEFT$(linea$, 1) = "%" THEN EXIT FUNCTION
  IF UCASE$(First$) = "SUB" OR UCASE$(First$) = "FUNCTION" OR UCASE$(First$) = "DECLARE" THEN EXIT FUNCTION


  IF First$ = LTRIM$(STR$(VAL(First$))) THEN
    Parsed$ = First$ + ":" + CHR$(13)
    Number = Number + 1
    linea$ = MID$(linea$, LEN(First$) + 1)
  END IF

  IF RIGHT$(First$, 1) = ":" THEN
    Parsed$ = First$ + CHR$(13)
    Number = Number + 1
    linea$ = MID$(linea$, LEN(First$) + 1)
  END IF


  FOR I = 1 TO LEN(linea$)
    m$ = MID$(linea$, I, 1): IF J > 0 THEN J = J - 1: m$ = ""

    IF m$ = CHR$(34) THEN Apices = 1 - Apices
    IF Apices = 0 THEN
      IF m$ = ":" THEN m$ = CHR$(13): Number = Number + 1
      IF m$ = "'" THEN EXIT FOR


      IF UCASE$(MID$(linea$, I, 6)) = " THEN " THEN
        J = 4
        m$ = " THEN" + CHR$(13)
        isThen = isThen + 1
      END IF
      IF UCASE$(MID$(linea$, I, 6)) = " ELSE " THEN
        J = 4
        m$ = CHR$(13) + "ELSE" + CHR$(13)
        Number = Number + 2
      END IF

    END IF
    Parsed$ = Parsed$ + m$
  NEXT
  Parsed$ = RTRIM$(Parsed$)
  IF RIGHT$(Parsed$, 1) = CHR$(13) THEN Parsed$ = LEFT$(Parsed$, LEN(Parsed$) - 1)
  IF UCASE$(RIGHT$(Parsed$, 4)) = "THEN" THEN isThen = 0
  DO WHILE isThen
    Parsed$ = Parsed$ + CHR$(13) + "END IF"
    isThen = isThen - 1
    Number = Number + 2
  LOOP
  a = 0
  codetext$ = ""
  FOR I = 1 TO LEN(Parsed$)
    m$ = MID$(Parsed$, I, 1)
    IF m$ = CHR$(13) THEN a = a + 1: m$ = ""
    IF Num = a THEN codetext$ = codetext$ + m$
  NEXT

  GetCommands = Number' - Num


END FUNCTION

FUNCTION GetNum (Expression$)
  Res = 0
  Par$ = ""
  E$ = LTRIM$(RTRIM$(Expression$)) + " "
  Precedence E$
  FOR I = 1 TO LEN(E$) + 1
    T$ = MID$(E$, I, 1)
    IF Par <> 0 THEN
      IF T$ = "(" THEN Par = Par + 1
      IF T$ = ")" THEN
        Par = Par - 1
        IF Par = 0 AND LTRIM$(Op$) = "" THEN
          Op$ = STR$(GetNum(Par$))
          Par$ = ""
          
        END IF
        IF Par = 0 THEN T$ = " "
      END IF
      Par$ = Par$ + T$

      T$ = ""
    END IF
    IF T$ = "(" THEN Par = Par + 1: T$ = ""
    IF T$ = " " THEN
      IF INSTR("+-*/\", Op$) <> 0 THEN
        Operat$ = Op$
      ELSE
        TT = ASC(UCASE$(LEFT$(Op$, 1)))
        IF TT > 64 AND TT < 91 THEN
          a = 0
          DO UNTIL a = CountSubs
            IF RTRIM$(SubRoutines(a).Name) = UCASE$(Op$) THEN
              InsideSub = InsideSub + 1
              CurVar(InsideSub) = 0
              Params = GetArgs(Par$)
              Main SubRoutines(a).Lines + 1
              Op = GetVar(Op$)

              Params = GetArgs(Par$)
              Riga$ = UCASE$(RTRIM$(LTRIM$(Prog$(SubRoutines(a).Lines)))) + " "
              Nome$ = (LTRIM$(MID$(Riga$, INSTR(Riga$, " ") + 1)))
              Arg$ = LTRIM$(RTRIM$(MID$(Nome$, INSTR(Nome$, " "))))
              REDIM R$(Params)
              FOR a = 0 TO Params
                R$(a) = LTRIM$(Args$(a))
              NEXT
              IF Params > GetArgs(GetPar(Arg$)) THEN Show.Error 1
              FOR a = 0 TO Params
                GetSubVar R$(a), Args$(a)
              NEXT
              InsideSub = InsideSub - 1
              a = CountSubs + 1
              EXIT DO
            END IF
            a = a + 1
          LOOP
          IF Par$ <> "" THEN Op$ = Op$ + "(" + Par$ + ")"
          IF a = CountSubs THEN Op = GetVar(Op$) ' Is a variable?
        ELSE
          Op = VAL(Op$)
        END IF

        SELECT CASE Operat$
        CASE "+"
          Res = Res + Op
        CASE "-"
          Res = Res - Op
        CASE "*"
          Res = Res * Op
        CASE "/"
          Res = Res / Op
        CASE ""
          Res = Op
        END SELECT
      END IF
      
      Op$ = ""
    ELSE
      Op$ = Op$ + T$
    END IF
  NEXT
  GetNum = Res
END FUNCTION

FUNCTION GetOpType (Text$)
  T$ = LTRIM$(Text$) + " "
  First$ = RTRIM$(LEFT$(T$, INSTR(T$, " ") - 1))
  IF LEFT$(First$, 1) = CHR$(34) THEN GetOpType = 1: EXIT FUNCTION
  IF First$ = "0" OR LTRIM$(RTRIM$(First$)) = LTRIM$(RTRIM$(STR$(VAL(First$)))) THEN
    GetOpType = 2: EXIT FUNCTION
  ELSE

  IF INSTR(First$, "(") THEN First$ = LEFT$(First$, INSTR(First$, "(") - 1)       'Is Array?
    FOR I = 1 TO CurVar(InsideSub)
      IF UCASE$(First$) = UCASE$(MID$(Variable$(I, InsideSub), 2)) THEN
        Tipo = ASC(LEFT$(Variable$(I, InsideSub), 1))
        IF Tipo > 64 THEN Tipo = Tipo - 64
        IF Tipo = tstring THEN GetOpType = 1 ELSE GetOpType = 2
        EXIT FUNCTION
      END IF
    NEXT
  END IF
  IF RIGHT$(First$, 1) = "$" THEN GetOpType = 1 ELSE GetOpType = 2
END FUNCTION

FUNCTION GetPar$ (Arg$)
  Par = 0: Found = 0: Virgolette = 0: GetPar$ = ""
  FOR I = 1 TO LEN(Arg$)
    T$ = MID$(Arg$, I, 1)
    IF T$ = CHR$(34) THEN Virgolette = 1 - Virgolette
    IF Virgolette = 0 THEN
      IF T$ = ")" THEN Par = Par - 1
      IF Par = 0 AND Found = 1 THEN GetPar$ = R$:  'EXIT FOR
      IF Par > 0 THEN R$ = R$ + T$
      IF T$ = " " AND Par = 0 THEN EXIT FOR
      IF T$ = "(" THEN
        IF Found = 1 AND Par = 0 THEN R$ = R$ + ", "
        Par = Par + 1: Found = 1
      END IF
    END IF
  NEXT
END FUNCTION

FUNCTION GetString$ (Arg$)
  'I = 1
'  FOR a = 1 TO Num
    Text$ = ""
    DO
      I = I + 1

      T$ = MID$(Arg$, I, 1)
     
      IF T$ = CHR$(34) THEN Virgolette = 1 - Virgolette: T$ = "": X$ = ""
      IF Virgolette THEN
        Text$ = Text$ + T$
      ELSE
        IF T$ = "," AND Par = 0 THEN EXIT DO

        IF MID$(Arg$, I, 4) = "STR$" THEN
          I = I + 3
          X$ = "": T$ = ""
          Text$ = Text$ + LTRIM$(STR$(GetNum(GetPar$(MID$(Arg$, I)))))
          DO
            I = I + 1
            IF MID$(Arg$, I, 1) = "(" THEN Par = Par + 1
            IF MID$(Arg$, I, 1) = ")" THEN Par = Par - 1
          LOOP UNTIL Par = 0
        END IF

        IF MID$(Arg$, I, 4) = "CHR$" THEN
          I = I + 3
          X$ = "": T$ = ""
          Text$ = Text$ + LTRIM$(CHR$(GetNum(GetPar$(MID$(Arg$, I)))))
          DO
            I = I + 1
            IF MID$(Arg$, I, 1) = "(" THEN Par = Par + 1
            IF MID$(Arg$, I, 1) = ")" THEN Par = Par - 1
          LOOP UNTIL Par = 0
        END IF

        IF T$ = " " AND Par = 0 THEN
          IF LTRIM$(X$) <> "" THEN
            IF INSTR(X$, "(") THEN
              Op$ = LEFT$(X$, INSTR(X$, "(") - 1)
              Par$ = MID$(X$, INSTR(X$, "("))
            END IF
            a = 0
            DO UNTIL a = CountSubs
              IF RTRIM$(SubRoutines(a).Name) = UCASE$(Op$) THEN
                InsideSub = InsideSub + 1
                CurVar(InsideSub) = 0
                Params = GetArgs(Par$)
                Main SubRoutines(a).Lines + 1
                Text$ = Text$ + GetStringVar$(Op$)

                Params = GetArgs(GetPar$(Par$))
                Riga$ = UCASE$(RTRIM$(LTRIM$(Prog$(SubRoutines(a).Lines)))) + " "
                Nome$ = (LTRIM$(MID$(Riga$, INSTR(Riga$, " ") + 1)))
                Arg$ = LTRIM$(RTRIM$(MID$(Nome$, INSTR(Nome$, " "))))
                REDIM R$(Params)
                FOR a = 0 TO Params
                  R$(a) = LTRIM$(Args$(a))
                NEXT
                IF Params > GetArgs(GetPar(Arg$)) THEN Show.Error 1
                FOR a = 0 TO Params
                  GetSubVar R$(a), Args$(a)
                NEXT
                InsideSub = InsideSub - 1
                a = CountSubs + 1

                EXIT DO
              END IF
              a = a + 1
            LOOP
            IF a = CountSubs THEN Text$ = Text$ + GetStringVar$(X$)
          END IF
          X$ = "": T$ = ""
        END IF
        IF T$ = "+" AND Par = 0 THEN T$ = ""
        IF T$ = "(" THEN Par = Par + 1: ' T$ = ""
        IF T$ = ")" THEN Par = Par - 1: ' T$ = ""
        X$ = X$ + T$
      END IF
    LOOP UNTIL I >= LEN(Arg$)
'  NEXT
  GetString$ = Text$
END FUNCTION

FUNCTION GetStringVar$ (Var$)
  IF INSTR(Var$, "(") THEN              'Is Array?
    FOR I = 1 TO CurVar(InsideSub)
      IF UCASE$(LEFT$(Var$, INSTR(Var$, "(") - 1)) = UCASE$(MID$(Variable$(I, InsideSub), 2)) THEN EXIT FOR
    NEXT
    IF I = CurVar(InsideSub) + 1 THEN Show.Error 4
    Par$ = GetPar$(Var$)
    I = I + GetNum(Par$)
    GetStringVar$ = VStrings(I, InsideSub)
  ELSE                                  'Is not an array
    FOR I = 1 TO CurVar(InsideSub)
      IF UCASE$(Var$) = UCASE$(MID$(Variable$(I, InsideSub), 2)) THEN EXIT FOR
    NEXT
    IF I = CurVar(InsideSub) + 1 THEN PRINT "Variabile non definita": END
    GetStringVar$ = VStrings(I, InsideSub)
  END IF
END FUNCTION

SUB GetSubVar (Var$, SubVar$)

  IF UCASE$(LEFT$(LTRIM$(SubVar$), 6)) = "BYVAL " THEN EXIT SUB

  IF INSTR(Var$, "(") THEN              'Is Array?
    FOR I = 1 TO CurVar(InsideSub - 1)
      IF UCASE$(LEFT$(Var$, INSTR(Var$, "(") - 1)) = UCASE$(MID$(Variable$(I, InsideSub - 1), 2)) THEN EXIT FOR
    NEXT
    IF I = CurVar(InsideSub - 1) + 1 THEN EXIT SUB
    Par$ = GetPar$(Var$)
    Tipo = ASC(LEFT$(Variable$(I, InsideSub - 1), 1)) - 64
    I = I + GetNum(Par$)
  ELSE                                  'Is not an array
    FOR I = 1 TO CurVar(InsideSub - 1)
      IF UCASE$(Var$) = UCASE$(MID$(Variable$(I, InsideSub - 1), 2)) THEN EXIT FOR
    NEXT
    IF I = CurVar(InsideSub - 1) + 1 THEN EXIT SUB
    Tipo = ASC(LEFT$(Variable$(I, InsideSub - 1), 1))
  END IF

  SELECT CASE Tipo
  CASE tlong
    VVariable(I, InsideSub - 1) = GetVar(SubVar$)
  CASE tstring
    VStrings(I, InsideSub - 1) = GetStringVar$(SubVar$)
  CASE ELSE
  END SELECT


END SUB

FUNCTION GetType (Var$)
  IF INSTR(Var$, "(") THEN V$ = LEFT$(Var$, INSTR(Var$, "(") - 1) ELSE V$ = Var$
  T$ = RIGHT$(V$, 1)
  SELECT CASE T$
    CASE "$"
      GetType = tstring
    CASE ELSE
      GetType = tlong
  END SELECT
END FUNCTION

FUNCTION GetVar (Var$)
  IF INSTR(Var$, "(") THEN              'Is Array?
    FOR I = 1 TO CurVar(InsideSub)
      IF UCASE$(LEFT$(Var$, INSTR(Var$, "(") - 1)) = UCASE$(MID$(Variable$(I, InsideSub), 2)) THEN EXIT FOR
    NEXT
    IF I = CurVar(InsideSub) + 1 THEN Show.Error 4
    Par$ = GetPar$(Var$)
    I = I + GetNum(Par$)
    GetVar = VVariable(I, InsideSub)
  ELSE                                  'Is not an array
    FOR I = 1 TO CurVar(InsideSub)
      IF UCASE$(Var$) = UCASE$(MID$(Variable$(I, InsideSub), 2)) THEN EXIT FOR
    NEXT
    IF I = CurVar(InsideSub) + 1 THEN PRINT "Variabile non definita": END
    GetVar = VVariable(I, InsideSub)
  END IF
END FUNCTION

SUB Main (Start)
  REDIM Labels(1)
  REDIM Labels$(1)
  REDIM LoopsIndex(1)
  I = Start
  DO
    Riga$ = RTRIM$(LTRIM$(Prog$(I)))
    IF RIGHT$(Riga$, 1) = ":" THEN
      Labels$(curlabel) = LEFT$(Riga$, LEN(Riga$) - 1)
      Labels(curlabel) = I
      curlabel = curlabel + 1
      REDIM PRESERVE Labels$(curlabel)
      REDIM PRESERVE Labels(curlabel)
    END IF
    I = I + 1
  LOOP UNTIL I > Lines
  I = Start

  IF InsideSub > 0 THEN
    Riga$ = UCASE$(RTRIM$(LTRIM$(Prog$(I - 1)))) + " "
    Nome$ = (LTRIM$(MID$(Riga$, INSTR(Riga$, " ") + 1)))
    Arg$ = LTRIM$(RTRIM$(MID$(Nome$, INSTR(Nome$, " "))))
    REDIM R$(Params)
    FOR a = 0 TO Params
      R$(a) = LTRIM$(Args$(a))
    NEXT
    IF Params > GetArgs(GetPar(Arg$)) THEN Show.Error 1
    FOR a = 0 TO Params
      SetSubVar Args$(a), R$(a) + " "
    NEXT
  END IF

  DO
    Riga$ = RTRIM$(LTRIM$(Prog$(I))) + " "
    Comando$ = RTRIM$(UCASE$(LEFT$(Riga$, INSTR(Riga$, " "))))
    Arg$ = LTRIM$(MID$(Riga$, INSTR(Riga$, " ") + 1))
   
   
    SELECT CASE Comando$

    CASE "DIM"
      CreateArray Arg$


    CASE "CLS"
      CLS
    CASE "INPUT"
      a = GetArgs(Arg$)
      IF a = 1 THEN PRINT Args$(0);
      INPUT tmp$
      SetVar RTRIM$(Args$(a)), CHR$(34) + tmp$ + CHR$(34)
    CASE "PRINT"
      IF GetOpType(Arg$) = 1 THEN PRINT GetString$(Arg$) ELSE PRINT GetNum(Arg$)

    CASE "IF"
      IF RIGHT$(UCASE$(RTRIM$(Arg$)), 4) = "THEN" THEN Arg$ = LEFT$(RTRIM$(Arg$), LEN(RTRIM$(Arg$)) - 4)
      WorkIf = WorkIf + 1: NewWorkIf = WorkIf
      Res = CND.IF(Arg$)
      IF Res = 0 THEN
        DO
          IF UCASE$(Riga$) = "ELSE" AND NewWorkIf = WorkIf THEN EXIT DO
          IF UCASE$(Riga$) = "END IF" THEN NewWorkIf = NewWorkIf - 1: IF NewWorkIf < WorkIf THEN WorkIf = WorkIf - 1: EXIT DO
          I = I + 1
          IF I > Lines THEN Show.Error 2
          Riga$ = RTRIM$(LTRIM$(Prog$(I)))
          Comando$ = RTRIM$(UCASE$(LEFT$(Riga$, INSTR(Riga$, " "))))
          IF Comando$ = "IF" THEN NewWorkIf = NewWorkIf + 1
        LOOP
      END IF
    CASE "ELSE"
      IF WorkIf > 0 THEN
        NewWorkIf = WorkIf
        DO
          'IF UCASE$(Riga$) = "ELSE" AND NewWorkIf = WorkIf THEN EXIT DO
          IF UCASE$(Riga$) = "END IF" THEN NewWorkIf = NewWorkIf - 1: IF NewWorkIf < WorkIf THEN WorkIf = WorkIf - 1: EXIT DO
          I = I + 1
          IF I > Lines THEN Show.Error 2
          Riga$ = RTRIM$(LTRIM$(Prog$(I)))
          Comando$ = RTRIM$(UCASE$(LEFT$(Riga$, INSTR(Riga$, " "))))
          IF Comando$ = "IF" THEN NewWorkIf = NewWorkIf + 1
        LOOP
      ELSE
        Show.Error 3
      END IF


    CASE "GOTO"
      WorkIf = 0
      FOR a = 0 TO curlabel
        IF Labels$(a) = RTRIM$(Arg$) THEN I = Labels(a)
      NEXT

    CASE "DO"
      LoopsStack = LoopsStack + 1
      REDIM PRESERVE LoopsIndex(LoopsStack)
      LoopsIndex(LoopsStack) = I
    CASE "LOOP"
      IF LoopsStack = 0 THEN Show.Error 10
      I = LoopsIndex(LoopsStack)

    CASE "FOR"
      LoopsStack = LoopsStack + 1
      REDIM PRESERVE LoopsIndex(LoopsStack)
      LoopsIndex(LoopsStack) = I
      ForLoop$ = LEFT$(Arg$, INSTR(UCASE$(Arg$), " TO "))

      Comando$ = RTRIM$(UCASE$(LEFT$(ForLoop$, INSTR(ForLoop$, " "))))
      Arg$ = LTRIM$(MID$(ForLoop$, INSTR(ForLoop$, "=") + 1))
      SetVar Comando$, Arg$
    CASE "NEXT"
      IF LoopsStack = 0 THEN Show.Error 12
      'I = LoopsIndex(LoopsStack)

      Riga$ = UCASE$(RTRIM$(LTRIM$(Prog$(LoopsIndex(LoopsStack)))))

      Comando$ = MID$(Riga$, INSTR(Riga$, " ") + 1)
      Comando$ = RTRIM$(LTRIM$(LEFT$(Comando$, INSTR(Comando$, "=") - 1)))
      Arg$ = RTRIM$(LTRIM$(MID$(Riga$, INSTR(Riga$, " TO ") + 4)))
      SetVar Comando$, Comando$ + " + 1"
      IF GetNum(Comando$) <= GetNum(Arg$) THEN I = LoopsIndex(LoopsStack) ELSE LoopsStack = LoopsStack - 1


    CASE "SUB", "FUNCTION"
      END
    CASE "END"
      IF UCASE$(RTRIM$(Arg$)) = "SUB" OR UCASE$(RTRIM$(Arg$)) = "FUNCTION" THEN I = Lines + 1
      IF UCASE$(RTRIM$(Arg$)) = "IF" THEN WorkIf = WorkIf - 1

      'END
    CASE ELSE
      IF LEFT$(Arg$, 1) = "=" THEN
        Arg$ = MID$(Arg$, 2)
        SetVar Comando$, Arg$
      ELSE
        a = 0
        DO UNTIL a = CountSubs
          IF RTRIM$(SubRoutines(a).Name) = Comando$ THEN
              InsideSub = InsideSub + 1
              CurVar(InsideSub) = 0
              Params = GetArgs(Arg$)
              Main SubRoutines(a).Lines + 1

              Params = GetArgs(Arg$)
              Riga$ = UCASE$(RTRIM$(LTRIM$(Prog$(SubRoutines(a).Lines)))) + " "
              Nome$ = (LTRIM$(MID$(Riga$, INSTR(Riga$, " ") + 1)))
              Arg$ = LTRIM$(RTRIM$(MID$(Nome$, INSTR(Nome$, " "))))
              REDIM R$(Params)
              FOR a = 0 TO Params
                R$(a) = LTRIM$(Args$(a))
              NEXT
              IF Params > GetArgs(GetPar(Arg$)) THEN Show.Error 1
              FOR a = 0 TO Params
                GetSubVar R$(a), Args$(a)
              NEXT
              a = CountSubs - 1
              InsideSub = InsideSub - 1

          END IF
          a = a + 1
        LOOP
      END IF
    END SELECT
    I = I + 1
  LOOP UNTIL I > Lines
  IF WorkIf > 0 THEN Show.Error 2
  IF LoopsStack > 0 THEN Show.Error 11
END SUB

SUB Precedence (Arg$)

Virgolette = 0
Prec$ = " */+-"
Text$ = ""
DIM Op$(30)
'DO
'  a$ = getword$(wordtype)
'  IF par$ <> "" THEN a$ = a$ + "(" + par$ + ")"
'  IF a$ = ":" THEN text$ = MID$(codetext$, getcharpos + 1): EXIT DO
'  IF a$ = lineeof THEN EXIT DO
'  op$(R) = a$
'  R = R + 1
'LOOP
FOR I = 1 TO LEN(Arg$)
  T$ = MID$(Arg$, I, 1)
  IF T$ = CHR$(34) THEN Virgolette = 1 - Virgolette
  IF Virgolette = 0 THEN
    IF T$ = "(" THEN Par = Par + 1
    IF T$ = ")" THEN Par = Par - 1
    a$ = a$ + T$
    IF T$ = " " AND Par = 0 THEN
      Op$(R) = RTRIM$(a$)
      a$ = ""
      R = R + 1
    END IF
  END IF
NEXT



I = 1
Operand = 2
DO
  OldOp = Operand
  IF Op$(I) = "" THEN EXIT DO
  Operand = INT(INSTR(Prec$, Op$(I)) / 2)
  IF Operand = 0 THEN Operand = OldOp
  'IF i = 1 THEN OldOp = Operand

  IF OldOp > Operand THEN
    IF I = 1 THEN
      skp = 1
    ELSE
      Op$(I - 1) = "(" + Op$(I - 1)
    END IF
  ELSEIF OldOp < Operand THEN
    IF skp = 0 THEN
      Op$(I - 1) = Op$(I - 1) + ")"
    ELSE
      skp = 0
    END IF
  END IF

  I = I + 2

LOOP UNTIL I > R
Arg$ = ""
FOR I = 0 TO R: Arg$ = Arg$ + Op$(I) + " ": NEXT
Arg$ = RTRIM$(Arg$) + " " + Text$
IF Operand = 1 AND skp = 0 THEN Arg$ = Arg$ + ") "
getcharpos = 0

END SUB

SUB SetSubVar (Var$, Valore$)
  IF UCASE$(LEFT$(LTRIM$(Var$), 6)) = "BYVAL " THEN Var$ = UCASE$(MID$(LTRIM$(Var$), 7))
  Tipo = GetType(Var$)
  CurVar(InsideSub) = CurVar(InsideSub) + 1
  I = CurVar(InsideSub)
  Variable$(I, InsideSub) = CHR$(Tipo) + Var$

  InsideSub = InsideSub - 1

  SELECT CASE Tipo
  CASE tlong
    VVariable(I, InsideSub + 1) = GetNum(Valore$)
  CASE tstring
    VStrings(I, InsideSub + 1) = GetString(Valore$)
  CASE ELSE
  END SELECT

  InsideSub = InsideSub + 1

END SUB

SUB SetVar (Var$, Valore$)
  IF INSTR(Var$, "(") THEN              'Is Array?
    FOR I = 1 TO CurVar(InsideSub)
      IF UCASE$(LEFT$(Var$, INSTR(Var$, "(") - 1)) = UCASE$(MID$(Variable$(I, InsideSub), 2)) THEN EXIT FOR
    NEXT
    IF I = CurVar(InsideSub) + 1 THEN Show.Error 4
    Tipo = ASC(LEFT$(Variable$(I, InsideSub), 1)) - 64

    Par$ = GetPar$(Var$)
    I = I + GetNum(Par$)
  ELSE                                  'In not an array

    FOR I = 1 TO CurVar(InsideSub)
      IF UCASE$(Var$) = UCASE$(MID$(Variable$(I, InsideSub), 2)) THEN EXIT FOR
    NEXT
    IF I = CurVar(InsideSub) + 1 THEN
      Tipo = GetType(Var$)
      Variable$(I, InsideSub) = CHR$(Tipo) + Var$
      CurVar(InsideSub) = CurVar(InsideSub) + 1
    ELSE
      Tipo = ASC(LEFT$(Variable$(I, InsideSub), 1))
    END IF
  END IF

  SELECT CASE Tipo
  CASE tlong
    VVariable(I, InsideSub) = GetNum(Valore$)
  CASE tstring
    VStrings(I, InsideSub) = GetString(Valore$)
  CASE ELSE
  END SELECT
END SUB

SUB Show.Error (Number%)
  IF Number% = 2 THEN PRINT "Block IF whitout END IF"
  IF Number% = 3 THEN PRINT "ELSE without IF"
  IF Number% = 4 THEN PRINT "Array not defined"
  IF Number% = 10 THEN PRINT "LOOP without DO"
  IF Number% = 11 THEN PRINT "FOR without NEXT"
  IF Number% = 12 THEN PRINT "NEXT without FOR"

  END
END SUB

Jack
Posts: 6
Joined: Aug 26, 2005 23:40

Post by Jack »

may i ask that you license it under the zlib license?
it's short and simple and you don't need a law degree to understand it (ie LGPL).
The zlib/libpng License
Copyright (c) <year> <copyright holders>

This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.

Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.

2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.

3. This notice may not be removed or altered from any source distribution.
angros47
Posts: 2329
Joined: Jun 21, 2005 19:04

Post by angros47 »

This program is an alpha version and is a bit buggy (only a bit? :-P)
Of course you can use it in your apps, if you add this as first line:

Code: Select all

' Basic Interpreter Engine written by Angelo Rosina, 2003 - 2005
If you made any change to the engine, you have to let me know it. Besides that, conditions of zlib license could be OK.

My goal was to write an IDE like QB for Windows, but I'm not skilled in Windows API. I made a try using RapidQ and its examples, but it crashes too often, so I'm not going to release it.
Jack
Posts: 6
Joined: Aug 26, 2005 23:40

Post by Jack »

thanks angros47 :)
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Post by D.J.Peters »

hi angros47,
nice work useable for an simple script engine too.

Joshy
Oz
Posts: 586
Joined: Jul 02, 2005 14:21
Location: Waterloo, Ontario, Canada
Contact:

Post by Oz »

not to make you annoyed, but it would be easier if you used a database lookup system, as opposed to doing things like:

Code: Select all

IF UCASE$(MID$(linea$, I, 6)) = " THEN " THEN
freebasic has mysql support, if i'm correct...you could easily store alot more commands, and minimize code severly

oz~
angros47
Posts: 2329
Joined: Jun 21, 2005 19:04

Post by angros47 »

I'm not used to SQL, anyway I start writind this interpreter in 2003 when I was working at UltraBasic. The source code was in QB and I'm continuing developing it in QB, because the Ide and the debug support are excellent.
Using SQL would require BIG changes to the Main function (it works with a simple SELECT ... CASE, it's not too a mess, I think) and would lose multiplatform features. Instead, I'm going to use only internal functions for a better portability to other languages.

About the "ugly" syntax you noticed, I only used it when I need to check a token that must be near another (IF ... THEN, FOR ... NEXT). These are particular cases and should not be a problem.
I hate syntax like:

Code: Select all

CONST TokenThen="THEN"
IF C$=TokenThen THEN ....
When I can use:
IF C$="THEN" THEN
A database engine would decrease readibility, IMHO

Oh yeah, in some day I'll post a version with SELECT .... CASE
I haven't a lot of time, but I'll try adding some other features...
angros47
Posts: 2329
Joined: Jun 21, 2005 19:04

Post by angros47 »

The last version....

Supporting SELECT . ... CASE
Fixed a bug in command likes
a(3 + 1) = 8


Code: Select all

' Basic Interpreter Engine written by Angelo Rosina, 2003 - 2005 
DECLARE SUB GetSubVar (Var$, SubVar$)
DECLARE SUB CreateArray (Var$)
DECLARE SUB SetSubVar (Var$, Valore$)
DECLARE SUB BuildCallSymbol ()
DECLARE FUNCTION GetArgs (Arg$)

TYPE SubFunction
  Name AS STRING * 20
  Typ AS INTEGER    '0=SUB             1=Function
  Args(30) AS INTEGER
  Lines AS INTEGER

END TYPE


DECLARE FUNCTION GetOpType (Text$)
DECLARE SUB Show.Error (Number%)
DECLARE FUNCTION CND.IF (Cond$)
DECLARE FUNCTION GetCommands (Line1$, Num)
DECLARE SUB Main (Start)
DECLARE FUNCTION GetNum (Arg$)
DECLARE FUNCTION GetPar$ (Arg$)
DECLARE FUNCTION GetString$ (Arg$)
DECLARE FUNCTION GetType (Var$)
DECLARE SUB Precedence (Arg$)
DECLARE SUB SetVar (Var$, Valore$)
DECLARE FUNCTION GetVar (Var$)
DECLARE FUNCTION GetStringVar$ (Var$)


CONST tbyte = 1, tinteger = 2, tlong = 3, tsingle = 4
CONST tdouble = 5, textended = 6, tstring = 7, arraytype = 1, tnullstring = 8
CONST tlongsingle = 9

DIM SHARED codetext$
DIM SHARED VVariable(200, 10)
DIM SHARED VStrings(200, 10) AS STRING
DIM SHARED Variable$(200, 10)
DIM SHARED CurVar(10), Lines
DIM SHARED SubRoutines(100) AS SubFunction
DIM SHARED CountSubs, InsideSub
DIM SHARED Params
DIM SHARED Args$(30)


CLS
DIM SHARED Prog$(1000)

DO
  READ c$
  X = GetCommands(c$, 0)
  FOR I = 0 TO X
    IF I > 0 THEN X = GetCommands(c$, I)
    Prog$(Riga) = codetext$
    Riga = Riga + 1
  NEXT
LOOP UNTIL Prog$(Riga - 1) = "END"
Lines = Riga

BuildCallSymbol

Main 0







DATA "a = 1 + 0"
DATA "Select case a"
DATA "case 1"
DATA "print 1"
DATA "case 2"
DATA "print 2"
DATA "end select"
DATA "END"


SUB BuildCallSymbol
  FOR a = 0 TO Lines
    IF UCASE$(LEFT$(RTRIM$(LTRIM$(Prog$(a))), 3)) = "SUB" OR UCASE$(LEFT$(RTRIM$(LTRIM$(Prog$(a))), 8)) = "FUNCTION" THEN
      Riga$ = UCASE$(RTRIM$(LTRIM$(Prog$(a)))) + " "
      Nome$ = (LTRIM$(MID$(Riga$, INSTR(Riga$, " ") + 1)))
      Arg$ = LTRIM$(RTRIM$(MID$(Nome$, INSTR(Nome$, " "))))
      Nome$ = RTRIM$(LEFT$(Nome$, INSTR(Nome$, " ")))


      SubRoutines(CountSubs).Name = Nome$
      SubRoutines(CountSubs).Typ = 0
      SubRoutines(CountSubs).Lines = a
                          
      Arg$ = GetPar$(Arg$)
      IF LTRIM$(Arg$) <> "" THEN
        Tot = GetArgs(Arg$)
        FOR I = 0 TO Tot
          SubRoutines(CountSubs).Args(I) = GetType(Args$(I))
        NEXT

      ELSE
        SubRoutines(CountSubs).Args(0) = 0
      END IF
      CountSubs = CountSubs + 1
    END IF
  NEXT

END SUB

FUNCTION CND.IF (Cond$)
R = 0
Op = 0
FOR I = 1 TO LEN(Cond$)
  T$ = MID$(Cond$, I, 1)
  IF Op = 0 THEN
    P1$ = P1$ + T$
  ELSEIF Op = 1 THEN
    Comp$ = Comp$ + T$
  ELSE
    P2$ = P2$ + T$
  END IF
  IF T$ = " " THEN Op = Op + 1
  

  
  IF Op = 3 THEN 'Comparing....
    IF GetOpType(P1$) <> GetOpType(P2$) THEN STOP
    IF GetOpType(P1$) = 2 THEN
    'For Numbers
      IF RTRIM$(LTRIM$(Comp$)) = "=" THEN
        P1 = GetNum(P1$)
        P2 = GetNum(P2$)
        IF P1 = P2 THEN R = -1 ELSE R = 0
      END IF
      IF RTRIM$(LTRIM$(Comp$)) = ">" THEN
        P1 = GetNum(P1$)
        P2 = GetNum(P2$)
        IF P1 > P2 THEN R = -1 ELSE R = 0
      END IF
      IF RTRIM$(LTRIM$(Comp$)) = "<" THEN
        P1 = GetNum(P1$)
        P2 = GetNum(P2$)
        IF P1 < P2 THEN R = -1 ELSE R = 0
      END IF
      IF RTRIM$(LTRIM$(Comp$)) = ">=" THEN
        P1 = GetNum(P1$)
        P2 = GetNum(P2$)
        IF P1 >= P2 THEN R = -1 ELSE R = 0
      END IF
      IF RTRIM$(LTRIM$(Comp$)) = "<=" THEN
        P1 = GetNum(P1$)
        P2 = GetNum(P2$)
        IF P1 <= P2 THEN R = -1 ELSE R = 0
      END IF
      IF RTRIM$(LTRIM$(Comp$)) = "<>" THEN
        P1 = GetNum(P1$)
        P2 = GetNum(P2$)
        IF P1 <> P2 THEN R = -1 ELSE R = 0
      END IF
    END IF

    IF GetOpType(P1$) = 1 THEN
    'For Strings
      IF RTRIM$(LTRIM$(Comp$)) = "=" THEN
        PP1$ = GetString$(P1$)
        PP2$ = GetString$(P2$)
        IF PP1$ = PP2$ THEN R = -1 ELSE R = 0
      END IF
    END IF

  END IF


NEXT
CND.IF = R

END FUNCTION

SUB CreateArray (Var$)

  IF INSTR(Var$, "(") = 0 THEN SetVar Var$, "": EXIT SUB

  I = CurVar(InsideSub) + 1
  Tipo = GetType(LEFT$(Var$, INSTR(Var$, "(") - 1))
  Variable$(I, InsideSub) = CHR$(Tipo + 64) + LEFT$(Var$, INSTR(Var$, "(") - 1)
  Par$ = GetPar$(Var$)
  CurVar(InsideSub) = CurVar(InsideSub) + GetNum(Par$)

END SUB

FUNCTION GetArgs (Arg$)
  G = 0
  Par = 0: Found = 0: Virgolette = 0:
  FOR I = 1 TO LEN(Arg$)
    T$ = MID$(Arg$, I, 1)
    IF T$ = CHR$(34) THEN Virgolette = 1 - Virgolette
    IF Virgolette = 0 THEN
      IF T$ = ")" THEN Par = Par - 1
      'IF T$ = " " AND Par = 0 THEN EXIT FOR
      IF T$ = "(" THEN Par = Par + 1
      IF T$ = "," AND Par = 0 THEN Args$(G) = R$: R$ = "": T$ = "": G = G + 1
    END IF
    R$ = R$ + T$

  NEXT
  Args$(G) = LTRIM$(RTRIM$(R$))
  GetArgs = G


END FUNCTION

FUNCTION GetCommands (Line1$, Num)
  codetext$ = Line1$
  IF Line1$ = "" THEN Number = 0: EXIT FUNCTION

  linea$ = LTRIM$(Line1$) + " "
  isThen = 0

  Parsed$ = "": Apices = 0
  First$ = LTRIM$(LEFT$(linea$, INSTR(linea$, " ") - 1))

  IF LEFT$(linea$, 1) = "%" THEN EXIT FUNCTION
  IF UCASE$(First$) = "SUB" OR UCASE$(First$) = "FUNCTION" OR UCASE$(First$) = "DECLARE" THEN EXIT FUNCTION


  IF First$ = LTRIM$(STR$(VAL(First$))) THEN
    Parsed$ = First$ + ":" + CHR$(13)
    Number = Number + 1
    linea$ = MID$(linea$, LEN(First$) + 1)
  END IF

  IF RIGHT$(First$, 1) = ":" THEN
    Parsed$ = First$ + CHR$(13)
    Number = Number + 1
    linea$ = MID$(linea$, LEN(First$) + 1)
  END IF


  FOR I = 1 TO LEN(linea$)
    m$ = MID$(linea$, I, 1): IF J > 0 THEN J = J - 1: m$ = ""

    IF m$ = CHR$(34) THEN Apices = 1 - Apices
    IF Apices = 0 THEN
      IF m$ = ":" THEN m$ = CHR$(13): Number = Number + 1
      IF m$ = "'" THEN EXIT FOR


      IF UCASE$(MID$(linea$, I, 6)) = " THEN " THEN
        J = 4
        m$ = " THEN" + CHR$(13)
        isThen = isThen + 1
      END IF
      IF UCASE$(MID$(linea$, I, 6)) = " ELSE " THEN
        J = 4
        m$ = CHR$(13) + "ELSE" + CHR$(13)
        Number = Number + 2
      END IF

    END IF
    Parsed$ = Parsed$ + m$
  NEXT
  Parsed$ = RTRIM$(Parsed$)
  IF RIGHT$(Parsed$, 1) = CHR$(13) THEN Parsed$ = LEFT$(Parsed$, LEN(Parsed$) - 1)
  IF UCASE$(RIGHT$(Parsed$, 4)) = "THEN" THEN isThen = 0
  DO WHILE isThen
    Parsed$ = Parsed$ + CHR$(13) + "END IF"
    isThen = isThen - 1
    Number = Number + 2
  LOOP
  a = 0
  codetext$ = ""
  FOR I = 1 TO LEN(Parsed$)
    m$ = MID$(Parsed$, I, 1)
    IF m$ = CHR$(13) THEN a = a + 1: m$ = ""
    IF Num = a THEN codetext$ = codetext$ + m$
  NEXT

  GetCommands = Number' - Num


END FUNCTION

FUNCTION GetNum (Expression$)
  Res = 0
  Par$ = ""
  E$ = LTRIM$(RTRIM$(Expression$)) + " "
  Precedence E$
  FOR I = 1 TO LEN(E$) + 1
    T$ = MID$(E$, I, 1)
    IF Par <> 0 THEN
      IF T$ = "(" THEN Par = Par + 1
      IF T$ = ")" THEN
        Par = Par - 1
        IF Par = 0 AND LTRIM$(Op$) = "" THEN
          Op$ = STR$(GetNum(Par$))
          Par$ = ""
          
        END IF
        IF Par = 0 THEN T$ = " "
      END IF
      Par$ = Par$ + T$

      T$ = ""
    END IF
    IF T$ = "(" THEN Par = Par + 1: T$ = ""
    IF T$ = " " THEN
      IF INSTR("+-*/\", Op$) <> 0 THEN
        Operat$ = Op$
      ELSE
        TT = ASC(UCASE$(LEFT$(Op$, 1)))
        IF TT > 64 AND TT < 91 THEN
          a = 0
          DO UNTIL a = CountSubs
            IF RTRIM$(SubRoutines(a).Name) = UCASE$(Op$) THEN
              InsideSub = InsideSub + 1
              CurVar(InsideSub) = 0
              Params = GetArgs(Par$)
              Main SubRoutines(a).Lines + 1
              Op = GetVar(Op$)

              Params = GetArgs(Par$)
              Riga$ = UCASE$(RTRIM$(LTRIM$(Prog$(SubRoutines(a).Lines)))) + " "
              Nome$ = (LTRIM$(MID$(Riga$, INSTR(Riga$, " ") + 1)))
              Arg$ = LTRIM$(RTRIM$(MID$(Nome$, INSTR(Nome$, " "))))
              REDIM R$(Params)
              FOR a = 0 TO Params
                R$(a) = LTRIM$(Args$(a))
              NEXT
              IF Params > GetArgs(GetPar(Arg$)) THEN Show.Error 1
              FOR a = 0 TO Params
                GetSubVar R$(a), Args$(a)
              NEXT
              InsideSub = InsideSub - 1
              a = CountSubs + 1
              EXIT DO
            END IF
            a = a + 1
          LOOP
          IF Par$ <> "" THEN Op$ = Op$ + "(" + Par$ + ")"
          IF a = CountSubs THEN Op = GetVar(Op$) ' Is a variable?
        ELSE
          Op = VAL(Op$)
        END IF

        SELECT CASE Operat$
        CASE "+"
          Res = Res + Op
        CASE "-"
          Res = Res - Op
        CASE "*"
          Res = Res * Op
        CASE "/"
          Res = Res / Op
        CASE ""
          Res = Op
        END SELECT
      END IF
      
      Op$ = ""
    ELSE
      Op$ = Op$ + T$
    END IF
  NEXT
  GetNum = Res
END FUNCTION

FUNCTION GetOpType (Text$)
  T$ = LTRIM$(Text$) + " "
  First$ = RTRIM$(LEFT$(T$, INSTR(T$, " ") - 1))
  IF LEFT$(First$, 1) = CHR$(34) THEN GetOpType = 1: EXIT FUNCTION
  IF First$ = "0" OR LTRIM$(RTRIM$(First$)) = LTRIM$(RTRIM$(STR$(VAL(First$)))) THEN
    GetOpType = 2: EXIT FUNCTION
  ELSE

  IF INSTR(First$, "(") THEN First$ = LEFT$(First$, INSTR(First$, "(") - 1)       'Is Array?
    FOR I = 1 TO CurVar(InsideSub)
      IF UCASE$(First$) = UCASE$(MID$(Variable$(I, InsideSub), 2)) THEN
        Tipo = ASC(LEFT$(Variable$(I, InsideSub), 1))
        IF Tipo > 64 THEN Tipo = Tipo - 64
        IF Tipo = tstring THEN GetOpType = 1 ELSE GetOpType = 2
        EXIT FUNCTION
      END IF
    NEXT
  END IF
  IF RIGHT$(First$, 1) = "$" THEN GetOpType = 1 ELSE GetOpType = 2
END FUNCTION

FUNCTION GetPar$ (Arg$)
  Par = 0: Found = 0: Virgolette = 0: GetPar$ = ""
  FOR I = 1 TO LEN(Arg$)
    T$ = MID$(Arg$, I, 1)
    IF T$ = CHR$(34) THEN Virgolette = 1 - Virgolette
    IF Virgolette = 0 THEN
      IF T$ = ")" THEN Par = Par - 1
      IF Par = 0 AND Found = 1 THEN GetPar$ = R$:  'EXIT FOR
      IF Par > 0 THEN R$ = R$ + T$
      IF T$ = " " AND Par = 0 THEN EXIT FOR
      IF T$ = "(" THEN
        IF Found = 1 AND Par = 0 THEN R$ = R$ + ", "
        Par = Par + 1: Found = 1
      END IF
    END IF
  NEXT
END FUNCTION

FUNCTION GetString$ (Arg$)
  'I = 1
'  FOR a = 1 TO Num
    Text$ = ""
    DO
      I = I + 1

      T$ = MID$(Arg$, I, 1)
     
      IF T$ = CHR$(34) THEN Virgolette = 1 - Virgolette: T$ = "": X$ = ""
      IF Virgolette THEN
        Text$ = Text$ + T$
      ELSE
        IF T$ = "," AND Par = 0 THEN EXIT DO

        IF MID$(Arg$, I, 4) = "STR$" THEN
          I = I + 3
          X$ = "": T$ = ""
          Text$ = Text$ + LTRIM$(STR$(GetNum(GetPar$(MID$(Arg$, I)))))
          DO
            I = I + 1
            IF MID$(Arg$, I, 1) = "(" THEN Par = Par + 1
            IF MID$(Arg$, I, 1) = ")" THEN Par = Par - 1
          LOOP UNTIL Par = 0
        END IF

        IF MID$(Arg$, I, 4) = "CHR$" THEN
          I = I + 3
          X$ = "": T$ = ""
          Text$ = Text$ + LTRIM$(CHR$(GetNum(GetPar$(MID$(Arg$, I)))))
          DO
            I = I + 1
            IF MID$(Arg$, I, 1) = "(" THEN Par = Par + 1
            IF MID$(Arg$, I, 1) = ")" THEN Par = Par - 1
          LOOP UNTIL Par = 0
        END IF

        IF T$ = " " AND Par = 0 THEN
          IF LTRIM$(X$) <> "" THEN
            IF INSTR(X$, "(") THEN
              Op$ = LEFT$(X$, INSTR(X$, "(") - 1)
              Par$ = MID$(X$, INSTR(X$, "("))
            END IF
            a = 0
            DO UNTIL a = CountSubs
              IF RTRIM$(SubRoutines(a).Name) = UCASE$(Op$) THEN
                InsideSub = InsideSub + 1
                CurVar(InsideSub) = 0
                Params = GetArgs(Par$)
                Main SubRoutines(a).Lines + 1
                Text$ = Text$ + GetStringVar$(Op$)

                Params = GetArgs(GetPar$(Par$))
                Riga$ = UCASE$(RTRIM$(LTRIM$(Prog$(SubRoutines(a).Lines)))) + " "
                Nome$ = (LTRIM$(MID$(Riga$, INSTR(Riga$, " ") + 1)))
                Arg$ = LTRIM$(RTRIM$(MID$(Nome$, INSTR(Nome$, " "))))
                REDIM R$(Params)
                FOR a = 0 TO Params
                  R$(a) = LTRIM$(Args$(a))
                NEXT
                IF Params > GetArgs(GetPar(Arg$)) THEN Show.Error 1
                FOR a = 0 TO Params
                  GetSubVar R$(a), Args$(a)
                NEXT
                InsideSub = InsideSub - 1
                a = CountSubs + 1

                EXIT DO
              END IF
              a = a + 1
            LOOP
            IF a = CountSubs THEN Text$ = Text$ + GetStringVar$(X$)
          END IF
          X$ = "": T$ = ""
        END IF
        IF T$ = "+" AND Par = 0 THEN T$ = ""
        IF T$ = "(" THEN Par = Par + 1: ' T$ = ""
        IF T$ = ")" THEN Par = Par - 1: ' T$ = ""
        X$ = X$ + T$
      END IF
    LOOP UNTIL I >= LEN(Arg$)
'  NEXT
  GetString$ = Text$
END FUNCTION

FUNCTION GetStringVar$ (Var$)
  IF INSTR(Var$, "(") THEN              'Is Array?
    FOR I = 1 TO CurVar(InsideSub)
      IF UCASE$(LEFT$(Var$, INSTR(Var$, "(") - 1)) = UCASE$(MID$(Variable$(I, InsideSub), 2)) THEN EXIT FOR
    NEXT
    IF I = CurVar(InsideSub) + 1 THEN Show.Error 4
    Par$ = GetPar$(Var$)
    I = I + GetNum(Par$)
    GetStringVar$ = VStrings(I, InsideSub)
  ELSE                                  'Is not an array
    FOR I = 1 TO CurVar(InsideSub)
      IF UCASE$(Var$) = UCASE$(MID$(Variable$(I, InsideSub), 2)) THEN EXIT FOR
    NEXT
    IF I = CurVar(InsideSub) + 1 THEN PRINT "Variabile non definita": END
    GetStringVar$ = VStrings(I, InsideSub)
  END IF
END FUNCTION

SUB GetSubVar (Var$, SubVar$)

  IF UCASE$(LEFT$(LTRIM$(SubVar$), 6)) = "BYVAL " THEN EXIT SUB

  IF INSTR(Var$, "(") THEN              'Is Array?
    FOR I = 1 TO CurVar(InsideSub - 1)
      IF UCASE$(LEFT$(Var$, INSTR(Var$, "(") - 1)) = UCASE$(MID$(Variable$(I, InsideSub - 1), 2)) THEN EXIT FOR
    NEXT
    IF I = CurVar(InsideSub - 1) + 1 THEN EXIT SUB
    Par$ = GetPar$(Var$)
    Tipo = ASC(LEFT$(Variable$(I, InsideSub - 1), 1)) - 64
    I = I + GetNum(Par$)
  ELSE                                  'Is not an array
    FOR I = 1 TO CurVar(InsideSub - 1)
      IF UCASE$(Var$) = UCASE$(MID$(Variable$(I, InsideSub - 1), 2)) THEN EXIT FOR
    NEXT
    IF I = CurVar(InsideSub - 1) + 1 THEN EXIT SUB
    Tipo = ASC(LEFT$(Variable$(I, InsideSub - 1), 1))
  END IF

  SELECT CASE Tipo
  CASE tlong
    VVariable(I, InsideSub - 1) = GetVar(SubVar$)
  CASE tstring
    VStrings(I, InsideSub - 1) = GetStringVar$(SubVar$)
  CASE ELSE
  END SELECT


END SUB

FUNCTION GetType (Var$)
  IF INSTR(Var$, "(") THEN V$ = LEFT$(Var$, INSTR(Var$, "(") - 1) ELSE V$ = Var$
  T$ = RIGHT$(V$, 1)
  SELECT CASE T$
    CASE "$"
      GetType = tstring
    CASE ELSE
      GetType = tlong
  END SELECT
END FUNCTION

FUNCTION GetVar (Var$)
  IF INSTR(Var$, "(") THEN              'Is Array?
    FOR I = 1 TO CurVar(InsideSub)
      IF UCASE$(LEFT$(Var$, INSTR(Var$, "(") - 1)) = UCASE$(MID$(Variable$(I, InsideSub), 2)) THEN EXIT FOR
    NEXT
    IF I = CurVar(InsideSub) + 1 THEN Show.Error 4
    Par$ = GetPar$(Var$)
    I = I + GetNum(Par$)
    GetVar = VVariable(I, InsideSub)
  ELSE                                  'Is not an array
    FOR I = 1 TO CurVar(InsideSub)
      IF UCASE$(Var$) = UCASE$(MID$(Variable$(I, InsideSub), 2)) THEN EXIT FOR
    NEXT
    IF I = CurVar(InsideSub) + 1 THEN PRINT "Variabile non definita": END
    GetVar = VVariable(I, InsideSub)
  END IF
END FUNCTION

SUB Main (Start)
  REDIM Labels(1)
  REDIM Labels$(1)
  REDIM LoopsIndex(1)
  I = Start
  DO
    Riga$ = RTRIM$(LTRIM$(Prog$(I)))
    IF RIGHT$(Riga$, 1) = ":" THEN
      Labels$(curlabel) = LEFT$(Riga$, LEN(Riga$) - 1)
      Labels(curlabel) = I
      curlabel = curlabel + 1
      REDIM PRESERVE Labels$(curlabel)
      REDIM PRESERVE Labels(curlabel)
    END IF
    I = I + 1
  LOOP UNTIL I > Lines
  I = Start

  IF InsideSub > 0 THEN
    Riga$ = UCASE$(RTRIM$(LTRIM$(Prog$(I - 1)))) + " "
    Nome$ = (LTRIM$(MID$(Riga$, INSTR(Riga$, " ") + 1)))
    Arg$ = LTRIM$(RTRIM$(MID$(Nome$, INSTR(Nome$, " "))))
    REDIM R$(Params)
    FOR a = 0 TO Params
      R$(a) = LTRIM$(Args$(a))
    NEXT
    IF Params > GetArgs(GetPar(Arg$)) THEN Show.Error 1
    FOR a = 0 TO Params
      SetSubVar Args$(a), R$(a) + " "
    NEXT
  END IF

  DO
    Riga$ = RTRIM$(LTRIM$(Prog$(I))) + " "
    Comando$ = RTRIM$(UCASE$(LEFT$(Riga$, INSTR(Riga$, " "))))
    Arg$ = LTRIM$(MID$(Riga$, INSTR(Riga$, " ") + 1))
   
   
    SELECT CASE Comando$

    CASE "DIM"
      CreateArray Arg$


    CASE "CLS"
      CLS
    CASE "INPUT"
      a = GetArgs(Arg$)
      IF a = 1 THEN PRINT Args$(0);
      INPUT tmp$
      SetVar RTRIM$(Args$(a)), CHR$(34) + tmp$ + CHR$(34)
    CASE "PRINT"
      IF GetOpType(Arg$) = 1 THEN PRINT GetString$(Arg$) ELSE PRINT GetNum(Arg$)
    CASE "LOCATE"
      a = GetArgs(Arg$)
      LOCATE GetNum(Args$(0)), GetNum(Args$(1))

    CASE "IF"
      IF RIGHT$(UCASE$(RTRIM$(Arg$)), 4) = "THEN" THEN Arg$ = LEFT$(RTRIM$(Arg$), LEN(RTRIM$(Arg$)) - 4)
      WorkIf = WorkIf + 1: NewWorkIf = WorkIf
      Res = CND.IF(Arg$)
      IF Res = 0 THEN
        DO
          IF UCASE$(Riga$) = "ELSE" AND NewWorkIf = WorkIf THEN EXIT DO
          IF UCASE$(Riga$) = "END IF" THEN NewWorkIf = NewWorkIf - 1: IF NewWorkIf < WorkIf THEN WorkIf = WorkIf - 1: EXIT DO
          I = I + 1
          IF I > Lines THEN Show.Error 2
          Riga$ = RTRIM$(LTRIM$(Prog$(I)))
          Comando$ = RTRIM$(UCASE$(LEFT$(Riga$, INSTR(Riga$, " "))))
          IF Comando$ = "IF" THEN NewWorkIf = NewWorkIf + 1
        LOOP
      END IF
    CASE "ELSE"
      IF WorkIf > 0 THEN
        NewWorkIf = WorkIf
        DO
          'IF UCASE$(Riga$) = "ELSE" AND NewWorkIf = WorkIf THEN EXIT DO
          IF UCASE$(Riga$) = "END IF" THEN NewWorkIf = NewWorkIf - 1: IF NewWorkIf < WorkIf THEN WorkIf = WorkIf - 1: EXIT DO
          I = I + 1
          IF I > Lines THEN Show.Error 2
          Riga$ = RTRIM$(LTRIM$(Prog$(I)))
          Comando$ = RTRIM$(UCASE$(LEFT$(Riga$, INSTR(Riga$, " "))))
          IF Comando$ = "IF" THEN NewWorkIf = NewWorkIf + 1
        LOOP
      ELSE
        Show.Error 3
      END IF
    CASE "SELECT"
      IF UCASE$(LEFT$(LTRIM$(Arg$), 4)) <> "CASE" THEN Show.Error 13 ELSE Arg$ = MID$(LTRIM$(Arg$), 6)
      
      DO
          IF LEFT$(UCASE$(Riga$), 4) = "CASE" THEN
            IF CND.IF(MID$(Riga$, 6) + " = " + Arg$) THEN EXIT DO
          END IF
          IF UCASE$(Riga$) = "END SELECT" THEN EXIT DO

          I = I + 1
          IF I > Lines THEN Show.Error 14
          Riga$ = RTRIM$(LTRIM$(Prog$(I)))
          Comando$ = RTRIM$(UCASE$(LEFT$(Riga$, INSTR(Riga$, " "))))
      LOOP
    CASE "CASE"
      DO
          IF UCASE$(Riga$) = "END SELECT" THEN EXIT DO
          I = I + 1
          IF I > Lines THEN Show.Error 14
          Riga$ = RTRIM$(LTRIM$(Prog$(I)))
          Comando$ = RTRIM$(UCASE$(LEFT$(Riga$, INSTR(Riga$, " "))))
      LOOP

    CASE "GOTO"
      WorkIf = 0
      FOR a = 0 TO curlabel
        IF Labels$(a) = RTRIM$(Arg$) THEN I = Labels(a)
      NEXT

    CASE "DO"
      LoopsStack = LoopsStack + 1
      REDIM PRESERVE LoopsIndex(LoopsStack)
      LoopsIndex(LoopsStack) = I
    CASE "LOOP"
      IF LoopsStack = 0 THEN Show.Error 10
      I = LoopsIndex(LoopsStack)

    CASE "FOR"
      LoopsStack = LoopsStack + 1
      REDIM PRESERVE LoopsIndex(LoopsStack)
      LoopsIndex(LoopsStack) = I
      ForLoop$ = LEFT$(Arg$, INSTR(UCASE$(Arg$), " TO "))

      Comando$ = RTRIM$(UCASE$(LEFT$(ForLoop$, INSTR(ForLoop$, " "))))
      Arg$ = LTRIM$(MID$(ForLoop$, INSTR(ForLoop$, "=") + 1))
      SetVar Comando$, Arg$
    CASE "NEXT"
      IF LoopsStack = 0 THEN Show.Error 12
      'I = LoopsIndex(LoopsStack)

      Riga$ = UCASE$(RTRIM$(LTRIM$(Prog$(LoopsIndex(LoopsStack)))))

      Comando$ = MID$(Riga$, INSTR(Riga$, " ") + 1)
      Comando$ = RTRIM$(LTRIM$(LEFT$(Comando$, INSTR(Comando$, "=") - 1)))
      Arg$ = RTRIM$(LTRIM$(MID$(Riga$, INSTR(Riga$, " TO ") + 4)))
      SetVar Comando$, Comando$ + " + 1"
      IF GetNum(Comando$) <= GetNum(Arg$) THEN I = LoopsIndex(LoopsStack) ELSE LoopsStack = LoopsStack - 1


    CASE "SUB", "FUNCTION"
      END
    CASE "END"
      IF UCASE$(RTRIM$(Arg$)) = "SUB" OR UCASE$(RTRIM$(Arg$)) = "FUNCTION" THEN I = Lines + 1
      IF UCASE$(RTRIM$(Arg$)) = "IF" THEN WorkIf = WorkIf - 1

      'END
    CASE ELSE
      IF INSTR(Comando$, "(") THEN
        a = 0
        DO
          a = 1
          IF INSTR(Arg$, ")") THEN
            IF INSTR(Arg$, ")") < INSTR(Arg$, "(") OR INSTR(Arg$, "(") = 0 THEN
              a = 0
              Comando$ = Comando$ + " " + LEFT$(Arg$, INSTR(Arg$, ")"))
              Arg$ = LTRIM$(MID$(Arg$, INSTR(Arg$, ")") + 1))
            END IF
          END IF
        LOOP UNTIL a = 1

      END IF
      IF LEFT$(Arg$, 1) = "=" THEN
        Arg$ = MID$(Arg$, 2)
        SetVar Comando$, Arg$
      ELSE
        a = 0
        DO UNTIL a = CountSubs
          IF RTRIM$(SubRoutines(a).Name) = Comando$ THEN
              InsideSub = InsideSub + 1
              CurVar(InsideSub) = 0
              Params = GetArgs(Arg$)
              Main SubRoutines(a).Lines + 1

              Params = GetArgs(Arg$)
              Riga$ = UCASE$(RTRIM$(LTRIM$(Prog$(SubRoutines(a).Lines)))) + " "
              Nome$ = (LTRIM$(MID$(Riga$, INSTR(Riga$, " ") + 1)))
              Arg$ = LTRIM$(RTRIM$(MID$(Nome$, INSTR(Nome$, " "))))
              REDIM R$(Params)
              FOR a = 0 TO Params
                R$(a) = LTRIM$(Args$(a))
              NEXT
              IF Params > GetArgs(GetPar(Arg$)) THEN Show.Error 1
              FOR a = 0 TO Params
                GetSubVar R$(a), Args$(a)
              NEXT
              a = CountSubs - 1
              InsideSub = InsideSub - 1

          END IF
          a = a + 1
        LOOP
      END IF
    END SELECT
    I = I + 1
  LOOP UNTIL I > Lines
  IF WorkIf > 0 THEN Show.Error 2
  IF LoopsStack > 0 THEN Show.Error 11
END SUB

SUB Precedence (Arg$)

Virgolette = 0
Prec$ = " */+-"
Text$ = ""
DIM Op$(30)
'DO
'  a$ = getword$(wordtype)
'  IF par$ <> "" THEN a$ = a$ + "(" + par$ + ")"
'  IF a$ = ":" THEN text$ = MID$(codetext$, getcharpos + 1): EXIT DO
'  IF a$ = lineeof THEN EXIT DO
'  op$(R) = a$
'  R = R + 1
'LOOP
FOR I = 1 TO LEN(Arg$)
  T$ = MID$(Arg$, I, 1)
  IF T$ = CHR$(34) THEN Virgolette = 1 - Virgolette
  IF Virgolette = 0 THEN
    IF T$ = "(" THEN Par = Par + 1
    IF T$ = ")" THEN Par = Par - 1
    a$ = a$ + T$
    IF T$ = " " AND Par = 0 THEN
      Op$(R) = RTRIM$(a$)
      a$ = ""
      R = R + 1
    END IF
  END IF
NEXT



I = 1
Operand = 2
DO
  OldOp = Operand
  IF Op$(I) = "" THEN EXIT DO
  Operand = INT(INSTR(Prec$, Op$(I)) / 2)
  IF Operand = 0 THEN Operand = OldOp
  'IF i = 1 THEN OldOp = Operand

  IF OldOp > Operand THEN
    IF I = 1 THEN
      skp = 1
    ELSE
      Op$(I - 1) = "(" + Op$(I - 1)
    END IF
  ELSEIF OldOp < Operand THEN
    IF skp = 0 THEN
      Op$(I - 1) = Op$(I - 1) + ")"
    ELSE
      skp = 0
    END IF
  END IF

  I = I + 2

LOOP UNTIL I > R
Arg$ = ""
FOR I = 0 TO R: Arg$ = Arg$ + Op$(I) + " ": NEXT
Arg$ = RTRIM$(Arg$) + " " + Text$
IF Operand = 1 AND skp = 0 THEN Arg$ = Arg$ + ") "
getcharpos = 0

END SUB

SUB SetSubVar (Var$, Valore$)
  IF UCASE$(LEFT$(LTRIM$(Var$), 6)) = "BYVAL " THEN Var$ = UCASE$(MID$(LTRIM$(Var$), 7))
  Tipo = GetType(Var$)
  CurVar(InsideSub) = CurVar(InsideSub) + 1
  I = CurVar(InsideSub)
  Variable$(I, InsideSub) = CHR$(Tipo) + Var$

  InsideSub = InsideSub - 1

  SELECT CASE Tipo
  CASE tlong
    VVariable(I, InsideSub + 1) = GetNum(Valore$)
  CASE tstring
    VStrings(I, InsideSub + 1) = GetString(Valore$)
  CASE ELSE
  END SELECT

  InsideSub = InsideSub + 1

END SUB

SUB SetVar (Var$, Valore$)
  IF INSTR(Var$, "(") THEN              'Is Array?
    FOR I = 1 TO CurVar(InsideSub)
      IF UCASE$(LEFT$(Var$, INSTR(Var$, "(") - 1)) = UCASE$(MID$(Variable$(I, InsideSub), 2)) THEN EXIT FOR
    NEXT
    IF I = CurVar(InsideSub) + 1 THEN Show.Error 4
    Tipo = ASC(LEFT$(Variable$(I, InsideSub), 1)) - 64

    Par$ = GetPar$(Var$)
    I = I + GetNum(Par$)
  ELSE                                  'In not an array

    FOR I = 1 TO CurVar(InsideSub)
      IF UCASE$(Var$) = UCASE$(MID$(Variable$(I, InsideSub), 2)) THEN EXIT FOR
    NEXT
    IF I = CurVar(InsideSub) + 1 THEN
      Tipo = GetType(Var$)
      Variable$(I, InsideSub) = CHR$(Tipo) + Var$
      CurVar(InsideSub) = CurVar(InsideSub) + 1
    ELSE
      Tipo = ASC(LEFT$(Variable$(I, InsideSub), 1))
    END IF
  END IF

  SELECT CASE Tipo
  CASE tlong
    VVariable(I, InsideSub) = GetNum(Valore$)
  CASE tstring
    VStrings(I, InsideSub) = GetString(Valore$)
  CASE ELSE
  END SELECT
END SUB

SUB Show.Error (Number%)
  IF Number% = 2 THEN PRINT "Block IF whitout END IF"
  IF Number% = 3 THEN PRINT "ELSE without IF"
  IF Number% = 4 THEN PRINT "Array not defined"
  IF Number% = 10 THEN PRINT "LOOP without DO"
  IF Number% = 11 THEN PRINT "FOR without NEXT"
  IF Number% = 12 THEN PRINT "NEXT without FOR"
  IF Number% = 13 THEN PRINT "CASE expected"
  IF Number% = 14 THEN PRINT "SELECT without END SELECT"

  END
END SUB

Hexadecimal Dude!
Posts: 360
Joined: Jun 07, 2005 20:59
Location: england, somewhere around the middle
Contact:

Post by Hexadecimal Dude! »

**WARNING!! THIS POST CONTAINS MILD UTOPEAN DREAMING**

It surprises me how few people have shown interest here. I think that an fb interpreter would be one of the greatest advances for freebasic. Being a nice "quick run"/debug tool is the least an interpreter does. My reasons for getting excited follow:

>It could be used from an fb program to do strange and wonderful things. (eg. if TYPE was supported, you could do UDTs..... but defined by the end user)

>It is useful in a restricted network. (eg. imagine a teacher wants to teach fb, but the network only allows exe files the admin have OKd. it would mean that every time you change your program, the admin would have to OK it before you could run, with newbies this would be a common occurance and slow learning down, with an interpreter only one .exe needs to get the goahead [if source file loading was added])

>But the MOST IMPORTANT!!! How many fb programs get to average user Bob? How many even get outside the fb programming world? The only programs i write that get outside my circle of friends are the ones I don't want to; the prank ones :). For average user Bob, getting an fb program involves:

.............1) Stumbling upon a website made by an fb coder
.............2) Dowloading and running a .exe file when his browser tells him it's dangerous and not to.

BUT! with an interpreter (and some extra work from people who know how), web pages themselves could be written in fb. All Bob would have to do was get the interpreter, which could be sent to several more trusted websites and distributed from there (of course, the browser would have to be told what to do... i don't know anything about this, but I'm sure others do). It would be even better if ( in the far future :) ) It could draw to a buffer, and then the website get hold of that buffer and draw it to a region of the webpage. And if that could recieve input. Think how easy it would be for newbie website designers to code freebasic games, instead of Java ones!

I'd love to help this in any posible way, but, having only looked at it properly this morning, it confuses me. But if you have any monotonous, repetetive tasks (eg "This line needs to be copied 100 times, and each time [blah] needs to be changed to [blah2]" , or "This command works like this, which is how all of these
  • commands work") I'd love to help.

    thx

    -Hex

    edit: ...OK maybe STRONG UTOPEAN DREAMING...... :)
Last edited by Hexadecimal Dude! on Sep 10, 2005 11:09, edited 1 time in total.
Hexadecimal Dude!
Posts: 360
Joined: Jun 07, 2005 20:59
Location: england, somewhere around the middle
Contact:

Post by Hexadecimal Dude! »

will

Code: Select all

source = freefile
open command$(1) for input as #source

DO
  input #source, c$
  X = GetCommands(c$, 0)
  FOR I = 0 TO X
    IF I > 0 THEN X = GetCommands(c$, I)
    Prog$(Riga) = codetext$
    Riga = Riga + 1
  NEXT
LOOP UNTIL eof(source)
Lines = Riga
work every time? (put it around line 50)

it runs the example that was in the data statements

edit: does the exapmle in the data statements from the previous version deliberately cause an error? + the ends without sleeps are nasty
Post Reply