QB64 Phoenix Edition
qb64pe's translation of .bas to .cpp ? - Printable Version

+- QB64 Phoenix Edition (https://qb64phoenix.com/forum)
+-- Forum: Chatting and Socializing (https://qb64phoenix.com/forum/forumdisplay.php?fid=11)
+--- Forum: General Discussion (https://qb64phoenix.com/forum/forumdisplay.php?fid=2)
+--- Thread: qb64pe's translation of .bas to .cpp ? (/showthread.php?tid=4390)



qb64pe's translation of .bas to .cpp ? - Fifi - 01-20-2026

Hello,

I've three little questions :

qb64pe translates its basic code (.bas) into C++ code (.cpp) witch then is compiled using GCC (or Mingw with windows).

1) what tool use qb64pe to translate the .bas code to the .cpp code (e.g. BaCon, NBCX, etc) ?

2) should I still use a specific parameter (-z) when I start qb64pe from the command line to save the .cpp translation ?

3) where (in what qb64pe sub directory) can I find the translated .ccp file to take a look at its C++ translation : i.e. hello.bas -> hello.cpp ?

TIA for your response.

Cheers.
Fifi


RE: qb64pe's translation of .bas to .cpp ? - Unseen Machine - 01-20-2026

  • [b]1) Translator Tool[/b]: QB64-PE does not use external tools like BaCon or BCX. It uses its [b]own internal translator[/b] built directly into the QB64-PE executable. This built-in engine parses the BASIC source and generates the equivalent C++ code before passing it to the MinGW-w64 (GCC) compiler.
  • [b]2) Command Line Parameter[/b]: Yes, you can use the 

    Code: (Select All)
    -z
    [b][/b] parameter.
    • Running 

      Code: (Select All)
      qb64pe -z yourfile.bas
       performs the first compile pass (syntax checking and C++ code generation) without opening the IDE or creating a final executable.
    • Standard compilation using 

      Code: (Select All)
      -c
       or 

      Code: (Select All)
      -x
       will also generate these files as part of the normal build process.

  • [b]3) Directory for Translated Files[/b]: You can find the generated files in the 

    Code: (Select All)
    internal/temp
    [b][/b] sub-directory within your main QB64-PE folder.
    • Code: (Select All)
      main.txt
      [b][/b]: This file contains the actual C++ translation of your BASIC code.
    • Code: (Select All)
      qbx.cpp
      [b][/b]: This is the core C++ wrapper that includes your code and the QB64 runtime libraries.
    • Code: (Select All)
      recompile.bat
      [b][/b]: A batch file that contains the exact GCC command line used to compile the program. 
Now, I KNOW people hate AI but please just ask Google! 

Unseen


RE: qb64pe's translation of .bas to .cpp ? - Fifi - 01-21-2026

(01-20-2026, 06:08 AM)Unseen Machine Wrote:
  • [b]1) Translator Tool[/b]: QB64-PE does not use external tools like BaCon or BCX. It uses its [b]own internal translator[/b] built directly into the QB64-PE executable. This built-in engine parses the BASIC source and generates the equivalent C++ code before passing it to the MinGW-w64 (GCC) compiler.

Unseen

Hi Unseen machine.

May you tell me where is the qb64pe source I can take a look at the translator part ?

Just currious.


RE: qb64pe's translation of .bas to .cpp ? - SMcNeill - 01-21-2026

(01-21-2026, 10:19 AM)Fifi Wrote:
(01-20-2026, 06:08 AM)Unseen Machine Wrote:
  • [b]1) Translator Tool[/b]: QB64-PE does not use external tools like BaCon or BCX. It uses its [b]own internal translator[/b] built directly into the QB64-PE executable. This built-in engine parses the BASIC source and generates the equivalent C++ code before passing it to the MinGW-w64 (GCC) compiler.

Unseen

Hi Unseen machine.

May you tell me where is the qb64pe source I can take a look at the translator part ?

Just currious.

Qb64pe.bas


RE: qb64pe's translation of .bas to .cpp ? - Fifi - 01-21-2026

(01-21-2026, 10:57 AM)SMcNeill Wrote: Qb64pe.bas
Hi SMcNeill,

Yes, I do assume it's in qb64pe.bas, but there are so few comments that I don't know where to look.

BTW, I've never seen such a long source code with so few comments, and I give the developers my two thumbs up for maintaining and improving this program!

So, could you tell me the line number in qb64pe.bas where the Basic keyword translator begins? 

For example, where can I find the translation of QB64PE keywords, such as "Print" or "Input," etc., into their C++ equivalents? 

