On Windows 10.
I have a simple program (650 lines of code, 36 functions) that is causing me problems
I compile with: fbc64 -lang qb tinybasic.bas
I run with: tinybasic hello.bas
If I put a debugging function at the begin and end of every function -
My program works great!
However, if I comment out said statements, in all 36 functions, my program crashes, but without any kind of error message.
for instance:
Code: Select all
function isdigit&(c as string)
call logit("isdigit begin")
isdigit& = (c >= "0" and c <= "9")
call logit("isdigit end")
end function
Code: Select all
sub logit(s as string)
'if instr(s, "isalpha") then exit sub
'if instr(s, "isdigit") then exit sub
'if instr(s, "nexttok") then exit sub
'print s
end sub
However, if I comment out all 72 calls (e.g., 2 in each function) to logit(), as per below:
Code: Select all
function isdigit&(c as string)
'call logit("isdigit begin")
isdigit& = (c >= "0" and c <= "9")
'call logit("isdigit end")
end function
tinybasic.bas - calls to logit commented out. Crashes. Uncomment the calls to logit, and the program works fine.
Code: Select all
'For QB64
'$console:only
const True = -1, False = 0, c_maxlines = 1000, c_maxvars = 26, c_at_max = 500, c_g_stack = 100
dim shared as string c_tab, c_squote, c_dquote
c_tab = chr$(9): c_squote = chr$(39): c_dquote = chr$(34)
dim shared as long c_default, c_ident, c_number, c_string, c_punct
c_default = 0: c_ident = 1: c_number = 2: c_string = 3: c_punct = 4
dim shared as string pgm(c_maxlines) ' program stored here
dim shared as long vars(c_maxvars) ' variable store
dim shared as long gstackln(c_g_stack) ' gosub line stack
dim shared as long gstacktp(c_g_stack) ' gosub textp stack
dim shared as long gsp ' gosub stack index
dim shared as long atarry(c_at_max) ' the @ array
dim shared as long forvar(c_maxvars)
dim shared as long forlimit(c_maxvars)
dim shared as long forline(c_maxvars)
dim shared as long forpos(c_maxvars)
dim shared as string tok ' current token
dim shared as string thelin, thech ' current program line, current character
dim shared as long curline, textp ' position in current line
dim shared as long num ' last number read by scanner
dim shared as long toktype, errors, tracing
dim shared as long hi_index ' highest used index in pgm
'Needed to compile with FreeBasic
declare function accept&(s as string)
declare function expression&(minprec as long)
declare function findline&(goal as long)
declare function getfilename$(action as string)
declare function getvarindex&
declare function isalpha&(c as string)
declare function isdigit&(c as string)
declare function parenexpr&
declare function validlinenum&
declare function idxtoline&(n as long)
call main
sub logit(s as string)
'if instr(s, "isalpha") then exit sub
'if instr(s, "isdigit") then exit sub
'if instr(s, "nexttok") then exit sub
'print s
end sub
sub main
'call logit("main begin")
call newstmt ' resets gsp, hi_index
if command$ <> "" then
pgm(0) = "run " + c_dquote + command$ + c_dquote
call initlex(0)
call docmd
else
call help
end if
do
errors = False
line input "tb> ", pgm(0)
if pgm(0) <> "" then
call initlex(0)
if toktype <> c_number then
call docmd
else
if validlinenum then
dim as long n, i, deleted
deleted = False
n = findline(num)
if n < 0 then ' replace line
n = abs(n)
' if just a number, delete that line
if textp > len(pgm(0)) then
for i = n to hi_index - 1
pgm(i) = pgm(i + 1)
next i
hi_index = hi_index - 1
deleted = True
end if
elseif n > 0 then ' insert line
for i = hi_index + 1 to n step -1
pgm(i) = pgm(i - 1)
next i
hi_index = hi_index + 1
else ' append
hi_index = hi_index + 1
n = hi_index
end if
if not deleted then pgm(n) = pgm(0)
end if
end if
end if
loop
'call logit("main end")
end sub
sub docmd
'call logit("docmd begin")
dim as long need_colon
do while not errors
if tracing and left$(tok, 1) <> ":" then print idxtoline(curline); " "; tok; thech; mid$(thelin, textp)
need_colon = False
select case tok
case "bye", "quit" : call nexttok: end
case "end", "stop" : call nexttok: exit sub
case "load", "old" : call nexttok: call loadstmt: exit sub
case "new" : call nexttok: call newstmt: exit sub
case "gosub" : call nexttok: call gosubstmt
case "goto" : call nexttok: call gotostmt
case "if" : call nexttok: call ifstmt
case "next" : call nexttok: call nextstmt ' colon checked in rtn
case "return" : call nexttok: call returnstmt ' colon checked in rtn
case "run" : call nexttok: call runstmt
case "clear" : call nexttok: call clearstmt :need_colon = True
case "cls" : call nexttok: cls :need_colon = True
case "for" : call nexttok: call forstmt :need_colon = True
case "help" : call nexttok: call help :need_colon = True
case "input" : call nexttok: call inputstmt :need_colon = True
case "list" : call nexttok: call liststmt :need_colon = True
case "print", "?" : call nexttok: call printstmt :need_colon = True
case "save" : call nexttok: call savestmt :need_colon = True
case "troff" : call nexttok: tracing = False :need_colon = True
case "tron" : call nexttok: tracing = True :need_colon = True
case ":", "" : call nexttok
case else
if tok = "let" then call nexttok
if toktype = c_ident then
call assign
elseif tok = "@" then
call nexttok: call arrassn
else
print "Unknown token '"; tok; "' at line:"; idxtoline(curline); " Col:"; textp; " : "; thelin: errors = True
end if
end select
if tok = "" then
while tok = "" and not errors
if curline = 0 or curline >= hi_index then
errors = True
else
call initlex(curline + 1)
end if
wend
elseif need_colon and not accept(":") then
print ": expected but found: "; tok: errors = True
end if
loop
'call logit("docmd end")
end sub
sub help
'call logit("help begin")
print "Tiny Basic (QBJS)"
print ""
print " bye, clear, cls, end/stop, help, list, load/save, new, run, tron/off"
print " for <var> = <expr1> to <expr2> ... next <var> "
print " gosub <expr> ... return "
print " goto <expr> "
print " if <expr> then <statement> "
print " input [prompt,] <var> "
print " <var>=<expr> "
print " print <expr|string>[,<expr|string>][;] "
print " rem <anystring> or ' <anystring> "
print " Operators: ^, * / \ mod + - < <= > >= = <>, not, and, or "
print " Integer variables a..z, and array @(expr) "
print " Functions: abs(expr), asc(ch), rnd(expr), sgn(expr) "
print ""
'call logit("help end")
end sub
sub assign ' ident = expr
'call logit("assign begin")
dim as long xvar
xvar = getvarindex: call nexttok
call expect("=")
vars(xvar) = expression(0)
if tracing then print "*** "; chr$(xvar + asc("a")); " = "; vars(xvar)
'call logit("assign end")
end sub
sub arrassn ' array assignment: @(expr) = expr
'call logit("arrassn begin")
dim as long atndx
atndx = parenexpr
call expect("=")
atarry(atndx) = expression(0)
if tracing then print "*** @("; atndx; ") = "; atarry(atndx)
'call logit("arrassn end")
end sub
sub clearstmt ' clear all variables
'call logit("clearstmt begin")
dim as long i
for i = 1 to c_maxvars
vars(i) = 0
next i
for i = 0 to c_at_max
atarry(i) = 0
next i
gsp = 0
'call logit("clearstmt end")
end sub
sub forstmt ' for i = expr to expr
'call logit("forstmt begin")
dim as long xvar, forndx
xvar = getvarindex
call assign
' vars(xvar) has the value; xvar has the number value of the variable in 0..25
forndx = xvar
forvar(forndx) = vars(xvar)
if tok <> "to" then
print "For: Expecting 'to', found:"; tok: errors = True
else
call nexttok
forlimit(forndx) = expression(0)
' need to store iter, limit, line, and col
forline(forndx) = curline
if tok = "" then forpos(forndx) = textp else forpos(forndx) = textp - 1
end if
'call logit("forstmt end")
end sub
sub gosubstmt ' gosub expr: for gosub: save the line and column
'call logit("gosubstmt begin")
gsp = gsp + 1
num = expression(0)
gstackln(gsp) = curline
if tok = "" then gstacktp(gsp) = textp else gstacktp(gsp) = textp - 1
call go(num, "gosub")
'call logit("gosubstmt end")
end sub
sub gotostmt ' goto expr
'call logit("gotostmt begin")
num = expression(0)
call go(num, "goto")
'call logit("gotostmt end")
end sub
sub ifstmt ' if expr then {stmt} {: stmt}
'call logit("ifstmt begin")
if expression(0) = 0 then call skiptoeol: exit sub
if tok = "then" then call nexttok
if toktype = c_number then call gotostmt
'call logit("ifstmt end")
end sub
sub inputstmt ' "input" [string ","] xvar
'call logit("inputstmt begin")
dim as long xvar
dim as string st
if toktype = c_string then
print mid$(tok, 2);
call nexttok
call expect(",")
else
print "? ";
end if
xvar = getvarindex: call nexttok
line input st
if st = "" then st = "0"
if (left$(st, 1) >= "0" and left$(st, 1) <= "9") or left$(st, 1) = "-" then
vars(xvar) = val(st)
else
vars(xvar) = asc(st)
end if
'call logit("inputstmt end")
end sub
sub liststmt
'call logit("liststmt begin")
dim as long i
for i = 1 to hi_index
print pgm(i)
next i
print
'call logit("liststmt end")
end sub
sub loadstmt ' load ["string"]
'call logit("loadstmt begin")
dim as string filename
filename = getfilename("Load")
if filename = "" then exit sub
call newstmt
open filename for input as #1
hi_index = 0
while not eof(1)
hi_index = hi_index + 1
line input #1, pgm(hi_index)
wend
close #1
curline = 0
'call logit("loadstmt end")
end sub
sub newstmt ' clears program and variable store
'call logit("newstmt begin")
dim as long i
call clearstmt
for i = 1 to c_maxlines
pgm(i) = ""
next i
hi_index = 0
'call logit("newstmt end")
end sub
sub nextstmt ' next ident - ident is required
'call logit("nextstmt begin")
dim as long forndx
' tok needs to have the variable
forndx = getvarindex&
forvar(forndx) = forvar(forndx) + 1
vars(forndx) = forvar(forndx)
if forvar(forndx) <= forlimit(forndx) then
curline = forline(forndx)
textp = forpos(forndx)
call initlex2
else
call nexttok ' skip the ident for now
if tok <> "" and tok <> ":" then
print "Next: expected ':' before statement, but found:"; tok: errors = True
end if
end if
'call logit("nextstmt end")
end sub
' "print" [[#num "," ] expr { "," [#num ","] expr }] [","] {":" stmt} eol
' expr can also be a literal string
sub printstmt
'call logit("printstmt begin")
dim as long printnl, printwidth, n
dim as string junk
printnl = True
do while tok <> ":" and tok <> "" and tok <> "else"
printnl = True
printwidth = 0
if accept("#") then
if num <= 0 then print "Expecting a print width, found:"; tok: exit sub
printwidth = num
call nexttok
if not accept(",") then print "Print: Expecting a ',', found:"; tok: exit sub
end if
if toktype = c_string then
junk = mid$(tok, 2)
call nexttok
elseif toktype = c_ident and tok = "chr" and thech = "$" then
textp = textp + 1 ' consume $
call nexttok ' get (
n = parenexpr&
junk = chr$(n)
else
n = expression(0)
junk = ltrim$(str$(n))
end if
printwidth = printwidth - len(junk)
if printwidth <= 0 then print junk; else print space$(printwidth); junk;
if accept(",") or accept(";") then printnl = False else exit do
loop
if printnl then print
'call logit("printstmt end")
end sub
sub returnstmt ' exit from a subroutine
'call logit("returnstmt begin")
curline = gstackln(gsp)
textp = gstacktp(gsp)
gsp = gsp - 1
call initlex2
if tok <> "" and tok <> ":" then
print "Return: expected ':' before statement, but found:"; tok: errors = True
end if
'call logit("returnstmt end")
end sub
sub runstmt ' run ["string"]
'call logit("runstmt begin")
if toktype = c_string then call loadstmt
call clearstmt
call initlex(1)
'call logit("runstmt end")
end sub
sub savestmt ' save ["string"]
'call logit("savestmt begin")
dim as long i
dim as string filename
filename = getfilename("Save")
if filename = "" then exit sub
open filename for output as #1
for i = 1 to hi_Index
if pgm(i) <> "" then print #1, pgm(i)
next i
close #1
'call logit("savestmt end")
end sub
sub go(n as long, s as string) ' transfer control to line n
'call logit("go begin")
dim as long i
if validlinenum then
i = findline(n)
if i < 0 then
call initlex(abs(i))
else
print s; " target not found: "; n: errors = True
end if
end if
'call logit("go end")
end sub
' find the goal line
' found exact match, return -i
' found first greater, return i
' else goal is > all, 0
function findline&(goal as long)
'call logit("findline begin")
dim as long lo, hi, closest, i, aline
findline = 0
aline = idxtoline(hi_index)
if toktype = c_number and goal > aline then exit function
lo = 1
hi = hi_index
while lo <= hi
i = lo + int((hi - lo) \ 2)
aline = idxtoline(i)
if aline = goal then
findline = -i: exit function
elseif aline < goal then
closest = i
lo = i + 1
else
hi = i - 1
end if
wend
i = closest + 1
while i <= hi_index
call initlex(i)
if aline < goal then
i = i + 1
else
closest = i
exit while
end if
wend
findline = closest
'call logit("findline end")
end function
function getfilename$(action as string)
'call logit("getfilename begin")
dim as string filename
if toktype = c_string then
filename = mid$(tok, 2)
else
print action; ": ";
line input filename
end if
if filename <> "" then
if instr(filename, ".") = 0 then filename = filename + ".bas"
end if
getfilename$ = filename
'call logit("getfilename end")
end function
function validlinenum&
'call logit("validlinenum begin")
validlinenum = True
if num <= 0 then print "Line number out of range": errors = True: validlinenum = False
'call logit("validlinenum end")
end function
function parenexpr&
'call logit("parenexpr begin")
call expect("("): if errors then exit function
parenexpr& = expression(0)
call expect(")")
'call logit("parenexpr end")
end function
function expression&(minprec as long)
'call logit("expression begin")
dim as long n, n2
' handle numeric operands - numbers and unary operators
if 0 then ' to allow elseif
elseif toktype = c_number then: n = num: call nexttok
elseif tok = "(" then: n = parenexpr
elseif tok = "not" then: call nexttok: n = expression(3) = 0
elseif tok = "abs" then: call nexttok: n = abs(parenexpr)
elseif tok = "asc" then: call nexttok: call expect("("): n = asc(mid$(tok, 2, 1)): call nexttok: call expect(")")
elseif tok = "rnd" or tok = "irnd" then: call nexttok: n = int(rnd * parenexpr) + 1
elseif tok = "sgn" then: call nexttok: n = sgn(parenexpr)
elseif toktype = c_ident then: n = vars(getvarindex): call nexttok
elseif tok = "@" then: call nexttok: n = atarry(parenexpr)
elseif tok = "-" then: call nexttok: n = -expression(7)
elseif tok = "+" then: call nexttok: n = expression(7)
else: print "("; idxtoline(curline); ") syntax error: expecting an operand, found: ", tok: errors = True: exit function
end if
do ' while binary operator and precedence of tok >= minprec
if 0 then ' to allow elseif
elseif minprec <= 1 and tok = "or" then: call nexttok: n2 = expression(2): n = n or n2
elseif minprec <= 2 and tok = "and" then: call nexttok: n2 = expression(3): n = n and n2
elseif minprec <= 4 and tok = "=" then: call nexttok: n = abs(n = expression(5))
elseif minprec <= 4 and tok = "<" then: call nexttok: n = abs(n < expression(5))
elseif minprec <= 4 and tok = ">" then: call nexttok: n = abs(n > expression(5))
elseif minprec <= 4 and tok = "<>" then: call nexttok: n = abs(n <> expression(5))
elseif minprec <= 4 and tok = "<=" then: call nexttok: n = abs(n <= expression(5))
elseif minprec <= 4 and tok = ">=" then: call nexttok: n = abs(n >= expression(5))
elseif minprec <= 5 and tok = "+" then: call nexttok: n = n + expression(6)
elseif minprec <= 5 and tok = "-" then: call nexttok: n = n - expression(6)
elseif minprec <= 6 and tok = "*" then: call nexttok: n = n * expression(7)
elseif minprec <= 6 and (tok = "/" or tok = "\") then: call nexttok: n = n \ expression(7): if n < 0 then n = fix(n) else n = int(n)
elseif minprec <= 6 and tok = "mod" then: call nexttok: n = n mod expression(7): if n < 0 then n = fix(n) else n = int(n)
elseif minprec <= 8 and tok = "^" then: call nexttok: n = clng(n ^ expression(9))
else: exit do
end if
loop
expression& = n
'call logit("expression end")
end function
function getvarindex& ' return the index in var store, in 0..25
'call logit("getvarindex begin")
if toktype <> c_ident then print "("; idxtoline(curline); ") Not a variable:"; tok: errors = True: exit function
getvarindex& = asc(left$(tok, 1)) - asc("a")
'call logit("getvarindex end")
end function
sub expect(s as string)
'call logit("expect begin")
if accept(s) then exit sub
print "("; idxtoline(curline); ") expecting "; s; " but found "; tok; " =>"; pgm(curline): errors = True
'call logit("expect end")
end sub
function accept&(s as string)
'call logit("accept begin")
accept& = False: if tok = s then accept = True: call nexttok
'call logit("accept end")
end function
sub initlex(n as long) ' if not line 0, skip the line number
'call logit("initlex begin")
curline = n: textp = 1
call initlex2
if n <> 0 and toktype = c_number then call nexttok
'call logit("initlex end")
end sub
sub initlex2 ' entry point to continue where we left off
'call logit("initlex2 begin")
thelin = pgm(curline)
call nexttok
'call logit("initlex2 end")
end sub
sub nexttok
'call logit("nexttok begin")
tok = "": toktype = c_default: thech = ""
do while textp <= len(thelin)
thech = mid$(thelin, textp, 1)
select case toktype
case c_default
if 0 then '
elseif thech <= " " then: rem just skip space, cf, lf
elseif isalpha(thech) then: toktype = c_ident
elseif isdigit(thech) then: toktype = c_number
elseif instr(",#()*+-/:;<=>?@\^", thech) then: toktype = c_punct
elseif thech = c_dquote then: toktype = c_string
elseif thech = c_squote then: call skiptoeol: exit sub
else: print "("; idxtoline(curline); ","; textp; ") "; "What>"; tok; "< "; thelin: errors = True: exit sub
end if
case c_ident: if not isalpha(thech) then exit do
case c_number: if not isdigit(thech) then exit do
case c_string: if thech = c_dquote then textp = textp + 1: exit sub
case c_punct
if (tok = "<" and (thech = ">" or thech = "=")) or (tok = ">" and thech = "=") then
tok = tok + thech
textp = textp + 1
end if
exit sub
end select
if toktype <> c_default then tok = tok + thech
textp = textp + 1
loop
if toktype = c_number then num = val(tok)
if toktype = c_string then print "String not terminated": errors = True
if toktype = c_ident then
tok = lcase$(tok)
if tok = "rem" then call skiptoeol
end if
'call logit("nexttok end")
end sub
sub skiptoeol
'call logit("skiptoeol begin")
tok = "": toktype = c_default
textp = len(thelin) + 1
'call logit("skiptoeol end")
end sub
function isalpha&(c as string)
'call logit("isalpha begin")
isalpha& = (c >= "a" and c <= "z") or (c >= "A" and c <= "Z")
'call logit("isalpha end")
end function
function isdigit&(c as string)
'call logit("isdigit begin")
isdigit& = (c >= "0" and c <= "9")
'call logit("isdigit end")
end function
function idxtoline&(n as long) ' return the line number at line n
'call logit("idxtoline begin")
dim as string s
dim as long p
s = ltrim$(pgm(n))
p = instr(s, " ")
if p <= 0 then idxtoline = val(s) else idxtoline = val(left$(s, p - 1))
'call logit("idxtoline end")
end function
Code: Select all
1 ? "hello"
2 ? "bye"