01-21-2026, 01:11 PM
(01-21-2026, 12:27 PM)Fifi Wrote:(01-21-2026, 10:57 AM)SMcNeill Wrote: Qb64pe.basHi 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 IfThen 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 SubAs 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!)"
regidid.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.