Is there, for instance, a conversion table listing all the QB64 keywords and their respective C++ translations?

Just out of curiosity.

Cheers.
Fifi


RE: qb64pe's translation of .bas to .cpp ? - SMcNeill - 01-21-2026

(01-21-2026, 12:27 PM)Fifi Wrote:
(01-21-2026, 10:57 AM)SMcNeill Wrote: Qb64pe.bas
Hi SMcNeill,

Yes, I do assume it's in qb64pe.bas, but there are so few comments that I don't know where to look.

BTW, I've never seen such a long source code with so few comments, and I give the developers my two thumbs up for maintaining and improving this program!

So, could you tell me the line number in qb64pe.bas where the Basic keyword translator begins? 

For example, where can I find the translation of QB64PE keywords, such as "Print" or "Input," etc., into their C++ equivalents? 

Is there, for instance, a conversion table listing all the QB64 keywords and their respective C++ translations?

Just out of curiosity.

Cheers.
Fifi

It's.... the whole thing.  And all the subdirectories and help files all added in as well.   For example, if you want PRINT, do a search for:

 firstelement$ = "PRINT"

Code: (Select All)
                If firstelement$ = "PRINT" Then 'file print
                    If n > 1 Then
                        If getelement$(a$, 2) = "#" Then
                            xfileprint a$, ca$, n
                            If Error_Happened Then GoTo errmes
                            l$ = tlayout$
                            layoutdone = 1: If Len(layout$) Then layout$ = layout$ + sp + l$ Else layout$ = l$
                            GoTo finishedline
                        End If '#
                    End If 'n>1
                End If '"print"

                If firstelement$ = "PRINT" Or firstelement$ = "LPRINT" Then
                    If secondelement$ <> "USING" Then 'check to see if we need to auto-add semicolons
                        elementon = 2
                        redosemi:
                        For i = elementon To n - 1
                            nextchar$ = getelement$(a$, i + 1)
                            If nextchar$ <> ";" And nextchar$ <> "," And nextchar$ <> "+" And nextchar$ <> ")" Then
                                temp1$ = getelement$(a$, i)
                                beginpoint = InStr(beginpoint, temp1$, Chr$(34))
                                endpoint = InStr(beginpoint + 1, temp1$, Chr$(34) + ",")
                                If beginpoint <> 0 And endpoint <> 0 Then 'if we have both positions
                                    'Quote without semicolon check (like PRINT "abc"123)
                                    textlength = endpoint - beginpoint - 1
                                    textvalue$ = Mid$(temp1$, endpoint + 2, Len(_ToStr$(textlength)))
                                    If Val(textvalue$) = textlength Then
                                        insertelements a$, i, ";"
                                        insertelements ca$, i, ";"
                                        n = n + 1
                                        elementon = i + 2 'just an easy way to reduce redundant calls to the routine
                                        GoTo redosemi
                                    End If
                                End If
                                If temp1$ <> "USING" Then
                                    If Left$(LTrim$(nextchar$), 1) = Chr$(34) Then
                                        If temp1$ <> ";" And temp1$ <> "," And temp1$ <> "+" And temp1$ <> "(" Then
                                            insertelements a$, i, ";"
                                            insertelements ca$, i, ";"
                                            n = n + 1
                                            elementon = i + 2 'just an easy way to reduce redundant calls to the routine
                                            GoTo redosemi
                                        End If
                                    End If
                                End If
                            End If
                        Next
                    End If

                    xprint a$, ca$, n
                    If Error_Happened Then GoTo errmes
                    l$ = tlayout$
                    layoutdone = 1: If Len(layout$) Then layout$ = layout$ + sp + l$ Else layout$ = l$
                    GoTo finishedline
                End If

Then you can see that if you're dealing with a file being printed, the first IF deals with that and sends off the translation to:                              xfileprint a$, ca$, n

For the screen printing, that's done in the next statement and it gets handed off to                      xprint a$, ca$, n

If you want to look at xprint, here it is:

Code: (Select All)
Sub xprint (a$, ca$, n)
    u$ = _ToStr$(uniquenumber)

    l$ = SCase$("Print")
    If Asc(a$) = 76 Then lp = 1: lp$ = "l": l$ = SCase$("LPrint"): WriteBufLine MainTxtBuf, "tab_LPRINT=1;": SetDependency DEPENDENCY_PRINTER '"L"

    'PRINT USING?
    If n >= 2 Then
        If getelement(a$, 2) = "USING" Then
            'get format string
            i = 3
            pujump:
            l$ = l$ + sp + SCase$("Using")
            e$ = "": b = 0: puformat$ = ""
            For i = i To n
                a2$ = getelement(ca$, i)
                If a2$ = "(" Then b = b + 1
                If a2$ = ")" Then b = b - 1
                If b = 0 Then
                    If a2$ = "," Then Give_Error "Expected PRINT USING formatstring ; ...": Exit Sub
                    If a2$ = ";" Then
                        e$ = fixoperationorder$(e$)
                        If Error_Happened Then Exit Sub
                        l$ = l$ + sp + tlayout$ + sp2 + ";"
                        e$ = evaluate(e$, typ)
                        If Error_Happened Then Exit Sub
                        If (typ And ISREFERENCE) Then e$ = refer(e$, typ, 0)
                        If Error_Happened Then Exit Sub
                        If (typ And ISSTRING) = 0 Then Give_Error "Expected PRINT USING formatstring ; ...": Exit Sub
                        puformat$ = e$
                        Exit For
                    End If ';
                End If 'b
                If Len(e$) Then e$ = e$ + sp + a2$ Else e$ = a2$
            Next
            If puformat$ = "" Then Give_Error "Expected PRINT USING formatstring ; ...": Exit Sub
            If i = n Then Give_Error "Expected PRINT USING formatstring ; ...": Exit Sub
            'create build string
            If TQBSset = 0 Then
                WriteBufLine MainTxtBuf, "tqbs=qbs_new(0,0);"
            Else
                WriteBufLine MainTxtBuf, "qbs_set(tqbs,qbs_new_txt_len(" + Chr$(34) + Chr$(34) + ",0));"
            End If
            'set format start/index variable
            WriteBufLine MainTxtBuf, "tmp_long=0;" 'scan format from beginning


            'create string to hold format in for multiple references
            puf$ = "print_using_format" + u$
            If subfunc = "" Then
                WriteBufLine DataTxtBuf, "static qbs *" + puf$ + ";"
            Else
                WriteBufLine DataTxtBuf, "qbs *" + puf$ + ";"
            End If
            WriteBufLine MainTxtBuf, puf$ + "=qbs_new(0,0); qbs_set(" + puf$ + "," + puformat$ + ");"
            WriteBufLine MainTxtBuf, "if (is_error_pending()) goto skip_pu" + u$ + ";"

            'print expressions
            b = 0
            e$ = ""
            last = 0
            For i = i + 1 To n
                a2$ = getelement(ca$, i)
                If a2$ = "(" Then b = b + 1
                If a2$ = ")" Then b = b - 1
                If b = 0 Then
                    If a2$ = ";" Or a2$ = "," Then
                        printulast:
                        e$ = fixoperationorder$(e$)
                        If Error_Happened Then Exit Sub
                        If last Then l$ = l$ + sp + tlayout$ Else l$ = l$ + sp + tlayout$ + sp2 + a2$
                        e$ = evaluate(e$, typ)
                        If Error_Happened Then Exit Sub
                        If (typ And ISREFERENCE) Then e$ = refer(e$, typ, 0)
                        If Error_Happened Then Exit Sub
                        If typ And ISSTRING Then

                            If Left$(e$, 9) = "func_tab(" Or Left$(e$, 9) = "func_spc(" Then

                                'TAB/SPC exception
                                'note: position in format-string must be maintained
                                '-print any string up until now
                                WriteBufLine MainTxtBuf, "qbs_" + lp$ + "print(tqbs,0);"
                                '-print e$
                                WriteBufLine MainTxtBuf, "qbs_set(tqbs," + e$ + ");"
                                WriteBufLine MainTxtBuf, "if (is_error_pending()) goto skip_pu" + u$ + ";"
                                If lp Then WriteBufLine MainTxtBuf, "lprint_makefit(tqbs);" Else WriteBufLine MainTxtBuf, "makefit(tqbs);"
                                WriteBufLine MainTxtBuf, "qbs_" + lp$ + "print(tqbs,0);"
                                '-set length of tqbs to 0
                                WriteBufLine MainTxtBuf, "tqbs->len=0;"

                            Else

                                'regular string
                                WriteBufLine MainTxtBuf, "tmp_long=print_using(" + puf$ + ",tmp_long,tqbs," + e$ + ");"

                            End If



                        Else 'not a string
                            If typ And ISFLOAT Then
                                If (typ And 511) = 32 Then WriteBufLine MainTxtBuf, "tmp_long=print_using_single(" + puf$ + "," + e$ + ",tmp_long,tqbs);"
                                If (typ And 511) = 64 Then WriteBufLine MainTxtBuf, "tmp_long=print_using_double(" + puf$ + "," + e$ + ",tmp_long,tqbs);"
                                If (typ And 511) > 64 Then WriteBufLine MainTxtBuf, "tmp_long=print_using_float(" + puf$ + "," + e$ + ",tmp_long,tqbs);"
                            Else
                                If ((typ And 511) = 64) And (typ And ISUNSIGNED) <> 0 Then
                                    WriteBufLine MainTxtBuf, "tmp_long=print_using_uinteger64(" + puf$ + "," + e$ + ",tmp_long,tqbs);"
                                Else
                                    WriteBufLine MainTxtBuf, "tmp_long=print_using_integer64(" + puf$ + "," + e$ + ",tmp_long,tqbs);"
                                End If
                            End If
                        End If 'string/not string
                        WriteBufLine MainTxtBuf, "if (is_error_pending()) goto skip_pu" + u$ + ";"
                        e$ = ""
                        If last Then Exit For
                        GoTo printunext
                    End If
                End If
                If Len(e$) Then e$ = e$ + sp + a2$ Else e$ = a2$
                printunext:
            Next
            If e$ <> "" Then a2$ = "": last = 1: GoTo printulast
            WriteBufLine MainTxtBuf, "skip_pu" + u$ + ":"
            'check for errors
            WriteBufLine MainTxtBuf, "if (is_error_pending()){"
            WriteBufLine MainTxtBuf, "g_tmp_long=new_error; new_error=0; qbs_" + lp$ + "print(tqbs,0); new_error=g_tmp_long;"
            WriteBufLine MainTxtBuf, "}else{"
            If a2$ = "," Or a2$ = ";" Then nl = 0 Else nl = 1 'note: a2$ is set to the last element of a$
            WriteBufLine MainTxtBuf, "qbs_" + lp$ + "print(tqbs," + _ToStr$(nl) + ");"
            WriteBufLine MainTxtBuf, "}"
            WriteBufLine MainTxtBuf, "qbs_free(tqbs);"
            WriteBufLine MainTxtBuf, "qbs_free(" + puf$ + ");"
            WriteBufLine MainTxtBuf, "skip" + u$ + ":"
            WriteBufLine MainTxtBuf, cleanupstringprocessingcall$ + "0);"
            If lp Then WriteBufLine MainTxtBuf, "tab_LPRINT=0;"
            tlayout$ = l$
            Exit Sub
        End If
    End If
    'end of print using code

    b = 0
    e$ = ""
    last = 0
    WriteBufLine MainTxtBuf, "tqbs=qbs_new(0,0);" 'initialize the temp string
    TQBSset = -1 'set the temporary flag so we don't create a temp string twice, in case USING comes after something
    For i = 2 To n
        a2$ = getelement(ca$, i)
        If a2$ = "(" Then b = b + 1
        If a2$ = ")" Then b = b - 1
        If b = 0 Then
            If a2$ = ";" Or a2$ = "," Or UCase$(a2$) = "USING" Then
                printlast:

                If UCase$(a2$) = "USING" Then
                    If e$ <> "" Then gotopu = 1 Else i = i + 1: GoTo pujump
                End If

                If Len(e$) Then
                    ebak$ = e$
                    pnrtnum = 0
                    printnumber:
                    e$ = fixoperationorder$(e$)
                    If Error_Happened Then Exit Sub
                    If pnrtnum = 0 Then
                        If last Then l$ = l$ + sp + tlayout$ Else l$ = l$ + sp + tlayout$ + sp2 + a2$
                    End If
                    e$ = evaluate(e$, typ)
                    If Error_Happened Then Exit Sub
                    If (typ And ISSTRING) = 0 Then
                        'not a string expression!
                        e$ = "STR$" + sp + "(" + sp + ebak$ + sp + ")" + sp + "+" + sp + Chr$(34) + " " + Chr$(34)
                        pnrtnum = 1
                        GoTo printnumber
                    End If
                    If (typ And ISREFERENCE) Then e$ = refer(e$, typ, 0)
                    If Error_Happened Then Exit Sub
                    WriteBufLine MainTxtBuf, "qbs_set(tqbs," + e$ + ");"
                    WriteBufLine MainTxtBuf, "if (is_error_pending()) goto skip" + u$ + ";"
                    If lp Then WriteBufLine MainTxtBuf, "lprint_makefit(tqbs);" Else WriteBufLine MainTxtBuf, "makefit(tqbs);"
                    WriteBufLine MainTxtBuf, "qbs_" + lp$ + "print(tqbs,0);"
                Else
                    If a2$ = "," Then l$ = l$ + sp + a2$
                    If a2$ = ";" Then
                        If Right$(l$, 1) <> ";" Then l$ = l$ + sp + a2$ 'concat ;; to ;
                    End If
                End If 'len(e$)
                If a2$ = "," Then WriteBufLine MainTxtBuf, "tab();"
                e$ = ""

                If gotopu Then i = i + 1: GoTo pujump

                If last Then
                    WriteBufLine MainTxtBuf, "qbs_" + lp$ + "print(nothingstring,1);" 'go to new line
                    Exit For
                End If

                GoTo printnext
            End If 'a2$
        End If 'b=0

        If Len(e$) Then e$ = e$ + sp + a2$ Else e$ = a2$
        printnext:
    Next
    If Len(e$) Then a2$ = "": last = 1: GoTo printlast
    If n = 1 Then WriteBufLine MainTxtBuf, "qbs_" + lp$ + "print(nothingstring,1);"
    WriteBufLine MainTxtBuf, "skip" + u$ + ":"
    WriteBufLine MainTxtBuf, "qbs_free(tqbs);"
    WriteBufLine MainTxtBuf, cleanupstringprocessingcall$ + "0);"
    If lp Then WriteBufLine MainTxtBuf, "tab_LPRINT=0;"
    tlayout$ = l$
End Sub

As you can see, it does a ton of WriteBufLine calls -- those are where the translation is taking place and it's writing to the translation buffer, which it only saves to disk before actually compiling to reduce wear and tear on any SSD drives.

If you look in subs_functions.bas, you'll see where a lot of the commands that can be almost directly processed are located.  These are all handled via QB64PE's own internal language/syntax, which takes a bit to learn and sort out.

Code: (Select All)
    clearid
    id.n = "Sin"
    id.subfunc = 1
    id.callname = "std::sin"
    id.args = 1
    id.arg = MKL$(FLOATTYPE - ISPOINTER)
    id.ret = FLOATTYPE - ISPOINTER
    id.hr_syntax = "SIN(radian_angle!)"
    regid

    clearid
    id.n = "Cos"
    id.subfunc = 1
    id.callname = "std::cos"
    id.args = 1
    id.arg = MKL$(FLOATTYPE - ISPOINTER)
    id.ret = FLOATTYPE - ISPOINTER
    id.hr_syntax = "COS(radian_angle!)"
    regid

    clearid
    id.n = "Tan"
    id.subfunc = 1
    id.callname = "std::tan"
    id.args = 1
    id.arg = MKL$(FLOATTYPE - ISPOINTER)
    id.ret = FLOATTYPE - ISPOINTER
    id.hr_syntax = "TAN(radian_angle!)"
    regid

    clearid
    id.n = "Atn"
    id.subfunc = 1
    id.callname = "std::atan"
    id.args = 1
    id.arg = MKL$(FLOATTYPE - ISPOINTER)
    id.ret = FLOATTYPE - ISPOINTER
    id.hr_syntax = "ATN(tangent!)"
    regid

id.n is the QB64 keyword name.
id.subfunc tells us if it's a SUB or FUNCTION and whether to expect a return value or not.
id.callname is the C-translated name which you'll find in libqb.cpp or one of the subfolders there.
id.args is the number of parameters which we're going to pass
id.arg is the type of parameters we're passing
id.ret is the type of value we're getting back
id.hr_syntax is the help row syntax which shows up as you're typing to help you remember what the command's syntax is quickly.
regid then registers that command for processing.

So you type ATN(.1), it gets translated to std:atan(1).

So not only are you looking in qb64pe.bas, but also in libqb.cpp and all the subfolders attached to it, to process your single line translation.

It's the whole thing, scattered in 50 different areas, and all working together to process each command independently.


RE: qb64pe's translation of .bas to .cpp ? - Fifi - 01-21-2026

(01-21-2026, 01:11 PM)SMcNeill Wrote: It's.... the whole thing.  And all the subdirectories and help files all added in as well. 
So not only are you looking in qb64pe.bas, but also in libqb.cpp and all the subfolders attached to it, to process your single line translation.
It's the whole thing, scattered in 50 different areas, and all working together to process each command independently.
Thank you for this detailed explanation. 

I now understand how difficult it is to maintain and improve qb64pe, as well as to find and motivate new developers. 

Once again, a big thank you to the developers! 

Cheers.
Fifi